1
2
3
4
5
6
7
8
9
10
11
12
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
31
32
33
34
35
36
37
38
39
40
41
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
60
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
73
74
75
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
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
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
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
135
136 public boolean isWithJansi() {
137 return withJansi;
138 }
139
140
141
142
143
144
145
146 public void setWithJansi(boolean withJansi) {
147 this.withJansi = withJansi;
148 }
149
150 }