CodeGym /Courses /JAVA 25 SELF /Logging fundamentals: java.util.logging, Log4j, SLF4J

Logging fundamentals: java.util.logging, Log4j, SLF4J

JAVA 25 SELF
Level 63, Lesson 0
Available

1. Why you need logging

Logging ≠ console output

When you’re just starting to program, it seems that to find problems it’s enough to sprinkle System.out.println("I was here!") everywhere. This works while the program is small and runs on your computer. But imagine a large project, a server that works 24/7, or an application used by thousands of people—you won’t sit behind every user’s shoulder and watch their console, will you?

Logging is not just message output. It’s a systematic record of information about the application’s operation: errors, warnings, business events, technical details. Logs are saved to files and databases, can be sent over the network—and let you analyze the program after it finishes or right while it’s running.

What logs are for

  • Problem diagnosis: if something goes wrong, logs are your main helper. They let you understand where and why an error occurred.
  • Audit and security: logs capture important user actions, which helps investigate incidents.
  • Debugging: sometimes bugs appear only under certain conditions, and without logs you won’t catch them.
  • Monitoring: logs let you track the application’s state, performance, and “health.”

Real-life example

If airplanes didn’t have “black boxes” (flight logs), investigating the causes of accidents would be almost impossible. In programming, logs are your black boxes.

2. Basic logging levels

Logging commonly uses message levels. It’s like a traffic light: red—danger, yellow—caution, green—everything is fine.

Main levels (from the “loudest” to the “quietest”):

Level Description
ERROR
Critical error; the application cannot continue running
WARN
Warning: something went wrong, but the program keeps running
INFO
Informational message about normal events
DEBUG
Detailed information for debugging (visible only to developers)
TRACE
The most detailed information—for deep diagnostics

Example:

  • A user failed to sign in due to a wrong password—this is WARN.
  • The database broke—this is ERROR.
  • The application started—this is INFO.
  • Printing a variable’s value in a loop—this is DEBUG or TRACE.

How to choose a level

Don’t write everything at the ERROR level—otherwise you’ll drown real errors out. Use levels deliberately: only truly critical failures should be errors.

3. Standard logging in Java (java.util.logging)

Java ships with a built-in logging system—the java.util.logging package (JUL for short). This is a “native” tool that’s always available, even if you didn’t add any libraries.

Key classes:

  • Logger — the main class for writing logs.
  • Level — the enumeration of logging levels (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST).
  • Handler — a handler indicating where to write logs (file, console, etc.).
  • Formatter — responsible for message formatting.

Example of using JUL

import java.util.logging.Logger;
import java.util.logging.Level;

public class LoggingExample {
    // Get a logger by class name
    private static final Logger logger = Logger.getLogger(LoggingExample.class.getName());

    public static void main(String[] args) {
        logger.info("Application started");
        logger.warning("This is a warning!");
        logger.severe("And this is an error!");

        int x = 42;
        logger.fine("Debug variable x=" + x); // Not shown by default
    }
}

Important: by default, JUL outputs only INFO and above. To see fine and other detailed messages, you need to configure the logging level.

Configuration via file

You can configure JUL via the logging.properties file (usually located in the JRE folder). There you can specify:

  • The minimum level for the logger
  • Where to write logs (file, console)
  • Message formatting

Example line in logging.properties:

.level=INFO

4. External logging libraries

Log4j (Apache)

Log4j is one of the most well-known logging libraries for Java. It’s flexible and powerful, supports different formats, asynchronous writing, file rotation, and much more.

Example of a simplest Log4j 2 configuration (XML):

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

Example of using Log4j 2 in code:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4jExample {
    private static final Logger logger = LogManager.getLogger(Log4jExample.class);

    public static void main(String[] args) {
        logger.info("Hello from Log4j!");
        logger.error("This is an error via Log4j");
    }
}

To make this work, you need to add Log4j dependencies to your project (for example, via Maven or Gradle).

SLF4J: a logging facade

SLF4J (Simple Logging Facade for Java) is not a separate logging system, but a “layer” between your code and a specific implementation (Log4j, Logback, JUL and others).

The facade is needed so you can write code once and, if necessary, change the actual logging system without rewriting code, even if different libraries use different loggers.

Example of using SLF4J:

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

public class Slf4jExample {
    private static final Logger logger = LoggerFactory.getLogger(Slf4jExample.class);

    public static void main(String[] args) {
        logger.info("Hello, logging via SLF4J!");
        logger.warn("Warning: something might go wrong");
        logger.error("Error via SLF4J");
    }
}

