Observability

Log Levels: When to Use Debug, Info, Warn, and Error

Learn how log levels work, when to use debug, info, warn, and error, and how to avoid noisy logs, missed alerts, and confusing production diagnostics.

Log Levels: When to Use Debug, Info, Warn, and Error

Logs are only useful when they help someone understand what happened.

That sounds obvious until every production service starts emitting thousands of lines per minute. Some entries are routine. Some are clues. Some are warnings. Some are real failures. If every message looks equally important, logs become noise.

Log levels solve that problem by giving each entry a severity.

debug, info, warn, and error are not just labels. They are signals to humans, alerting systems, dashboards, and log pipelines about how much attention an event deserves.

Used well, log levels make troubleshooting faster. Used poorly, they create alert fatigue, hide real incidents, and turn observability into a very expensive pile of text.

What Are Log Levels?

Log levels are severity categories assigned to log entries.

They answer a simple question:

How important is this event?

A typical application uses levels like:

  • debug
  • info
  • warn
  • error

Some systems also include trace, fatal, critical, or notice, but the four-level model is enough for most application logging.

The level affects how logs are filtered, stored, searched, displayed, and alerted on. In development, you might enable debug. In production, you might keep only info and above, or sample debug logs heavily.

OpenTelemetry’s logs data model maps severity levels into numeric ranges, with DEBUG, INFO, WARN, ERROR, and FATAL represented as increasing severity. The names differ slightly across platforms, but the idea is the same: higher severity means more attention.

The Quick Rule

Here is the simplest way to choose a log level:

LevelUse When
debugA developer may need this while diagnosing behavior
infoA normal, meaningful business or system event happened
warnSomething unexpected happened, but the system can continue
errorSomething failed and needs investigation or remediation

That table gets you most of the way there.

The tricky part is deciding what “meaningful,” “unexpected,” and “failed” mean in your system.

Debug: For Diagnostic Detail

Use debug for detailed information that helps developers understand internal behavior.

Good debug logs answer questions like:

  • Which branch did the code take?
  • What retry attempt is this?
  • What cache key was checked?
  • What feature flag value was active?
  • What intermediate result was calculated?
  • Why did the code choose this provider?

Example:

{
  "level": "debug",
  "service": "billing-api",
  "requestId": "req_123",
  "customerId": "cus_42",
  "cacheKey": "subscription:cus_42",
  "message": "Subscription cache miss"
}

This is useful during investigation, but it should not wake anyone up. A cache miss might explain performance, but it is not automatically a problem.

When Not to Use Debug

Do not use debug for events you need in normal production operations.

If a log entry is required to understand customer activity, audit important actions, or explain an incident after the fact, it probably should not be hidden behind debug-only logging.

Also avoid dumping huge objects at debug level just because “debug logs are off in production.” They may be turned on during an incident, and suddenly the system emits sensitive data, massive payloads, or expensive serialization work.

Debug should be detailed, not careless.

Info: For Normal Meaningful Events

Use info when something normal happened and it is worth recording.

Good info logs describe important lifecycle events:

  • Service started
  • Request completed
  • User signed in
  • Payment succeeded
  • Job finished
  • Email was queued
  • Feature flag changed behavior
  • Background worker processed a batch

Example:

{
  "level": "info",
  "service": "orders-api",
  "requestId": "req_456",
  "orderId": "ord_789",
  "statusCode": 201,
  "durationMs": 84,
  "message": "Order created"
}

An info log should be boring in the best way. It confirms that normal work happened.

This is why info is often the default production level. It gives operators a timeline of important activity without drowning them in every internal detail.

Info Is Not a Dumping Ground

The most common logging mistake is overusing info.

If every function logs “started” and “finished” at info, production logs become bloated and expensive. If every HTTP request logs five info entries, the signal disappears.

Ask:

Will this entry help someone operate or investigate the system later?

If not, downgrade it to debug or remove it.

For production systems, structured log fields matter as much as level choice. See JSON logging best practices for the fields that make info logs searchable instead of decorative.

Warn: For Recoverable Problems

Use warn when something unexpected happened, but the application can continue.

A warning means:

This is not an incident yet,
but someone may need to know.

Good warn examples include:

  • A retryable dependency timeout
  • A rate limit response that was handled
  • A fallback path was used
  • A queue lag threshold was crossed
  • A deprecated API field was received
  • A request was rejected for expected validation reasons at unusual volume
  • A disk or memory threshold is approaching a dangerous level

Example:

