2

I am developing a java application which communicates with lots of devices. For each device I need to create a different log file to log it's communication with device. This is the wrapper class I developed. It creates two log files but the data is written to only the first one. The second file is created but nothing is written to it. The output that should go to second file goes to console. If I uncomment createRootLogger() in constructor nothing is written to both the files, everything goes to console. I have gone through log4j2 documentation but it is poorly written with very few code samples. Here is my wrapper class, where is the error? I am using log4j-api-2.9.0.jar and log4j-core-2.9.0.jar.

package xyz;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.Configurator;
import org.apache.logging.log4j.core.config.builder.api.*;

import java.util.Hashtable;

public class LogManager
{
    static protected LogManager m_clsInstance = null;

    protected Hashtable<String, Logger> m_clsLoggers = new Hashtable<String, Logger>();

    private LogManager()
    {
        //createRootLogger();
    }
    /**
     * getInstance is used to get reference to the singalton class obj ......
     */
    static synchronized public LogManager getInstance()
    {
        try
        {
            if (m_clsInstance == null)
            {
                m_clsInstance = new LogManager();
                //Configurator.setRootLevel(Level.TRACE);
            }
        }
        catch (Exception xcpE)
        {
            System.err.println(xcpE);
        }

        return m_clsInstance;
    }

    static public Logger getLogger(String sLogger)
    {
        try
        {
            return getInstance().m_clsLoggers.get(sLogger);
        }
        catch (Exception xcpE)
        {
            System.err.println(xcpE);
        }

        return null;
    }

    public Logger createLogger(String strName, String sPath, int nBackupSize, long lngMaxSize, String strPattern, String strLevel)
    {
        try
        {
            ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder();

            builder.setStatusLevel(Level.getLevel(strLevel));
            builder.setConfigurationName("RollingBuilder"+strName);

            // create a console appender
            AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE").addAttribute("target",
                                                                                                             ConsoleAppender.Target.SYSTEM_OUT);
            appenderBuilder.add(builder.newLayout("PatternLayout")
                                       .addAttribute("pattern", strPattern));
            builder.add( appenderBuilder );

            // create a rolling file appender
            LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout")
                                                          .addAttribute("pattern", strPattern);
            ComponentBuilder triggeringPolicy = builder.newComponent("Policies")
                                                      // .addComponent(builder.newComponent("CronTriggeringPolicy").addAttribute("schedule", "0 0 0 * * ?"))
                                                       .addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", lngMaxSize));
             appenderBuilder = builder.newAppender("rolling"+strName, "RollingFile")
                                     .addAttribute("fileName", sPath)
                                     .addAttribute("filePattern",  "d:\\trash\\archive\\rolling-%d{MM-dd-yy}.log.gz")
                                     .add(layoutBuilder)
                                     .addComponent(triggeringPolicy);
            builder.add(appenderBuilder);

            // create the new logger
            builder.add( builder.newLogger( strName, Level.getLevel(strLevel) )
                                .add( builder.newAppenderRef( "rolling"+strName ) )
                                .addAttribute( "additivity", false ) );

            Configuration clsCnfg = (Configuration) builder.build();
            LoggerContext ctx = Configurator.initialize(clsCnfg);

            Logger clsLogger =  ctx.getLogger(strName);
            m_clsLoggers.put(strName, clsLogger);
            return clsLogger;
        }
        catch (Exception xcpE)
        {
            System.err.println(xcpE);
        }

        return null;
    }

    protected void createRootLogger()
    {
        try
        {
            ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder();

            builder.setStatusLevel(Level.getLevel("TRACE"));
            builder.setConfigurationName("rootConfig");

            // create a console appender
            AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE").addAttribute("target",
                                                                                                             ConsoleAppender.Target.SYSTEM_OUT);
            appenderBuilder.add(builder.newLayout("PatternLayout")
                                       .addAttribute("pattern", "[%d{yyyy-MMM-dd HH:mm:ss:SSS}][%-5p %l][%t] %m%n"));
            builder.add( appenderBuilder );

            builder.add( builder.newRootLogger( Level.getLevel("TRACE"))
                                .add( builder.newAppenderRef( "Stdout") ) );

            Configuration clsCnfg = (Configuration) builder.build();
            LoggerContext ctx = Configurator.initialize(clsCnfg);

            Logger clsLogger =  ctx.getRootLogger();
            m_clsLoggers.put("root", clsLogger);
        }
        catch (Exception xcpE)
        {
            System.err.println(xcpE);
        }
    }

    static public void main(String args[])
    {
        //Logger clsLogger = setLogger();

        Logger clsLogger = Emflex.LogManager.getInstance().createLogger(
                "AnsiAmrController_" + 5555,
                "d:\\trash\\LogManagerTest5555.log",
                10,
                100000000,
                "[%d{yyyy-MMM-dd HH:mm:ss:SSS}][%-5p %l][%t] %m%n",
                "TRACE"
                                                                       );

        Logger clsLogger2 = Emflex.LogManager.getInstance().createLogger(
                "AnsiAmrController_" + 6666,
                "d:\\trash\\LogManagerTest6666.log",
                10,
                100000000,
                "[%d{yyyy-MMM-dd HH:mm:ss:SSS}][%-5p %l][%t] %m%n",
                "TRACE"
                                                                       );

        for (int i=0;i<100;i++)
        {
            clsLogger.error("Testing - ["+i+"]");
            clsLogger2.error("Testing - ["+(i*i)+"]");
        }
    }
}
Ram
  • 1,225
  • 2
  • 24
  • 48

