1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.appender;
18
19 import java.io.IOException;
20 import java.io.OutputStream;
21 import java.io.PrintStream;
22 import java.io.Serializable;
23 import java.io.UnsupportedEncodingException;
24 import java.lang.reflect.Constructor;
25 import java.nio.charset.Charset;
26
27 import org.apache.logging.log4j.core.Filter;
28 import org.apache.logging.log4j.core.Layout;
29 import org.apache.logging.log4j.core.config.plugins.Plugin;
30 import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
31 import org.apache.logging.log4j.core.config.plugins.PluginElement;
32 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
33 import org.apache.logging.log4j.core.layout.PatternLayout;
34 import org.apache.logging.log4j.core.util.Booleans;
35 import org.apache.logging.log4j.core.util.Loader;
36 import org.apache.logging.log4j.util.PropertiesUtil;
37
38
39
40
41
42
43
44
45
46
47
48 @Plugin(name = "Console", category = "Core", elementType = "appender", printObject = true)
49 public final class ConsoleAppender extends AbstractOutputStreamAppender<OutputStreamManager> {
50
51 private static final String JANSI_CLASS = "org.fusesource.jansi.WindowsAnsiOutputStream";
52 private static ConsoleManagerFactory factory = new ConsoleManagerFactory();
53
54
55
56
57 public enum Target {
58
59 SYSTEM_OUT,
60
61 SYSTEM_ERR
62 }
63
64 private ConsoleAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
65 final OutputStreamManager manager,
66 final boolean ignoreExceptions) {
67 super(name, layout, filter, ignoreExceptions, true, manager);
68 }
69
70
71
72
73
74
75
76
77
78
79
80
81 @PluginFactory
82 public static ConsoleAppender createAppender(
83 @PluginElement("Layout") Layout<? extends Serializable> layout,
84 @PluginElement("Filter") final Filter filter,
85 @PluginAttribute(value = "target", defaultString = "SYSTEM_OUT") final String targetStr,
86 @PluginAttribute("name") final String name,
87 @PluginAttribute(value = "follow", defaultBoolean = false) final String follow,
88 @PluginAttribute(value = "ignoreExceptions", defaultBoolean = true) final String ignore) {
89 if (name == null) {
90 LOGGER.error("No name provided for ConsoleAppender");
91 return null;
92 }
93 if (layout == null) {
94 layout = PatternLayout.createDefaultLayout();
95 }
96 final boolean isFollow = Boolean.parseBoolean(follow);
97 final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
98 final Target target = targetStr == null ? Target.SYSTEM_OUT : Target.valueOf(targetStr);
99 return new ConsoleAppender(name, layout, filter, getManager(isFollow, target, layout), ignoreExceptions);
100 }
101
102 private static OutputStreamManager getManager(final boolean follow, final Target target, final Layout<? extends Serializable> layout) {
103 final String type = target.name();
104 final OutputStream os = getOutputStream(follow, target);
105 return OutputStreamManager.getManager(target.name() + '.' + follow, new FactoryData(os, type, layout), factory);
106 }
107
108 private static OutputStream getOutputStream(final boolean follow, final Target target) {
109 final String enc = Charset.defaultCharset().name();
110 PrintStream printStream = null;
111 try {
112 printStream = target == Target.SYSTEM_OUT ?
113 follow ? new PrintStream(new SystemOutStream(), true, enc) : System.out :
114 follow ? new PrintStream(new SystemErrStream(), true, enc) : System.err;
115 } catch (final UnsupportedEncodingException ex) {
116 throw new IllegalStateException("Unsupported default encoding " + enc, ex);
117 }
118 final PropertiesUtil propsUtil = PropertiesUtil.getProperties();
119 if (!propsUtil.getStringProperty("os.name").startsWith("Windows") ||
120 propsUtil.getBooleanProperty("log4j.skipJansi")) {
121 return printStream;
122 }
123 try {
124
125 final Class<?> clazz = Loader.loadClass(JANSI_CLASS);
126 final Constructor<?> constructor = clazz.getConstructor(OutputStream.class);
127 return (OutputStream) constructor.newInstance(printStream);
128 } catch (final ClassNotFoundException cnfe) {
129 LOGGER.debug("Jansi is not installed, cannot find {}", JANSI_CLASS);
130 } catch (final NoSuchMethodException nsme) {
131 LOGGER.warn("{} is missing the proper constructor", JANSI_CLASS);
132 } catch (final Exception ex) {
133 LOGGER.warn("Unable to instantiate {}", JANSI_CLASS);
134 }
135 return printStream;
136 }
137
138
139
140
141 private static class SystemErrStream extends OutputStream {
142 public SystemErrStream() {
143 }
144
145 @Override
146 public void close() {
147
148 }
149
150 @Override
151 public void flush() {
152 System.err.flush();
153 }
154
155 @Override
156 public void write(final byte[] b) throws IOException {
157 System.err.write(b);
158 }
159
160 @Override
161 public void write(final byte[] b, final int off, final int len)
162 throws IOException {
163 System.err.write(b, off, len);
164 }
165
166 @Override
167 public void write(final int b) {
168 System.err.write(b);
169 }
170 }
171
172
173
174
175 private static class SystemOutStream extends OutputStream {
176 public SystemOutStream() {
177 }
178
179 @Override
180 public void close() {
181
182 }
183
184 @Override
185 public void flush() {
186 System.out.flush();
187 }
188
189 @Override
190 public void write(final byte[] b) throws IOException {
191 System.out.write(b);
192 }
193
194 @Override
195 public void write(final byte[] b, final int off, final int len)
196 throws IOException {
197 System.out.write(b, off, len);
198 }
199
200 @Override
201 public void write(final int b) throws IOException {
202 System.out.write(b);
203 }
204 }
205
206
207
208
209 private static class FactoryData {
210 private final OutputStream os;
211 private final String type;
212 private final Layout<? extends Serializable> layout;
213
214
215
216
217
218
219
220 public FactoryData(final OutputStream os, final String type, final Layout<? extends Serializable> layout) {
221 this.os = os;
222 this.type = type;
223 this.layout = layout;
224 }
225 }
226
227
228
229
230 private static class ConsoleManagerFactory implements ManagerFactory<OutputStreamManager, FactoryData> {
231
232
233
234
235
236
237
238 @Override
239 public OutputStreamManager createManager(final String name, final FactoryData data) {
240 return new OutputStreamManager(data.os, data.type, data.layout);
241 }
242 }
243
244 }