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 ch.qos.logback.core;
15  
16  import java.io.OutputStream;
17  import java.io.PrintStream;
18  import java.lang.reflect.Method;
19  import java.lang.reflect.Modifier;
20  import java.util.Arrays;
21  import java.util.NoSuchElementException;
22  import java.util.Optional;
23  
24  import ch.qos.logback.core.joran.spi.ConsoleTarget;
25  import ch.qos.logback.core.status.Status;
26  import ch.qos.logback.core.status.WarnStatus;
27  import ch.qos.logback.core.util.Loader;
28  
29  /**
30   * ConsoleAppender appends log events to <code>System.out</code> or
31   * <code>System.err</code> using a layout specified by the user. The default
32   * target is <code>System.out</code>.
33   * <p>
34   * &nbsp;
35   * </p>
36   * For more information about this appender, please refer to the online manual
37   * at http://logback.qos.ch/manual/appenders.html#ConsoleAppender
38   *
39   * @author Ceki G&uuml;lc&uuml;
40   * @author Tom SH Liu
41   * @author Ruediger Dohna
42   */
43  
44  public class ConsoleAppender<E> extends OutputStreamAppender<E> {
45  
46      protected ConsoleTarget target = ConsoleTarget.SystemOut;
47      protected boolean withJansi = false;
48  
49      private final static String AnsiConsole_CLASS_NAME = "org.fusesource.jansi.AnsiConsole";
50      private final static String JANSI2_OUT_METHOD_NAME = "out";
51      private final static String JANSI2_ERR_METHOD_NAME = "err";
52      private final static String wrapSystemOut_METHOD_NAME = "wrapSystemOut";
53      private final static String wrapSystemErr_METHOD_NAME = "wrapSystemErr";
54      private final static Class<?>[] ARGUMENT_TYPES = { PrintStream.class };
55  
56      private final static String CONSOLE_APPENDER_WARNING_URL = CoreConstants.CODES_URL+"#slowConsole";
57  
58      /**
59       * Sets the value of the <b>Target</b> option. Recognized values are
60       * "System.out" and "System.err". Any other value will be ignored.
61       */
62      public void setTarget(String value) {
63          ConsoleTarget t = ConsoleTarget.findByName(value.trim());
64          if (t == null) {
65              targetWarn(value);
66          } else {
67              target = t;
68          }
69      }
70  
71      /**
72       * Returns the current value of the <b>target</b> property. The default value of
73       * the option is "System.out".
74       * <p>
75       * See also {@link #setTarget}.
76       */
77      public String getTarget() {
78          return target.getName();
79      }
80  
81      private void targetWarn(String val) {
82          Status status = new WarnStatus("[" + val + "] should be one of " + Arrays.toString(ConsoleTarget.values()),
83                  this);
84          status.add(new WarnStatus("Using previously set target, System.out by default.", this));
85          addStatus(status);
86      }
87  
88      @Override
89      public void start() {
90          addInfo("BEWARE: Writing to the console can be very slow. Avoid logging to the ");
91          addInfo("console in production environments, especially in high volume systems.");
92          addInfo("See also "+CONSOLE_APPENDER_WARNING_URL);
93          OutputStream targetStream = target.getStream();
94          // enable jansi only if withJansi set to true
95          if (withJansi) {
96              targetStream = wrapWithJansi(targetStream);
97          }
98          setOutputStream(targetStream);
99          super.start();
100     }
101 
102     private OutputStream wrapWithJansi(OutputStream targetStream) {
103         try {
104             addInfo("Enabling JANSI AnsiPrintStream for the console.");
105             ClassLoader classLoader = Loader.getClassLoaderOfObject(context);
106             Class<?> classObj = classLoader.loadClass(AnsiConsole_CLASS_NAME);
107 
108             // check for JAnsi 2
109             String methodNameJansi2 = target == ConsoleTarget.SystemOut ? JANSI2_OUT_METHOD_NAME
110                     : JANSI2_ERR_METHOD_NAME;
111             final Optional<Method> optOutMethod = Arrays.stream(classObj.getMethods())
112                     .filter(m -> m.getName().equals(methodNameJansi2))
113                     .filter(m -> m.getParameters().length == 0)
114                     .filter(m -> Modifier.isStatic(m.getModifiers()))
115                     .filter(m -> PrintStream.class.isAssignableFrom(m.getReturnType()))
116                     .findAny();
117             if (optOutMethod.isPresent()) {
118                 final Method outMethod = optOutMethod.orElseThrow(() -> new NoSuchElementException("No value present"));
119                 return (PrintStream) outMethod.invoke(null);
120             }
121 
122             // JAnsi 1
123             String methodName = target == ConsoleTarget.SystemOut ? wrapSystemOut_METHOD_NAME
124                     : wrapSystemErr_METHOD_NAME;
125             Method method = classObj.getMethod(methodName, ARGUMENT_TYPES);
126             return (OutputStream) method.invoke(null, new PrintStream(targetStream));
127         } catch (Exception e) {
128             addWarn("Failed to create AnsiPrintStream. Falling back on the default stream.", e);
129         }
130         return targetStream;
131     }
132 
133     /**
134      * @return whether to use JANSI or not.
135      */
136     public boolean isWithJansi() {
137         return withJansi;
138     }
139 
140     /**
141      * If true, this appender will output to a stream provided by the JANSI library.
142      *
143      * @param withJansi whether to use JANSI or not.
144      * @since 1.0.5
145      */
146     public void setWithJansi(boolean withJansi) {
147         this.withJansi = withJansi;
148     }
149 
150 }