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 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      /**
57       * Sets the value of the <b>Target</b> option. Recognized values are
58       * "System.out" and "System.err". Any other value will be ignored.
59       */
60      public void setTarget(String value) {
61          ConsoleTarget t = ConsoleTarget.findByName(value.trim());
62          if (t == null) {
63              targetWarn(value);
64          } else {
65              target = t;
66          }
67      }
68  
69      /**
70       * Returns the current value of the <b>target</b> property. The default value of
71       * the option is "System.out".
72       * <p>
73       * See also {@link #setTarget}.
74       */
75      public String getTarget() {
76          return target.getName();
77      }
78  
79      private void targetWarn(String val) {
80          Status status = new WarnStatus("[" + val + "] should be one of " + Arrays.toString(ConsoleTarget.values()),
81                  this);
82          status.add(new WarnStatus("Using previously set target, System.out by default.", this));
83          addStatus(status);
84      }
85  
86      @Override
87      public void start() {
88          OutputStream targetStream = target.getStream();
89          // enable jansi only if withJansi set to true
90          if (withJansi) {
91              targetStream = wrapWithJansi(targetStream);
92          }
93          setOutputStream(targetStream);
94          super.start();
95      }
96  
97      private OutputStream wrapWithJansi(OutputStream targetStream) {
98          try {
99              addInfo("Enabling JANSI AnsiPrintStream for the console.");
100             ClassLoader classLoader = Loader.getClassLoaderOfObject(context);
101             Class<?> classObj = classLoader.loadClass(AnsiConsole_CLASS_NAME);
102 
103             // check for JAnsi 2
104             String methodNameJansi2 = target == ConsoleTarget.SystemOut ? JANSI2_OUT_METHOD_NAME
105                     : JANSI2_ERR_METHOD_NAME;
106             final Optional<Method> optOutMethod = Arrays.stream(classObj.getMethods())
107                     .filter(m -> m.getName().equals(methodNameJansi2))
108                     .filter(m -> m.getParameters().length == 0)
109                     .filter(m -> Modifier.isStatic(m.getModifiers()))
110                     .filter(m -> PrintStream.class.isAssignableFrom(m.getReturnType()))
111                     .findAny();
112             if (optOutMethod.isPresent()) {
113                 final Method outMethod = optOutMethod.orElseThrow(() -> new NoSuchElementException("No value present"));
114                 return (PrintStream) outMethod.invoke(null);
115             }
116 
117             // JAnsi 1
118             String methodName = target == ConsoleTarget.SystemOut ? wrapSystemOut_METHOD_NAME
119                     : wrapSystemErr_METHOD_NAME;
120             Method method = classObj.getMethod(methodName, ARGUMENT_TYPES);
121             return (OutputStream) method.invoke(null, new PrintStream(targetStream));
122         } catch (Exception e) {
123             addWarn("Failed to create AnsiPrintStream. Falling back on the default stream.", e);
124         }
125         return targetStream;
126     }
127 
128     /**
129      * @return whether to use JANSI or not.
130      */
131     public boolean isWithJansi() {
132         return withJansi;
133     }
134 
135     /**
136      * If true, this appender will output to a stream provided by the JANSI library.
137      *
138      * @param withJansi whether to use JANSI or not.
139      * @since 1.0.5
140      */
141     public void setWithJansi(boolean withJansi) {
142         this.withJansi = withJansi;
143     }
144 
145 }