3 Answers3

3

You said your objective is:

For each device I need to create a different log file to log it's communication with device.

There are many different ways to accomplish this without programmatic configuration. Programmatic configuration is bad because it forces you to depend on the logging implementation rather than the public interface.

For example you could use a context map key in conjunction with a Routing Appender to separate your logs, similar to the example I gave in another answer. Note that in the other answer I used the variable as the folder where the log is stored but you can use it for the log name if you wish.

Another way to do what you want would be to use a MapMessage as shown in the log4j2 manual.

Yet another way would be to use markers in combination with a RoutingAppender. Here is some example code for this approach:

package example;

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 LogLvlByMarkerMain {
    private static final Logger log = LogManager.getLogger();
    private static final Marker DEVICE1 = MarkerManager.getMarker("DEVICE1");
    private static final Marker DEVICE2 = MarkerManager.getMarker("DEVICE2");

    public static void main(String[] args) {
        log.info(DEVICE1, "The first device got some input");
        log.info(DEVICE2, "The second device now has input");
    }
}

Configuration:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Routing name="MyRoutingAppender">
            <Routes pattern="$${marker:}">
                <Route>
                    <File
                        fileName="logs/${marker:}.txt"
                        name="appender-${marker:}">
                        <PatternLayout>
                            <Pattern>[%date{ISO8601}][%-5level][%t] %m%n</Pattern>
                        </PatternLayout>
                    </File>
                </Route>
            </Routes>
        </Routing>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <PatternLayout pattern="[%date{ISO8601}][%-5level][%t] %m%n" />
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="example" level="TRACE" additivity="false">
            <AppenderRef ref="STDOUT" />
            <AppenderRef ref="MyRoutingAppender" />
        </Logger>
        <Root level="WARN">
            <AppenderRef ref="STDOUT" />
        </Root>
    </Loggers>
</Configuration>

Output:

This will generate 2 log files - DEVICE1.txt and DEVICE2.txt as shown in the image below.

Generated Log Files

The first log will contain only messages that were marked as DEVICE1 and the second will contain only DEVICE2 logs.

I.e. the first log contains:

[2017-09-21T09:52:04,171][INFO ][main] The first device got some input

and the second contains:

[2017-09-21T09:52:04,176][INFO ][main] The second device now has input
D.B.
  • 4,523
  • 2
  • 19
  • 39
  • I tried your code here, but didn't see log files. Where are they generated? I tried changing fileName="logs/${marker:}.txt" to fileName="d:/trash/logs/${marker:}.txt" in xml file but still no files. However the link to another answer which you have given seems to be working. I will try modifying it. – Ram Sep 22 '17 at 12:13
  • The way I have it configured in the sample code the logs would be generated in a "logs" folder that is relative to the working directory. In my case I'm running inside an eclipse project so it's just creating a folder inside my project. I'm using version 2.8.1 of log4j2, so if you're using an older version you might try updating to a newer one. – D.B. Sep 22 '17 at 12:39
  • Hello @D.B., can I ask if it is possible to use this RoutingAppender with Rolling Files? So it would sort of have separate logs for separate objects, and each log would roll over depending on the cycle? – experiment unit 1998X Feb 15 '23 at 14:32
1

The approach log4j2 is initialize programmatically and later configuration is modified is different. And you you trying to add dynamic appender and logger using initialization approach.

So, first you should initialize your RootLogger using initialization approach that seems correct in your code.

After that, add dynamic appender and logger using approach mentioned here

Vikas Sachdeva
  • 5,633
  • 2
  • 17
  • 26
0

adding on D.B answer:

I had trouble making this write to file. (and yes I tried using log4j2 version 2.8.1 but still didn't work)

To make it work I edited this part

<Root level="WARN">
    <AppenderRef ref="STDOUT" />
</Root>

to this:

<Root level="WARN">
    <AppenderRef ref="STDOUT" />
    <AppenderRef ref="MyRoutingAppender" />
</Root>

And since the Debug level is set to WARN

<Configuration status="WARN">

and we trying to log info

log.info(DEVICE$, "The $ device now has input");

the info log wont be written (WARN will only print: warn, error, fatal check this link log4j logging level)

you can simply change

log.info() --> log.warn() 

just as a proof of concept.