{
  "level": "warn",
  "service": "analytics-api",
  "requestId": "req_789",
  "provider": "instagram",
  "statusCode": 429,
  "retryAfterSeconds": 60,
  "message": "Provider rate limit reached"
}

The system handled the rate limit, so this is not necessarily an error. But it may explain slower processing or future failure if it becomes frequent.

Warning Does Not Mean Broken

A warning should not mean the request failed.

If the user action failed, use error. If the system recovered and the user was not affected, warn is often right.

For example:

First payment provider timed out.
Fallback provider succeeded.

That is a warning. Something went wrong internally, but the business operation completed.

Now compare:

All payment providers failed.
Checkout failed.

That is an error.

OpenTelemetry’s exception log conventions use a similar distinction: handled exceptions may be warnings, while lower-importance diagnostic exceptions may be debug.

Error: For Failed Operations

Use error when something failed and needs investigation, remediation, or at least visibility.

Good error logs include:

  • A request failed with a server-side exception
  • A job exhausted retries
  • A payment could not be processed
  • A database write failed
  • A required dependency is unavailable
  • A message could not be parsed and was moved to a dead-letter queue
  • A security-sensitive operation failed unexpectedly

Example:

{
  "level": "error",
  "service": "payments-api",
  "requestId": "req_abc",
  "orderId": "ord_123",
  "errorCode": "payment_provider_unavailable",
  "message": "Payment failed after all retries"
}

An error should represent a failed operation, not merely an unusual condition.

That distinction keeps alerts useful. If every harmless oddity is logged as an error, the team learns to ignore errors. That is how real incidents slip through.

Error Does Not Always Mean Page Someone

Not every error should trigger an alert.

A single failed login attempt might be an error from the user’s perspective, but it is not an operational incident. A malformed request from a bot might deserve a warn or info, not an error. A single failed email send may be tolerable if retries succeed.

Alerting should usually depend on rate, impact, and service-level objectives, not one log level alone.

One error:
  investigate if needed

Error rate spike:
  alert

Critical path failing:
  alert immediately

Logs are evidence. Alerts are decisions.

Debug vs Info

Use debug when the information is mainly useful to a developer diagnosing code behavior.

Use info when the event is part of the normal operational story of the system.

Example:

debug: "Selected cache shard 3"
info:  "Invoice generated"

The cache shard may explain an issue later, but it is internal detail. The invoice being generated is a meaningful business event.

If you are unsure, ask whether the entry should normally be visible in production. If yes, lean info. If no, lean debug.

Info vs Warn

Use info when things are working normally.

Use warn when something abnormal happened but the system recovered or can continue.

Example:

info: "Webhook processed"
warn: "Webhook signature missing; request rejected"

The first event is routine. The second deserves attention, even if it does not mean the application is broken.

Warnings are especially useful for early signals. A rising warning count can show a dependency becoming flaky before full errors appear.

Warn vs Error

Use warn when the system can continue and the user or business operation may not have failed.

Use error when an operation failed.

Example:

warn:  "Cache unavailable; served from database"
error: "Database unavailable; request failed"

In the warning case, the fallback worked. In the error case, the operation failed.

This fallback distinction is one of the easiest ways to choose correctly.

What About Fatal or Critical?

Some systems include fatal or critical.

Use these sparingly for events that require immediate attention or indicate the process cannot continue safely:

  • Application cannot start
  • Required configuration is missing
  • Database migration failed during startup
  • Process is about to exit
  • Data corruption was detected
  • A critical dependency is unavailable for the whole service

Many applications do not need a separate fatal level in everyday code. If your platform supports it, reserve it for events where the process, service, or critical workflow is effectively dead.

What About Trace?

trace is usually more detailed than debug.

Use it for extremely fine-grained diagnostics:

  • Function entry and exit
  • SQL parameter details
  • Protocol messages
  • Low-level library behavior
  • Very high-volume diagnostic events

Most application teams can start with debug, info, warn, and error. Add trace only when you have a clear need and a plan for sampling or disabling it in production.

Do not confuse trace-level logs with distributed traces. A log level named trace is not the same as a trace ID or span. For that distinction, see correlation ID vs trace ID and distributed tracing.

Log Levels and HTTP Status Codes

HTTP status codes do not map perfectly to log levels.

Useful defaults:

HTTP ResultTypical LevelNotes
2xx successinfo or no logLog important operations, not every tiny request
3xx redirectdebug or infoUsually routine
4xx client errorinfo or warnOften expected, unless volume is unusual
429 rate limitwarnEspecially if it affects service behavior
5xx server errorerrorServer-side failure

A 404 for /favicon.ico is not an error. A 404 for a critical internal dependency might be. Context matters.

