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