Logging in Java Applications: Best Practices in Practice

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Logging in Java Applications: Best Practices in Practice



Logging is not just about writing messages to the console. In modern Java applications, a properly configured logging system is the foundation for monitoring, debugging, auditing, and incident analysis. Without it, a developer is blind, and a DevOps engineer is deaf. In this article, we will cover best practices for logging in Java with a focus on practical implementation: from choosing a library to configuring performance and security.



1. Choosing the Stack: SLF4J + Logback as the Standard



Today, the industry standard is the combination of SLF4J (Simple Logging Facade for Java) and Logback. SLF4J acts as an abstraction (facade), allowing you to easily switch the implementation in the future (e.g., to Log4j2 or java.util.logging). Logback is the successor to Log4j, which is faster, has a smaller footprint, and supports modern features (asynchronous logging, conditional filters).



Add dependencies to pom.xml (Maven):



<dependency>    <groupId>org.slf4j</groupId>    <artifactId>slf4j-api</artifactId>    <version>2.0.12</version></dependency><dependency>    <groupId>ch.qos.logback</groupId>    <artifactId>logback-classic</artifactId>    <version>1.5.6</version></dependency>


For Gradle:



implementation 'org.slf4j:slf4j-api:2.0.12'implementation 'ch.qos.logback:logback-classic:1.5.6'


Using SLF4J in code:



import org.slf4j.Logger;import org.slf4j.LoggerFactory;

public class OrderService { private static final Logger log = LoggerFactory.getLogger(OrderService.class);

public void processOrder(String orderId) { log.info("Starting order processing: {}", orderId); // business logic log.debug("Order {} successfully processed", orderId); }}


Important: always use parameterized messages ({}), not string concatenation. This avoids the overhead of creating a string if the log level is disabled.



2. Log Levels: When and Which to Use



Choosing the right level is half the battle. In Java (SLF4J/Logback), the following levels are used (in increasing order of severity):



  • TRACE — finest details (method entry/exit, variable values). For development only, never enable in production.
  • DEBUG — information for the developer (object state, intermediate computation results). Enabled temporarily for troubleshooting.
  • INFO — important business events (application startup, order creation, task execution). Should always be enabled.
  • WARN — potentially dangerous situations (connection pool exhaustion, deprecated API, low performance). The application continues to run.
  • ERROR — critical error (exception, data loss, external service unavailability). Requires immediate intervention.


Practical rule: in production, INFO and above are typically enabled. DEBUG and TRACE are enabled only for a specific package or class when investigating an incident.



Example logback.xml configuration with package-level separation:



<configuration>    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">        <file>logs/app.log</file>        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">            <fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>            <maxHistory>30</maxHistory>        </rollingPolicy>                

Blogs

Book Recommendations