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
57
58
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
71
72
73
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
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
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
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
130
131 public boolean isWithJansi() {
132 return withJansi;
133 }
134
135
136
137
138
139
140
141 public void setWithJansi(boolean withJansi) {
142 this.withJansi = withJansi;
143 }
144
145 }