RizTech Academy logo
RizTech Academy
ObservabilityLesson 2 of 425 min

Logging that is actually useful

Logs are the pillar you will use most, and the difference between good logs and bad logs is the difference between diagnosing a production problem in two minutes and staring at useless noise for an hour. Most applications log badly - too much, too little, or in a form you cannot search. This lesson is logging that is actually useful: what to log, what not to, and the practices (levels, structure, context) that make logs a tool rather than a wall of text.

What makes a log useful

A log line exists to answer a future question - "what happened here, and why?" - often at 3am during an incident. So a good log line is one that, months later, a stranger (or you, tired) can read and understand without the code in front of them. That means:

  • It records something worth knowing - a meaningful event, a decision, an error - not trivia.
  • It has enough context to be actionable - which user, which order, what value, what error.
  • It can be found - searchable and filterable, because in production you have millions of lines and need the few that matter.

Bad logs fail these: they are either noise (logging every trivial step, drowning the signal), or too sparse ("an error occurred" - which? where? why?), or unsearchable (free-form text you cannot filter). Good logging is a skill, and it is worth getting right because you will rely on it exactly when things are going wrong.

Log levels: say how important each line is

Every log line should have a level saying how significant it is, so you can filter to what matters:

  • ERROR - something failed that needs attention: an exception, a failed payment, a lost database connection. These are what you search for first in an incident.
  • WARN - something unexpected but handled: a retry, a deprecated path, approaching a limit. Worth noting, not yet broken.
  • INFO - normal significant events: "server started", "user logged in", "order placed". The high-level story of what the system is doing.
  • DEBUG - detailed diagnostic information, useful when investigating, too verbose for normal running.

Levels matter because they let you turn the volume up or down. In normal production you might log INFO and above (a manageable stream); when debugging a problem, you turn on DEBUG for the detail. And you can alert on ERROR specifically. Using levels correctly - not logging everything as INFO, not logging routine events as ERROR (which cries wolf, like a flaky test) - is the first mark of good logging.

Structured logging: logs a machine can read

The single biggest upgrade to your logs is making them structured - logging as key-value data (usually JSON) rather than free-form sentences. Compare:

# Unstructured - hard to search or filter
Payment failed for user 42 amount 500 reason insufficient funds

# Structured - a machine can filter and aggregate this
{"level":"error","event":"payment_failed","userId":42,"amount":500,"reason":"insufficient_funds","ts":"2026-01-01T10:00:00Z"}

Structured logs are transformative because your log system can then query them: "show me all payment_failed events for userId=42", "count errors by reason", "find every log for this request". With free-form text you are reduced to grepping and guessing; with structured logs you filter and aggregate precisely - which, at production scale (millions of lines), is the difference between finding the problem and not. Log in JSON (most languages have a structured-logging library), and your future self will thank you.

Include context - especially a correlation ID

A log line is only useful if it says enough. Always include the context needed to act:

  • Who/what - the user id, order id, request id, the values involved (userId, orderId, amount).
  • A correlation / request ID. This is the most valuable piece: a unique id attached to every log line produced while handling one request (and passed between services). With it, you can filter to all the logs for a single user's single request, across the whole system - turning scattered lines into one coherent story. Without it, you cannot tell which log lines belong together under load. A correlation id is the log equivalent of a trace, and adding one is one of the highest-value logging practices.
  • The error detail - for an error, the actual message and stack trace, not just "failed".

The test: could someone act on this log line alone? "Error in handler" - no. {"event":"payment_failed", "userId":42,"orderId":88,"reason":"insufficient_funds","requestId":"abc-123"} - yes: you know exactly what, who, and can find everything else about that request.

What NOT to log

Just as important as what to log:

  • Never log secrets or sensitive data. Passwords, API keys, tokens, full card numbers, personal data - a log is stored, shipped and widely readable, so a secret in a log is a leak (the CI/CD secrets lesson). Redact or omit them. This is a common, serious mistake.
  • Do not log so much that you drown the signal. Logging every trivial step (or inside a tight loop) produces noise that hides the important lines and costs money to store. Log meaningful events, not everything.
  • Do not log sensitive data "temporarily" to debug and forget to remove it - that is how secrets and PII end up in logs permanently.

The balance is: log what you would want to see during an incident, with enough context to act, at the right level, in a structured form, without secrets. Get that right and your logs become the fast path from "something broke" to "here's exactly what and why" - which is the whole point of the pillar.

Check your work

Useful log = answers a future "what happened and why?" readable by a tired stranger: records something worth knowing, has enough context to act, and is searchable. Bad logs are noise, too sparse, or unsearchable.

Levels: ERROR (failed, needs attention - search first, alert on), WARN (unexpected but handled), INFO (normal significant events - the story), DEBUG (verbose diagnostics). Levels let you turn volume up/down and alert precisely; don't log everything as INFO or routine events as ERROR (crying wolf).

Structured logging (biggest upgrade): log key-value JSON, not free-form sentences, so the log system can query and aggregate ("all payment_failed for userId=42", "errors by reason"). At millions of lines, this is the difference between finding a problem and not.

Context - especially a correlation/request ID: include who/what (ids, values), the error detail, and a request id tying every line for one request together across services (a log-level trace). Test: could someone act on this line alone?

Don't log: secrets/PII (a log is stored and widely read - a leak), or so much you drown the signal, or sensitive data "temporarily". Log incident-worthy events, with context, right level, structured, no secrets.

Practice

  1. Take a vague log ("error occurred") and rewrite it as a useful structured log line with context.
  2. Assign the right level (ERROR/WARN/INFO/DEBUG) to five example events and justify each.
  3. Convert an unstructured log line to structured JSON and explain what querying it now enables.
  4. Explain what a correlation/request ID is and how it helps diagnose one user's request across services.
  5. Give three things that must never appear in logs and why, and one way people leak them accidentally.
  6. Explain the cost of logging too much, and how to decide what is worth logging.

Official documentation

Next: monitoring and alerting basics.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship