When and what should I be logging?
This is a follow-up to Sergiy’s post Errors, traces, logs, metrics: when to reach for what.
Modern observability platforms, like Sentry, give developers a lot of choice. For a given problem, should you use traces, profiles, metrics, logs?
If you take away one thing from this post, I hope it’s this: when in doubt, start by adding a few targeted log lines.
Logs are straightforward to add to your application and they’re a fast way to start collecting real information about how your software is running in production. When writing a new feature, I try to add enough logs that it’s debuggable without a deployment.
It’s okay for logs to be temporary instrumentation. Add them when investigating a problem or validating a feature, remove them when they’re no longer useful.
Here are some best practices for using logs to make your application easier to debug and reason about.
Great things to consider logging
Important runtime decisions made by your application
Different users often see different flows. When you’re debugging unexpected behavior, you want to know all the different decisions that determined how a request was served.
Some examples:
- A user has a feature flag enabled that exposes an experimental version of a page.
- Mobile users are redirected to a different experience.
- Paid and free users receive different functionality.
When your application chooses between multiple code paths, consider logging both why the decision was made and what behavior resulted:
// Runtime decision: this user has a feature flag enabled
// that exposes an experimental version of the feed.
Sentry.logger.info("Check Feature Flags", {
feature_flag: "fiesta_mode",
feed_experience: "party mode",
"user.id": user_uuid
});These logs make it easier to understand why two users may have experienced the application differently and to reproduce bugs that only affect specific cohorts.
Whether a feature or algorithm is behaving as expected
Logs are useful when a feature performs multiple steps. By recording intermediate outcomes, you can understand where a process is breaking down and why.
Here’s a real-world example. My site, allaboard.dev, allows users to import a climbing log-book from an external service. Logging the outcome of the import process helps me verify that the source data is being parsed correctly and identify where and if it fails.
// Stage 1 outcome: the export was authenticated and parsed.
// Record how much work we're about to do — if a user reports
// a bad import, the first question is "how many entries did
// we even receive?"
Sentry.logger.info("Third-party import started", {
"import.source": "aurora",
"import.entries_received": body.ascents.length,
});
// Algorithm runs here, populating skipDetails.
// Final stage outcome: a flat, queryable breakdown of how the
// run resolved. Each skip reason is its own scalar field so you
// can chart, say, a spike in `import.skipped.unknown_grade`
// (a Font→V-scale conversion gap) on its own.
Sentry.logger.info("Third-party import finished", {
"import.source": "aurora",
"import.entries_received": body.ascents.length,
"import.imported": imported,
"import.climbs_created": climbsCreated,
"import.skipped": skipped,
"import.skipped.missing_name": skipDetails.missingName,
"import.skipped.unknown_grade": skipDetails.unknownGrade,
"import.skipped.invalid_angle": skipDetails.invalidAngle,
"import.skipped.already_imported": skipDetails.alreadyImported,
});Audit and access events (creates, updates, deletes, access, permissions)
Audit logs help answer questions like “Who changed this?”, “When did it happen?”, and “Was this action expected?”
These types of logs can be great for root causing support cases.
Perhaps a user writes in and asks “where the heck did my team’s weekly dashboard go?” Because your application logs mutating operations (creates, deletes, updates), you’re able to see that another member of their team accidentally deleted the dashboard a few days earlier. You restore the dashboard, let the user know exactly what happened, and have the peace of mind that your application isn’t randomly deleting things.
Logging access and permissions can also be a requirement for some standards, like HIPAA.
Note: Audit logs are only one part of meeting compliance requirements. See Sentry and Your Data to learn about the privacy and security controls Sentry provides. If you have complex compliance or privacy requirements, talk to us.
Context surrounding errors and failures
For exceptions, you’ll often be better off using Sentry’s Capture Error functionality rather than adding a log line. This gives you the benefit of Issue Grouping, triage workflows, Autofix, and other issue-focused features.
However, not every failure should immediately become an error in Sentry. For example, you may rely on a flaky upstream API and allow certain HTTP status codes to be retried N times. When you hit N, you want an error to be raised with Sentry. For attempts before N, a log line explaining why the retry loop is happening can be useful during debugging.
What context might you want to log?
- Retry count or attempt number.
- Status codes, error codes, or other non-sensitive details returned by upstream services.
- Non-sensitive request/response attributes that help explain the failure.
- Runtime state relevant to the failure, such as feature flags, configuration.
Now that we’ve given some recommendations on what to log, let’s look at how to structure your log messages.
How to write your log messages
Use structured log messages
Rather than plain-text logs like "DID I GET HERE", prefer structured logs that capture information as consistent key/value pairs.
Structured logs benefit both humans and machines. Consistent fields such as user_id, request_id, feature_flag, or action make debugging easier, and they can be used by logging platforms for search, visualizations, and alerting.
A good log message typically answers three questions:
- Who performed the action (for example, the authenticated user).
- What happened (a human-readable message and supporting metadata).
- When it happened (typically added automatically by the logging system).
Note: For including context about the currently authenticated user, Sentry provides the setUser method.
Add context as a request evolves
Logs should accumulate context as a request moves through your application. Emit that context alongside event-specific metadata in your log messages.
For example, logs emitted before authentication won’t contain user information. Later in the request lifecycle, after authentication, user-specific details should be included alongside context specific to the event being logged.
One particularly valuable piece of context is the Trace ID. This allows you to connect a log entry back to a distributed trace, making it easier to understand the sequence of events that led to the log message being emitted.
Good news: Sentry’s logs are trace-connected out of the box, so trace context is automatically included when available.
Choose the appropriate log level
Using appropriate log levels is an additional way to convey meaning in your log messages.
Use debug for detailed diagnostic information that is useful during development, or certain investigations. Debug logs are often disabled in production and enabled temporarily when troubleshooting a problem.
Note: Sentry’s
beforeSendLogmethod can be used to filter out debug level logs by looking at thelevelproperty.beforeSendLog(log) {return log.level !== 'debug'}
Use info for normal application events. Runtime decisions being made, algorithm behaviour, audit logs, these are all candidates for info-level logs.
Use warn for recoverable events that may require attention. A good example might be when an API call to an external service reaches a threshold for latency.
Use error for unexpected failures that are handled gracefully. If a failure results in an exception, prefer Sentry’s Capture Error over a duplicate error log.
What not to log
Every function call, or line of code, in your application
Instrumenting every function call is usually better handled by profiling and tracing. Did you know Sentry has profiling and tracing! (Rahul made me say this).
PII and other sensitive information
Whenever logging a piece of information, ask yourself: “What would the impact be if the wrong person gained access to this information?”
Some guidelines:
- Prefer opaque user IDs over email addresses or full names when possible.
- Passwords, access tokens, API keys, and similar secrets should never appear in logs. Store them only in systems designed for secret storage.
- Other types of personal information may also be regulated depending on jurisdiction, including age, gender, and postal code.
- Be aware of local and international laws and standards, such as PCI, GDPR, CCPA, and HIPAA, which provide guidance on what should and shouldn’t be logged, retained, or exposed.
Note: Sentry has Server-Side Data Scrubbing. If you use structured logging, this can help protect against some common PII and password pitfalls. It is configurable, so you can include additional fields applicable to your application. You can also use
beforeSendLogto perform client-side filtering of sensitive information.
TL;DR: Be intentional about what you log. Log the minimum information necessary to debug and operate your application, and understand the requirements that apply to your industry, your country, and your customers’ countries.
Large blobs of data (without a specific purpose)
There are legitimate reasons to log large, unstructured blobs of data:
- Seeing a full LLM prompt and response may help you understand whether your product is behaving as expected.
- Logging a webhook body may help you debug issues with an external integration.
However, logging this type of data has both costs and risks:
- Users may include personal or sensitive information in an LLM prompt.
- Entire HTTP requests and responses may contain access tokens, secrets, or other sensitive data.
- Many logging products, Sentry included, charge based on volume. Ask yourself whether you’ll actually use the information you’re storing.
For AI assistants, you may also be better off using a purpose-built solution, such as Sentry’s Conversations feature, rather than logging entire conversations.
As with the PII discussion, the important takeaway is to be intentional about what you log. Consider the cost and risk associated with the data, and when possible, prefer logging the specific fields you need rather than an entire request, response, or document.
Applying these suggestions to allaboard.dev
Here are some examples of the logs I’ve added to my aforementioned side project bcoe/allaboard.dev, following the advice from this article:
- Runtime decisions, such as feature flags.
- Steps and outcome from an algorithm.
- Audit and access events.
- Context for non-critical errors.
I’m now much more confident that, when something goes wrong, I have enough context to debug the problem without deploying a new version of the website.
What’s next?
I hope this article has given you some inspiration about the what and how of logging, to help speed up debugging next time something tricky happens in production.
If you’d like some help getting started, try the sentry-instrument-logging skill in getsentry/sentry-for-ai. It analyzes your codebase and suggests an initial set of high-value structured log messages to add.
For Claude Code:
/plugins install sentry@claude-plugins-official
/reload-skills
/sentry-instrument-loggingFor Cursor:
Add getsentry/plugin-cursor from Cursor Settings > Plugins.
Note: getsentry/sentry-for-ai is being iterated on rapidly and the paths to some skills may change. Keep an eye on the repository for up-to-date install instructions.
Have any feedback, or just want to talk more about logging? Join us on Discord.