1717package org .apache .logging .log4j .osgi .tests ;
1818
1919import static org .junit .Assert .assertEquals ;
20+ import static org .junit .Assert .assertNull ;
2021import static org .junit .Assert .assertTrue ;
2122import static org .ops4j .pax .exam .CoreOptions .junitBundles ;
2223import static org .ops4j .pax .exam .CoreOptions .linkBundle ;
2324import static org .ops4j .pax .exam .CoreOptions .options ;
2425
26+ import com .lmax .disruptor .ExceptionHandler ;
27+ import java .io .IOException ;
28+ import java .net .URL ;
29+ import java .util .concurrent .atomic .AtomicReference ;
2530import org .apache .logging .log4j .Level ;
2631import org .apache .logging .log4j .LogManager ;
2732import org .apache .logging .log4j .Logger ;
2833import org .apache .logging .log4j .core .LogEvent ;
2934import org .apache .logging .log4j .core .LoggerContext ;
3035import org .apache .logging .log4j .core .async .AsyncLoggerContext ;
36+ import org .apache .logging .log4j .core .async .RingBufferLogEvent ;
3137import org .junit .Test ;
3238import org .junit .runner .RunWith ;
39+ import org .junitpioneer .jupiter .Issue ;
3340import org .ops4j .pax .exam .Option ;
3441import org .ops4j .pax .exam .junit .PaxExam ;
3542import org .ops4j .pax .exam .spi .reactors .ExamReactorStrategy ;
3946@ ExamReactorStrategy (PerClass .class )
4047public class DisruptorTest {
4148
49+ private static final int MESSAGE_COUNT = 128 ;
50+
4251 @ org .ops4j .pax .exam .Configuration
4352 public Option [] config () {
4453 return options (
@@ -59,26 +68,87 @@ public Option[] config() {
5968 }
6069
6170 @ Test
62- public void testDisruptorLog () {
63- // Logger context
64- LoggerContext context = getLoggerContext ();
65- assertTrue ("LoggerContext is an instance of AsyncLoggerContext" , context instanceof AsyncLoggerContext );
66- final CustomConfiguration custom = (CustomConfiguration ) context .getConfiguration ();
67- // Logging
68- final Logger logger = LogManager .getLogger (getClass ());
69- logger .info ("Hello OSGI from Log4j2!" );
70-
71- context .stop ();
72- assertEquals (1 , custom .getEvents ().size ());
73- final LogEvent event = custom .getEvents ().get (0 );
74- assertEquals ("Hello OSGI from Log4j2!" , event .getMessage ().getFormattedMessage ());
75- assertEquals (Level .INFO , event .getLevel ());
76- custom .clearEvents ();
71+ @ Issue ("https://github.com/apache/logging-log4j2/issues/3706" )
72+ public void testDisruptorLog () throws IOException {
73+ ClassLoader threadContextClassLoader = Thread .currentThread ().getContextClassLoader ();
74+ ClassLoader classLoader = createClassLoader ();
75+ try {
76+ // Set the context classloader to an empty classloader, so attempts to use the TCCL will not find any
77+ // classes.
78+ Thread .currentThread ().setContextClassLoader (classLoader );
79+ // Logger context
80+ LoggerContext context = getLoggerContext ();
81+ assertTrue ("LoggerContext is an instance of AsyncLoggerContext" , context instanceof AsyncLoggerContext );
82+ final CustomConfiguration custom = (CustomConfiguration ) context .getConfiguration ();
83+ // Logging
84+ final Logger logger = LogManager .getLogger (getClass ());
85+ for (int i = 0 ; i < MESSAGE_COUNT ; i ++) {
86+ logger .info ("Hello OSGI from Log4j2! {}" , i );
87+ }
88+
89+ context .stop ();
90+ assertEquals (MESSAGE_COUNT , custom .getEvents ().size ());
91+ for (int i = 0 ; i < MESSAGE_COUNT ; i ++) {
92+ final LogEvent event = custom .getEvents ().get (i );
93+ assertEquals (
94+ "Message nr " + i ,
95+ "Hello OSGI from Log4j2! " + i ,
96+ event .getMessage ().getFormattedMessage ());
97+ assertEquals (Level .INFO , event .getLevel ());
98+ }
99+ custom .clearEvents ();
100+ assertNull ("Asynchronous exception" , TestExceptionHandler .exception .get ());
101+ } finally {
102+ Thread .currentThread ().setContextClassLoader (threadContextClassLoader );
103+ }
104+ }
105+
106+ private static ClassLoader createClassLoader () {
107+ // We want a classloader capable only of loading TestExceptionHandler.
108+ // This is needed to detect exceptions thrown by the asynchronous thread.
109+ return new ClassLoader () {
110+ @ Override
111+ public Class <?> loadClass (String name ) throws ClassNotFoundException {
112+ if (name .equals (TestExceptionHandler .class .getName ())) {
113+ return TestExceptionHandler .class ;
114+ }
115+ throw new ClassNotFoundException (name );
116+ }
117+
118+ @ Override
119+ public URL getResource (String name ) {
120+ return null ; // No resources available.
121+ }
122+ };
77123 }
78124
79125 private static LoggerContext getLoggerContext () {
80126 final LoggerContext ctx = (LoggerContext ) LogManager .getContext (false );
81127 assertEquals ("AsyncDefault" , ctx .getName ());
82128 return ctx ;
83129 }
130+
131+ public static class TestExceptionHandler implements ExceptionHandler <RingBufferLogEvent > {
132+
133+ private static final AtomicReference <Throwable > exception = new AtomicReference <>();
134+
135+ @ Override
136+ public void handleEventException (Throwable ex , long sequence , RingBufferLogEvent event ) {
137+ setException (ex );
138+ }
139+
140+ @ Override
141+ public void handleOnStartException (Throwable ex ) {
142+ setException (ex );
143+ }
144+
145+ @ Override
146+ public void handleOnShutdownException (Throwable ex ) {
147+ setException (ex );
148+ }
149+
150+ private static void setException (Throwable ex ) {
151+ exception .compareAndSet (null , ex );
152+ }
153+ }
84154}
0 commit comments