5

I'm using Log4j 2 to log the events of my application. However I'm stuck at the following problem.

Currently all logging messages are being written to two different appenders. One has RollingFile type, while the other has Console type.

What I want is for the RollingFile appender to log messages with an INFO level or higher (ERROR, FATAL), and for the Console appender to log messages with an ERROR level or higher (FATAL).

Inside my log4j2.xml file I seem to be only able to declare the logging level for an entire logger (including all of its appenders). Here is my log4j2.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="30">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout>
                <Pattern>%d %level %msg%n</Pattern>
            </PatternLayout>
        </Console>
        
        <RollingFile name="Log" fileName="log/Log.log" filePattern="log/Log-%d{yyyy-MM-dd}-%i.log" append="false">
            <PatternLayout>
                <Pattern>%d %level %msg%n</Pattern>
            </PatternLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="1 MB" />
            </Policies>
            <DefaultRolloverStrategy max="10"/>
        </RollingFile>
    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console" />
            <AppenderRef ref="Log" />
        </Root>
    </Loggers>
</Configuration>

Is there an easy way of doing so? I searched log4j documentation but couldn't find what I was looking for (Maybe I missed it?). If it's possible I would really prefer for the solution to be applicable on any appenders' types; not specific for RollingFile and Console.

EDIT:

I saw many questions where it was asked to write ONLY the messages from a certain level to a file, while writing the messages from a different level to a different file. In my case I need the messages with a certain level of HIGHER to be written to different files. For example in the case I provided messages with level ERROR or FATAL will be written to both the RollingFile and Console, while messages with level INFO will be written to RollingFile only.

Said A. Sryheni
  • 697
  • 1
  • 9
  • 29
  • Possible duplicate of [Different level of logs in different log files](https://stackoverflow.com/questions/28740468/different-level-of-logs-in-different-log-files) – Parth Jan 31 '18 at 12:54
  • I really don't think so. In the question you provided it was about writing **ONLY** the INFO messages to a file, and **ONLY** the ERROR messages to a different file. In my case I want to write the messages from a certain level or **HIGHER** to separate files. – Said A. Sryheni Jan 31 '18 at 13:02

2 Answers2

5

To limit logging level on specific appender in log4j2 you should use ThresholdFilter element of an appender.

In your case log4j2.xml file will look like:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="30">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout>
                <Pattern>%d %level %msg%n</Pattern>
            </PatternLayout>
        </Console>
    
        <RollingFile name="Log" fileName="log/Log.log" filePattern="log/Log-%d{yyyy-MM-dd}-%i.log" append="false">
            <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout>
                <Pattern>%d %level %msg%n</Pattern>
            </PatternLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="1 MB" />
            </Policies>
            <DefaultRolloverStrategy max="10"/>
        </RollingFile>
    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console" />
            <AppenderRef ref="Log" />
        </Root>
    </Loggers>
</Configuration>

Here is a simple test:

public class Log4jTest {
    private static final Logger logger = LogManager.getLogger(Log4jTest.class);

    public static void main(String[] args) {
        logger.debug("Debug");
        logger.info("Info");
        logger.warn("Warning");
        logger.error("Error");
        logger.fatal("Fatal");
    }
}

Console output:

2020-02-25 12:33:50,587 ERROR Error
2020-02-25 12:33:50,589 FATAL Fatal

Log.log contents:

2020-02-25 12:33:50,585 INFO Info
2020-02-25 12:33:50,587 WARN Warning
2020-02-25 12:33:50,587 ERROR Error
2020-02-25 12:33:50,589 FATAL Fatal

In first version of log4j it was Threshold property of appender. On how to solve the same in log4j see the answer on question.

With aid of filters Log4j2 allows to configure the output to a specific appender much more flexible then log4j.

Community
  • 1
  • 1
Eliahu
  • 172
  • 3
  • 9
  • @Said, I don’t think that this is still relevant for you, the question was asked 2 years ago. I was just looking for a solution to a similar problem and found your question unanswered. I published here the solution that worked for me. – Eliahu Feb 25 '20 at 11:26
1

This should do the trick.

<Loggers>
    <Root level="debug" additivity="false">
        <AppenderRef ref="Console"/>
    </Root>
    <Logger name="com.project.package" level="info" additivity="false">
        <AppenderRef ref="Log" />
    </Logger>
</Loggers>

Alternatively, you could do it like this - Different level of logs in different log files

BTW, this is explained very nicely in the Log4j2 documentation. See - https://logging.apache.org/log4j/2.x/manual/configuration.html

Parth
  • 367
  • 6
  • 18
  • What does this exactly mean? and what if I had 3 appenders? what should I do then? – Said A. Sryheni Jan 31 '18 at 12:33
  • You can have as many 'appenders' as you like. The 'additivity' attribute along with the 'name' attribute will help you control whether you want those logs logged in all 'appenders' or not. – Parth Jan 31 '18 at 12:35
  • Look at the 'Additivity' section in Logj2 documentation in the link that I have shared. – Parth Jan 31 '18 at 12:36
  • That wouldn't solve my problem. From the documentation the general approach is to use LogManager.getLogger() to get the logger corresponding to each class. You are not suggesting that I do this for each package in my application are you? :) – Said A. Sryheni Jan 31 '18 at 12:37
  • I believe that is indeed what you should do. The alternatives - https://softwareengineering.stackexchange.com/questions/287896/is-logger-getloggermyclass-class-the-best-way-to-initialise-log4j-loggers are not great! :) – Parth Jan 31 '18 at 12:42
  • I really hope someone gives me a better solution. Otherwise I will need to change my log4j2.xml file every time I create a new package. I don't think that's a good approach to go with. – Said A. Sryheni Jan 31 '18 at 12:44
  • You don't have to do it every time you add a new package. You misunderstand - you only need to do this for packages where you want to use different appenders. If you have packages like 'com.myorg.a', 'com.myorg.b', 'com.myorg.c', etc. Just defining a single logger with 'com.myorg' as the package name will do the trick for you. – Parth Jan 31 '18 at 12:46
  • That's the point. I want this to be done for the entire application. – Said A. Sryheni Jan 31 '18 at 12:52
  • Anyways I will wait for a little time to see if I can get a better answer. If not, I will mark your solution as the answer to my question. – Said A. Sryheni Jan 31 '18 at 13:11