Search code examples
javalogginglog4jlog4j2

Log4j2 Logger Class with 2 seperate Log Files


im currently trying to create a LocalLogger Class which i can use in my Project to log stuff.

General Idea is:

Logs from TRACE to INFO -> infoLog.log Logs from WARN to FATAL -> errorLog.log ALL LOGS IN CONSOLE

I want to set the info and error Log Path dynamically from a Config Class in my Project. Thats why i wanted to use the programmatically provided configuration.

I tried out stuff, but currently all i get are FATAL logs, in console and both log files. No matter what i do.

So basically the structure is like this:

public class LocalLogger {

    private final Logger logger;

    private final Class<?> logClass;

    private Configuration config;
    private final Date startTime;


    public LocalLogger(Configuration config, Class<?> logClass) {
        this.config = config;
        this.logClass = logClass;
        this.startTime = new Date();

        configureLogger();
        logger = LogManager.getLogger(logClass);

        logInfo("Starting Time: " + new SimpleDateFormat("dd.MM.yyyy - hh:mm:ss").format(startTime));
        logInfo("ErrorLogFile set to -> " + config.getErrorLogFile());
    }

    public LocalLogger(Class<?> logClass) {
        this.logClass = logClass;
        this.startTime = new Date();

        configureLogger();
        logger = LogManager.getLogger(logClass);

        logInfo("Starting Time: " + new SimpleDateFormat("dd.MM.yyyy - hh:mm:ss").format(startTime));
    }

    /***
     * Sets Logging to configured LogFile/ErrorLogFile and LogLevel in xml-configurationFile
     */
    private void configureLogger() {
        /* this is the magic part, where i need help (when config is not set / null -> just use "infoLog.log" and "errorLog.log" */
    }


    /**
     * Redirection to Log4j Logger.
     *
     * @param message message to log
     */
    public void logInfo(String message) {
        logger.info(message);
    } // info

    /**
     * Redirection to Log4j Logger.
     *
     * @param message message to log
     */
    public void logWarning(String message) {
        logger.warn(message);
    } // warn

    /**
     * Redirection to Log4j Logger.
     *
     * @param message message to log
     */
    public void logFine(String message) {
        logger.debug(message);
    } // debug

    /**
     * Redirection to Log4j Logger.
     *
     * @param message message to log
     */
    public void logFinest(String message) {
        logger.trace(message);
    } // debug

    /**
     * Handles truncated stack trace logging via Log4j Logging
     *
     * @param key unique String value (e.g. file path)
     * @param message individual log message
     * @param exception the exception to log with max stack trace length
     *
     */
    public void logSevere(String key, String message, Exception exception) {
        //TODO special exception Handling?

        String msg = key + ":" + message;

        logger.fatal(msg);
    } // error

I tried it like that:

private void configureLogger() {
        String infoLog = config != null && config.getLogFile() != null && config.getLogFile().isEmpty() ? config.getLogFile() : "infoLog.log";
        String errorLog = config != null && config.getErrorLogFile() != null && config.getErrorLogFile().isEmpty() ? config.getErrorLogFile() : "errorLog.log";
        String logLevel = config != null && config.getLogLevel() != null && config.getLogLevel().isEmpty() ? config.getLogLevel() : "TRACE";

        
        try (LoggerContext context = (LoggerContext) LogManager.getContext(true)) {
            ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();

            
            builder.setStatusLevel(org.apache.logging.log4j.Level.WARN);
            builder.setConfigurationName("DynamicConfig");

            
            AppenderComponentBuilder consoleAppender = builder.newAppender("Console", "CONSOLE")
                    .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
            consoleAppender.add(builder.newLayout("PatternLayout")
                    .addAttribute("pattern", "%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level: %msg%n"));
            builder.add(consoleAppender);

            
            AppenderComponentBuilder fileInfoAppender = builder.newAppender("InfoFile", "FILE")
                    .addAttribute("fileName", infoLog)
                    .addAttribute("append", "true");
            fileInfoAppender.add(builder.newLayout("PatternLayout")
                    .addAttribute("pattern", "%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level: %msg%n"));
            fileInfoAppender.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
                    .addAttribute("level", org.apache.logging.log4j.Level.INFO));
            builder.add(fileInfoAppender);

            
            AppenderComponentBuilder fileErrorAppender = builder.newAppender("ErrorFile", "FILE")
                    .addAttribute("fileName", errorLog)
                    .addAttribute("append", "true");
            fileErrorAppender.add(builder.newLayout("PatternLayout")
                    .addAttribute("pattern", "%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level: %msg%n"));
            fileErrorAppender.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
                    .addAttribute("level", org.apache.logging.log4j.Level.WARN));
            builder.add(fileErrorAppender);

            
            RootLoggerComponentBuilder rootLogger = builder.newRootLogger(org.apache.logging.log4j.Level.getLevel(logLevel));
            rootLogger.add(builder.newAppenderRef("Console"));
            rootLogger.add(builder.newAppenderRef("InfoFile"));
            rootLogger.add(builder.newAppenderRef("ErrorFile"));
            builder.add(rootLogger);

            
            org.apache.logging.log4j.core.config.Configuration configuration = builder.build();
            context.start(configuration);  
        } 
    }