Log Levels and Exceptions

Not every exception should be logged as error.

Consider these cases:

debug:
  Parser tried one format, failed, then succeeded with another.

info:
  User submitted invalid input and received a validation response.

warn:
  Dependency timed out once, retry succeeded.

error:
  Dependency failed after all retries and the request failed.

The exception type alone is not enough. The outcome matters.

Also avoid logging the same exception at every layer. If a low-level function logs an error and rethrows, then the API handler logs the same error again, dashboards may show one failure as five errors.

Log exceptions at the boundary where you have enough context to explain the impact.

Log Levels and Alerts

Log levels are often used in alert rules, but they should not be the only input.

Bad alert:

Alert on every error log.

Better alert:

Alert when checkout error rate exceeds 2% for 5 minutes.

Better still:

Alert when checkout error rate exceeds its SLO budget
and successful orders drop below expected volume.

An error log is a signal. A useful alert combines severity, frequency, business impact, and context.

This is where logs, metrics, and traces work together. Logs explain what happened. Metrics show scale and trend. Traces show where time and failure occurred across services.

Log Levels in Development vs Production

Development and production usually need different verbosity.

In development:

  • debug is often useful
  • Logs can be more chatty
  • Human readability matters
  • Local troubleshooting is the priority

In production:

  • info and above are common defaults
  • debug should be sampled or temporarily enabled
  • Structured fields matter
  • Sensitive data must be redacted
  • Cost and alert noise matter

For production services, structured logging usually pays off quickly. Structured logging vs plain text logs covers that trade-off in more detail.

A Practical Decision Tree

When choosing a log level, ask:

Did an operation fail?
  yes -> error
  no  -> continue

Did something unexpected happen that may need attention?
  yes -> warn
  no  -> continue

Is this a normal meaningful event?
  yes -> info
  no  -> continue

Is this useful for diagnosis?
  yes -> debug
  no  -> do not log it

That decision tree prevents most level mistakes.

It also leaves room for a quiet but important truth: not everything needs to be logged.

Common Mistakes

Logging everything at info. This makes production logs expensive and hard to search.

Using error for expected user behavior. Invalid passwords, normal validation failures, and routine 404s are often not operational errors.

Using warn as a softer error. Warning should mean recoverable or concerning, not “I did not want to decide.”

Hiding important business events at debug. If operations or support need the event, it should usually be info.

Logging the same error repeatedly. Log once with enough context instead of at every stack layer.

Forgetting structured fields. A perfect level with no requestId, traceId, service, or error code is still hard to investigate.

Alerting directly on every error. Alert on impact and rate, not raw individual messages.

Good Log Messages

A good log message is short, specific, and supported by structured fields.

Weak:

Something went wrong

Better:

Payment failed after all retries

Best as structured JSON:

{
  "level": "error",
  "service": "payments-api",
  "requestId": "req_123",
  "orderId": "ord_456",
  "provider": "stripe",
  "attempts": 3,
  "errorCode": "provider_timeout",
  "message": "Payment failed after all retries"
}

The message tells the story. The fields make it searchable.

If you are improving production observability, try these topics:

For external references, start with the OpenTelemetry logs data model and exception log semantic conventions.

Frequently Asked Questions

What are the main log levels? The most common application log levels are debug, info, warn, and error. Some systems also use trace, fatal, or critical for more granular severity.

When should I use debug logs? Use debug for diagnostic details that help developers understand internal behavior, such as branch decisions, cache checks, retries, or intermediate values. Debug logs are usually disabled or sampled in production.

When should I use info logs? Use info for normal meaningful events, such as service startup, job completion, user actions, successful payments, or important lifecycle changes.

When should I use warn logs? Use warn when something unexpected happened but the system recovered or can continue, such as a retryable timeout, fallback path, rate limit, or approaching resource threshold.

When should I use error logs? Use error when an operation failed and needs visibility, investigation, or remediation. Examples include exhausted retries, failed database writes, unhandled exceptions, and critical workflow failures.

Conclusion

Log levels help turn raw application events into useful operational signals. debug explains internal behavior, info records normal meaningful events, warn highlights recoverable problems, and error marks failed operations.

The best level choice depends on impact, not vibes. Did the operation fail? Use error. Did something unexpected happen but recovery succeeded? Use warn. Is it normal and worth remembering? Use info. Is it only useful while diagnosing? Use debug.

Clear log levels make production systems easier to operate, alerts easier to trust, and incidents easier to investigate.

Written by the Workshelve team, who write practical explainers on data integrity, networking, and developer tooling.