Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
169: Logging with SLF4J and Logback
A few years ago, I was on call for a fintech app when a critical bug hit: payments were failing for about 5% of our users, but only on Tuesdays. I spent three hours staring at a production log that was essentially a wasteland of System.out.println statements. Some were missing, some were swallowed by the container, and others were just useless strings like "here 1" or "processing...". When I finally managed to swap in a proper logging framework and set the level to DEBUG, the culprit jumped out in seconds: a timezone mismatch in the payment gateway's API. That's the difference between a three-hour nightmare and a three-minute fix.
In Java, we don't just "print" things to the console. We use logging frameworks. Specifically, you'll see the combination of SLF4J and Logback almost everywhere. The key thing to understand here is that they aren't the same thing. SLF4J (Simple Logging Facade for Java) is an abstraction—a set of interfaces. Logback is the engine that actually does the work of writing the logs to a file or the console. By coding to the SLF4J interface, you can swap Logback for something else later without changing a single line of your business logic. It's the "Interface over Implementation" principle applied to your diagnostic data.
Plugging in the SLF4J Facade
To get started, you don't instantiate a logger with new. Instead, you use a LoggerFactory. I always recommend making your logger private static final so it's shared across all instances of the class and cannot be accidentally changed.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
public void processOrder(String orderId, double amount) {
logger.info("Processing order {} for amount ${}", orderId, amount);
if (amount > 10000) {
logger.warn("High-value order detected: {}. Flagging for manual review.", orderId);
}
}
}
Notice those curly braces {}? Those are placeholders. One of the biggest mistakes I see junior devs make is using string concatenation, like logger.debug("User " + user + " logged in"). The problem is that the JVM performs that string concatenation even if the log level is set to INFO and the DEBUG message is discarded. With placeholders, SLF4J only constructs the final string if the log level is actually enabled, which saves a surprising amount of CPU and memory in high-traffic apps.
Taming the Noise with Logback Configuration
Logback is configured via an XML file (usually logback.xml) placed in your resources folder. This is where you decide what actually gets recorded. You don't want DEBUG logs in production—they'll fill up your disks in minutes—but you definitely want them in your local dev environment.
You can define "Appenders" (where the logs go, like the Console or a File) and "Loggers" (which classes should log at which level). For example, you might want your own code to log at DEBUG level, but you want the Spring Framework or Hibernate logs to stay at WARN so they don't drown out your own messages.
- TRACE: The most fine-grained information. Use this for "I am entering this loop" type of logs.
- DEBUG: Useful for developers during debugging. Things like "Query returned 5 results."
- INFO: High-level events. "Order #123 processed successfully."
- WARN: Something unexpected happened, but the app is still running. "Connection timed out, retrying..."
- ERROR: Something broke. "NullPointerException in PaymentGateway."
Managing Log Rotation and Retention
If you log everything to a single file, that file will eventually grow to several gigabytes and crash your server. I've seen this happen in the wild more than once. To prevent this, Logback provides a RollingFileAppender. This allows you to "roll" the log file based on time (daily) or size (e.g., every 10MB), and automatically delete files older than 30 days.
Setting this up in your logback.xml ensures that your production environment stays lean while still giving you a historical trail to follow when something inevitably goes wrong at 3:00 AM on a Tuesday.
📋 Practical Task
Exercise: Implementing a Rotating File Logger for a User Authentication System
You are tasked with adding professional logging to a UserAuthService class. Currently, the class uses System.out.println, which is unacceptable for production.
Your Requirements:
- Add the SLF4J Logger to the
UserAuthServiceclass using theLoggerFactory. - Replace all
System.out.printlncalls with appropriate SLF4J levels:- Use
INFOfor successful logins. - Use
WARNfor failed login attempts (incorrect password). - Use
ERRORfor system exceptions (e.g., database connection failure). - Use
DEBUGto log the username being attempted before the validation logic runs.
- Use
- Use parameterized logging (the
{}syntax) for all variables; do not use string concatenation. - Create a
logback.xmlfile in the resources folder that:- Sets the root logging level to
INFO. - Adds a
ConsoleAppenderto output logs to the terminal. - Adds a
RollingFileAppenderthat saves logs tologs/auth.log, rolls the file daily, and keeps a maximum of 7 days of history.
- Sets the root logging level to
There are no comments for now.