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