One Bad Log Can Halve Spring Boot Throughput – How to Log Without Losing Performance
The article explains why effective logging is crucial for Java applications, compares proper and improper logging practices with SLF4J and Logback, and provides fourteen concrete best‑practice guidelines—including correct logger setup, level usage, placeholders, asynchronous and structured logging—to avoid performance degradation and security risks.
Effective logging is a vital component of any Java application, providing observability, faster fault isolation, enhanced event response, and compliance support, especially in production environments.
Benefits of proper logging
Improved observability through detailed runtime records.
Accelerated troubleshooting by pinpointing root causes.
Better incident response with chronological event traces.
Compliance and security evidence for audits.
Choosing SLF4J and Logback
SLF4J is a popular logging façade that decouples application code from the underlying logging implementation. Logback is a widely used backend offering rich features and configurability. Combining them yields flexibility and powerful logging capabilities.
Best Practices
2.1 Use SLF4J as the logging façade
Correct
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
}Incorrect
import org.apache.log4j.Logger;
public class UserController {
private static final Logger logger = Logger.getLogger(UserController.class);
}2.2 Configure Logback
Correct
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>Incorrect
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- Deprecated layout class -->
<layout class="ch.qos.logback.classic.PatternLayout">
</layout>
</appender>
</configuration>2.3 Use appropriate log levels
Correct
logger.info("Application has started.");
logger.debug("The value of X is {}", x);
logger.error("Unable to process the request.", e);Incorrect
logger.error("xxxooo."); // misuse of level
logger.error("x = " + x); // inefficient string concatenation2.4 Log meaningful information
Correct
logger.info("Order ID: {}", orderId);Incorrect
logger.info("This is my info."); // meaningless2.5 Use placeholders for dynamic content
Correct
logger.info("User {} logged in at {}", username, LocalDateTime.now());Incorrect
logger.debug("User " + username + " logged in at " + LocalDateTime.now());If the logger level is INFO, the concatenation still occurs, so placeholders should be used to avoid unnecessary computation.
2.6 Record stack traces for exceptions
Correct
try {
// ...
} catch (Exception e) {
logger.error("Payment exception: {}", e);
}Incorrect
try {
// ...
} catch (Exception e) {
logger.error("Payment exception: {}", e.getMessage());
}2.7 Use asynchronous logging
Correct
<configuration>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>application.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="ASYNC" />
</root>
</configuration>Asynchronous logging moves the I/O work to a separate thread, improving application performance.
2.8 Log with appropriate granularity
Correct
public void processOrder(Order order) {
logger.info("Processing order: {}", order.getId());
logger.debug("Order details: {}", order);
orderRepository.save(order);
logger.info("Order processed successfully");
}Incorrect
public void processOrder(Order order) {
logger.trace("开始处理订单");
logger.debug("订单新: {}", order);
logger.info("准备处理订单: {}", order.getId());
logger.debug("Step 1: 校验订单有效性");
// ...
logger.info("订单处理成功");
}Excessive high‑granularity logs in production can cause performance issues and log flooding.
2.9 Monitor and rotate log files
Correct
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/myapp-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>Incorrect
// No rotation – log files grow without bound, eventually exhausting disk space.2.10 Protect sensitive information
Correct
log.info("Payment card: {}", maskCreditCard(creditCardNumber));
public String maskCreditCard(String creditCardNumber) {
int length = creditCardNumber.length();
if (length < 4) return "Invalid number";
return "****-****-****-" + creditCardNumber.substring(length - 4);
}Incorrect
logger.info("Payment card: {}", creditCardNumber); // logs raw sensitive data2.11 Structured logging
Correct
<configuration>
<appender name="JSON_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp><timeZone>UTC</timeZone></timestamp>
<version />
<logLevel />
<threadName />
<loggerName />
<message />
<context />
<StackTrace />
</providers>
</encoder>
</appender>
<root level="info">
<appender-ref ref="JSON_CONSOLE" />
</root>
</configuration>Example output:
{"@timestamp":"2024-10-26 15:52:34","@version":"1","message":"Order processing completed","logger_name":"Application","thread_name":"main","level":"INFO"}Incorrect – using unstructured plain‑text logs makes automated parsing difficult.
2.12 Integrate with monitoring tools
Correct – connect logging to monitoring and alerting systems to automatically detect anomalies.
Incorrect – ignoring integration delays problem discovery.
2.13 Log aggregation
Correct – use centralized log aggregation in distributed environments to simplify analysis.
Incorrect – scattered logs across services complicate troubleshooting.
2.14 Intelligent logging with AOP
Example of using Spring AOP to log method execution:
@Component
@Aspect
public class LogAspect {
@Pointcut("@target(pack)")
private void log(Pack pack) {}
@Around("log(pack)")
public Object around(ProceedingJoinPoint pjp, Pack pack) throws Throwable {
System.err.println("before...");
Object ret = pjp.proceed();
System.err.println("after...");
return ret;
}
}Following these practices helps maintain high observability while preserving application performance and security.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
