1   /**
2    * Logback: the generic, reliable, fast and flexible logging framework.
3    * 
4    * Copyright (C) 1999-2006, QOS.ch
5    * 
6    * This library is free software, you can redistribute it and/or modify it under
7    * the terms of the GNU Lesser General Public License as published by the Free
8    * Software Foundation.
9    */
10  package ch.qos.logback.classic;
11  
12  import static org.junit.Assert.assertTrue;
13  
14  import org.junit.Before;
15  import org.junit.Test;
16  import org.slf4j.helpers.BogoPerf;
17  
18  import ch.qos.logback.classic.spi.LoggingEvent;
19  import ch.qos.logback.classic.turbo.NOPTurboFilter;
20  import ch.qos.logback.core.CoreConstants;
21  import ch.qos.logback.core.UnsynchronizedAppenderBase;
22  import ch.qos.logback.core.appender.NOPAppender;
23  import ch.qos.logback.core.testUtil.Env;
24  
25  public class LoggerPerfTest {
26  
27    static final long NANOS_IN_ONE_SEC = 1000 * 1000 * 1000L;
28    static long NORMAL_RUN_LENGTH = 1 * 1000 * 1000;
29    static long SHORTENED_RUN_LENGTH = 500 * 1000;
30  
31    LoggerContext lc = new LoggerContext();
32    Logger lbLogger = lc.getLogger(this.getClass());
33    org.slf4j.Logger logger = lbLogger;
34  
35    @Before
36    public void setUp() throws Exception {
37    }
38  
39    // ===========================================================================
40    @Test
41    public void durationOfDisabledLogsWith_1_NOPFilter() {
42      double avg = computeDurationOfDisabledLogsWith_1_NOPFilter(1,
43          NORMAL_RUN_LENGTH);
44      System.out.println("durationOfDisabledLogsWith_1_NOPFilter=" + avg);
45      long referencePerf = 60;
46  
47      BogoPerf.assertDuration(avg, referencePerf, CoreConstants.REFERENCE_BIPS);
48    }
49  
50    double computeDurationOfDisabledLogsWith_1_NOPFilter(int numOfFilters,
51        long len) {
52      for (int i = 0; i < numOfFilters; i++) {
53        lc.addTurboFilter(new NOPTurboFilter());
54      }
55      lbLogger.setLevel(Level.OFF);
56      for (long i = 0; i < len; i++)
57        logger.debug("Toto");
58  
59      long start = System.nanoTime();
60      for (long i = 0; i < len; i++)
61        logger.debug("Toto");
62  
63      return (System.nanoTime() - start) / len;
64    }
65  
66    // ===========================================================================
67    @Test
68    public void durationOfIsDebugEnabled() {
69      double avg = computedurationOfIsDebugEnabled(10 * NORMAL_RUN_LENGTH);
70      System.out.println("durationOfIsDebugEnabled=" + avg);
71  
72      long referencePerf = 15;
73      BogoPerf.assertDuration(avg, referencePerf, CoreConstants.REFERENCE_BIPS);
74    }
75  
76    double computedurationOfIsDebugEnabled(final long len) {
77      lbLogger.setLevel(Level.OFF);
78      for (long i = 0; i < len; i++)
79        logger.isDebugEnabled();
80  
81      long start = System.nanoTime();
82      for (long i = 0; i < len; i++)
83        logger.isDebugEnabled();
84      return (System.nanoTime() - start) / len;
85    }
86  
87    // ===========================================================================
88    @Test
89    public void durationOfDisabledLog_NoParameters() {
90      double avg = computeDurationOfDisabledLog_NoParameters(10 * NORMAL_RUN_LENGTH);
91      System.out.println("durationOfDisabledLog_NoParameters=" + avg);
92  
93      long referencePerf = 18;
94      BogoPerf.assertDuration(avg, referencePerf, CoreConstants.REFERENCE_BIPS);
95    }
96  
97    double computeDurationOfDisabledLog_NoParameters(final long len) {
98      lbLogger.setLevel(Level.OFF);
99      for (long i = 0; i < len; i++)
100       logger.debug("Toto");
101 
102     long start = System.nanoTime();
103     for (long i = 0; i < len; i++)
104       logger.debug("Toto");
105     return (System.nanoTime() - start) / len;
106   }
107 
108   // ===========================================================================
109 
110   @Test
111   public void durationOfDisabledLog_1_Parameter() {
112     double avgDuration = computeDurationOfDisabledLog_1_Parameter(NORMAL_RUN_LENGTH);
113     System.out.println("durationOfDisabledLog_1_Parameter=" + avgDuration);
114 
115     long referencePerf = 30;
116     BogoPerf.assertDuration(avgDuration, referencePerf,
117         CoreConstants.REFERENCE_BIPS);
118   }
119 
120   double computeDurationOfDisabledLog_1_Parameter(long len) {
121     lbLogger.setLevel(Level.OFF);
122     final Object o = new Object();
123     for (long i = 0; i < len; i++)
124       logger.debug("Toto {}", o);
125 
126     long start = System.nanoTime();
127     for (long i = 0; i < len; i++)
128       logger.debug("Toto {}", o);
129 
130     long end = System.nanoTime();
131     return (end - start) / len;
132   }
133 
134   // ===========================================================================
135 
136   @Test
137   public void durationOfEnabledLog() {
138     if (Env.isLinux()) {
139       // the JIT on Linux behaves very differently
140       return;
141     }
142     double avgDuration = computeDurationOfEnabledLog(SHORTENED_RUN_LENGTH);
143     System.out.println("durationOfEnabledLog=" + avgDuration);
144 
145     long referencePerf = 800;
146     BogoPerf.assertDuration(avgDuration, referencePerf,
147         CoreConstants.REFERENCE_BIPS);
148   }
149 
150   double computeDurationOfEnabledLog(long len) {
151     lbLogger.setLevel(Level.ALL);
152 
153     NOPAppender<LoggingEvent> nopAppender = new NOPAppender<LoggingEvent>();
154     nopAppender.start();
155     ((ch.qos.logback.classic.Logger) logger).addAppender(nopAppender);
156     for (long i = 0; i < len; i++) {
157       logger.debug("Toto");
158     }
159     long start = System.nanoTime();
160     for (long i = 0; i < len; i++) {
161       logger.debug("Toto");
162     }
163     long end = System.nanoTime();
164     return (end - start) / len;
165   }
166 
167   // ===========================================================================
168 
169   @Test
170   public void testThreadedLogging() throws InterruptedException {
171     SleepAppender<LoggingEvent> appender = new SleepAppender<LoggingEvent>();
172 
173     int MILLIS_PER_CALL = 250;
174     int NANOS_PER_CALL = 250 * 1000 * 1000;
175 
176     appender.setDuration(MILLIS_PER_CALL);
177     appender.start();
178 
179     lbLogger.addAppender(appender);
180     lbLogger.setLevel(Level.DEBUG);
181     long start;
182     long end;
183     int threadCount = 10;
184     int iterCount = 5;
185     TestRunner[] threads = new TestRunner[threadCount];
186     for (int i = 0; i < threads.length; ++i) {
187       threads[i] = new TestRunner(logger, iterCount);
188     }
189     start = System.nanoTime();
190     for (Thread thread : threads) {
191       thread.start();
192     }
193     for (TestRunner thread : threads) {
194       thread.join();
195     }
196     end = System.nanoTime();
197     double tolerance = threadCount * .125; // Very little thread contention
198     // should occur in this test.
199     double max = ((((double) NANOS_PER_CALL) / NANOS_IN_ONE_SEC) * iterCount)
200         * tolerance;
201     double serialized = (((double) NANOS_PER_CALL) / NANOS_IN_ONE_SEC)
202         * iterCount * threadCount;
203     double actual = ((double) (end - start)) / NANOS_IN_ONE_SEC;
204     System.out
205         .printf(
206             "Sleep duration: %,.4f seconds. Max expected: %,.4f seconds, Serialized: %,.4f\n",
207             actual, max, serialized);
208     assertTrue("Exceeded maximum expected time.", actual < max);
209   }
210 
211   // ============================================================
212   private static class TestRunner extends Thread {
213     private org.slf4j.Logger logger;
214     private long len;
215 
216     public TestRunner(org.slf4j.Logger logger, long len) {
217       this.logger = logger;
218       this.len = len;
219     }
220 
221     public void run() {
222       Thread.yield();
223       for (long i = 0; i < len; i++) {
224         logger.debug("Toto");
225       }
226     }
227   }
228 
229   // ============================================================
230   public static class SleepAppender<E> extends UnsynchronizedAppenderBase<E> {
231     private static long duration = 500;
232 
233     public void setDuration(long millis) {
234       duration = millis;
235     }
236 
237     @Override
238     protected void append(E eventObject) {
239       try {
240         Thread.sleep(duration);
241       } catch (InterruptedException ie) {
242         // Ignore
243       }
244     }
245   }
246   // ============================================================
247 }