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