Test Class was this:

public class TestingMain {

    public static void main(String[] args) {
        LocalLogger logger = new LocalLogger(TestingMain.class);

        logger.logInfo("Test");
        logger.logSevere("Test", "123", new Exception());
    }
}

OUTPUT:

2024-08-28T08:31:57.614119700Z main WARN No Log4j 2 configuration file found. Using default configuration (logging only errors to the console), or user programmatically provided configurations. Set system property 'log4j2.debug' to show Log4j 2 internal initialization logging. See https://logging.apache.org/log4j/2.x/manual/configuration.html for instructions on how to configure Log4j 2

10:31:57.640 [main] FATAL de.tde.ecm.vp.fp8.start.TestingMain - Test:123


Solution

  • Ok, so I went through your configuration and went through the log4j wiki since yesterday. The root cause is that when using a ConfigurationBuilder, you need to initialize the builder using the Configurator.initialize methods otherwise a default configuration is used for logging.

    public class misc {
        private final Logger logger;
    
        public misc() {
            configureLogger();
            logger = LogManager.getLogger(this);
        }
    
        public static void main(String[] args) {
            misc m = new misc();
            m.logFine("fine");
            m.logInfo("info");
            m.logWarning("warning");
        }
    
        private void configureLogger() {
            String infoLog = "infoLog.log";
            String errorLog = "errorLog.log";
            String logLevel = "TRACE";
    
    
            try (LoggerContext context = (LoggerContext) LogManager.getContext(true)) {
                ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
    
                builder.setStatusLevel(org.apache.logging.log4j.Level.WARN);
                builder.setConfigurationName("DynamicConfig");
    
                LayoutComponentBuilder standard
                        = builder.newLayout("PatternLayout");
                standard.addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable");
    
    
                AppenderComponentBuilder consoleAppender = builder.newAppender("Console", "Console")
                        .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
                consoleAppender.add(standard);
                builder.add(consoleAppender);
    
    
                AppenderComponentBuilder fileInfoAppender = builder.newAppender("InfoFile", "File")
                        .addAttribute("fileName", infoLog)
                        .addAttribute("append", "true");
                fileInfoAppender.add(standard);
                fileInfoAppender.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
                        .addAttribute("level", org.apache.logging.log4j.Level.INFO));
                builder.add(fileInfoAppender);
    
    
                AppenderComponentBuilder fileErrorAppender = builder.newAppender("ErrorFile", "File")
                        .addAttribute("fileName", errorLog)
                        .addAttribute("append", "true");
                fileErrorAppender.add(standard);
                fileErrorAppender.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
                        .addAttribute("level", org.apache.logging.log4j.Level.WARN));
                builder.add(fileErrorAppender);
    
    
                RootLoggerComponentBuilder rootLogger = builder.newRootLogger(org.apache.logging.log4j.Level.getLevel(logLevel));
                rootLogger.add(builder.newAppenderRef("Console"));
                rootLogger.add(builder.newAppenderRef("InfoFile"));
                rootLogger.add(builder.newAppenderRef("ErrorFile"));
                builder.add(rootLogger);
    
                var ctx = Configurator.initialize(builder.build()); // <--
    //            org.apache.logging.log4j.core.config.Configuration configuration = builder.build();
    //            context.start(configuration);
            }
        }
    
        /**
         * Redirection to Log4j Logger.
         *
         * @param message message to log
         */
        public void logInfo(String message) {
            logger.info(message);
        } // info
    
        /**
         * Redirection to Log4j Logger.
         *
         * @param message message to log
         */
        public void logWarning(String message) {
            logger.warn(message);
        } // warn
    
        /**
         * Redirection to Log4j Logger.
         *
         * @param message message to log
         */
        public void logFine(String message) {
            logger.debug(message);
        } // debug
    
        /**
         * Redirection to Log4j Logger.
         *
         * @param message message to log
         */
        public void logFinest(String message) {
            logger.trace(message);
        } // debug
    
        /**
         * Handles truncated stack trace logging via Log4j Logging
         *
         * @param key unique String value (e.g. file path)
         * @param message individual log message
         * @param exception the exception to log with max stack trace length
         *
         */
        public void logSevere(String key, String message, Exception exception) {
            //TODO special exception Handling?
    
            String msg = key + ":" + message;
    
            logger.fatal(msg);
        } // error
    }
    

    Also, while perusing the Log4j2 wiki, it seems the try-with resources wasn't really used, so at the end of the day, I removed it to end up with the following:

        private void configureLogger() {
            String infoLog = "infoLog.log";
            String errorLog = "errorLog.log";
            String logLevel = "TRACE";
    
    
            ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
    
            builder.setStatusLevel(org.apache.logging.log4j.Level.WARN);
            builder.setConfigurationName("DynamicConfig");
    
            LayoutComponentBuilder standard
                    = builder.newLayout("PatternLayout");
            standard.addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable");
    
    
            AppenderComponentBuilder consoleAppender = builder.newAppender("Console", "Console")
                    .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
            consoleAppender.add(standard);
            builder.add(consoleAppender);
    
    
            AppenderComponentBuilder fileInfoAppender = builder.newAppender("InfoFile", "File")
                    .addAttribute("fileName", infoLog)
                    .addAttribute("append", "true");
            fileInfoAppender.add(standard);
            fileInfoAppender.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
                    .addAttribute("level", org.apache.logging.log4j.Level.INFO));
            builder.add(fileInfoAppender);
    
    
            AppenderComponentBuilder fileErrorAppender = builder.newAppender("ErrorFile", "File")
                    .addAttribute("fileName", errorLog)
                    .addAttribute("append", "true");
            fileErrorAppender.add(standard);
            fileErrorAppender.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
                    .addAttribute("level", org.apache.logging.log4j.Level.WARN));
            builder.add(fileErrorAppender);
    
    
            RootLoggerComponentBuilder rootLogger = builder.newRootLogger(org.apache.logging.log4j.Level.getLevel(logLevel));
            rootLogger.add(builder.newAppenderRef("Console"));
            rootLogger.add(builder.newAppenderRef("InfoFile"));
            rootLogger.add(builder.newAppenderRef("ErrorFile"));
            builder.add(rootLogger);
    
            var ctx = Configurator.initialize(builder.build());
        }
    

    but, as I mentioned in the comments, your use of rootlogger for everything means that your infoLog.log will look like:

    2024-08-29 10:40:51,816 [main] INFO : info
    2024-08-29 10:40:51,820 [main] WARN : warning
    2024-08-29 10:42:16,552 [main] INFO : info
    2024-08-29 10:42:16,554 [main] WARN : warning
    2024-08-29 10:43:04,961 [main] INFO : info
    2024-08-29 10:43:04,963 [main] WARN : warning
    2024-08-29 10:43:52,780 [main] INFO : info
    2024-08-29 10:43:52,782 [main] WARN : warning
    2024-08-29 10:54:20,617 [main] INFO : info
    2024-08-29 10:54:20,619 [main] WARN : warning
    

    Then, you need to configure new loggers for your specific purposes like so:

    private final Logger logger;
    private final Logger elogger;
    private final Logger ilogger;
    
    public misc() {
        configureLogger();
        logger = LogManager.getLogger(this);
        elogger = LogManager.getLogger("errorLogger");
        ilogger = LogManager.getLogger("infoLogger");
    }
    
    public static void main(String[] args) {
        misc m = new misc();
        m.logFine("fine");
        m.logInfo("info");
        m.logWarning("warning");
    }
    
    private void configureLogger() {
        String infoLog = "infoLog.log";
        String errorLog = "errorLog.log";
        String logLevel = "TRACE";
    
    
        ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
    
        builder.setStatusLevel(org.apache.logging.log4j.Level.WARN);
        builder.setConfigurationName("DynamicConfig");
    
        LayoutComponentBuilder standard
                = builder.newLayout("PatternLayout");
        standard.addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable");
    
    
        AppenderComponentBuilder consoleAppender = builder.newAppender("Console", "Console")
                .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
        consoleAppender.add(standard);
        builder.add(consoleAppender);
    
    
        AppenderComponentBuilder fileInfoAppender = builder.newAppender("InfoFile", "File")
                .addAttribute("fileName", infoLog)
                .addAttribute("append", "true");
        fileInfoAppender.add(standard);
        fileInfoAppender.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
                .addAttribute("level", org.apache.logging.log4j.Level.INFO));
        builder.add(fileInfoAppender);
    
    
        AppenderComponentBuilder fileErrorAppender = builder.newAppender("ErrorFile", "File")
                .addAttribute("fileName", errorLog)
                .addAttribute("append", "true");
        fileErrorAppender.add(standard);
        fileErrorAppender.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
                .addAttribute("level", org.apache.logging.log4j.Level.WARN));
        builder.add(fileErrorAppender);
    
    
        RootLoggerComponentBuilder rootLogger = builder.newRootLogger(org.apache.logging.log4j.Level.getLevel(logLevel));
        rootLogger.add(builder.newAppenderRef("Console"));
        builder.add(rootLogger);
    
        var errorLogger = builder.newLogger("errorLogger", Level.WARN);
        errorLogger.add(builder.newAppenderRef("ErrorFile"));
        builder.add(errorLogger);
    
        var infoLogger = builder.newLogger("infoLogger", Level.INFO);
        infoLogger.add(builder.newAppenderRef("InfoFile"));
        builder.add(infoLogger);
    
        var ctx = Configurator.initialize(builder.build());
    }
    
    /**
     * Redirection to Log4j Logger.
     *
     * @param message message to log
     */
    public void logInfo(String message) {
        ilogger.info(message);
    } // info
    
    /**
     * Redirection to Log4j Logger.
     *
     * @param message message to log
     */
    public void logWarning(String message) {
        elogger.warn(message);
    } // warn
    
    /**
     * Redirection to Log4j Logger.
     *
     * @param message message to log
     */
    public void logFine(String message) {
        logger.debug(message);
    } // debug
    
    /**
     * Redirection to Log4j Logger.
     *
     * @param message message to log
     */
    public void logFinest(String message) {
        logger.trace(message);
    } // debug
    
    /**
     * Handles truncated stack trace logging via Log4j Logging
     *
     * @param key unique String value (e.g. file path)
     * @param message individual log message
     * @param exception the exception to log with max stack trace length
     *
     */
    public void logSevere(String key, String message, Exception exception) {
        //TODO special exception Handling?
    
        String msg = key + ":" + message;
    
        elogger.fatal(msg);
    } // error