View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package chapters.appenders;
15  
16  import java.io.FileWriter;
17  import java.io.IOException;
18  
19  import org.slf4j.Logger;
20  
21  import ch.qos.logback.classic.LoggerContext;
22  import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
23  import ch.qos.logback.classic.spi.ILoggingEvent;
24  import ch.qos.logback.core.FileAppender;
25  import ch.qos.logback.core.util.StatusPrinter;
26  
27  public class IOPerformance extends Thread {
28      static String MSG = "ABCDEGHIJKLMNOPQRSTUVWXYZabcdeghijklmnopqrstuvwxyz1234567890";
29      static String LOG_FILE;
30      public static String PARALLEL_FILE;
31  
32      static int NUM_THREADS = 1;
33      static long l;
34      long len;
35      boolean immediateFlush;
36      Logger logger;
37      LoggerContext context;
38      double throughput;
39  
40      public IOPerformance(boolean _immediateFlush, long _len) {
41          this.len = _len;
42          this.immediateFlush = _immediateFlush;
43          context = new LoggerContext();
44          logger = context.getLogger("logger-" + getName());
45  
46          // A FileAppender is created according to the buffering and
47          // immediate flush setting of this IO instance.
48          FileAppender<ILoggingEvent> fa = new FileAppender<ILoggingEvent>();
49          fa.setName("FILE");
50          PatternLayoutEncoder pa = new PatternLayoutEncoder();
51          pa.setPattern("%r %5p %c [%t] - %m%n");
52          pa.setContext(context);
53          pa.start();
54          fa.setEncoder(pa);
55  
56          fa.setFile(LOG_FILE);
57          fa.setAppend(true);
58          fa.setContext(context);
59          fa.start();
60  
61          ((ch.qos.logback.classic.Logger) logger).addAppender(fa);
62  
63          StatusPrinter.print(context);
64      }
65  
66      public static void main(String[] argv) throws Exception {
67          if (argv.length != 3) {
68              usage("Wrong number of arguments.");
69          }
70  
71          l = Integer.parseInt(argv[0]);
72          LOG_FILE = argv[1];
73          PARALLEL_FILE = argv[2];
74  
75          // ----------------------------------------------------
76          // first test with immediate flushing
77          perfCase(true, l);
78  
79          // ----------------------------------------------------
80          // Second test with no immediate flushing
81          perfCase(false, l);
82  
83          // There is no fourth test as buffered IO and immediate flushing
84          // do not make sense.
85      }
86  
87      static void usage(String msg) {
88          System.err.println(msg);
89          System.err.println("Usage: java " + IOPerformance.class.getName() + " runLength logFile otherFile\n"
90                          + "   runLength (integer) the number of logs to generate perthread\n" + "   logFile path to a logFile\n"
91                          + "   otherFile path to a second file\n");
92          System.exit(1);
93      }
94  
95      static void perfCase(boolean immediateFlush, long len) throws Exception {
96          IOPerformance[] threads = new IOPerformance[NUM_THREADS];
97          OtherIO otherIOThread = new OtherIO();
98          otherIOThread.start();
99  
100         // First create the threads
101         for (int i = 0; i < NUM_THREADS; i++) {
102             threads[i] = new IOPerformance(immediateFlush, len);
103         }
104 
105         // then start them
106         for (int i = 0; i < NUM_THREADS; i++) {
107             threads[i].start();
108         }
109 
110         // wait for them to processPriorToRemoval, compute the average throughput
111         double sum = 0;
112 
113         for (int i = 0; i < NUM_THREADS; i++) {
114             threads[i].join();
115             sum += threads[i].throughput;
116         }
117 
118         // setting the interrupted field will cause counterThread to processPriorToRemoval
119         otherIOThread.interrupted = true;
120         otherIOThread.join();
121 
122         System.out.println("On total throughput of " + (sum) + " logs per microsecond.");
123         System.out.println("------------------------------------------------");
124     }
125 
126     public void run() {
127 
128         long before = System.nanoTime();
129 
130         for (int i = 0; i < len; i++) {
131             logger.debug(MSG);
132         }
133 
134         throughput = (len * 1.0) / ((System.nanoTime() - before) / 1000);
135         System.out.println(getName() + ", immediateFlush: " + immediateFlush + ", throughput: " + throughput + " logs per microsecond.");
136     }
137 }
138 
139 class OtherIO extends Thread {
140     public boolean interrupted = false;
141     public int counter = 0;
142 
143     public void run() {
144         long before = System.nanoTime();
145         try {
146             FileWriter fw = new FileWriter(IOPerformance.PARALLEL_FILE, true);
147 
148             while (!interrupted) {
149                 counter++;
150                 fw.write("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
151                 fw.flush();
152             }
153         } catch (IOException e) {
154             e.printStackTrace();
155         }
156 
157         double tput = (counter * 1.0) / (System.nanoTime() - before);
158         System.out.println("Counter thread " + getName() + " incremented counter by " + tput + " per nanosecond.");
159     }
160 }