Search code examples
javaspringspring-bootlogginglog4j

Use separate Log4j Core log file per level


I want my warnings, errors, and info to be saved in separate files. All 2 files are created automatically, however in the info.log, I see warnings as well. How can I fix log4j2.xml, so all three files, that is warn.log, info.log, and error.log contain ONLY warnings, info, and errors respectively?

Here is the log4j2.xml implementation:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Properties>
        <Property name="PID">%pid</Property>
        <Property name="LOG_DIRECTORY">/media/edoyou/HDD/Kanan/k2s-project/k2sbeauty/logs</Property>
        <Property name="LOG_EXCEPTION_CONVERSION_WORD">%xwEx</Property>
        <Property name="LOG_LEVEL_PATTERN">%5p</Property>
        <Property name="LOG_DATEFORMAT_PATTERN">yyyy-MM-dd HH:mm:ss.SSS</Property>
        <Property name="CONSOLE_LOG_PATTERN">%clr{%d{${LOG_DATEFORMAT_PATTERN}}}{faint} %clr{${LOG_LEVEL_PATTERN}}
            %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint}
            %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}
        </Property>
        <Property name="FILE_LOG_PATTERN">%d{${LOG_DATEFORMAT_PATTERN}} ${LOG_LEVEL_PATTERN} ${sys:PID} --- [%t]
            %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}
        </Property>
    </Properties>

    <Appenders>
        <Console name="Console" target="SYSTEM_OUT" follow="true">
            <PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}"/>
        </Console>
        <RollingFile name="InfoFile" fileName="${LOG_DIRECTORY}/info.log"
                     filePattern="${LOG_DIRECTORY}/logs/info-%d{yyyy-MM-dd}-%i.log.gz">
            <PatternLayout pattern="${sys:FILE_LOG_PATTERN}"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="10"/>
            <Filters>
                <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
                <ThresholdFilter level="warn" onMatch="DENY" onMismatch="DENY"/>
                </Filters>
        </RollingFile>
        <RollingFile name="ErrorFile" fileName="${LOG_DIRECTORY}/error.log"
                     filePattern="${LOG_DIRECTORY}/logs/error-%d{yyyy-MM-dd}-%i.log.gz">
            <PatternLayout pattern="${sys:FILE_LOG_PATTERN}"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="10"/>
            <Filters>
                <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
                <ThresholdFilter level="warn" onMatch="DENY" onMismatch="DENY"/>
            </Filters>
        </RollingFile>
        <RollingFile name="WarnFile" fileName="${LOG_DIRECTORY}/warn.log"
                     filePattern="${LOG_DIRECTORY}/logs/warn-%d{yyyy-MM-dd}-%i.log.gz">
            <PatternLayout pattern="${sys:FILE_LOG_PATTERN}"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="10"/>
            <Filters>
                <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
                <ThresholdFilter level="info" onMatch="DENY" onMismatch="DENY"/>
            </Filters>
        </RollingFile>

    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="InfoFile"/>
            <AppenderRef ref="WarnFile"/>
            <AppenderRef ref="ErrorFile"/>
        </Root>
    </Loggers>
</Configuration>


Solution

  • Filters can have three results:

    • ACCEPT - accepts the message,
    • DENY - drops the message,
    • NEUTRAL - continues to the next filter.

    In your configuration:

    <Filters>
        <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
        <ThresholdFilter level="warn" onMatch="DENY" onMismatch="DENY"/>
    </Filters>
    

    the first filter accepts all messages at least as specific as INFO. The second filter is never used. What you want is:

    <Filters>
        <ThresholdFilter level="INFO" onMatch="NEUTRAL" onMismatch="DENY"/>
        <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
    </Filters>
    

    Remark: since version 2.13.0 there is a LevelMatchFilter that you can use instead of the entire <Filters> component:

    <LevelMatchFilter level="INFO"/>
    

    Alternatively you can use a RoutingAppender to define a single appender without filters, e.g.:

    <Routing name="FILE">
      <Routes pattern="$${event:Level}">
        <Route>
          <RollingFile name="${event:Level}File"
                       fileName="${LOG_DIRECTORY}/${lower:${event:Level}}.log"
                       filePattern="${LOG_DIRECTORY}/logs/${lower:${event:Level}}-%d{yyyy-MM-dd}-%i.log.gz">
            <PatternLayout pattern="${sys:FILE_LOG_PATTERN}"/>
            <SizeBasedTriggeringPolicy size="10 MB"/>
            <DefaultRolloverStrategy max="10"/>
          </RollingFile>
        </Route>
      </Routes>
    </Routing>