Safeguard
Security

Spring Boot Logging Best Practices That Keep Secrets Out of Your Logs

Spring Boot logging best practices focused on security: structured logs, keeping secrets and PII out, safe log levels, and avoiding the mistakes that turned Log4Shell into a catastrophe.

Marcus Chen
DevSecOps Engineer
7 min read

The most important Spring Boot logging best practice is to treat your logs as a security surface: they must never contain secrets, credentials, tokens, or unredacted personal data, and the logging framework itself must be patched and configured so it cannot be turned into a remote-code-execution vector. Logs are where the two failure modes of logging security meet — leaking sensitive data outward, and letting attacker-controlled input flow inward. This guide covers both, with concrete configuration for Spring Boot's default Logback stack.

Start from what Spring Boot gives you

Spring Boot ships with SLF4J as the logging facade and Logback as the default implementation. You write against the SLF4J API (private static final Logger log = LoggerFactory.getLogger(MyService.class);) and configure the backend through application.properties, application.yml, or a logback-spring.xml file. Because the framework wires this up for you, the fastest way to get logging wrong is to fight the defaults — pulling in a second logging framework, or reintroducing a bridge you do not need.

Use logback-spring.xml rather than plain logback.xml so you get Spring's profile support and property substitution. That lets you log verbosely in dev and conservatively in prod from one file.

Never log secrets, credentials, or full PII

This is the rule that causes the most real-world incidents. Logs get shipped to aggregation systems, replicated to backups, and read by people who were never meant to see production secrets. A password, API key, session token, or full credit-card number written to a log at INFO level has effectively been exfiltrated to every system that touches that log.

Concrete practices:

  • Never log entire request or response objects. A blanket log.info("Request: {}", request) will happily serialize an Authorization header or a password field. Log specific, safe fields.
  • Redact at the boundary. Wrap sensitive types so their toString() returns a mask like ****, and never override it to reveal the value "for debugging."
  • Filter known-sensitive keys. A Logback custom converter or a rewrite policy can scrub fields named password, token, secret, authorization, ssn, and similar before they hit disk.
  • Be careful with exception logging. A stack trace for a failed authentication can include the submitted credentials if they were passed into the failing method. Log the failure and a correlation ID, not the inputs.

Do not rely on developers remembering to be careful. Add a redaction layer in the logging pipeline so a stray log.debug("user={}", user) cannot leak the whole object even when someone forgets.

Use structured logging with correlation IDs

Plain-text logs are fine for a laptop and useless at scale. Emit JSON in production so your aggregation system can index fields instead of grepping strings. With Logback you can add the logstash-logback-encoder and switch the appender's encoder to JSON, or use Spring Boot 3's built-in structured logging support via logging.structured.format.console=ecs (or logstash/gelf).

Attach a correlation ID to every request so you can trace one transaction across services without dumping payloads. Spring's MDC (mapped diagnostic context) is the standard mechanism: set a request ID in a filter, include %X{requestId} in your pattern, and clear it when the request ends. Now you can answer "what happened to this user's request" without logging the user's data.

Get log levels right for security

Log level is a security control, not just a verbosity dial.

  • DEBUG and TRACE frequently log payloads, SQL parameters, and internal state. They belong in development, never enabled globally in production. If you need them in prod to chase a bug, scope them to one package for a short window and turn them back off.
  • INFO should record security-relevant events — authentication successes and failures, authorization denials, configuration changes — with enough context to investigate but no sensitive values.
  • WARN and ERROR should capture anomalies without leaking the offending input verbatim.

A common mistake is shipping to production with logging.level.org.hibernate.SQL=DEBUG or a framework left at DEBUG, which floods logs with query parameters that may include personal data. Audit your levels before release.

Log the security events that matter

Detection depends on having logged the right thing. At minimum, record authentication attempts (success and failure with source IP and username, never the password), authorization failures, session lifecycle events, input-validation rejections on sensitive endpoints, and administrative actions. These are the events an incident responder will look for first. Spring Security can publish authentication and authorization events you can subscribe to and log cleanly, which is far safer than trying to log them from inside your controllers.

Learn the Log4Shell lesson even on Logback

In December 2021, CVE-2021-44228 — "Log4Shell" — showed that a logging library could be a remote-code-execution vector. Apache Log4j 2 evaluated JNDI lookup expressions embedded in logged strings, so logging an attacker-controlled value like a crafted User-Agent header could make the server fetch and execute remote code. It was one of the most severe vulnerabilities in years precisely because logging user input felt harmless.

Spring Boot's default is Logback, not Log4j 2, and Logback was not affected by that specific JNDI flaw. But the lesson generalizes to every logging setup, including yours:

  • Keep the logging framework patched. Whether you run Logback or opt into Log4j 2, a logging library is a dependency with its own CVE history. Track it. An SCA tool such as Safeguard can flag a vulnerable logback-classic or log4j-core version transitively, before it reaches production.
  • Treat logged user input as data, never as an instruction. Do not build logging configurations that evaluate expressions found in log messages. Log user input as a plain string argument (log.info("login attempt for {}", username)), not by concatenating it into a format that some layout might interpret.
  • Know your transitive dependencies. Many teams in 2021 did not realize Log4j 2 was pulled in transitively by a library they did not choose directly. Generate an SBOM so you actually know what logging code runs in your app.

For a broader treatment of hardening Spring apps, see our Spring Boot security best practices.

Handle rotation, retention, and access

Finally, logs need lifecycle controls. Configure Logback's SizeAndTimeBasedRollingPolicy so files rotate and old ones are deleted on a defined schedule — indefinite retention is both a storage problem and a data-exposure liability. Restrict who can read production logs; a log store with production data in it deserves the same access controls as the database. If a compliance regime applies, align retention with its requirements rather than guessing.

FAQ

Was Spring Boot affected by Log4Shell?

Spring Boot's default logging uses Logback, which was not affected by the specific JNDI lookup flaw in CVE-2021-44228. However, applications that had opted into Log4j 2, or pulled it in transitively through another dependency, were exposed. Check your actual dependency tree rather than assuming.

How do I stop secrets from ending up in Spring Boot logs?

Never log whole request/response objects, mask sensitive fields in their toString(), add a redaction filter in the logging pipeline that scrubs keys like password and authorization, and avoid logging the inputs to failed authentication calls.

Should I use JSON logging in Spring Boot?

In production, yes. Structured JSON logs are indexable by aggregation systems. Spring Boot 3 has built-in structured logging formats, or you can use the logstash-logback-encoder. Add a correlation ID via MDC so you can trace requests without logging payloads.

What log level should production use?

Run production at INFO for application logs, with WARN/ERROR for anomalies. Keep DEBUG and TRACE out of global production configuration because they tend to log payloads and query parameters. Enable debug narrowly and briefly if you must chase a specific issue.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.