17
17
package org .apache .logging .log4j .osgi .tests ;
18
18
19
19
import static org .junit .Assert .assertEquals ;
20
+ import static org .junit .Assert .assertNull ;
20
21
import static org .junit .Assert .assertTrue ;
21
22
import static org .ops4j .pax .exam .CoreOptions .junitBundles ;
22
23
import static org .ops4j .pax .exam .CoreOptions .linkBundle ;
23
24
import static org .ops4j .pax .exam .CoreOptions .options ;
24
25
26
+ import com .lmax .disruptor .ExceptionHandler ;
27
+ import java .io .IOException ;
28
+ import java .net .URL ;
29
+ import java .util .concurrent .atomic .AtomicReference ;
25
30
import org .apache .logging .log4j .Level ;
26
31
import org .apache .logging .log4j .LogManager ;
27
32
import org .apache .logging .log4j .Logger ;
28
33
import org .apache .logging .log4j .core .LogEvent ;
29
34
import org .apache .logging .log4j .core .LoggerContext ;
30
35
import org .apache .logging .log4j .core .async .AsyncLoggerContext ;
36
+ import org .apache .logging .log4j .core .async .RingBufferLogEvent ;
31
37
import org .junit .Test ;
32
38
import org .junit .runner .RunWith ;
39
+ import org .junitpioneer .jupiter .Issue ;
33
40
import org .ops4j .pax .exam .Option ;
34
41
import org .ops4j .pax .exam .junit .PaxExam ;
35
42
import org .ops4j .pax .exam .spi .reactors .ExamReactorStrategy ;
39
46
@ ExamReactorStrategy (PerClass .class )
40
47
public class DisruptorTest {
41
48
49
+ private static final int MESSAGE_COUNT = 128 ;
50
+
42
51
@ org .ops4j .pax .exam .Configuration
43
52
public Option [] config () {
44
53
return options (
@@ -59,26 +68,87 @@ public Option[] config() {
59
68
}
60
69
61
70
@ 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
+ };
77
123
}
78
124
79
125
private static LoggerContext getLoggerContext () {
80
126
final LoggerContext ctx = (LoggerContext ) LogManager .getContext (false );
81
127
assertEquals ("AsyncDefault" , ctx .getName ());
82
128
return ctx ;
83
129
}
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
+ }
84
154
}
0 commit comments