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