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