How does it work?

  • You write code via SLF4J.
  • Add the required implementation to the classpath (for example, Logback or Log4j).
  • SLF4J itself forwards calls to the chosen implementation.

SLF4J interaction with other loggers: SLF4J can work on top of JUL, Log4j, Logback and others, which lets you migrate easily without rewriting code.

5. Practice: comparing System.out.println and logging

Example 1: System.out.println

public class PrintlnExample {
    public static void main(String[] args) {
        System.out.println("Application started");
        System.out.println("An error occurred: something went wrong");
    }
}

What’s wrong:

  • No message level (error, information, warning—all look the same).
  • No time, class name, or thread.
  • All messages go into one pile.
  • No flexible configuration (for example, print errors only).

Example 2: Logging via SLF4J + Logback

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

public class LoggingVsPrintln {
    private static final Logger logger = LoggerFactory.getLogger(LoggingVsPrintln.class);

    public static void main(String[] args) {
        logger.info("Application started");
        logger.error("An error occurred: something went wrong");
    }
}

Advantages:

  • Each message is accompanied by time, level, class name, and thread.
  • You can filter messages by level.
  • You can write logs to a file, send them over the network, format, and archive them.
  • Logging is thread-safe (important for multithreaded applications).

Demonstration of the difference

System.out.println
Logging (SLF4J/Logback)
Application started 12:34:56 [main] INFO LoggingVsPrintln - Application started
An error occurred: something went wrong 12:34:56 [main] ERROR LoggingVsPrintln - An error occurred: something went wrong

6. How to add logging to your project

For the standard logger (JUL)

Everything is already in the JDK; you can use it right away:

import java.util.logging.Logger;

public class MyApp {
    private static final Logger logger = Logger.getLogger(MyApp.class.getName());

    public static void main(String[] args) {
        logger.info("This is an info message");
    }
}

For Log4j or SLF4J

  1. Add dependencies (for example, via Maven).

    For Log4j 2:

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.20.0</version>
    </dependency>
    

    For SLF4J + Logback:

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.7</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.4.7</version>
    </dependency>
    
  2. Create a configuration file (for example, logback.xml for Logback or log4j2.xml for Log4j).
  3. Use the logger in your code (see examples above).

7. Mini app: adding logging

Suppose we have a simple application—a command-line calculator. Let’s add logging to it.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Scanner;

public class CalculatorApp {
    private static final Logger logger = LoggerFactory.getLogger(CalculatorApp.class);

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        logger.info("Calculator started");

        try {
            System.out.print("Enter the first number: ");
            int a = Integer.parseInt(scanner.nextLine());
            logger.debug("First number entered: {}", a);

            System.out.print("Enter the second number: ");
            int b = Integer.parseInt(scanner.nextLine());
            logger.debug("Second number entered: {}", b);

            int sum = a + b;
            logger.info("Sum of numbers: {}", sum);
            System.out.println("Result: " + sum);
        } catch (NumberFormatException ex) {
            logger.error("Number input error", ex);
            System.out.println("Error: enter a valid number!");
        } catch (Exception ex) {
            logger.error("Unknown error", ex);
            System.out.println("An error occurred!");
        }
        logger.info("Calculator finished");
    }
}

What’s happening here:

  • All key events are recorded in the log.
  • Errors are logged at ERROR with the full stack trace.
  • All information is available for analysis after the program finishes.

8. Common mistakes when working with logging

Mistake No. 1: Using only System.out.println for debugging and diagnostics.
It’s convenient at the start, but it’s not suitable for real applications. You won’t be able to control message levels, you won’t see the time, and you won’t be able to analyze logs if the program runs on a server.

Mistake No. 2: Logging too much information at ERROR level.
If you label everything as an error, you’ll stop distinguishing what is truly important from what is just debugging information.

Mistake No. 3: Logging sensitive data (passwords, tokens, card numbers).
Logs must be safe! Don’t write anything there that others shouldn’t see.

Mistake No. 4: No logging configuration.
If you don’t configure levels, format, and storage for logs, you can unexpectedly drown in gigabytes of useless information—or miss an error altogether.

Mistake No. 5: Not using a logging facade (SLF4J) in large projects.
If your project grows and you switch from one library to another—doing it without a facade will be painful. SLF4J lets you change the logging “engine” without rewriting code.

1
Task
JAVA 25 SELF, level 63, lesson 0
Locked
Plant Engineer
Plant Engineer
1
Task
JAVA 25 SELF, level 63, lesson 0
Locked
New Developer
New Developer
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION