1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.log4j;
19  
20  import junit.framework.TestCase;
21  
22  import java.util.Vector;
23  
24  import org.apache.log4j.spi.LoggingEvent;
25  
26  /**
27     A superficial but general test of log4j.
28   */
29  public class AsyncAppenderTestCase extends TestCase {
30  
31    public AsyncAppenderTestCase(String name) {
32      super(name);
33    }
34  
35    public void setUp() {
36    }
37  
38    public void tearDown() {
39      LogManager.shutdown();
40    }
41  
42    // this test checks whether it is possible to write to a closed AsyncAppender
43    public void closeTest() throws Exception {    
44      Logger root = Logger.getRootLogger();
45      VectorAppender vectorAppender = new VectorAppender();
46      AsyncAppender asyncAppender = new AsyncAppender();
47      asyncAppender.setName("async-CloseTest");
48      asyncAppender.addAppender(vectorAppender);
49      root.addAppender(asyncAppender); 
50  
51      root.debug("m1");
52      asyncAppender.close();
53      root.debug("m2");
54      
55      Vector v = vectorAppender.getVector();
56      assertEquals(v.size(), 1);
57    }
58  
59    // this test checks whether appenders embedded within an AsyncAppender are also 
60    // closed 
61    public void test2() {
62      Logger root = Logger.getRootLogger();
63      VectorAppender vectorAppender = new VectorAppender();
64      AsyncAppender asyncAppender = new AsyncAppender();
65      asyncAppender.setName("async-test2");
66      asyncAppender.addAppender(vectorAppender);
67      root.addAppender(asyncAppender); 
68  
69      root.debug("m1");
70      asyncAppender.close();
71      root.debug("m2");
72      
73      Vector v = vectorAppender.getVector();
74      assertEquals(v.size(), 1);
75      assertTrue(vectorAppender.isClosed());
76    }
77  
78    // this test checks whether appenders embedded within an AsyncAppender are also 
79    // closed 
80    public void test3() {
81      int LEN = 200;
82      Logger root = Logger.getRootLogger();
83      VectorAppender vectorAppender = new VectorAppender();
84      AsyncAppender asyncAppender = new AsyncAppender();
85      asyncAppender.setName("async-test3");
86      asyncAppender.addAppender(vectorAppender);
87      root.addAppender(asyncAppender); 
88  
89      for(int i = 0; i < LEN; i++) {
90        root.debug("message"+i);
91      }
92      
93      System.out.println("Done loop.");
94      System.out.flush();
95      asyncAppender.close();
96      root.debug("m2");
97      
98      Vector v = vectorAppender.getVector();
99      assertEquals(v.size(), LEN);
100     assertTrue(vectorAppender.isClosed());
101   }
102 
103     private static class NullPointerAppender extends AppenderSkeleton {
104           public NullPointerAppender() {
105           }
106 
107 
108           /**
109              This method is called by the {@link org.apache.log4j.AppenderSkeleton#doAppend}
110              method.
111 
112           */
113           public void append(org.apache.log4j.spi.LoggingEvent event) {
114               throw new NullPointerException();
115           }
116 
117           public void close() {
118           }
119 
120           public boolean requiresLayout() {
121             return false;
122           }
123     }
124 
125 
126     /**
127      * Tests that a bad appender will switch async back to sync.
128      * See bug 23021
129      * @since 1.2.12
130      * @throws Exception thrown if Thread.sleep is interrupted
131      */
132     public void testBadAppender() throws Exception {
133         Appender nullPointerAppender = new NullPointerAppender();
134         AsyncAppender asyncAppender = new AsyncAppender();
135         asyncAppender.addAppender(nullPointerAppender);
136         asyncAppender.setBufferSize(5);
137         asyncAppender.activateOptions();
138         Logger root = Logger.getRootLogger();
139         root.addAppender(nullPointerAppender);
140         try {
141            root.info("Message");
142            Thread.sleep(10);
143            root.info("Message");
144            fail("Should have thrown exception");
145         } catch(NullPointerException ex) {
146 
147         }
148     }
149 
150     /**
151      * Tests location processing when buffer is full and locationInfo=true.
152      * See bug 41186.
153      */
154     public void testLocationInfoTrue() {
155         BlockableVectorAppender blockableAppender = new BlockableVectorAppender();
156         AsyncAppender async = new AsyncAppender();
157         async.addAppender(blockableAppender);
158         async.setBufferSize(5);
159         async.setLocationInfo(true);
160         async.setBlocking(false);
161         async.activateOptions();
162         Logger rootLogger = Logger.getRootLogger();
163         rootLogger.addAppender(async);
164         Greeter greeter = new Greeter(rootLogger, 100);
165         synchronized(blockableAppender.getMonitor()) {
166             greeter.run();
167             rootLogger.error("That's all folks.");
168         }
169         async.close();
170         Vector events = blockableAppender.getVector();
171         LoggingEvent initialEvent = (LoggingEvent) events.get(0);
172         LoggingEvent discardEvent = (LoggingEvent) events.get(events.size() - 1);
173         PatternLayout layout = new PatternLayout();
174         layout.setConversionPattern("%C:%L %m%n");
175         layout.activateOptions();
176         String initialStr = layout.format(initialEvent);
177         assertEquals(AsyncAppenderTestCase.class.getName(),
178                 initialStr.substring(0, AsyncAppenderTestCase.class.getName().length()));
179         String discardStr = layout.format(discardEvent);
180         assertEquals("?:? ", discardStr.substring(0, 4));
181     }
182 
183 
184     /**
185      * Tests location processing when buffer is full and locationInfo=false.
186      * See bug 41186.
187      */
188     public void testLocationInfoFalse() {
189         BlockableVectorAppender blockableAppender = new BlockableVectorAppender();
190         AsyncAppender async = new AsyncAppender();
191         async.addAppender(blockableAppender);
192         async.setBufferSize(5);
193         async.setLocationInfo(false);
194         async.setBlocking(false);
195         async.activateOptions();
196         Logger rootLogger = Logger.getRootLogger();
197         rootLogger.addAppender(async);
198         Greeter greeter = new Greeter(rootLogger, 100);
199         synchronized(blockableAppender.getMonitor()) {
200             greeter.run();
201             rootLogger.error("That's all folks.");
202         }
203         async.close();
204         Vector events = blockableAppender.getVector();
205         LoggingEvent initialEvent = (LoggingEvent) events.get(0);
206         LoggingEvent discardEvent = (LoggingEvent) events.get(events.size() - 1);
207         PatternLayout layout = new PatternLayout();
208         layout.setConversionPattern("%C:%L %m%n");
209         layout.activateOptions();
210         String initialStr = layout.format(initialEvent);
211         assertEquals("?:? ", initialStr.substring(0, 4));
212         String discardStr = layout.format(discardEvent);
213         assertEquals("?:? ", discardStr.substring(0, 4));
214     }
215 
216     /**
217      *  Logging request runnable.
218      */
219     private static final class Greeter implements Runnable {
220       /**
221        * Logger.
222        */
223       private final Logger logger;
224 
225       /**
226        * Repetitions.
227        */
228       private final int repetitions;
229 
230       /**
231        * Create new instance.
232        * @param logger logger, may not be null.
233        * @param repetitions repetitions.
234        */
235       public Greeter(final Logger logger, final int repetitions) {
236         if (logger == null) {
237           throw new IllegalArgumentException("logger");
238         }
239 
240         this.logger = logger;
241         this.repetitions = repetitions;
242       }
243 
244       /**
245        * {@inheritDoc}
246        */
247       public void run() {
248         try {
249           for (int i = 0; i < repetitions; i++) {
250             logger.info("Hello, World");
251             Thread.sleep(1);
252           }
253         } catch (InterruptedException ex) {
254           Thread.currentThread().interrupt();
255         }
256       }
257     }
258 
259 
260 
261     /**
262      * Vector appender that can be explicitly blocked.
263      */
264     private static final class BlockableVectorAppender extends VectorAppender {
265       /**
266        * Monitor object used to block appender.
267        */
268       private final Object monitor = new Object();
269 
270 
271       /**
272        * Create new instance.
273        */
274       public BlockableVectorAppender() {
275         super();
276       }
277 
278       /**
279        * {@inheritDoc}
280        */
281       public void append(final LoggingEvent event) {
282         synchronized (monitor) {
283           super.append(event);
284             //
285             //   if fatal, echo messages for testLoggingInDispatcher
286             //
287             if (event.getLevel() == Level.FATAL) {
288                 Logger logger = Logger.getLogger(event.getLoggerName());
289                 logger.error(event.getMessage().toString());
290                 logger.warn(event.getMessage().toString());
291                 logger.info(event.getMessage().toString());
292                 logger.debug(event.getMessage().toString());
293             }
294         }
295       }
296 
297       /**
298        * Get monitor object.
299        * @return monitor.
300        */
301       public Object getMonitor() {
302         return monitor;
303       }
304 
305     }
306 
307 
308     /**
309      * Test that a mutable message object is evaluated before
310      * being placed in the async queue.
311      * See bug 43559.
312      */
313     public void testMutableMessage() {
314         BlockableVectorAppender blockableAppender = new BlockableVectorAppender();
315         AsyncAppender async = new AsyncAppender();
316         async.addAppender(blockableAppender);
317         async.setBufferSize(5);
318         async.setLocationInfo(false);
319         async.activateOptions();
320         Logger rootLogger = Logger.getRootLogger();
321         rootLogger.addAppender(async);
322         StringBuffer buf = new StringBuffer("Hello");
323         synchronized(blockableAppender.getMonitor()) {
324             rootLogger.info(buf);
325             buf.append(", World.");
326         }
327         async.close();
328         Vector events = blockableAppender.getVector();
329         LoggingEvent event = (LoggingEvent) events.get(0);
330         PatternLayout layout = new PatternLayout();
331         layout.setConversionPattern("%m");
332         layout.activateOptions();
333         String msg = layout.format(event);
334         assertEquals("Hello", msg);
335     }
336 
337 
338 
339 }