Apache Log4j (Legacy)

Historic Java logging framework by Apache Foundation. Used in many Java applications for years, but migration to Log4j2 is strongly recommended due to discovery of security vulnerabilities (Log4Shell).

LoggingJavaJakarta EEApacheEnterpriseDebug

GitHub Overview

apache/logging-log4j1

Apache log4j1

Stars868
Watchers110
Forks576
Created:May 21, 2009
Language:Java
License:Apache License 2.0

Topics

log4j

Star History

apache/logging-log4j1 Star History
Data as of: 10/22/2025, 04:10 AM

Library

Apache Log4j

Overview

Apache Log4j is the most established and proven logging library in the Java ecosystem. Designed as a "versatile, feature-rich, efficient logging API for Java," it's used across a wide range of applications from standalone applications to enterprise systems. It provides rich output destinations including files, networks, databases, SMTP, and flexible formatting capabilities, making it the de facto standard for Java logging.

Details

Apache Log4j 2.25.0, released in June 2025, is the latest version featuring complete GraalVM native image support and performance improvements. With its modular design separating API, implementation, and deployment support components, it achieves high extensibility and customization. Supporting Java 8 and above, it's widely used in modern Java application development with robust enterprise-grade features.

Key Features

  • Rich Appenders: Multiple output destinations including files, networks, databases, SMTP, JMS, and more
  • Flexible Layouts: Support for CSV, HTML, JSON, Syslog, and other formatting options
  • High-Performance Filters: Advanced filtering by rate, regex, scripts, time, and more
  • Dynamic Configuration Reload: Automatic reload upon configuration file changes (without log event loss)
  • GraalVM Support: Complete support for native image generation
  • Garbage Collector Free: High-performance design that doesn't burden garbage collection

Pros and Cons

Pros

  • Most established logger in Java ecosystem (20+ years of development)
  • Stable governance and support by Apache Software Foundation
  • Rich ecosystem (integration with Spring Boot, Hibernate, etc.)
  • Enterprise-grade features (asynchronous logging, clustering, etc.)
  • High performance and scalable (garbage collector-free design)
  • Comprehensive documentation and active community

Cons

  • Complex initial configuration with high learning curve
  • XML configuration files can be detailed and difficult to manage
  • Modern Java annotation-based configuration lags behind other libraries
  • Error messages during debugging can sometimes be unclear
  • Relatively high memory usage due to extensive features
  • May be overkill for lightweight logging needs

References

Code Examples

Basic Setup

<!-- Maven dependencies -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.25.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.25.0</version>
</dependency>

<!-- SLF4J bridge (when using SLF4J in existing apps) -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j2-impl</artifactId>
    <version>2.25.0</version>
</dependency>
// Gradle dependencies
implementation 'org.apache.logging.log4j:log4j-core:2.25.0'
implementation 'org.apache.logging.log4j:log4j-api:2.25.0'

Simple Logger Usage

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

public class MyApp {
    // Get logger instance
    private static final Logger logger = LogManager.getLogger(MyApp.class);
    
    public static void main(String[] args) {
        // Basic log output
        logger.trace("Trace message");
        logger.debug("Debug message");
        logger.info("Info message");
        logger.warn("Warning message");
        logger.error("Error message");
        
        // Parameterized log output
        String userId = "user123";
        int age = 25;
        logger.info("User info: ID={}, Age={}", userId, age);
        
        // Exception logging
        try {
            int result = 10 / 0;
        } catch (Exception e) {
            logger.error("Calculation error occurred", e);
        }
    }
}

Basic XML Configuration (log4j2.xml)

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <!-- Console output -->
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
        
        <!-- File output -->
        <File name="FileAppender" fileName="logs/app.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </File>
        
        <!-- Rolling file -->
        <RollingFile name="RollingFileAppender" fileName="logs/app.log" 
                     filePattern="logs/app.%d{yyyy-MM-dd}.%i.log.gz">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="10"/>
        </RollingFile>
    </Appenders>
    
    <Loggers>
        <!-- Specific package log level settings -->
        <Logger name="com.example.database" level="DEBUG" additivity="false">
            <AppenderRef ref="FileAppender"/>
        </Logger>
        
        <!-- Root logger -->
        <Root level="INFO">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingFileAppender"/>
        </Root>
    </Loggers>
</Configuration>

