skip to content

Application Logging

How a Spring application logs: the facade and backend it ships with, configuring levels and patterns, structured output, and correlating log lines with a trace. Interviewers ask because logs are the first thing anyone reads during an incident.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

20

What is the default logging setup in a Spring Boot application, and what role does SLF4J play?

level: juniorimportance: must knowfreq 70%

answer

  1. SLF4J = facade, Logback = engine
  2. starter-logging transitively included
  3. bridges: jul/log4j/jcl -> slf4j
  4. LoggerFactory.getLogger, org.slf4j import
  5. zero-config console + logging.level.*

basics

~10 s

Spring Boot uses Logback as the default logging backend, pulled in via spring-boot-starter-logging. Your code logs through SLF4J, a facade/API, and SLF4J routes those calls to Logback at runtime.

solid answer

~30 s

Every Spring Boot starter transitively includes spring-boot-starter-logging, which brings in Logback plus SLF4J and bridges (jul-to-slf4j, log4j-to-slf4j). SLF4J (Simple Logging Facade for Java) is an abstraction: you code against org.slf4j.Logger via LoggerFactory.getLogger(...), and at runtime SLF4J binds to Logback as the concrete implementation. This decoupling lets you swap the backend (e.g., to Log4j2) without touching application code. Spring Boot also auto-configures a sensible default pattern, colored console output, and log levels you can tune with logging.level.* properties in application.yml. You get working console logging with zero configuration files.

code

java · 16 lines
java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class GreetingService {

    // Facade type from org.slf4j — never the concrete Logback/Log4j2 class
    private static final Logger log = LoggerFactory.getLogger(GreetingService.class);

    public String greet(String name) {
        // Parameterized logging: no string concatenation unless DEBUG is enabled
        log.debug("Building greeting for {}", name);
        return "Hello, " + name;
    }
}

go deeper

for a junior

Know the two words: Logback is default, SLF4J is the facade you code against.

for a middle

Explain the transitive starter-logging dependency and the bridge jars that unify other APIs.

for a senior

Discuss why the facade enables backend swapping and the single-binding constraint.

for a principal

Frame logging abstraction as a dependency-inversion boundary; reason about classpath binding hygiene across a large module graph.

## The pieces **SLF4J (Simple Logging Facade for Java)** is *not* a logging implementation — it is an API/facade. Your application code depends only on `org.slf4j.Logger` and `org.slf4j.LoggerFactory`. Calling `LoggerFactory.getLogger(MyClass.class)` returns an SLF4J `Logger`, and calls like `log.info("...")` are forwarded to whatever backend is on the classpath. **Logback** is the concrete logging engine that actually formats and writes log events (to console, files, etc.). It was written by the same author as SLF4J and implements the SLF4J API *natively* — no adapter is needed, which is why it is the Spring Boot default. ## How Spring Boot wires it `spring-boot-starter` (and every other starter) transitively depends on **`spring-boot-starter-logging`**. That starter brings: - `logback-classic` (the Logback backend, which pulls in `logback-core` and `slf4j-api`), - **bridge libraries** that redirect other logging APIs into SLF4J so the whole app funnels through one backend: `jul-to-slf4j` (java.util.logging), `log4j-to-slf4j` (Log4j 2 API calls), and `jcl-over-slf4j` (Apache Commons Logging — Spring Framework itself uses `spring-jcl`). So even libraries that don't use SLF4J directly end up routed to Logback. ## Zero-config behavior With no config files at all you still get: - Console output with a default pattern (timestamp, level, PID, thread, logger, message), - Colored output on capable terminals, - Default level INFO for application code. You tune levels via properties without any XML: `logging.level.root=WARN`, `logging.level.com.myapp=DEBUG`. File output turns on by setting `logging.file.name` or `logging.file.path`. ## Getting a logger (idiomatic) ```java private static final Logger log = LoggerFactory.getLogger(MyService.class); ``` Use parameterized logging (`log.info("user {} logged in", id)`) so the message string is only built if that level is enabled. ## Gotchas - Don't import `ch.qos.logback.classic.Logger` or a Log4j `Logger` in app code — always import `org.slf4j.Logger`. Importing the concrete class defeats the facade and breaks backend swapping. - Only one SLF4J binding may be on the classpath; multiple bindings cause the classic 'multiple SLF4J providers' warning and non-deterministic backend selection. Spring Boot's starters keep this clean for you.

  • Why is it better to write log.debug("user {} logged in", id) than log.debug("user " + id + " logged in")?
    The parameterized form defers building the final string until SLF4J confirms DEBUG is enabled. With string concatenation the message is always assembled, wasting CPU even when the level is disabled.
  • What does spring-boot-starter-logging actually pull in besides Logback?
    slf4j-api, logback-classic/logback-core, and the bridge jars jul-to-slf4j, log4j-to-slf4j, and jcl-over-slf4j so other logging APIs are routed into SLF4J/Logback.

saying these in an interview costs you the question

  • Thinking SLF4J is itself a logging implementation rather than a facade
  • Believing you must add a config file to get any logging
  • Importing the concrete Logback or Log4j Logger class in application code
  • Claiming Log4j2 is the Spring Boot default

context

open as a page

What are the Spring Boot log levels, how are they ordered, and how do you set the threshold for a specific logger?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Levels from least to most severe: TRACE, DEBUG, INFO, WARN, ERROR. Setting a logger to a level shows that level and everything more severe. Configure with logging.level.<logger>, e.g. logging.level.org.springframework.web=DEBUG. Default root level is INFO.

open as a page

How do you turn on JSON structured logging in Spring Boot 3.4, and why would you want it?

level: juniorimportance: must knowfreq 55%

basics

~10 s

Set logging.structured.format.console=ecs (or logstash/gelf) in application.properties. Boot then prints each log line as one JSON object instead of plain text, so log aggregators like Elasticsearch or Loki can parse fields automatically.

open as a page

Why use logback-spring.xml instead of logback.xml, and what do <springProfile> and <springProperty> do?

level: middleimportance: must knowfreq 65%

basics

~10 s

logback-spring.xml is loaded by Spring Boot (not raw Logback), so it supports Spring's extensions: <springProfile> includes config only for certain active profiles, and <springProperty> pulls values from Spring's Environment (application.yml) into the Logback config.

open as a page

How does Spring Boot get traceId and spanId into log lines by default? Explain the pattern, MDC, and what enables it.

level: middleimportance: must knowfreq 65%

basics

~20 s

Add Micrometer Tracing to the classpath. When a span is active it copies traceId and spanId into SLF4J's MDC (a thread-local map). Spring Boot's default log pattern includes a correlation part that prints those MDC values as [app,traceId,spanId].

open as a page

Why do traceId/spanId disappear from logs on @Async, thread pools, or reactive chains, and how do you fix it?

level: seniorimportance: must knowfreq 60%

basics

~20 s

traceId/spanId live in MDC, which is thread-local. When work moves to another thread (executor, @Async, reactor), the new thread has no MDC, so logs show empty IDs. Fix it by propagating context: wrap executors, use a TaskDecorator, or enable Reactor context propagation.

open as a page

What is a correlation ID (trace ID) in application logs, and why is it useful?

level: juniorimportance: should knowfreq 55%

basics

~20 s

A correlation ID (trace ID) is a unique identifier attached to every log line produced while handling one request. It lets you filter logs and see everything that happened for that single request, even across services.

open as a page

