andersonbd1
andersonbd1

Reputation: 5416

How to configure log4j2's additivity to respect parent's level?

I think this code explains well enough what I'm trying to do

package us.benanderson;

public class MyClass {

  private static final Logger LOG = LogManager.getLogger(MyClass.class);
  ...
    // within a method
    LOG.debug("only output to appFile");
    LOG.error("output to both appFile and errorFile");

This is what I'd guess should be my config

<Loggers>
    <Logger name="us.benanderson" level="debug" additivity="true">
        <AppenderRef ref="appFile" />
    </Logger>

    <Root level="error">
        <AppenderRef ref="errorFile" />
    </Root>
</Loggers>

However, I'm finding that when additivity="true", debug messages are also output to errorFile. When additivity="false" nothing is output to errorFile. Is there any way to do what I'm trying to do?

Upvotes: 6

Views: 20685

Answers (4)

Remko Popma
Remko Popma

Reputation: 36844

I think the simplest way to achieve what you want is to put a level directly on the appender-ref. This eliminates the need to have a named logger in your config, simplifying the configuration a little.

Named loggers are still useful if you want to redirect log events for a certain package to a separate appender.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <File name="appFile" fileName="logs/appFile.log" append="true">
      <PatternLayout pattern="%-5p %d{ABSOLUTE} [%t] %c - %m%n" />
    </File>
    <File name="errorFile" fileName="logs/errorFile.log" append="true">
      <PatternLayout pattern="%-5p %d{ABSOLUTE} [%t] %c - %m%n" />
    </File>
  </Appenders>
  <Loggers>
    <Root level="trace">
      <AppenderRef ref="appFile" level="debug" />
      <AppenderRef ref="errorFile" level="error" />
    </Root>
  </Loggers>
</Configuration>

Threshold filters and filter combinations can be especially useful if you want to do more unusual filtering. For example, if you want to send only INFO and WARN level events to a certain appender, excluding TRACE/DEBUG, and also excluding ERROR and FATAL level events(!), you could do this:

<Console name="only-info-warn">
    <PatternLayout pattern="%-5p %c %message %n" />
    <Filters>

        <!-- First deny error and fatal messages -->
        <ThresholdFilter level="error" onMatch="DENY"   onMismatch="NEUTRAL"/>
        <ThresholdFilter level="fatal" onMatch="DENY"   onMismatch="NEUTRAL"/>

        <!-- Then accept info, warn, error, fatal and deny debug/trace -->
        <ThresholdFilter level="warn"  onMatch="ACCEPT" onMismatch="NEUTRAL"/>
        <ThresholdFilter level="info"  onMatch="ACCEPT" onMismatch="DENY"/>
    </Filters>
</Console>

Upvotes: 8

Paul Vargas
Paul Vargas

Reputation: 42060

Since you are using Log4j 2, you may want to use a ThresholdFilter. Using this filter, your configuration file can be as follows:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <File name="appFile" fileName="logs/appFile.log" append="true">
      <PatternLayout pattern="%d %-5p (%c.java:%L).%M - %m%n" />
      <ThresholdFilter level="DEBUG" />
    </File>
    <File name="errorFile" fileName="logs/errorFile.log" append="true">
      <PatternLayout pattern="%d %-5p (%c.java:%L).%M - %m%n" />
      <ThresholdFilter level="ERROR" />
    </File>
  </Appenders>
  <Loggers>
    <Logger name="us.benanderson" level="debug">
      <AppenderRef ref="appFile" />
    </Logger>
    <Root level="error">
      <AppenderRef ref="errorFile" />
    </Root>
  </Loggers>
</Configuration>

Upvotes: 3

M..
M..

Reputation: 900

Based on your requirements; shouldn't the configuration be as below(with comments):-

<Loggers>
    <!-- Do not trickle down 'debug' logs to parent logger using additivity as false; keeping them only in appFile -->
    <Logger name="us.benanderson" level="debug" additivity="false">
            <AppenderRef ref="appFile" />
    </Logger>
    <!-- Error messages by default go to both appfile and errorFile -->
    <Root level="error">
            <AppenderRef ref="errorFile" />
        <AppenderRef ref="appFile" />
    </Root>
</Loggers>

If you are specifically worried about error logs from "us.benanderson" package; you could try having a separate logger for 'error' level. So the configuration would look like :-

<Loggers>
    <Logger name="us.benanderson" level="debug" additivity="false">
        <AppenderRef ref="appFile" />
    </Logger>
    <!-- Not specifying additivity here would default to 'true'; hence the error logs   would also be relayed to the root/errorFile -->
    <Logger name="us.benanderson" level="error">
        <AppenderRef ref="appFile" />
    </Logger>
    <Root level="error">
        <AppenderRef ref="errorFile" />
    </Root>
</Loggers>

Upvotes: 2

Pokechu22
Pokechu22

Reputation: 5046

Use a filter.

Here's a configuration that outputs everything to C:/Logs/Log.log, and additionally outputs debug only to C:/Logs/Debug.log:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="true">
    <appender name="general-out" class="org.apache.log4j.FileAppender"> 
        <param name="File" value="C:/Logs/Log.log"/> 
        <layout class="org.apache.log4j.PatternLayout"> 
            <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p %c{1} : %m%n"/> 
        </layout> 
    </appender> 

    <appender name="debug-out" class="org.apache.log4j.FileAppender"> 
        <param name="File" value="C:/Logs/Debug.log"/>
        <layout class="org.apache.log4j.PatternLayout"> 
            <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p %c{1} : %m%n"/> 
        </layout> 
        <filter class="org.apache.log4j.varia.LevelMatchFilter">
            <param name="LevelToMatch" value="debug"/> 
            <param name="AcceptOnMatch" value="true"/> 
        </filter>     
        <filter class="org.apache.log4j.varia.DenyAllFilter"/> 
    </appender> 

    <root>
        <appender-ref ref="debug-out" />
        <appender-ref ref="general-out" /> 
    </root> 
</log4j:configuration>

The key part here is this:

<filter class="org.apache.log4j.varia.LevelMatchFilter">
    <param name="LevelToMatch" value="debug"/> 
    <param name="AcceptOnMatch" value="true"/> 
</filter>     
<filter class="org.apache.log4j.varia.DenyAllFilter"/> 

This allows things logged with debug, and then denies anything else.

Source: http://wiki.apache.org/logging-log4j/LogByLevel

Upvotes: 4

Related Questions