asynchronousloggingsynchronizationlog4j2mixed

mixed sync/async logging log4j does not work


I am trying to analyze and implement mixed sync and async logging. I am using Spring boot application along with disruptor API. My log4j configuration:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug">
<Appenders>
    <Console name="Console-Appender" target="SYSTEM_OUT">
        <PatternLayout>
            <pattern>
                [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
            </pattern>>
        </PatternLayout>
    </Console>        
</Appenders>
<Loggers>
    <AsyncLogger name="com.example.logging" level="debug">
        <AppenderRef ref="Console-Appender"/>
    </AsyncLogger>
    <Root level="info">
        <AppenderRef ref="Console-Appender"/>            
    </Root>
</Loggers>

Demo class 1:

package com.example.logging;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

@SpringBootApplication
public class DemoApplication2 {

static Logger logger = LogManager.getLogger(DemoApplication2.class);

public static void main(String[] args) {
    SpringApplication.run(DemoApplication2.class, args);        
    long startTime = System.currentTimeMillis();
    
    for(int i = 0; i < 2000; i++) { 
        logger.debug("Async : " + i);   
    }
                            
    System.out.println("time taken:: " + (System.currentTimeMillis() - startTime));     
 }

}

Using above code, I am expecting "System.out" should print before the logging of all "debug" statements as I am using async logging for "debug" level. So, few debugger logs would be logged first (e.g. few 100 or 150) then SOP should be printed and then remaining debugger logs should be logged. But, when I run my application, all debugger statements log first and then SOP prints which is not expected result. Furthermore, if I use additivity="false" in the "asynclogger" (<AsyncLogger name="com.example.logging" level="debug" additivity="false">) then I can see my expected above mentioned result. Now I have 2nd demo class:

package com.example.logging;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

@SpringBootApplication
public class DemoApplication3 {

static Logger logger = LogManager.getLogger(DemoApplication3.class);

public static void main(String[] args) {
    SpringApplication.run(DemoApplication3.class, args);        
    long startTime = System.currentTimeMillis();                    
    
    for(int i = 0; i < 2000; i++) {
        logger.info("Sync : " + i);
    }
    
    System.out.println("time taken:: " + (System.currentTimeMillis() - startTime));     
 }

}

Now with above class, I am expecting all sync logging first and SOP should be printed after all info logs. But if add "additivity="false" to my configuration then all logs are async only.

Finally, I couldn't able to configure sync and async logging at the same time. Kindly help and suggest.


Solution

  • I'm not really sure what you think you are testing.

    When additivity is enabled the log event will be copied and placed into the Disruptor's Ring Buffer where it will be routed to the console appender on a different thread. After placing the copied event in the buffer the event will be passed to the root logger and routed to the Console Appender in the same thread. Since both the async Logger and sync Logger are doing the same thing they are going to take approximately the same time. So I am not really sure why you believe anything will be left around by the time the System.out call is made.

    When you only use the async logger the main thread isn't doing anything but placing events in the queue, so it will respond much more quickly and it would be quite likely your System.out message would appear before all log events have been written.

    I suspect there is one very important piece of information you are overlooking. When an event is routed to a Logger the level specified on the LoggerConfig the Logger is associated with is checked. When additivity is true the event is not routed to a parent Logger (there isn't one). It is routed to the LoggerConfig's parent LoggerConfig. A LoggerConfig calls isFiltered(event) which ONLY checks Filters that have been configured on the LoggerConfig. So even though you have level="info" on your Root logger, debug events sent to it via the AsyncLogger will still be logged. You would have to add a ThresholdFilter to the RootLogger to prevent that.