Java Logging
Logging Levels, Handlers, Formatters
In software development, logs are more than just lines of text — they’re your application’s black box. Java provides a powerful and flexible logging framework in the java.util.logging
package, often referred to as JUL (Java Util Logging).
This tutorial introduces you to Java Logging step by step — perfect if you're just getting started. We'll look at log levels, handlers, formatters, and how to customize everything to suit your project.
Why Logging Matters
Before diving into the code, let’s ask a simple question — why log?
- To trace issues during development
- To monitor system health in production
- To audit and track user behavior
Unlike System.out.println()
, logging frameworks allow you to filter messages, write them to files, or even send them over the network.
Basic Java Logging Setup
Let’s start by creating a simple logger.
import java.util.logging.Logger;
public class BasicLogger {
private static final Logger logger = Logger.getLogger(BasicLogger.class.getName());
public static void main(String[] args) {
logger.info("Application started");
logger.warning("This is a warning");
logger.severe("Something went wrong");
}
}
Mar 16, 2025 10:20:01 AM BasicLogger main
INFO: Application started
Mar 16, 2025 10:20:01 AM BasicLogger main
WARNING: This is a warning
Mar 16, 2025 10:20:01 AM BasicLogger main
SEVERE: Something went wrong
Understanding Log Levels
Java logging has built-in severity levels that determine which messages get logged. They are:
SEVERE
– Critical failuresWARNING
– Potential issuesINFO
– General informationCONFIG
– Static configuration infoFINE
,FINER
,FINEST
– Debugging details
Setting the Logging Level
By default, only INFO
and above are shown. To see debug-level logs, set a lower level on the logger:
import java.util.logging.*;
public class LevelExample {
private static final Logger logger = Logger.getLogger(LevelExample.class.getName());
public static void main(String[] args) {
logger.setLevel(Level.FINE);
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.FINE);
logger.addHandler(handler);
logger.fine("This is a FINE log message");
logger.info("This is an INFO log message");
}
}
Mar 16, 2025 10:25:34 AM LevelExample main
FINE: This is a FINE log message
Mar 16, 2025 10:25:34 AM LevelExample main
INFO: This is an INFO log message
Important: You need to set both logger and handler levels — otherwise, messages won’t show.
Log Handlers: Where Logs Go
A Handler determines the destination of a log message. Common handlers include:
ConsoleHandler
– prints to consoleFileHandler
– writes to a file- Custom handlers for email, sockets, etc.
import java.io.IOException;
import java.util.logging.*;
public class FileLogging {
private static final Logger logger = Logger.getLogger(FileLogging.class.getName());
public static void main(String[] args) throws IOException {
FileHandler fileHandler = new FileHandler("app.log", true);
fileHandler.setLevel(Level.ALL);
logger.addHandler(fileHandler);
logger.setLevel(Level.ALL);
logger.info("Logged to file");
}
}
Log Formatters: How Logs Look
By default, logs have timestamps and class names. But you can customize the format using a Formatter
.
import java.util.logging.*;
public class CustomFormat {
public static void main(String[] args) throws Exception {
Logger logger = Logger.getLogger("MyLogger");
ConsoleHandler handler = new ConsoleHandler();
handler.setFormatter(new SimpleFormatter());
logger.addHandler(handler);
logger.setUseParentHandlers(false);
logger.info("Formatted log");
}
}
Disabling Parent Handlers
If you don't disable parent handlers, your logs might appear twice.
logger.setUseParentHandlers(false);
Custom Formatter Example
You can go a step further and create your own format:
import java.util.logging.*;
public class MyFormatter extends Formatter {
@Override
public String format(LogRecord record) {
return "[" + record.getLevel() + "] " + record.getMessage() + "\n";
}
public static void main(String[] args) {
Logger logger = Logger.getLogger("CustomLogger");
ConsoleHandler handler = new ConsoleHandler();
handler.setFormatter(new MyFormatter());
logger.addHandler(handler);
logger.setUseParentHandlers(false);
logger.warning("This is a custom log format!");
}
}
[WARNING] This is a custom log format!
Logging Best Practices
- Use
Logger.getLogger()
with the class name - Don’t log sensitive data
- Use appropriate log levels — don’t log everything as INFO
- Use external config for log settings in production
Real-World Logging Use Case
Imagine a banking app. You could log:
INFO
: User loginWARNING
: Failed login attemptsSEVERE
: Database unreachable
Conclusion
Logging in Java is not just for debugging — it’s for accountability, monitoring, and performance analysis. With java.util.logging
, you get a solid foundation out of the box. As your application grows, consider integrating with frameworks like Log4j or SLF4J for even more power.
QUIZ
Question 1:What will be the output of the following code?
import java.util.logging.*;
public class LoggerTest {
public static void main(String[] args) {
Logger logger = Logger.getLogger(LoggerTest.class.getName());
logger.info("App started");
logger.fine("Debug message");
}
}
import java.util.logging.*;
public class LoggerTest {
public static void main(String[] args) {
Logger logger = Logger.getLogger(LoggerTest.class.getName());
logger.info("App started");
logger.fine("Debug message");
}
}