Asynchronous Logging Configuration

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <!-- Async logger configuration -->
    <AsyncLogger name="com.example.performance" level="INFO" additivity="false">
        <AppenderRef ref="FileAppender"/>
    </AsyncLogger>
    
    <!-- Full async mode -->
    <AsyncRoot level="INFO">
        <AppenderRef ref="Console"/>
        <AppenderRef ref="FileAppender"/>
    </AsyncRoot>
    
    <!-- LMAX Disruptor dependency required -->
    <!-- 
    <dependency>
        <groupId>com.lmax</groupId>
        <artifactId>disruptor</artifactId>
        <version>3.4.4</version>
    </dependency>
    -->
</Configuration>

Programmatic Configuration

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.builder.api.*;
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;

public class ProgrammaticConfig {
    public static void main(String[] args) {
        // Programmatic configuration using Configuration Builder
        ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
        
        // Console Appender configuration
        AppenderComponentBuilder consoleAppender = builder.newAppender("Console", "CONSOLE")
            .addAttribute("target", "SYSTEM_OUT")
            .add(builder.newLayout("PatternLayout")
                .addAttribute("pattern", "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"));
        
        // File Appender configuration
        AppenderComponentBuilder fileAppender = builder.newAppender("File", "File")
            .addAttribute("fileName", "logs/programmatic.log")
            .add(builder.newLayout("PatternLayout")
                .addAttribute("pattern", "%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"));
        
        builder.add(consoleAppender);
        builder.add(fileAppender);
        
        // Root Logger configuration
        RootLoggerComponentBuilder rootLogger = builder.newRootLogger("INFO")
            .add(builder.newAppenderRef("Console"))
            .add(builder.newAppenderRef("File"));
        
        builder.add(rootLogger);
        
        // Apply configuration
        LoggerContext context = (LoggerContext) LogManager.getContext(false);
        Configuration config = builder.build();
        context.start(config);
        
        // Use logger
        Logger logger = LogManager.getLogger(ProgrammaticConfig.class);
        logger.info("Log output with programmatic configuration");
    }
}

Filters and Markers Usage

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            <!-- Level filter -->
            <ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
        </Console>
        
        <File name="ErrorFile" fileName="logs/error.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            <!-- Error level only -->
            <LevelRangeFilter minLevel="ERROR" maxLevel="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
        </File>
        
        <File name="SecurityFile" fileName="logs/security.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            <!-- Marker filter -->
            <MarkerFilter marker="SECURITY" onMatch="ACCEPT" onMismatch="DENY"/>
        </File>
    </Appenders>
    
    <Loggers>
        <Root level="DEBUG">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="ErrorFile"/>
            <AppenderRef ref="SecurityFile"/>
        </Root>
    </Loggers>
</Configuration>
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;

public class FilterExample {
    private static final Logger logger = LogManager.getLogger(FilterExample.class);
    private static final Marker SECURITY_MARKER = MarkerManager.getMarker("SECURITY");
    private static final Marker PERFORMANCE_MARKER = MarkerManager.getMarker("PERFORMANCE");
    
    public static void main(String[] args) {
        // Regular log
        logger.info("Application started");
        
        // Security marker log (output to security.log)
        logger.warn(SECURITY_MARKER, "Unauthorized access attempt detected: IP={}", "192.168.1.100");
        
        // Performance marker log
        logger.debug(PERFORMANCE_MARKER, "DB query execution time: {}ms", 150);
        
        // Error log (output to error.log)
        logger.error("Database connection error occurred");
    }
}

Spring Boot Integration

<!-- Replace default logback with Log4j2 in Spring Boot -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
# application.yml
logging:
  config: classpath:log4j2-spring.xml
  level:
    com.example: DEBUG
    org.springframework: INFO
    root: WARN
// Usage in Spring Boot application
@RestController
@Slf4j  // When using Lombok
public class UserController {
    private static final Logger logger = LogManager.getLogger(UserController.class);
    
    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        logger.info("User info request: userId={}", id);
        
        try {
            User user = userService.findById(id);
            logger.debug("User info retrieved successfully: {}", user);
            return ResponseEntity.ok(user);
        } catch (UserNotFoundException e) {
            logger.warn("User not found: userId={}", id);
            return ResponseEntity.notFound().build();
        } catch (Exception e) {
            logger.error("Error occurred while retrieving user info: userId={}", id, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}