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.FileDescriptor;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.io.PrintStream;
24 import java.io.Serializable;
25 import java.io.UnsupportedEncodingException;
26 import java.lang.reflect.Constructor;
27 import java.nio.charset.Charset;
28 import java.util.concurrent.atomic.AtomicInteger;
29
30 import org.apache.logging.log4j.core.Appender;
31 import org.apache.logging.log4j.core.Filter;
32 import org.apache.logging.log4j.core.Layout;
33 import org.apache.logging.log4j.core.config.plugins.Plugin;
34 import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
35 import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
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.util.LoaderUtil;
41 import org.apache.logging.log4j.util.PropertiesUtil;
42
43
44
45
46
47
48
49
50
51 @Plugin(name = ConsoleAppender.PLUGIN_NAME, category = "Core", elementType = Appender.ELEMENT_TYPE, printObject = true)
52 public final class ConsoleAppender extends AbstractOutputStreamAppender<OutputStreamManager> {
53
54 public static final String PLUGIN_NAME = "Console";
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 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, final 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, false, layout), ignoreExceptions, target);
109 }
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 @Deprecated
127 public static ConsoleAppender createAppender(
128
129 Layout<? extends Serializable> layout,
130 final Filter filter,
131 Target target,
132 final String name,
133 final boolean follow,
134 final boolean direct,
135 final boolean ignoreExceptions) {
136
137 if (name == null) {
138 LOGGER.error("No name provided for ConsoleAppender");
139 return null;
140 }
141 if (layout == null) {
142 layout = PatternLayout.createDefaultLayout();
143 }
144 target = target == null ? Target.SYSTEM_OUT : target;
145 if (follow && direct) {
146 LOGGER.error("Cannot use both follow and direct on ConsoleAppender");
147 return null;
148 }
149 return new ConsoleAppender(name, layout, filter, getManager(target, follow, direct, layout), ignoreExceptions, target);
150 }
151
152 public static ConsoleAppender createDefaultAppenderForLayout(final Layout<? extends Serializable> layout) {
153
154 return new ConsoleAppender("DefaultConsole-" + COUNT.incrementAndGet(), layout, null,
155 getDefaultManager(DEFAULT_TARGET, false, false, layout), true, DEFAULT_TARGET);
156 }
157
158 @PluginBuilderFactory
159 public static <B extends Builder<B>> B newBuilder() {
160 return new Builder<B>().asBuilder();
161 }
162
163
164
165
166
167 public static class Builder<B extends Builder<B>> extends AbstractOutputStreamAppender.Builder<B>
168 implements org.apache.logging.log4j.core.util.Builder<ConsoleAppender> {
169
170 @PluginBuilderAttribute
171 @Required
172 private Target target = DEFAULT_TARGET;
173
174 @PluginBuilderAttribute
175 private boolean follow;
176
177 @PluginBuilderAttribute
178 private boolean direct;
179
180 public B setTarget(final Target aTarget) {
181 this.target = aTarget;
182 return asBuilder();
183 }
184
185 public B setFollow(final boolean shouldFollow) {
186 this.follow = shouldFollow;
187 return asBuilder();
188 }
189
190 public B setDirect(final boolean shouldDirect) {
191 this.direct = shouldDirect;
192 return asBuilder();
193 }
194
195 @Override
196 public ConsoleAppender build() {
197 if (follow && direct) {
198 throw new IllegalArgumentException("Cannot use both follow and direct on ConsoleAppender '" + getName() + "'");
199 }
200 final Layout<? extends Serializable> layout = getOrCreateLayout();
201 return new ConsoleAppender(getName(), layout, getFilter(), getManager(target, follow, direct, layout),
202 isIgnoreExceptions(), target);
203 }
204 }
205
206 private static OutputStreamManager getDefaultManager(final Target target, final boolean follow, final boolean direct,
207 final Layout<? extends Serializable> layout) {
208 final OutputStream os = getOutputStream(follow, direct, target);
209
210
211 final String managerName = target.name() + '.' + follow + '.' + direct + "-" + COUNT.get();
212 return OutputStreamManager.getManager(managerName, new FactoryData(os, managerName, layout), factory);
213 }
214
215 private static OutputStreamManager getManager(final Target target, final boolean follow, final boolean direct,
216 final Layout<? extends Serializable> layout) {
217 final OutputStream os = getOutputStream(follow, direct, target);
218 final String managerName = target.name() + '.' + follow + '.' + direct;
219 return OutputStreamManager.getManager(managerName, new FactoryData(os, managerName, layout), factory);
220 }
221
222 private static OutputStream getOutputStream(final boolean follow, final boolean direct, final Target target) {
223 final String enc = Charset.defaultCharset().name();
224 OutputStream outputStream;
225 try {
226
227 outputStream = target == Target.SYSTEM_OUT ?
228 direct ? new FileOutputStream(FileDescriptor.out) :
229 (follow ? new PrintStream(new SystemOutStream(), true, enc) : System.out) :
230 direct ? new FileOutputStream(FileDescriptor.err) :
231 (follow ? new PrintStream(new SystemErrStream(), true, enc) : System.err);
232
233 outputStream = new CloseShieldOutputStream(outputStream);
234 } catch (final UnsupportedEncodingException ex) {
235 throw new IllegalStateException("Unsupported default encoding " + enc, ex);
236 }
237 final PropertiesUtil propsUtil = PropertiesUtil.getProperties();
238 if (!propsUtil.isOsWindows() || propsUtil.getBooleanProperty("log4j.skipJansi") || direct) {
239 return outputStream;
240 }
241 try {
242
243 final Class<?> clazz = LoaderUtil.loadClass(JANSI_CLASS);
244 final Constructor<?> constructor = clazz.getConstructor(OutputStream.class);
245 return new CloseShieldOutputStream((OutputStream) constructor.newInstance(outputStream));
246 } catch (final ClassNotFoundException cnfe) {
247 LOGGER.debug("Jansi is not installed, cannot find {}", JANSI_CLASS);
248 } catch (final NoSuchMethodException nsme) {
249 LOGGER.warn("{} is missing the proper constructor", JANSI_CLASS);
250 } catch (final Exception ex) {
251 LOGGER.warn("Unable to instantiate {}", JANSI_CLASS);
252 }
253 return outputStream;
254 }
255
256
257
258
259 private static class SystemErrStream extends OutputStream {
260 public SystemErrStream() {
261 }
262
263 @Override
264 public void close() {
265
266 }
267
268 @Override
269 public void flush() {
270 System.err.flush();
271 }
272
273 @Override
274 public void write(final byte[] b) throws IOException {
275 System.err.write(b);
276 }
277
278 @Override
279 public void write(final byte[] b, final int off, final int len) throws IOException {
280 System.err.write(b, off, len);
281 }
282
283 @Override
284 public void write(final int b) {
285 System.err.write(b);
286 }
287 }
288
289
290
291
292 private static class SystemOutStream extends OutputStream {
293 public SystemOutStream() {
294 }
295
296 @Override
297 public void close() {
298
299 }
300
301 @Override
302 public void flush() {
303 System.out.flush();
304 }
305
306 @Override
307 public void write(final byte[] b) throws IOException {
308 System.out.write(b);
309 }
310
311 @Override
312 public void write(final byte[] b, final int off, final int len) throws IOException {
313 System.out.write(b, off, len);
314 }
315
316 @Override
317 public void write(final int b) throws IOException {
318 System.out.write(b);
319 }
320 }
321
322
323
324
325 private static class FactoryData {
326 private final OutputStream os;
327 private final String name;
328 private final Layout<? extends Serializable> layout;
329
330
331
332
333
334
335
336
337 public FactoryData(final OutputStream os, final String type, final Layout<? extends Serializable> layout) {
338 this.os = os;
339 this.name = type;
340 this.layout = layout;
341 }
342 }
343
344
345
346
347 private static class ConsoleManagerFactory implements ManagerFactory<OutputStreamManager, FactoryData> {
348
349
350
351
352
353
354
355
356 @Override
357 public OutputStreamManager createManager(final String name, final FactoryData data) {
358 return new OutputStreamManager(data.os, data.name, data.layout, true);
359 }
360 }
361
362 public Target getTarget() {
363 return target;
364 }
365
366 }