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 import ch.qos.logback.core.util.ReentryGuard;
29 import ch.qos.logback.core.util.ReentryGuardFactory;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 public class ConsoleAppender<E> extends OutputStreamAppender<E> {
47
48 protected ConsoleTarget target = ConsoleTarget.SystemOut;
49 protected boolean withJansi = false;
50
51 private final static String AnsiConsole_CLASS_NAME = "org.fusesource.jansi.AnsiConsole";
52 private final static String JANSI2_OUT_METHOD_NAME = "out";
53 private final static String JANSI2_ERR_METHOD_NAME = "err";
54 private final static String WRAP_SYSTEM_OUT_METHOD_NAME = "wrapSystemOut";
55 private final static String WRAP_SYSTEM_ERR_METHOD_NAME = "wrapSystemErr";
56 private final static String SYSTEM_INSTALL_METHOD_NAME = "systemInstall";
57 private final static Class<?>[] ARGUMENT_TYPES = { PrintStream.class };
58
59 private final static String CONSOLE_APPENDER_WARNING_URL = CoreConstants.CODES_URL+"#slowConsole";
60
61
62
63
64
65 public void setTarget(String value) {
66 ConsoleTarget t = ConsoleTarget.findByName(value.trim());
67 if (t == null) {
68 targetWarn(value);
69 } else {
70 target = t;
71 }
72 }
73
74
75
76
77
78
79
80 public String getTarget() {
81 return target.getName();
82 }
83
84 private void targetWarn(String val) {
85 Status status = new WarnStatus("[" + val + "] should be one of " + Arrays.toString(ConsoleTarget.values()),
86 this);
87 status.add(new WarnStatus("Using previously set target, System.out by default.", this));
88 addStatus(status);
89 }
90
91 @Override
92 public void start() {
93 addInfo("NOTE: Writing to the console can be slow. Try to avoid logging to the ");
94 addInfo("console in production environments, especially in high volume systems.");
95 addInfo("See also "+CONSOLE_APPENDER_WARNING_URL);
96 OutputStream targetStream = target.getStream();
97
98 if (withJansi) {
99 targetStream = wrapWithJansi(targetStream);
100 }
101 setOutputStream(targetStream);
102 super.start();
103 }
104
105
106
107
108
109 protected ReentryGuard buildReentryGuard() {
110 return ReentryGuardFactory.makeGuard(ReentryGuardFactory.GuardType.THREAD_LOCAL);
111 }
112
113 private OutputStream wrapWithJansi(OutputStream targetStream) {
114 try {
115 addInfo("Enabling JANSI AnsiPrintStream for the console.");
116 ClassLoader classLoader = Loader.getClassLoaderOfObject(context);
117 Class<?> classObj = classLoader.loadClass(AnsiConsole_CLASS_NAME);
118
119 Method systemInstallMethod = classObj.getMethod(SYSTEM_INSTALL_METHOD_NAME);
120 if(systemInstallMethod != null) {
121 systemInstallMethod.invoke(null);
122 }
123
124
125
126
127
128
129
130
131
132
133
134
135
136 String methodNameJansi2 = target == ConsoleTarget.SystemOut ? JANSI2_OUT_METHOD_NAME
137 : JANSI2_ERR_METHOD_NAME;
138 final Optional<Method> optOutMethod = Arrays.stream(classObj.getMethods())
139 .filter(m -> m.getName().equals(methodNameJansi2))
140 .filter(m -> m.getParameters().length == 0)
141 .filter(m -> Modifier.isStatic(m.getModifiers()))
142 .filter(m -> PrintStream.class.isAssignableFrom(m.getReturnType()))
143 .findAny();
144 if (optOutMethod.isPresent()) {
145 final Method outMethod = optOutMethod.orElseThrow(() -> new NoSuchElementException("No out/err method present"));
146 return (PrintStream) outMethod.invoke(null);
147 }
148
149
150 String methodName = target == ConsoleTarget.SystemOut ? WRAP_SYSTEM_OUT_METHOD_NAME
151 : WRAP_SYSTEM_ERR_METHOD_NAME;
152 Method method = classObj.getMethod(methodName, ARGUMENT_TYPES);
153 return (OutputStream) method.invoke(null, new PrintStream(targetStream));
154 } catch (Exception e) {
155 addWarn("Failed to create AnsiPrintStream. Falling back on the default stream.", e);
156 }
157 return targetStream;
158 }
159
160
161
162
163 public boolean isWithJansi() {
164 return withJansi;
165 }
166
167
168
169
170
171
172
173 public void setWithJansi(boolean withJansi) {
174 this.withJansi = withJansi;
175 }
176
177 }