Skip to content

Commit

Permalink
LOG4J2-3472 (master) make disruptor WaitStrategy configurable in Log4…
Browse files Browse the repository at this point in the history
…j configuration (#830)

LOG4J2-3472 (master) make disruptor WaitStrategy configurable in Log4j configuration (#830)
  • Loading branch information
remkop committed Apr 25, 2022
1 parent f951d93 commit 3d15928
Show file tree
Hide file tree
Showing 21 changed files with 647 additions and 60 deletions.
@@ -0,0 +1,68 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.core.async;

import com.lmax.disruptor.YieldingWaitStrategy;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.test.categories.AsyncLoggers;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.util.Constants;
import org.apache.logging.log4j.util.Strings;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import static org.junit.Assert.*;

@Category(AsyncLoggers.class)
public class AsyncWaitStrategyFactoryConfigGlobalLoggersTest {

@BeforeClass
public static void beforeClass() {
System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR,
AsyncLoggerContextSelector.class.getName());
System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
"AsyncWaitStrategyFactoryConfigGlobalLoggerTest.xml");
}

@AfterClass
public static void afterClass() {
System.clearProperty(Constants.LOG4J_CONTEXT_SELECTOR);
System.clearProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY);
}

@Ignore("This test succeeds when run individually but fails when run by Surefire with all other tests")
@Test
public void testConfigWaitStrategyAndFactory() throws Exception {
final AsyncLogger logger = (AsyncLogger) LogManager.getLogger("com.foo.Bar");

final LoggerContext context = (LoggerContext) LogManager.getContext(false);
assertTrue("context is AsyncLoggerContext", context instanceof AsyncLoggerContext);

AsyncWaitStrategyFactory asyncWaitStrategyFactory = context.getConfiguration().getAsyncWaitStrategyFactory();
assertEquals(AsyncWaitStrategyFactoryConfigTest.YieldingWaitStrategyFactory.class, asyncWaitStrategyFactory.getClass());
assertTrue("factory is YieldingWaitStrategyFactory", asyncWaitStrategyFactory instanceof AsyncWaitStrategyFactoryConfigTest.YieldingWaitStrategyFactory);

AsyncLoggerDisruptor delegate = logger.getAsyncLoggerDisruptor();

assertEquals(YieldingWaitStrategy.class, delegate.getWaitStrategy().getClass());
assertTrue("waitstrategy is YieldingWaitStrategy", delegate.getWaitStrategy() instanceof YieldingWaitStrategy);
}
}
@@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.core.async;

import com.lmax.disruptor.TimeoutBlockingWaitStrategy;
import com.lmax.disruptor.WaitStrategy;
import com.lmax.disruptor.YieldingWaitStrategy;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.test.categories.AsyncLoggers;
import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
import org.apache.logging.log4j.core.test.junit.Named;
import org.apache.logging.log4j.core.test.appender.ListAppender;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

@Tag("async")
public class AsyncWaitStrategyFactoryConfigTest {

@Test
@LoggerContextSource("AsyncWaitStrategyFactoryConfigTest.xml")
public void testConfigWaitStrategyFactory(final LoggerContext context) throws Exception {
AsyncWaitStrategyFactory asyncWaitStrategyFactory = context.getConfiguration().getAsyncWaitStrategyFactory();
assertThat(asyncWaitStrategyFactory.getClass()).isEqualTo(YieldingWaitStrategyFactory.class);
assertThat(asyncWaitStrategyFactory instanceof YieldingWaitStrategyFactory); //"factory is YieldingWaitStrategyFactory"
}

@Test
@LoggerContextSource("AsyncWaitStrategyFactoryConfigTest.xml")
public void testWaitStrategy(final LoggerContext context) throws Exception {

org.apache.logging.log4j.Logger logger = context.getRootLogger();

AsyncLoggerConfig loggerConfig = (AsyncLoggerConfig) ((org.apache.logging.log4j.core.Logger) logger).get();
AsyncLoggerConfigDisruptor delegate = (AsyncLoggerConfigDisruptor) loggerConfig.getAsyncLoggerConfigDelegate();
assertThat(delegate.getWaitStrategy().getClass()).isEqualTo(YieldingWaitStrategy.class);
assertThat(delegate.getWaitStrategy() instanceof com.lmax.disruptor.YieldingWaitStrategy);// "waitstrategy is YieldingWaitStrategy");
}

@Test
@LoggerContextSource("AsyncWaitStrategyIncorrectFactoryConfigTest.xml")
public void testIncorrectConfigWaitStrategyFactory(final LoggerContext context) throws Exception {
AsyncWaitStrategyFactory asyncWaitStrategyFactory = context.getConfiguration().getAsyncWaitStrategyFactory();
assertThat(asyncWaitStrategyFactory).isNull(); // because invalid configuration
}

@Test
@LoggerContextSource("AsyncWaitStrategyIncorrectFactoryConfigTest.xml")
public void testIncorrectWaitStrategyFallsBackToDefault(
@Named("WaitStrategyAppenderList") final ListAppender list1,
final LoggerContext context) throws Exception {
org.apache.logging.log4j.Logger logger = context.getRootLogger();

AsyncLoggerConfig loggerConfig = (AsyncLoggerConfig) ((org.apache.logging.log4j.core.Logger) logger).get();
AsyncLoggerConfigDisruptor delegate = (AsyncLoggerConfigDisruptor) loggerConfig.getAsyncLoggerConfigDelegate();
assertThat(delegate.getWaitStrategy().getClass()).isEqualTo(TimeoutBlockingWaitStrategy.class);
assertThat(delegate.getWaitStrategy() instanceof TimeoutBlockingWaitStrategy); //"waitstrategy is TimeoutBlockingWaitStrategy"
}

public static class YieldingWaitStrategyFactory implements AsyncWaitStrategyFactory {
@Override
public WaitStrategy createWaitStrategy() {
return new YieldingWaitStrategy();
}
}
}
@@ -0,0 +1,64 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.core.async;

