Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,19 @@ final class DisruptorUtil {
static final boolean ASYNC_CONFIG_SYNCHRONIZE_ENQUEUE_WHEN_QUEUE_FULL = PropertiesUtil.getProperties()
.getBooleanProperty("AsyncLoggerConfig.SynchronizeEnqueueWhenQueueFull", true);

static final int DISRUPTOR_MAJOR_VERSION =
LoaderUtil.isClassAvailable("com.lmax.disruptor.SequenceReportingEventHandler") ? 3 : 4;
static final int DISRUPTOR_MAJOR_VERSION = detectDisruptorMajorVersion();

// TODO: replace with LoaderUtil.isClassAvailable() when TCCL is removed
// See: https://github.com/apache/logging-log4j2/issues/3706
private static int detectDisruptorMajorVersion() {
try {
Class.forName(
"com.lmax.disruptor.SequenceReportingEventHandler", true, DisruptorUtil.class.getClassLoader());
return 3;
} catch (final ClassNotFoundException e) {
return 4;
}
}

private DisruptorUtil() {}

Expand Down
2 changes: 2 additions & 0 deletions log4j-osgi-test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@
<include>org.apache.logging.log4j.osgi.tests.DisruptorTest</include>
</includes>
<systemPropertyVariables>
<log4j2.asyncLoggerExceptionHandler>org.apache.logging.log4j.osgi.tests.DisruptorTest$TestExceptionHandler</log4j2.asyncLoggerExceptionHandler>
<log4j2.contextSelector>org.apache.logging.log4j.core.async.BasicAsyncLoggerContextSelector</log4j2.contextSelector>
</systemPropertyVariables>
</configuration>
Expand Down Expand Up @@ -291,6 +292,7 @@
<include>org.apache.logging.log4j.osgi.tests.DisruptorTest</include>
</includes>
<systemPropertyVariables>
<log4j2.asyncLoggerExceptionHandler>org.apache.logging.log4j.osgi.tests.DisruptorTest$TestExceptionHandler</log4j2.asyncLoggerExceptionHandler>
<log4j2.contextSelector>org.apache.logging.log4j.core.async.BasicAsyncLoggerContextSelector</log4j2.contextSelector>
</systemPropertyVariables>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,26 @@
package org.apache.logging.log4j.osgi.tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.ops4j.pax.exam.CoreOptions.junitBundles;
import static org.ops4j.pax.exam.CoreOptions.linkBundle;
import static org.ops4j.pax.exam.CoreOptions.options;

import com.lmax.disruptor.ExceptionHandler;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.async.AsyncLoggerContext;
import org.apache.logging.log4j.core.async.RingBufferLogEvent;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junitpioneer.jupiter.Issue;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
Expand All @@ -39,6 +46,8 @@
@ExamReactorStrategy(PerClass.class)
public class DisruptorTest {

private static final int MESSAGE_COUNT = 128;

@org.ops4j.pax.exam.Configuration
public Option[] config() {
return options(
Expand All @@ -59,26 +68,87 @@ public Option[] config() {
}

@Test
public void testDisruptorLog() {
// Logger context
LoggerContext context = getLoggerContext();
assertTrue("LoggerContext is an instance of AsyncLoggerContext", context instanceof AsyncLoggerContext);
final CustomConfiguration custom = (CustomConfiguration) context.getConfiguration();
// Logging
final Logger logger = LogManager.getLogger(getClass());
logger.info("Hello OSGI from Log4j2!");

context.stop();
assertEquals(1, custom.getEvents().size());
final LogEvent event = custom.getEvents().get(0);
assertEquals("Hello OSGI from Log4j2!", event.getMessage().getFormattedMessage());
assertEquals(Level.INFO, event.getLevel());
custom.clearEvents();
@Issue("https://github.com/apache/logging-log4j2/issues/3706")
public void testDisruptorLog() throws IOException {
ClassLoader threadContextClassLoader = Thread.currentThread().getContextClassLoader();
ClassLoader classLoader = createClassLoader();
try {
// Set the context classloader to an empty classloader, so attempts to use the TCCL will not find any
// classes.
Thread.currentThread().setContextClassLoader(classLoader);
// Logger context
LoggerContext context = getLoggerContext();
assertTrue("LoggerContext is an instance of AsyncLoggerContext", context instanceof AsyncLoggerContext);
final CustomConfiguration custom = (CustomConfiguration) context.getConfiguration();
// Logging
final Logger logger = LogManager.getLogger(getClass());
for (int i = 0; i < MESSAGE_COUNT; i++) {
logger.info("Hello OSGI from Log4j2! {}", i);
}

context.stop();
assertEquals(MESSAGE_COUNT, custom.getEvents().size());
for (int i = 0; i < MESSAGE_COUNT; i++) {
final LogEvent event = custom.getEvents().get(i);
assertEquals(
"Message nr " + i,
"Hello OSGI from Log4j2! " + i,
event.getMessage().getFormattedMessage());
assertEquals(Level.INFO, event.getLevel());
}
custom.clearEvents();
assertNull("Asynchronous exception", TestExceptionHandler.exception.get());
} finally {
Thread.currentThread().setContextClassLoader(threadContextClassLoader);
}
}

private static ClassLoader createClassLoader() {
// We want a classloader capable only of loading TestExceptionHandler.
// This is needed to detect exceptions thrown by the asynchronous thread.
return new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.equals(TestExceptionHandler.class.getName())) {
return TestExceptionHandler.class;
}
throw new ClassNotFoundException(name);
}

@Override
public URL getResource(String name) {
return null; // No resources available.
}
};
}

private static LoggerContext getLoggerContext() {
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
assertEquals("AsyncDefault", ctx.getName());
return ctx;
}

public static class TestExceptionHandler implements ExceptionHandler<RingBufferLogEvent> {

private static final AtomicReference<Throwable> exception = new AtomicReference<>();

@Override
public void handleEventException(Throwable ex, long sequence, RingBufferLogEvent event) {
setException(ex);
}

@Override
public void handleOnStartException(Throwable ex) {
setException(ex);
}

@Override
public void handleOnShutdownException(Throwable ex) {
setException(ex);
}

private static void setException(Throwable ex) {
exception.compareAndSet(null, ex);
}
}
}
12 changes: 12 additions & 0 deletions src/changelog/.2.x.x/3706_disruptor-tccl.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="https://logging.apache.org/xml/ns"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
https://logging.apache.org/xml/ns
https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
type="fixed">
<issue id="3706" link="https://github.com/apache/logging-log4j2/issues/3706"/>
<description format="asciidoc">
Fix detection of the Disruptor major version in some environments.
</description>
</entry>