1
2
3
4
5
6
7
8
9
10
11
12
13
14 package eu.fbk.dkm.pikes.rdf;
15
16 import java.io.FilterOutputStream;
17 import java.io.IOException;
18 import java.io.OutputStream;
19 import java.io.PrintStream;
20 import java.lang.reflect.Constructor;
21
22 import org.slf4j.Logger;
23 import org.slf4j.MDC;
24
25 import ch.qos.logback.classic.Level;
26 import ch.qos.logback.classic.pattern.ClassicConverter;
27 import ch.qos.logback.classic.spi.ILoggingEvent;
28 import ch.qos.logback.core.UnsynchronizedAppenderBase;
29 import ch.qos.logback.core.encoder.Encoder;
30 import ch.qos.logback.core.pattern.color.ANSIConstants;
31 import ch.qos.logback.core.pattern.color.ForegroundCompositeConverterBase;
32 import ch.qos.logback.core.spi.DeferredProcessingAware;
33 import ch.qos.logback.core.status.ErrorStatus;
34 import ch.qos.logback.core.util.EnvUtil;
35
36 public final class Logging {
37
38 public static final String MDC_CONTEXT = "context";
39
40 public static void setLevel(final Logger logger, final String level) {
41 final Level l = Level.valueOf(level);
42 ((ch.qos.logback.classic.Logger) logger).setLevel(l);
43 }
44
45 public static final class NormalConverter extends
46 ForegroundCompositeConverterBase<ILoggingEvent> {
47
48 @Override
49 protected String getForegroundColorCode(final ILoggingEvent event) {
50 final ch.qos.logback.classic.Level level = event.getLevel();
51 switch (level.toInt()) {
52 case ch.qos.logback.classic.Level.ERROR_INT:
53 return ANSIConstants.RED_FG;
54 case ch.qos.logback.classic.Level.WARN_INT:
55 return ANSIConstants.MAGENTA_FG;
56 default:
57 return ANSIConstants.DEFAULT_FG;
58 }
59 }
60
61 }
62
63 public static final class BoldConverter extends
64 ForegroundCompositeConverterBase<ILoggingEvent> {
65
66 @Override
67 protected String getForegroundColorCode(final ILoggingEvent event) {
68 final ch.qos.logback.classic.Level level = event.getLevel();
69 switch (level.toInt()) {
70 case ch.qos.logback.classic.Level.ERROR_INT:
71 return ANSIConstants.BOLD + ANSIConstants.RED_FG;
72 case ch.qos.logback.classic.Level.WARN_INT:
73 return ANSIConstants.BOLD + ANSIConstants.MAGENTA_FG;
74 default:
75 return ANSIConstants.BOLD + ANSIConstants.DEFAULT_FG;
76 }
77 }
78
79 }
80
81 public static final class ContextConverter extends ClassicConverter {
82
83 @Override
84 public String convert(final ILoggingEvent event) {
85 final String context = MDC.get(MDC_CONTEXT);
86 final String logger = event.getLevel().toInt() >= Level.WARN_INT ? event
87 .getLoggerName() : null;
88 if (context == null) {
89 return logger == null ? "" : "[" + logger + "] ";
90 } else {
91 return logger == null ? "[" + context + "] " : "[" + context + "][" + logger
92 + "] ";
93 }
94 }
95
96 }
97
98 public static final class StatusAppender<E> extends UnsynchronizedAppenderBase<E> {
99
100 private static final int MAX_STATUS_LENGTH = 256;
101
102 private boolean withJansi;
103
104 private Encoder<E> encoder;
105
106 public synchronized boolean isWithJansi() {
107 return this.withJansi;
108 }
109
110 public synchronized void setWithJansi(final boolean withJansi) {
111 if (isStarted()) {
112 addStatus(new ErrorStatus("Cannot configure appender named \"" + this.name
113 + "\" after it has been started.", this));
114 }
115 this.withJansi = withJansi;
116 }
117
118 public synchronized Encoder<E> getEncoder() {
119 return this.encoder;
120 }
121
122 public synchronized void setEncoder(final Encoder<E> encoder) {
123 if (isStarted()) {
124 addStatus(new ErrorStatus("Cannot configure appender named \"" + this.name
125 + "\" after it has been started.", this));
126 }
127 this.encoder = encoder;
128 }
129
130 @SuppressWarnings("resource")
131 @Override
132 public synchronized void start() {
133
134
135 if (this.started) {
136 return;
137 }
138
139
140 if (this.encoder == null) {
141 addStatus(new ErrorStatus("No encoder set for the appender named \"" + this.name
142 + "\".", this));
143 return;
144 }
145
146
147 if (System.console() == null) {
148 return;
149 }
150
151
152 final PrintStream out = System.out;
153 final StatusAcceptorStream acceptor = new StatusAcceptorStream(out);
154 OutputStream generator = new StatusGeneratorStream(acceptor);
155
156
157 if (EnvUtil.isWindows() && this.withJansi) {
158 try {
159 final Class<?> clazz = Class
160 .forName("org.fusesource.jansi.WindowsAnsiOutputStream");
161 final Constructor<?> constructor = clazz.getConstructor(OutputStream.class);
162 generator = (OutputStream) constructor.newInstance(generator);
163 } catch (final Throwable ex) {
164
165 }
166 }
167
168 try {
169
170 this.encoder.init(generator);
171 System.setOut(new PrintStream(acceptor));
172 super.start();
173 } catch (final IOException ex) {
174 addStatus(new ErrorStatus("Failed to initialize encoder for appender named \""
175 + this.name + "\".", this, ex));
176 }
177 }
178
179 @Override
180 public synchronized void stop() {
181 if (!isStarted()) {
182 return;
183 }
184 try {
185 this.encoder.close();
186
187
188 } catch (final IOException ex) {
189 addStatus(new ErrorStatus("Failed to write footer for appender named \""
190 + this.name + "\".", this, ex));
191 } finally {
192 super.stop();
193 }
194 }
195
196 @Override
197 protected synchronized void append(final E event) {
198 if (!isStarted()) {
199 return;
200 }
201 try {
202 if (event instanceof DeferredProcessingAware) {
203 ((DeferredProcessingAware) event).prepareForDeferredProcessing();
204 }
205 this.encoder.doEncode(event);
206 } catch (final IOException ex) {
207 stop();
208 addStatus(new ErrorStatus("IO failure in appender named \"" + this.name + "\".",
209 this, ex));
210 }
211 }
212
213 private static final class StatusAcceptorStream extends FilterOutputStream {
214
215 private static final int ESC = 27;
216
217 private byte[] status;
218
219 private boolean statusEnabled;
220
221 public StatusAcceptorStream(final OutputStream stream) {
222 super(stream);
223 this.status = null;
224 this.statusEnabled = true;
225 }
226
227 @Override
228 public synchronized void write(final int b) throws IOException {
229 enableStatus(false);
230 this.out.write(b);
231 enableStatus(b == '\n');
232 }
233
234 @Override
235 public synchronized void write(final byte[] b) throws IOException {
236 enableStatus(false);
237 super.write(b);
238 enableStatus(b[b.length - 1] == '\n');
239 }
240
241 @Override
242 public synchronized void write(final byte[] b, final int off, final int len)
243 throws IOException {
244 enableStatus(false);
245 super.write(b, off, len);
246 enableStatus(len > 0 && b[off + len - 1] == '\n');
247 }
248
249 synchronized void setStatus(final byte[] status) {
250 final boolean oldEnabled = this.statusEnabled;
251 enableStatus(false);
252 this.status = status;
253 enableStatus(oldEnabled);
254 }
255
256 private void enableStatus(final boolean enabled) {
257 try {
258 if (enabled == this.statusEnabled) {
259 return;
260 }
261 this.statusEnabled = enabled;
262 if (this.status == null) {
263 return;
264 } else if (enabled) {
265 final int length = Math.min(this.status.length, MAX_STATUS_LENGTH);
266 this.out.write(this.status, 0, length);
267 this.out.write('\n');
268 } else {
269 final int length = Math.min(this.status.length, MAX_STATUS_LENGTH);
270 int newlines = 1;
271 for (int i = 0; i < length; ++i) {
272 if (this.status[i] == '\n') {
273 ++newlines;
274 }
275 }
276
277 this.out.write(ESC);
278 this.out.write('[');
279 this.out.write(Integer.toString(newlines).getBytes());
280 this.out.write('A');
281
282
283 this.out.write('\n');
284 this.out.write(ESC);
285 this.out.write('[');
286 this.out.write('1');
287 this.out.write('A');
288
289
290
291
292 this.out.write(ESC);
293 this.out.write('[');
294 this.out.write('0');
295 this.out.write('J');
296 }
297 } catch (final Throwable ex) {
298 if (ex instanceof Error) {
299 throw (Error) ex;
300 } else if (ex instanceof RuntimeException) {
301 throw (RuntimeException) ex;
302 } else {
303 throw new RuntimeException(ex);
304 }
305 }
306 }
307 }
308
309 private static final class StatusGeneratorStream extends OutputStream {
310
311 private final StatusAcceptorStream stream;
312
313 private final byte[] buffer;
314
315 private int offset;
316
317 public StatusGeneratorStream(final StatusAcceptorStream stream) {
318 this.stream = stream;
319 this.buffer = new byte[MAX_STATUS_LENGTH];
320 this.offset = 0;
321 }
322
323 @Override
324 public void write(final int b) throws IOException {
325 int emitCount = -1;
326 if (b == '\n') {
327 if (this.offset < MAX_STATUS_LENGTH) {
328 emitCount = this.offset;
329 }
330 this.offset = 0;
331 } else if (this.offset < MAX_STATUS_LENGTH) {
332 this.buffer[this.offset++] = (byte) b;
333 if (this.offset == MAX_STATUS_LENGTH) {
334 emitCount = this.offset;
335 }
336 }
337 if (emitCount >= 0) {
338 final byte[] status = new byte[emitCount];
339 System.arraycopy(this.buffer, 0, status, 0, emitCount);
340 this.stream.setStatus(status);
341 }
342 }
343
344 @Override
345 public void write(final byte[] b) throws IOException {
346 for (int i = 0; i < b.length; ++i) {
347 write(b[i]);
348 }
349 }
350
351 @Override
352 public void write(final byte[] b, final int off, final int len) throws IOException {
353 final int to = off + len;
354 for (int i = off; i < to; ++i) {
355 write(b[i]);
356 }
357 }
358
359 }
360
361 }
362
363 }