How do you configure CONSOLE and FILE appenders in Spring Boot, both via properties and via logback-spring.xml (including reusing Boot's defaults)?

level: middleimportance: should knowfreq 40%

basics

~20 s

For simple needs, set logging.file.name (or logging.file.path) to enable a rolling file appender; console is on by default. For control, define ConsoleAppender and RollingFileAppender in logback-spring.xml, or <include> Spring Boot's console-appender.xml/file-appender.xml to reuse its defaults.

open as a page

Spring Boot ships two predefined log groups. Which are they, and what would logging.level.sql=DEBUG turn on?

level: middleimportance: should knowfreq 40%

basics

~10 s

The predefined groups are web and sql. logging.level.sql=DEBUG raises the SQL-related loggers — Spring JDBC core, Hibernate's SQL logger, and jOOQ's logger listener — so you can see executed SQL without listing each package.

open as a page

What is logging.group in Spring Boot and why would you use it?

level: middleimportance: should knowfreq 45%

basics

~10 s

logging.group lets you give one name to a set of packages, then set the level for all of them at once. Define logging.group.<name>=pkgA,pkgB, then logging.level.<name>=DEBUG applies the level to every package in the group.

open as a page

How do you enrich structured logs in Spring Boot 3.4 with custom fields (like traceId or a service name) without writing a full custom formatter?

level: middleimportance: should knowfreq 35%

basics

~10 s

Put per-request values in SLF4J's MDC — they appear as fields automatically. Use logging.structured.json.add/.rename/.exclude to shape static fields, and the format's service properties (e.g. logging.structured.ecs.service.name) for service metadata.

open as a page

Compare the three built-in structured logging formats in Spring Boot 3.4 (ecs, logstash, gelf). When would you pick each?

level: middleimportance: should knowfreq 40%

basics

~20 s

ecs = Elastic Common Schema (for Elasticsearch/Kibana), logstash = the Logstash JSON layout, gelf = Graylog Extended Log Format (for Graylog). Pick the one your log backend understands natively so fields map without extra transformation.

open as a page

How do you switch a Spring Boot application from Logback to Log4j2, and what are the trade-offs?

level: seniorimportance: should knowfreq 45%

basics

~10 s

Exclude spring-boot-starter-logging (which brings Logback) everywhere it's pulled in, then add spring-boot-starter-log4j2. Your SLF4J code is unchanged; put Log4j2 config in log4j2-spring.xml.

open as a page

How is the same traceId carried across service boundaries so logs from different services correlate, and how do those logs tie back to a trace?

level: seniorimportance: should knowfreq 48%

basics

~20 s

The traceId is sent to downstream services in an HTTP header (by default the W3C traceparent header). The downstream service extracts it, continues the same trace, and stamps the same traceId into its own logs. Because logs and the tracing backend share that traceId, you can jump between them.

open as a page

Explain how Spring resolves the effective level of a logger given multiple logging.level entries at different specificities.

level: seniorimportance: should knowfreq 38%

basics

~20 s

Logger names form a dotted tree rooted at root. A logger's effective level comes from the most specific configured ancestor prefix; if none is set it inherits down from root. The longest matching prefix wins.

open as a page

How do you implement and register a custom structured logging format in Spring Boot 3.4 when none of the built-in ones fit?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Implement org.springframework.boot.logging.structured.StructuredLogFormatter<E> (E is the log-framework event type, e.g. Logback's ILoggingEvent), return a JSON string from format(event), then set logging.structured.format.console (or .file) to that class's fully-qualified name.

open as a page

Logging initializes before most of the Spring context. What does that imply for configuration, and how would you standardize logging backends across many services?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Because logging boots very early, only the Environment (profiles, properties) is available to config — not application beans. Standardize by keeping app code on SLF4J, sharing a common logback-spring.xml (or log4j2-spring.xml) fragment, and centralizing the backend choice and versions via the BOM.

open as a page

How would you add a custom field (e.g. userId or tenantId) to every log line AND propagate it downstream alongside traceId? Contrast MDC vs baggage.

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Plain MDC.put adds a field to local logs only and doesn't cross services. Use Micrometer Tracing baggage: create a baggage field, and configure it to be written into MDC and propagated in headers. Then userId/tenantId appears in every service's logs, not just the first.

open as a page

How would you design a log-level and log-group strategy across environments, and what pitfalls do levels/groups introduce at scale?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Keep root at INFO (or WARN) in prod, DEBUG for your own packages in dev. Use groups (custom plus web/sql) as named toggles so operators raise a subsystem without listing packages. Avoid DEBUG/TRACE on hot paths and never leak sensitive data.

open as a page

Compare Boot 3.4 native structured logging with the encoder-based approach (logstash-logback-encoder). How would you architect JSON logging for an aggregation pipeline across many services?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Boot 3.4 native structured logging gives JSON via one property with no XML or extra dependency, covering ecs/logstash/gelf. The encoder approach (logstash-logback-encoder in logback-spring.xml) is more configurable but heavier. Prefer native; drop to the encoder only for needs native can't express.

open as a page