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
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
59
60
61
62 public enum Target {
63
64 SYSTEM_OUT,
65
66 SYSTEM_ERR
67 }
68
69 private ConsoleAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
70 final OutputStreamManager manager,
71 final boolean ignoreExceptions) {
72 super(name, layout, filter, ignoreExceptions, true, manager);
73 }
74
75
76
77
78
79
80
81
82
83
84
85
86 @PluginFactory
87 public static ConsoleAppender createAppender(
88 @PluginElement("Layout") Layout<? extends Serializable> layout,
89 @PluginElement("Filter") final Filter filter,
90 @PluginAttribute(value = "target", defaultString = "SYSTEM_OUT") final String targetStr,
91 @PluginAttribute("name") final String name,
92 @PluginAttribute(value = "follow", defaultBoolean = false) final String follow,
93 @PluginAttribute(value = "ignoreExceptions", defaultBoolean = true) final String ignore) {
94 if (name == null) {
95 LOGGER.error("No name provided for ConsoleAppender");
96 return null;
97 }
98 if (layout == null) {
99 layout = PatternLayout.createDefaultLayout();
100 }
101 final boolean isFollow = Boolean.parseBoolean(follow);
102 final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
103 final Target target = targetStr == null ? DEFAULT_TARGET : Target.valueOf(targetStr);
104 return new ConsoleAppender(name, layout, filter, getManager(isFollow, target, layout), ignoreExceptions);
105 }
106
107 public static ConsoleAppender createDefaultAppenderForLayout(final Layout<? extends Serializable> layout) {
108
109 return new ConsoleAppender("Console", layout, null, getManager(false, DEFAULT_TARGET, layout), true);
110 }
111
112 @PluginBuilderFactory
113 public static Builder newBuilder() {
114 return new Builder();
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> layout) {
141 this.layout = layout;
142 return this;
143 }
144
145 public Builder setFilter(final Filter filter) {
146 this.filter = filter;
147 return this;
148 }
149
150 public Builder setTarget(final Target target) {
151 this.target = target;
152 return this;
153 }
154
155 public Builder setName(final String name) {
156 this.name = name;
157 return this;
158 }
159
160 public Builder setFollow(final boolean follow) {
161 this.follow = follow;
162 return this;
163 }
164
165 public Builder setIgnoreExceptions(final boolean ignoreExceptions) {
166 this.ignoreExceptions = ignoreExceptions;
167 return this;
168 }
169
170 @Override
171 public ConsoleAppender build() {
172 return new ConsoleAppender(name, layout, filter, getManager(follow, target, layout), ignoreExceptions);
173 }
174 }
175
176 private static OutputStreamManager getManager(final boolean follow, final Target target, final Layout<? extends Serializable> layout) {
177 final String type = target.name();
178 final OutputStream os = getOutputStream(follow, target);
179 return OutputStreamManager.getManager(target.name() + '.' + follow, new FactoryData(os, type, layout), factory);
180 }
181
182 private static OutputStream getOutputStream(final boolean follow, final Target target) {
183 final String enc = Charset.defaultCharset().name();
184 OutputStream outputStream = null;
185 try {
186
187 outputStream = target == Target.SYSTEM_OUT ?
188 follow ? new PrintStream(new SystemOutStream(), true, enc) : System.out :
189 follow ? new PrintStream(new SystemErrStream(), true, enc) : System.err;
190
191 outputStream = new CloseShieldOutputStream(outputStream);
192 } catch (final UnsupportedEncodingException ex) {
193 throw new IllegalStateException("Unsupported default encoding " + enc, ex);
194 }
195 final PropertiesUtil propsUtil = PropertiesUtil.getProperties();
196 if (!propsUtil.getStringProperty("os.name").startsWith("Windows")
197 || propsUtil.getBooleanProperty("log4j.skipJansi")) {
198 return outputStream;
199 }
200 try {
201
202 final Class<?> clazz = Loader.loadClass(JANSI_CLASS);
203 final Constructor<?> constructor = clazz.getConstructor(OutputStream.class);
204 return new CloseShieldOutputStream((OutputStream) constructor.newInstance(outputStream));
205 } catch (final ClassNotFoundException cnfe) {
206 LOGGER.debug("Jansi is not installed, cannot find {}", JANSI_CLASS);
207 } catch (final NoSuchMethodException nsme) {
208 LOGGER.warn("{} is missing the proper constructor", JANSI_CLASS);
209 } catch (final Exception ex) {
210 LOGGER.warn("Unable to instantiate {}", JANSI_CLASS);
211 }
212 return outputStream;
213 }
214
215
216
217
218 private static class SystemErrStream extends OutputStream {
219 public SystemErrStream() {
220 }
221
222 @Override
223 public void close() {
224
225 }
226
227 @Override
228 public void flush() {
229 System.err.flush();
230 }
231
232 @Override
233 public void write(final byte[] b) throws IOException {
234 System.err.write(b);
235 }
236
237 @Override
238 public void write(final byte[] b, final int off, final int len)
239 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)
273 throws IOException {
274 System.out.write(b, off, len);
275 }
276
277 @Override
278 public void write(final int b) throws IOException {
279 System.out.write(b);
280 }
281 }
282
283
284
285
286 private static class CloseShieldOutputStream extends OutputStream {
287
288 private final OutputStream delegate;
289
290 public CloseShieldOutputStream(final OutputStream delegate) {
291 this.delegate = delegate;
292 }
293
294 @Override
295 public void close() {
296
297 }
298
299 @Override
300 public void flush() throws IOException {
301 delegate.flush();
302 }
303
304 @Override
305 public void write(final byte[] b) throws IOException {
306 delegate.write(b);
307 }
308
309 @Override
310 public void write(final byte[] b, final int off, final int len)
311 throws IOException {
312 delegate.write(b, off, len);
313 }
314
315 @Override
316 public void write(final int b) throws IOException {
317 delegate.write(b);
318 }
319 }
320
321
322
323
324 private static class FactoryData {
325 private final OutputStream os;
326 private final String type;
327 private final Layout<? extends Serializable> layout;
328
329
330
331
332
333
334
335 public FactoryData(final OutputStream os, final String type, final Layout<? extends Serializable> layout) {
336 this.os = os;
337 this.type = type;
338 this.layout = layout;
339 }
340 }
341
342
343
344
345 private static class ConsoleManagerFactory implements ManagerFactory<OutputStreamManager, FactoryData> {
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);
356 }
357 }
358
359 }