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.PluginBuilderAttribute;
32 import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
33 import org.apache.logging.log4j.core.config.plugins.PluginElement;
34 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
35 import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
36 import org.apache.logging.log4j.core.layout.PatternLayout;
37 import org.apache.logging.log4j.core.util.Booleans;
38 import org.apache.logging.log4j.core.util.Loader;
39 import org.apache.logging.log4j.util.PropertiesUtil;
40
41
42
43
44
45
46
47
48
49 @Plugin(name = "Console", category = "Core", elementType = "appender", printObject = true)
50 public final class ConsoleAppender extends AbstractOutputStreamAppender<OutputStreamManager> {
51
52 private static final long serialVersionUID = 1L;
53 private static final String JANSI_CLASS = "org.fusesource.jansi.WindowsAnsiOutputStream";
54 private static ConsoleManagerFactory factory = new ConsoleManagerFactory();
55 private static final Target DEFAULT_TARGET = Target.SYSTEM_OUT;
56
57
58
59
60 public static enum Target {
61
62 SYSTEM_OUT,
63
64 SYSTEM_ERR
65 }
66
67 private ConsoleAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
68 final OutputStreamManager manager, final boolean ignoreExceptions) {
69 super(name, layout, filter, ignoreExceptions, true, manager);
70 }
71
72
73
74
75
76
77
78
79
80
81
82
83
84 @PluginFactory
85 public static ConsoleAppender createAppender(@PluginElement("Layout") Layout<? extends Serializable> layout,
86 @PluginElement("Filter") final Filter filter,
87 @PluginAttribute(value = "target", defaultString = "SYSTEM_OUT") final String targetStr,
88 @PluginAttribute("name") final String name,
89 @PluginAttribute(value = "follow", defaultBoolean = false) final String follow,
90 @PluginAttribute(value = "ignoreExceptions", defaultBoolean = true) final String ignore) {
91 if (name == null) {
92 LOGGER.error("No name provided for ConsoleAppender");
93 return null;
94 }
95 if (layout == null) {
96 layout = PatternLayout.createDefaultLayout();
97 }
98 final boolean isFollow = Boolean.parseBoolean(follow);
99 final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
100 final Target target = targetStr == null ? DEFAULT_TARGET : Target.valueOf(targetStr);
101 return new ConsoleAppender(name, layout, filter, getManager(target, isFollow, layout), ignoreExceptions);
102 }
103
104 public static ConsoleAppender createDefaultAppenderForLayout(final Layout<? extends Serializable> layout) {
105
106 return new ConsoleAppender("Console", layout, null, getManager(DEFAULT_TARGET, false, layout), true);
107 }
108
109 @PluginBuilderFactory
110 public static Builder newBuilder() {
111 return new Builder();
112 }
113
114
115
116
117 public static class Builder implements org.apache.logging.log4j.core.util.Builder<ConsoleAppender> {
118
119 @PluginElement("Layout")
120 @Required
121 private Layout<? extends Serializable> layout = PatternLayout.createDefaultLayout();
122
123 @PluginElement("Filter")
124 private Filter filter;
125
126 @PluginBuilderAttribute
127 @Required
128 private Target target = DEFAULT_TARGET;
129
130 @PluginBuilderAttribute
131 @Required
132 private String name;
133
134 @PluginBuilderAttribute
135 private boolean follow = false;
136
137 @PluginBuilderAttribute
138 private boolean ignoreExceptions = true;
139
140 public Builder setLayout(final Layout<? extends Serializable> aLayout) {
141 this.layout = aLayout;
142 return this;
143 }
144
145 public Builder setFilter(final Filter aFilter) {
146 this.filter = aFilter;
147 return this;
148 }
149
150 public Builder setTarget(final Target aTarget) {
151 this.target = aTarget;
152 return this;
153 }
154
155 public Builder setName(final String aName) {
156 this.name = aName;
157 return this;
158 }
159
160 public Builder setFollow(final boolean shouldFollow) {
161 this.follow = shouldFollow;
162 return this;
163 }
164
165 public Builder setIgnoreExceptions(final boolean shouldIgnoreExceptions) {
166 this.ignoreExceptions = shouldIgnoreExceptions;
167 return this;
168 }
169
170 @Override
171 public ConsoleAppender build() {
172 return new ConsoleAppender(name, layout, filter, getManager(target, follow, layout), ignoreExceptions);
173 }
174 }
175
176 private static OutputStreamManager getManager(final Target target, final boolean follow,
177 final Layout<? extends Serializable> layout) {
178 final OutputStream os = getOutputStream(follow, target);
179 final String managerName = target.name() + '.' + follow;
180 return OutputStreamManager.getManager(managerName, new FactoryData(os, managerName, layout), factory);
181 }
182
183 private static OutputStream getOutputStream(final boolean follow, final Target target) {
184 final String enc = Charset.defaultCharset().name();
185 OutputStream outputStream = null;
186 try {
187
188 outputStream = target == Target.SYSTEM_OUT ?
189 follow ? new PrintStream(new SystemOutStream(), true, enc) : System.out :
190 follow ? new PrintStream(new SystemErrStream(), true, enc) : System.err;
191
192 outputStream = new CloseShieldOutputStream(outputStream);
193 } catch (final UnsupportedEncodingException ex) {
194 throw new IllegalStateException("Unsupported default encoding " + enc, ex);
195 }
196 final PropertiesUtil propsUtil = PropertiesUtil.getProperties();
197 if (!propsUtil.getStringProperty("os.name").startsWith("Windows")
198 || propsUtil.getBooleanProperty("log4j.skipJansi")) {
199 return outputStream;
200 }
201 try {
202
203 final Class<?> clazz = Loader.loadClass(JANSI_CLASS);
204 final Constructor<?> constructor = clazz.getConstructor(OutputStream.class);
205 return new CloseShieldOutputStream((OutputStream) constructor.newInstance(outputStream));
206 } catch (final ClassNotFoundException cnfe) {
207 LOGGER.debug("Jansi is not installed, cannot find {}", JANSI_CLASS);
208 } catch (final NoSuchMethodException nsme) {
209 LOGGER.warn("{} is missing the proper constructor", JANSI_CLASS);
210 } catch (final Exception ex) {
211 LOGGER.warn("Unable to instantiate {}", JANSI_CLASS);
212 }
213 return outputStream;
214 }
215
216
217
218
219 private static class SystemErrStream extends OutputStream {
220 public SystemErrStream() {
221 }
222
223 @Override
224 public void close() {
225
226 }
227
228 @Override
229 public void flush() {
230 System.err.flush();
231 }
232
233 @Override
234 public void write(final byte[] b) throws IOException {
235 System.err.write(b);
236 }
237
238 @Override
239 public void write(final byte[] b, final int off, final int len) throws IOException {
240 System.err.write(b, off, len);
241 }
242
243 @Override
244 public void write(final int b) {
245 System.err.write(b);
246 }
247 }
248
249
250
251
252 private static class SystemOutStream extends OutputStream {
253 public SystemOutStream() {
254 }
255
256 @Override
257 public void close() {
258
259 }
260
261 @Override
262 public void flush() {
263 System.out.flush();
264 }
265
266 @Override
267 public void write(final byte[] b) throws IOException {
268 System.out.write(b);
269 }
270
271 @Override
272 public void write(final byte[] b, final int off, final int len) throws IOException {
273 System.out.write(b, off, len);
274 }
275
276 @Override
277 public void write(final int b) throws IOException {
278 System.out.write(b);
279 }
280 }
281
282
283
284
285 private static class CloseShieldOutputStream extends OutputStream {
286
287 private final OutputStream delegate;
288
289 public CloseShieldOutputStream(final OutputStream delegate) {
290 this.delegate = delegate;
291 }
292
293 @Override
294 public void close() {
295
296 }
297
298 @Override
299 public void flush() throws IOException {
300 delegate.flush();
301 }
302
303 @Override
304 public void write(final byte[] b) throws IOException {
305 delegate.write(b);
306 }
307
308 @Override
309 public void write(final byte[] b, final int off, final int len) throws IOException {
310 delegate.write(b, off, len);
311 }
312
313 @Override
314 public void write(final int b) throws IOException {
315 delegate.write(b);
316 }
317 }
318
319
320
321
322 private static class FactoryData {
323 private final OutputStream os;
324 private final String type;
325 private final Layout<? extends Serializable> layout;
326
327
328
329
330
331
332
333
334 public FactoryData(final OutputStream os, final String type, final Layout<? extends Serializable> layout) {
335 this.os = os;
336 this.type = type;
337 this.layout = layout;
338 }
339 }
340
341
342
343
344 private static class ConsoleManagerFactory implements ManagerFactory<OutputStreamManager, FactoryData> {
345
346
347
348
349
350
351
352
353 @Override
354 public OutputStreamManager createManager(final String name, final FactoryData data) {
355 return new OutputStreamManager(data.os, data.type, data.layout, true);
356 }
357 }
358
359 }