Hello, As suggested, I'm trying to reduce the ringbuffer size to 1024 or other experimental values(even numbers). But log4j2 is taking the default value 262144, even though I have added the system properties. I have tried 3 ways, 1. Added VM argument as -Dlog4j2.asyncLoggerRingBufferSize=1024 -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector -Dlog4j2.debug 2. Added environment variable LOG4J_ASYNC_LOGGER_RING_BUFFER_SIZE=1024 3. Added the log4j2.component.properties file in the resources folder of spring boot application with the value log4j2.asyncLoggerRingBufferSize=1024
But still log4j2 is picking the default ringbuffer size 262144. Starting AsyncLogger disruptor for this context with ringbufferSize=262144, waitStrategy=TimeoutBlockingWaitStrategy Version details. 1. Spring boot version 3.0.5 (spring-boot-starter-parent), excluded spring-boot-starter-logging from startup web 2. Java version: 17 3. log4j2 version 2.20.0 (log4j-core and log4j-api) 4. tomcat version 10.1.7 5. disruptor version 3.4.4 Below is the log DEBUG StatusLogger Starting OutputStreamManager SYSTEM_OUT.false.false-1 TRACE StatusLogger [AsyncContext@46fbb2c1] AsyncLoggerDisruptor creating new disruptor for this context. DEBUG StatusLogger No AsyncWaitStrategyFactory was configured in the configuration, using default factory... TRACE StatusLogger DefaultAsyncWaitStrategyFactory property AsyncLogger.WaitStrategy=TIMEOUT TRACE StatusLogger DefaultAsyncWaitStrategyFactory creating TimeoutBlockingWaitStrategy(timeout=10, unit=MILLIS) DEBUG StatusLogger [AsyncContext@46fbb2c1] Starting AsyncLogger disruptor for this context with ringbufferSize=262144, waitStrategy=TimeoutBlockingWaitStrategy, exceptionHandler=org.apache.logging.log4j.core.async.AsyncLoggerDefaultExceptionHandler@418e7838. .. TRACE StatusLogger [AsyncContext@46fbb2c1] AsyncLoggers use a threadlocal translator DEBUG StatusLogger Starting LoggerContext[name=AsyncContext@46fbb2c1, org.apache.logging.log4j.core.async.AsyncLoggerContext@3c130745]... TRACE StatusLogger Reregistering MBeans after reconfigure. Selector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector@51dcb805 TRACE StatusLogger Reregistering context (1/1): 'AsyncContext@46fbb2c1' org.apache.logging.log4j.core.async.AsyncLoggerContext@3c130745 TRACE StatusLogger Unregistering 1 MBeans: [org.apache.logging.log4j2:type=AsyncContext@46fbb2c1] TRACE StatusLogger Unregistering 1 MBeans: [org.apache.logging.log4j2:type=AsyncContext@46fbb2c1 ,component=StatusLogger] TRACE StatusLogger Unregistering 1 MBeans: [org.apache.logging.log4j2:type=AsyncContext@46fbb2c1 ,component=ContextSelector] TRACE StatusLogger Unregistering but no MBeans found matching 'org.apache.logging.log4j2:type=AsyncContext@46fbb2c1 ,component=Loggers,name=*' TRACE StatusLogger Unregistering 1 MBeans: [org.apache.logging.log4j2:type=AsyncContext@46fbb2c1 ,component=Appenders,name=DefaultConsole-2] TRACE StatusLogger Unregistering but no MBeans found matching 'org.apache.logging.log4j2:type=AsyncContext@46fbb2c1 ,component=AsyncAppenders,name=*' TRACE StatusLogger Unregistering 1 MBeans: [org.apache.logging.log4j2:type=AsyncContext@46fbb2c1 ,component=AsyncLoggerRingBuffer] TRACE StatusLogger Unregistering but no MBeans found matching 'org.apache.logging.log4j2:type=AsyncContext@46fbb2c1 ,component=Loggers,name=*,subtype=RingBuffer' DEBUG StatusLogger Registering MBean org.apache.logging.log4j2:type=AsyncContext@46fbb2c1 DEBUG StatusLogger Registering MBean org.apache.logging.log4j2:type=AsyncContext@46fbb2c1 ,component=AsyncLoggerRingBuffer DEBUG StatusLogger Registering MBean org.apache.logging.log4j2:type=AsyncContext@46fbb2c1,component=StatusLogger DEBUG StatusLogger Registering MBean org.apache.logging.log4j2:type=AsyncContext@46fbb2c1 ,component=ContextSelector DEBUG StatusLogger Registering MBean org.apache.logging.log4j2:type=AsyncContext@46fbb2c1 ,component=Appenders,name=DefaultConsole-3 TRACE StatusLogger Using DummyNanoClock for nanosecond timestamps. Thanks, Ganesh S On Tue, Feb 20, 2024 at 4:43 PM Remko Popma <remko.po...@gmail.com> wrote: > 197 KB for each log message! > That’s about 100-1000x larger than the average message size. > > I would suggest having a much smaller ringbuffer, meaning fewer slots. > > The system property that controls this is > log4j2.asyncLoggerRingBufferSize > > The default is (256 * 1024), which is a lot. The minimum is 128. You will > have to experiment what works best for the workload and “spikeyness” of > your application. > > If you specify 1024, then you’ll reduce memory usage by 256x. > > See https://logging.apache.org/log4j/2.x/manual/async.html for details. > > > > > On Feb 20, 2024, at 20:00, Anthony Maire <maire.anth...@gmail.com> > wrote: > > > > There are JVM properties to reduce both the number of slot of the ring > > buffer, and the maximum size of the StringBuilder used inside each slot. > > Tuning these should help to lower the extra memory footprint. I don't > > remember them (and cannot do the search right now) but they are > documented > > on log4j website. > > > > Using async loggers is a tradeoff, you hide the disk storage latency and > > reduce object allocations at the cost of some extra memory footprint. If > > this is a limiting factor, maybe you should re-evaluate your options to > see > > if async loggers are the best configuration for your use case, especially > > if your application does not log massively. > > > >> Le mar. 20 févr. 2024 à 11:24, Ganesh S <ganesh123.g...@gmail.com> a > écrit : > >> > >> Hello, > >> I'm using asynchronous Rolling file appender by passing > >> > *-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector* > >> as VM argument, which internally uses lmax disruptor. > >> *Load test specs:* > >> 1000 threads, 0 ramp-up period, 1 loop count. > >> Business application which will log 197 KB json for each log message > >> and each request thread will log 2 MB considering all the log > statements in > >> the application. Similarly there will be 1000 simultaneous request > threads > >> coming to the tomcat server. > >> When we compare asynchronous logging with synchronous logging the memory > >> consumption is too high in asynchronous logging. > >> > >> The major memory contributor is the byte[] object, which is 2.02 GB and > >> instance count is 1,73,734. > >> Whereas in synchronous logging the byte[] object took 900 MB max. > >> [image: image.png] > >> > >> If we check the incoming reference to the byte[] object, > >> RingBufferLogEvent is pointing to the byte[] object through > SimpleMessage > >> class. > >> [image: image.png] > >> > >> *RingBufferLogEvent is pointing to SimpleMessage which is storing the > log > >> message as byte[], which is of the size 197 kB (equal to single log > message > >> size).* > >> > >> [image: image.png] > >> *Similar to that, there are 1,73,534 instances.* > >> > >> *If we see the shallow size of RingBufferLogEvent, it is 27 MB, but > >> including the external reference object, i.e., byte[], it reaches 2.02 > GB.* > >> [image: image.png] > >> *In Async logging, number of waiting thread are high, since lmax > >> disruptor is based on inter-thread messaging* > >> > >> [image: image.png] > >> > >> *In sync logging number of blocking threads are high, since it uses lock > >> based logging* > >> > >> [image: image.png] > >> > >> Waiting and runnable threads are more in async logging therefore the > >> memory consumed could be high. If we see the throughput of request > threads, > >> sync took 32 sec and async took 5 sec for 1000 concurrent requests. > Async > >> logging took an extra 5 sec in the background, since the daemon thread > was > >> pushing remaining logs to the file, which won't affect the business > thread > >> throughput. > >> > >> *Problem:* > >> But the problem is, we can't afford extra 2 GB memory on top of the > memory > >> required for running the business logic. Atleast async logging should > take > >> reasonable memory near to synchronous logging with good throughput. If > we > >> allocate reasonable memory, due to a spike in the memory (though GC will > >> collect the unreferred objects after the load is less), application will > >> return the heap memory issue. > >> > >> Below is the programmatic initialization of the rolling file appender > that > >> we are using. > >> > >> ConfigurationBuilder<BuiltConfiguration> builder = > ConfigurationBuilderFactory.newConfigurationBuilder(); > >> LayoutComponentBuilder layoutBuilder = > builder.newLayout("PatternLayout") > >> .addAttribute("pattern", "%m%n"); > >> AppenderComponentBuilder appenderBuilder = > builder.newAppender("rolling", "RollingFile") > >> .addAttribute("fileName", path) > >> .addAttribute("filePattern", name + "-%i.log") > >> .add(layoutBuilder); > >> appenderBuilder.addComponent(builder.newComponent("Policies") > >> > .addComponent(builder.newComponent("SizeBasedTriggeringPolicy") > >> .addAttribute("size", maxFileSize))); > >> > appenderBuilder.addComponent(builder.newComponent("DefaultRolloverStrategy") > >> .addAttribute("max", maxBackUpFile) > >> .addAttribute("fileIndex", "max")); > >> appenderBuilder.add(builder.newFilter("ThresholdFilter", > Filter.Result.ACCEPT, Filter.Result.DENY) > >> .addAttribute("level", fileLevel)); > >> builder.add(appenderBuilder); > >> builder.add(builder.newRootLogger(fileLevel) > >> .add(builder.newAppenderRef("rolling"))); > >> Configurator.reconfigure(builder.build()); > >> > >> Which is similar to > >> > >> <?xml version="1.0" encoding="UTF-8"?> > >> <Configuration status="INFO"> > >> <Appenders> > >> <RollingFile > >> name="rolling" > >> fileName="path" > >> filePattern="name-%i.log"> > >> <PatternLayout pattern="%m%n"/> > >> <Policies> > >> <SizeBasedTriggeringPolicy size="10MB"/> > >> </Policies> > >> <DefaultRolloverStrategy max="5" fileIndex="max"/> > >> <Filters> > >> <ThresholdFilter level="debug" onMatch="ACCEPT" > onMismatch="DENY"/> > >> </Filters> > >> </RollingFile> > >> </Appenders> > >> > >> <Loggers> > >> <Root level="debug"> > >> <AppenderRef ref="rolling"/> > >> </Root> > >> </Loggers> > >> </Configuration> > >> > >> logger = LogManager.getLogger(); > >> logger.info("message1"); > >> logger.debug("message2"); > >> logger.error("message3"); > >> > >> Any input or solution is appreciated. Thanks in advance. > >> > >> Thanks, > >> Ganesh S > >> > >> > >> > >> >