import com.lmax.disruptor.TimeoutBlockingWaitStrategy;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.test.categories.AsyncLoggers;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.util.Constants;
import org.apache.logging.log4j.util.Strings;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import static org.junit.Assert.*;

@Category(AsyncLoggers.class)
public class AsyncWaitStrategyFactoryIncorrectConfigGlobalLoggersTest {

@BeforeClass
public static void beforeClass() {
System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR,
AsyncLoggerContextSelector.class.getName());
System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
"AsyncWaitStrategyIncorrectFactoryConfigGlobalLoggerTest.xml");
}

@AfterClass
public static void afterClass() {
System.clearProperty(Constants.LOG4J_CONTEXT_SELECTOR);
System.clearProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY);
}


@Test
public void testIncorrectConfigWaitStrategyFactory() throws Exception {
final LoggerContext context = (LoggerContext) LogManager.getContext(false);
assertTrue("context is AsyncLoggerContext", context instanceof AsyncLoggerContext);

AsyncWaitStrategyFactory asyncWaitStrategyFactory = context.getConfiguration().getAsyncWaitStrategyFactory();
assertNull(asyncWaitStrategyFactory);

AsyncLogger logger = (AsyncLogger) context.getRootLogger();
AsyncLoggerDisruptor delegate = logger.getAsyncLoggerDisruptor();
assertEquals(TimeoutBlockingWaitStrategy.class, delegate.getWaitStrategy().getClass());
assertTrue("waitstrategy is TimeoutBlockingWaitStrategy", delegate.getWaitStrategy() instanceof TimeoutBlockingWaitStrategy);
}
}
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="asyncwait-global1" >
<!-- packages="org.apache.logging.log4j.core.async" -->

<AsyncWaitStrategyFactory
class="org.apache.logging.log4j.core.async.AsyncWaitStrategyFactoryConfigTest$YieldingWaitStrategyFactory" />

<Appenders>
<List name="WaitStrategyAppenderList" />
</Appenders>

<Loggers>
<Root level="info">
<AppenderRef ref="WaitStrategyAppenderList"/>
</Root>
</Loggers>
</Configuration>
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="async-wait-1" >
<!-- packages="org.apache.logging.log4j.core.async" -->

<AsyncWaitStrategyFactory
class="org.apache.logging.log4j.core.async.AsyncWaitStrategyFactoryConfigTest$YieldingWaitStrategyFactory" />

<Appenders>
<List name="WaitStrategyAppenderList" />
</Appenders>

<Loggers>
<AsyncRoot level="info">
<AppenderRef ref="WaitStrategyAppenderList"/>
</AsyncRoot>
</Loggers>
</Configuration>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="asyncwait-global2">

<AsyncWaitStrategyFactory
class="nonexisting.Factory" />

<Appenders>
<List name="WaitStrategyAppenderList" />
</Appenders>

<Loggers>
<Root level="info">
<AppenderRef ref="WaitStrategyAppenderList"/>
</Root>
</Loggers>
</Configuration>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="async-wait-2">

<AsyncWaitStrategyFactory
class="nonexisting.Factory" />

<Appenders>
<List name="WaitStrategyAppenderList" />
</Appenders>

