/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2015, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ package chapters.appenders; import org.slf4j.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.FileAppender; import ch.qos.logback.core.encoder.EchoEncoder; public class IO extends Thread { static String msgLong = "ABCDEGHIJKLMNOPQRSTUVWXYZabcdeghijklmnopqrstuvwxyz1234567890"; static String msgShort = "Hello"; static boolean scarceCPU; static int numThreads; static long l; static boolean longMessage; long len; boolean buffered; boolean immediateFlush; Logger logger; LoggerContext context; double throughput; public IO(boolean _buffered, boolean _immediateFlush, long _len) { this.len = _len; this.buffered = _buffered; this.immediateFlush = _immediateFlush; context = new LoggerContext(); logger = context.getLogger("logger-" + getName()); // A FileAppender is created according to the buffering and // immediate flush setting of this IO instance. FileAppender<ILoggingEvent> fa = new FileAppender<ILoggingEvent>(); if (longMessage) { PatternLayoutEncoder pa = new PatternLayoutEncoder(); pa.setPattern("%r %5p %c [%t] - %m%n"); pa.setContext(context); pa.start(); fa.setEncoder(pa); } else { fa.setEncoder(new EchoEncoder<ILoggingEvent>()); } fa.setFile(getName() + ".log"); fa.setAppend(false); fa.setContext(context); fa.start(); } public static void main(String[] argv) throws Exception { if (argv.length != 4) { usage("Wrong number of arguments."); } l = Integer.parseInt(argv[0]); numThreads = Integer.parseInt(argv[1]); scarceCPU = "true".equalsIgnoreCase(argv[2]); longMessage = "long".equalsIgnoreCase(argv[3]); // ---------------------------------------------------- // first test with unbuffered IO and immediate flushing perfCase(false, true, l); // ---------------------------------------------------- // Second test with unbuffered IO and no immediate flushing perfCase(false, false, l); // ---------------------------------------------------- // Third test with buffered IO and no immediate flushing perfCase(true, false, l); // There is no fourth test as buffered IO and immediate flushing // do not make sense. } static void usage(String msg) { System.err.println(msg); System.err.println("Usage: java " + IO.class.getName() + " runLength numThreads scarceCPU (short|long)\n" + " runLength (integer) the number of logs to generate perthread\n" + " numThreads (integer) the number of threads.\n" + " scarceCPU (boolean) if true an additional CPU intensive thread is created\n" + " (short|long) length of log messages."); System.exit(1); } static void perfCase(boolean buffered, boolean immediateFlush, long len) throws Exception { IO[] threads = new IO[numThreads]; Counter counterThread = null; if (scarceCPU) { counterThread = new Counter(); counterThread.start(); } // First create the threads for (int i = 0; i < numThreads; i++) { threads[i] = new IO(buffered, immediateFlush, len); } // then start them for (int i = 0; i < numThreads; i++) { threads[i].start(); } // wait for them to processPriorToRemoval, compute the average throughput double sum = 0; for (int i = 0; i < numThreads; i++) { threads[i].join(); sum += threads[i].throughput; } if (scarceCPU) { // setting the interrupted field will cause counterThread to processPriorToRemoval counterThread.interrupted = true; counterThread.join(); } System.out.println("On average throughput of " + (sum / numThreads) * 1000 + " logs per microsecond."); System.out.println("------------------------------------------------"); } public void run() { String msg = msgShort; if (longMessage) { msg = msgLong; } long before = System.nanoTime(); for (int i = 0; i < len; i++) { logger.debug(msg); } throughput = (len * 1.0) / (System.nanoTime() - before); System.out.println(getName() + ", buffered: " + buffered + ", immediateFlush: " + immediateFlush + ", throughput: " + throughput + " logs per nanosecond."); } } class Counter extends Thread { public boolean interrupted = false; public double counter = 0; public void run() { long before = System.nanoTime(); while (!interrupted) { if (counter % 1000 == 0) { Thread.yield(); } counter += 1.0; } double tput = (counter * 1.0) / (System.nanoTime() - before); System.out.println("Counter thread " + getName() + " incremented counter by " + tput + " per nanosecond."); } }