Introduction

Log4j2's rolling file appender rotates log files based on size or time triggers, but misconfiguration causes logs to grow unbounded, roll over too frequently, or fail silently due to permission errors. The most common issue is the triggering policy not matching the file pattern -- when the date pattern in fileName does not align with the rolling interval, Log4j2 cannot determine when to rotate. Additionally, missing directories, incorrect permissions, and wrong filePattern format all cause silent failures where logging continues to the same file indefinitely.

Symptoms

bash
# Single log file grows to gigabytes
ls -lh /var/log/app/application.log
-rw-r--r-- 1 app app 4.2G Apr  9 10:00 /var/log/app/application.log
# No rotated files in the directory

Or:

bash
2026-04-09 10:00:00 ERROR Unable to move file /var/log/app/application.log to /var/log/app/archive/application-2026-04-09.log.gz
java.io.IOException: Permission denied

Common Causes

  • No triggering policy configured: RollingFile without SizeBasedTriggeringPolicy
  • File pattern does not match trigger: Daily pattern with hourly trigger
  • Archive directory does not exist: Log4j2 does not create parent directories
  • File permissions: Application user cannot write to log directory
  • Max file size not set: SizeBasedTriggeringPolicy missing size attribute
  • Rollover strategy not configured: DefaultRolloverStrategy limits file count

Step-by-Step Fix

Step 1: Configure size and time-based rolling

```xml <?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <RollingFile name="RollingFile" fileName="/var/log/app/application.log" filePattern="/var/log/app/archive/application-%d{yyyy-MM-dd}-%i.log.gz"> <PatternLayout> <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Pattern> </PatternLayout>

<Policies> <!-- Rotate daily --> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> <!-- Or when file reaches 100MB --> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies>

<!-- Keep 30 days of logs, max 10 files per day --> <DefaultRolloverStrategy max="10"> <Delete basePath="/var/log/app/archive" maxDepth="1"> <IfFileName glob="application-*.log.gz"/> <IfLastModified age="30d"/> </Delete> </DefaultRolloverStrategy> </RollingFile> </Appenders>

<Loggers> <Root level="info"> <AppenderRef ref="RollingFile"/> </Root> </Loggers> </Configuration> ```

Step 2: Ensure directory and permissions

```bash # Create log directories mkdir -p /var/log/app/archive chown -R appuser:appgroup /var/log/app chmod 755 /var/log/app chmod 750 /var/log/app/archive

# Verify ls -la /var/log/app/ ```

Step 3: Programmatic configuration

```java import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.core.appender.RollingFileAppender;

public class LogConfig { public static void setupRollingFile(String logDir) { // Ensure directories exist Path logPath = Paths.get(logDir); Path archivePath = Paths.get(logDir, "archive");

try { Files.createDirectories(archivePath); } catch (IOException e) { System.err.println("Failed to create log directories: " + e.getMessage()); }

// Configuration can also be done programmatically // But XML/YAML configuration is preferred } } ```

Prevention

  • Always configure both TimeBasedTriggeringPolicy and SizeBasedTriggeringPolicy
  • Use filePattern with date and index (%d{yyyy-MM-dd}-%i) for proper rotation
  • Set DefaultRolloverStrategy with Delete action to prevent unlimited log growth
  • Create log directories before application startup in deployment scripts
  • Monitor log file sizes with alerts to catch rotation failures early
  • Test log rotation in staging by generating enough logs to trigger rotation
  • Use gzip compression (filePattern ending in .gz) to save disk space