<Loggers>
<AsyncRoot level="info">
<AppenderRef ref="WaitStrategyAppenderList"/>
</AsyncRoot>
</Loggers>
</Configuration>
Expand Up @@ -516,4 +516,9 @@ private static StringMap getContextData(final RingBufferLogEvent event) {
}
return contextData;
}

// package-protected for tests
AsyncLoggerDisruptor getAsyncLoggerDisruptor() {
return loggerDisruptor;
}
}
Expand Up @@ -129,6 +129,11 @@ protected void log(final LogEvent event, final LoggerConfigPredicate predicate)
}
}

// package-protected for testing
AsyncLoggerConfigDelegate getAsyncLoggerConfigDelegate() {
return delegate;
}

@Override
protected void callAppenders(final LogEvent event) {
super.callAppenders(event);
Expand Down
Expand Up @@ -166,10 +166,18 @@ private void notifyIntermediateProgress(final long sequence) {
private EventFactory<Log4jEventWrapper> factory;
private EventTranslatorTwoArg<Log4jEventWrapper, LogEvent, AsyncLoggerConfig> translator;
private volatile boolean alreadyLoggedWarning;
private final AsyncWaitStrategyFactory asyncWaitStrategyFactory;
private WaitStrategy waitStrategy;

private final Object queueFullEnqueueLock = new Object();

public AsyncLoggerConfigDisruptor() {
public AsyncLoggerConfigDisruptor(AsyncWaitStrategyFactory asyncWaitStrategyFactory) {
this.asyncWaitStrategyFactory = asyncWaitStrategyFactory; // may be null
}

// package-protected for testing
WaitStrategy getWaitStrategy() {
return waitStrategy;
}

// called from AsyncLoggerConfig constructor
Expand All @@ -195,7 +203,8 @@ public synchronized void start() {
}
LOGGER.trace("AsyncLoggerConfigDisruptor creating new disruptor for this configuration.");
ringBufferSize = DisruptorUtil.calculateRingBufferSize("AsyncLoggerConfig.RingBufferSize");
final WaitStrategy waitStrategy = DisruptorUtil.createWaitStrategy("AsyncLoggerConfig.WaitStrategy");
waitStrategy = DisruptorUtil.createWaitStrategy(
"AsyncLoggerConfig.WaitStrategy", asyncWaitStrategyFactory);

final ThreadFactory threadFactory = new Log4jThreadFactory("AsyncLoggerConfig", true, Thread.NORM_PRIORITY) {
@Override
Expand Down
Expand Up @@ -37,33 +37,33 @@ public class AsyncLoggerContext extends LoggerContext {

public AsyncLoggerContext(final String name) {
super(name);
loggerDisruptor = new AsyncLoggerDisruptor(name);
loggerDisruptor = new AsyncLoggerDisruptor(name, () -> getConfiguration().getAsyncWaitStrategyFactory());
}

public AsyncLoggerContext(final String name, final Object externalContext) {
super(name, externalContext);
loggerDisruptor = new AsyncLoggerDisruptor(name);
loggerDisruptor = new AsyncLoggerDisruptor(name, () -> getConfiguration().getAsyncWaitStrategyFactory());
}

public AsyncLoggerContext(final String name, final Object externalContext, final URI configLocn) {
super(name, externalContext, configLocn);
loggerDisruptor = new AsyncLoggerDisruptor(name);
loggerDisruptor = new AsyncLoggerDisruptor(name, () -> getConfiguration().getAsyncWaitStrategyFactory());
}

public AsyncLoggerContext(final String name, final Object externalContext, final URI configLocn, final Injector injector) {
super(name, externalContext, configLocn, injector);
loggerDisruptor = new AsyncLoggerDisruptor(name);
loggerDisruptor = new AsyncLoggerDisruptor(name, () -> getConfiguration().getAsyncWaitStrategyFactory());
}

public AsyncLoggerContext(final String name, final Object externalContext, final String configLocn) {
super(name, externalContext, configLocn);
loggerDisruptor = new AsyncLoggerDisruptor(name);
loggerDisruptor = new AsyncLoggerDisruptor(name, () -> getConfiguration().getAsyncWaitStrategyFactory());
}

public AsyncLoggerContext(
final String name, final Object externalContext, final String configLocn, final Injector injector) {
super(name, externalContext, configLocn, injector);
loggerDisruptor = new AsyncLoggerDisruptor(name);
loggerDisruptor = new AsyncLoggerDisruptor(name, () -> getConfiguration().getAsyncWaitStrategyFactory());
}

@Override
Expand Down

0 comments on commit 3d15928

Please sign in to comment.