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 failures
  • WARNING – Potential issues
  • INFO – General information
  • CONFIG – Static configuration info
  • FINE, 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 console
  • FileHandler – 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 login
  • WARNING: Failed login attempts
  • SEVERE: 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");
    }
}

Question 2:The default log formatter in Java always includes a timestamp and class name in the output.

Question 3:Which of the following are correct practices when using Java's logging system?

Question 4:Which handler should be used if you want to log messages into a file instead of displaying them on the console?