Search code examples
javalogback

Is there a way to configure logback from two separate logback.xml files per log level?


I have a logback.xml outside my packaged .war that I have to use for INFO & ERROR logging configuration - and no changes can be made to it . [edited: might be possible to add a or something similarly minimal ]
The goal I would like to set up my debug logging in such a way that each module has its own .log file, etc - and I can't add this configuration to the existing logback.xml, as stated above. [edited in] Also, I need a way to enable debug for specific module only -preferably via "my" added logback.xml

How do I get logback to read just the debug configuration from a different .xml file ?

Edited to add sample files:

the main/aka unchangeable logback.xml

<?xml version="1.0" encoding="UTF-8"?>

<property scope="system" name="component-name" value="COMPONENT_A" />
<property file="$configuration.yaml" />
<property scope="context" name="enable-all-log" value="false" />

    <property name="default-log-pattern"
    value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%X{uuid}|%X{instanceID}|%thread||%X{userId}|%level />

<!-- All log -->
<if condition='property("enable-all-log").equalsIgnoreCase("true")'>
    <then>
        <appender name="ALL_ROLLING"
            class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${log.home}/${component-name}/${subcomponent-name}/all.log
            </file>

            <rollingPolicy
                class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
                <fileNamePattern>${log.home}/${component-name}/${subcomponent-name}/all.log.%i
                </fileNamePattern>
                <minIndex>1</minIndex>
                <maxIndex>10</maxIndex>
            </rollingPolicy>

            <triggeringPolicy
                class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                <maxFileSize>20MB</maxFileSize>
            </triggeringPolicy>
            <encoder>
                <pattern>${default-log-pattern}</pattern>
            </encoder>
        </appender>

        <appender name="ASYNC_ALL" class="ch.qos.logback.classic.AsyncAppender">
            <appender-ref ref="ALL_ROLLING" />
        </appender>
    </then>
</if>

<!-- Error log -->
<appender name="ERROR_ROLLING"
    class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${log.home}/${component-name}/${subcomponent-name}/error.log
    </file>

    <!-- Audit messages filter - deny audit messages -->
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
        <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
            <marker>AUDIT_MARKER</marker>
        </evaluator>
        <onMismatch>NEUTRAL</onMismatch>
        <onMatch>DENY</onMatch>
    </filter>   
<!-- Debug log -->
<appender name="DEBUG_ROLLING"
    class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${log.home}/${component-name}/${subcomponent-name}/debug.log
    </file>


    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
        <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
            <marker>TRANSACTION_MARKER</marker>
        </evaluator>
        <onMismatch>NEUTRAL</onMismatch>
        <onMatch>DENY</onMatch>
    </filter>

    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
        <evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator">
            <expression>
                e.level.toInt() &lt;= DEBUG.toInt()
            </expression>
        </evaluator>
        <OnMismatch>DENY</OnMismatch>
        <OnMatch>NEUTRAL</OnMatch>
    </filter>

    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
        <fileNamePattern>${log.home}/${component-name}/${subcomponent-name}/debug.log.%i
        </fileNamePattern>
        <minIndex>1</minIndex>
        <maxIndex>10</maxIndex>
    </rollingPolicy>

    <triggeringPolicy
        class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
        <maxFileSize>20MB</maxFileSize>
    </triggeringPolicy>
    <encoder>
        <pattern>${default-log-pattern}</pattern>
    </encoder>
</appender>

<root level="INFO">
    <appender-ref ref="ASYNC_ERROR" />
    <appender-ref ref="ASYNC_DEBUG" />
    <if condition='property("enable-all-log").equalsIgnoreCase("true")'>
        <then>
            <appender-ref ref="ALL_ROLLING" />
        </then>
    </if>
</root>

<logger name="foo.bar" level="INFO" />


Solution

  • You can use the Logback fileInclusion mechanism.

    From the documentation :

    Joran supports including parts of a configuration file from another file. This is done by declaring a element.

    If you don't know, Joran is a component on which Logback relies on. You have no additional library to add.
    Here are the 3 ways of including a configuration file :

    The contents to include can be referenced as a file, as a resource, or as a URL.

    As a file: To include a file use the file attribute. You can use relative paths but note that the current directory is defined by the application and is not necessarily related to the path of the configuration file.

    As a resource:

    To include a resource, i.e a file found on the class path, use the resource attribute.

    <include resource="includedConfig.xml"/>
    

    As a URL:

    To include the contents of a URL use the url attribute.


    Here are some hints about how to implement it.

    Beware of duplicated elements between configuration files

    I would like to warn you about unique elements which should not be duplicated between the logback configuration files, as for example the root element.

    Your actual configuration declares very probably the root element, so you should not specify it again in the new configuration file.
    It is the same thing for the logger elements for a specific class or package : you should not declare them twice to avoid unexpected behavior at runtime.

    Which configuration file(s) should include other(s)

    If the actual configuration file contains general configuration and the the new configuration files contain specific configurations, it makes sense to make the actual configuration file which one that includes the others.

    Split configuration files

    If you think that having multiple configurations may help you to have a cleaner configuration, don't hesitate to split it in multiples files :

    ...   
    <include resource="other-logback-debug_product_a.xml"/>
    <include resource="other-logback-debug_product_b.xml"/>
    ...
    

    Be aware of the additivity

    In the included resources, you should probably specify the additivity to false (which is true by default) for all loggers in so far as I think you don't want that the debugging logs to be duplicated in the ancestor's appenders.

    Example

    The included file defines a RollingFileAppender named FILE_DEBUG_KIOSK_PRODUCT.
    It also defines a logger for the com.product.kiosk.area package with the level of log to DEBUG and it attaches to it the appender.
    I have cooked it by starting from one of appenders your posted (I removed not relevant parts).

    The configuration file that includes :

    <configuration>    
      <include file="includedConfFooLogback.xml"/>  
    </configuration>
    

    includedConfFooLogback.xml, the included configuration file :

    <?xml version="1.0" encoding="UTF-8"?>
    
    <included>
    
        <!-- Debug log -->
        <appender name="FILE_DEBUG_KIOSK_PRODUCT"
            class="ch.qos.logback.core.rolling.RollingFileAppender">
    
            <file>${debug.kiosk.product.dir}/debug.log</file>
    
            <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
                <fileNamePattern>${log.home}/${component-name}/${subcomponent-name}/debug.log.%i
                </fileNamePattern>
                <minIndex>1</minIndex>
                <maxIndex>10</maxIndex>
            </rollingPolicy>
    
            <triggeringPolicy
                class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                <maxFileSize>20MB</maxFileSize>
            </triggeringPolicy>
            <encoder>
               <pattern>${default-log-pattern}</pattern>
            </encoder>
        </appender>
    
        <logger name="com.product.kiosk.area" level="DEBUG" additivity="false">
            <appender-ref ref="FILE_DEBUG_KIOSK_PRODUCT" />
        </logger>
    
    </included>