Generating multiple log-files with log4j2

I just wanted to do a small excourse into great log4j2-logging-framework, which, by the way, still is „only“ a release-candidate 2.0-rc, but brings a lot of features most developers are missing in log4j.
I decided to create a tiny generator that can generate a ton of logs. Maybe next weeks or months I will have time to take a look at logstash? We’ll see.

But back to the log-generator: The idea behind is to create a boilerplate-configuration where I can have a look at new features and basic log4j2-configuration with all the appenders and loggers.

So the setup is only a standard-maven-layout (maven 3.0.5 with jdk 1.8.0_05) with following pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.jtaddeus.playground</groupId>
	<artifactId>loggenerator</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<dependencies>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-api</artifactId>
			<version>2.0-rc1</version>			
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>2.0-rc1</version>
		</dependency>
	</dependencies>

</project>

The single class that produces the logs:

package org.jtaddeus.playground.loggenerator;

import java.util.ArrayList;
import java.util.List;

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

/**
 * This class represents a Generator for some logs. It will print out logs in form of "Logging in user <some userame>
 * with id <some id>.
 * Therefor it uses static lists of users and loggers.
 * @author christoph burmeister
 * 
 */
public class LogGenerator {
    /** List of users. */
    private static final List<String> users = new ArrayList<String>();
    /** List of loggers. */
    private static final List<Logger> loggers = new ArrayList<Logger>();

    /** Some static initialization of users- and logger-lists. */
    static {
        users.add("anakin");
        users.add("luke");
        users.add("leia");
        users.add("darth");
        users.add("mace");
        users.add("padme");
        users.add("yoda");
        users.add("jarjar");
        users.add("han");
        users.add("chewie");

        // the loggers that will appear in the log4j2.xml
        loggers.add(LogManager.getLogger("org.jtaddeus.playground.loggenerator.Server1"));
        loggers.add(LogManager.getLogger("org.jtaddeus.playground.loggenerator.Server2"));
    }

    /**
     * Entry-point.
     * 
     * @param args
     *            doesn't matter in this case
     */
    public static void main(String[] args) {
        while (true) {
            int logLevel = getRandomNumber(0, 3); // 4 different log-levels
            int loggerId = getRandomNumber(0, loggers.size() - 1);
            Logger logger = loggers.get(loggerId);
            int userId = getRandomNumber(0, users.size() - 1);
            String user = users.get(userId);

            log(user, userId, logger, logLevel);

            try {
                long sleeptime = getRandomNumber(200, 500);
                Thread.sleep(sleeptime);
            } catch (InterruptedException e) {
                System.err.println(e.getMessage());
            }

        }
    }

    /**
     * Method to do the actual logging.
     * 
     * @param user
     *            the username
     * @param userId
     *            the user-id
     * @param logger
     *            the logger to use
     * @param logLevel
     *            the loglevel to use
     */
    private static void log(String user, int userId, Logger logger, int logLevel) {
        switch (logLevel) {
        case 0:
            logger.error("Logging in user {} with id {} ", user, userId);
            break;
        case 1:
            logger.warn("Logging in user {} with id {} ", user, userId);
            break;
        case 2:
            logger.info("Logging in user {} with id {} ", user, userId);
            break;
        case 3:
            logger.debug("Logging in user {} with id {} ", user, userId);
            break;
        }
    }

    /**
     * Method to wrap random-mechanism. gets a random number in range of [min...max].
     * 
     * @param min
     *            the minium-value
     * @param max
     *            the maximum-value
     * @return the random int.
     */
    private static int getRandomNumber(int min, int max) {
        int incMax = max + 1;
        int random = min + (int) (Math.random() * (incMax - min) + min);
        return random;
    }
}

And, finally the log4j2.xml (take care of the „2“ in the filename!). There we have 5 appenders: one for the console, 2 file-appender for server1-logs (all and error) and 2 file-appender for server2-logs (again all and error). Then I configure 3 Loggers which refer to the appenders.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="30" status="debug">
	<!-- monitorInterval: automatic reload every 30sec on changes -->
	<!-- status: log4j2 internal logging (not for the app) -->

	<!-- fantastic: properties within config-file finally in log4j2 🙂 -->
	<Properties>
		<Property name="filename-server1-all">logs/server1-all.log</Property>
		<Property name="filename-server1-error">logs/server1-err.log</Property>
		<Property name="filename-server2-all">logs/server2-all.log</Property>
		<Property name="filename-server2-error">logs/server2-err.log</Property>
	</Properties>

	<Appenders>
		<!-- the standard-console-appender -->
		<Console name="appender-Console-all" target="SYSTEM_OUT">
			<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
		</Console>

		<!-- the appenders for server 1 -->
		<File name="appender-server-1-all" fileName="${filename-server1-all}" append="true">
			<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
		</File>
		<File name="appender-server-1-error" fileName="${filename-server1-error}" append="true">
			<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
		</File>

		<!-- the appenders for server 2 -->
		<File name="appender-server-2-all" fileName="${filename-server2-all}" append="true">
			<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
		</File>
		<File name="appender-server-2-error" fileName="${filename-server2-error}" append="true">
			<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
		</File>
	</Appenders>


	<Loggers>
		<!-- Every configuration must have a root logger. If one is not configured the default root LoggerConfig is ERROR with Console appender attached. -->
		<Root level="DEBUG">
			<AppenderRef ref="appender-Console-all" />
		</Root>

		<!-- server1-loggers -->
		<!-- additivity means, that parent-logger (in every case the root-logger) will also get the chance to log this stuff -->
		<Logger name="org.jtaddeus.playground.loggenerator.Server1" additivity="TRUE" level="ALL">
			<AppenderRef ref="appender-server-1-all" level="ALL" />
			<AppenderRef ref="appender-server-1-error" level="ERROR" />
		</Logger>

		<!-- server2-loggers -->
		<!-- additivity means, that parent-logger (in every case the root-logger) will also get the chance to log this stuff -->
		<Logger name="org.jtaddeus.playground.loggenerator.Server2" additivity="TRUE" level="ALL">
			<AppenderRef ref="appender-server-2-all" level="ALL" />
			<AppenderRef ref="appender-server-2-error" level="ERROR" />
		</Logger>
	</Loggers>
</Configuration>

references: