Blog
A Log Line Nobody Can Search Is a Diary
- logging
- debugging
- laravel
- typescript
Something breaks in production. You open the logs. You find this:
Something went wrong processing order
Which order? Which user? Which step? Nobody knows. That line is not a log. It's a diary entry.
Logs are for searching, not reading
Most of the time you don't read logs top to bottom. You search them. You filter by an order ID, a user, a job name, a request.
If the useful details are buried in a sentence, or missing, search can't help you. So write log lines for the person grepping at 2am, not for the person writing the code.
Log an event, attach context
Keep the message short and stable. Put the details in context.
In Laravel, that context is the second argument:
Log::warning('order.payment_failed', [
'order_id' => $order->id,
'user_id' => $order->user_id,
'gateway' => $gateway->name(),
'attempt' => $attempt,
]);The message never changes, so you can search for every payment failure at once. The context tells you which one you're looking at.
Set context once, not on every line
If every log line in a request needs the same request ID, don't pass it by hand everywhere. Laravel lets you share context for the rest of the request:
Log::withContext([
'request_id' => $request->header('X-Request-Id') ?? (string) Str::uuid(),
]);Put that in a middleware and every log line after it carries the ID. One search and you have the whole story of one request.
Same idea in Node
Here is the same idea as a small helper in TypeScript that writes JSON lines:
type Context = Record<string, unknown>;
export function logEvent(event: string, context: Context = {}) {
console.log(JSON.stringify({
event,
...context,
at: new Date().toISOString(),
}));
}
logEvent('webhook.rejected', { provider: 'stripe', reason: 'bad_signature' });One JSON object per line is easy for basically any log tool to parse. A pretty sentence is not.
Things that don't belong in logs
Context is good. Too much context is a security problem.
- Passwords, tokens, and API keys. Ever.
- Full card numbers or other payment data.
- Whole request bodies "just in case".
Log the ID of the thing, not the thing.
A quick gut check
Before you ship a log line, ask:
- Could I search for this event by name?
- Does it tell me which record, user, or request it belongs to?
- Would I be fine with this line showing up in a screenshot?
If the answer to any of those is no, fix the line now. You won't have time to fix it during the outage.