Source code: org/mortbay/util/OutputStreamLogSink.java
1 // ========================================================================
2 // Copyright (c) 1997 MortBay Consulting, Sydney
3 // $Id: OutputStreamLogSink.java,v 1.17 2003/04/24 21:57:52 bretts Exp $
4 // ========================================================================
5
6 package org.mortbay.util;
7
8 import java.io.IOException;
9 import java.io.OutputStream;
10 import java.util.TimeZone;
11
12
13 /* ------------------------------------------------------------ */
14 /** A Log sink.
15 * This class represents both a concrete or abstract sink of
16 * Log data. The default implementation logs to System.err, but
17 * other output stream or files may be specified.
18 *
19 * Currently this Stream only writes in ISO8859_1 encoding. For
20 * Other encodings use the less efficient WriterLogSink.
21 *
22 * If a logFilename is specified, output is sent to that file.
23 * If the filename contains "yyyy_mm_dd", the log file date format
24 * is used to create the actual filename and the log file is rolled
25 * over at local midnight.
26 * If append is set, existing logfiles are appended to, otherwise
27 * a backup is created with a timestamp.
28 * Dated log files are deleted after retain days.
29 *
30 * <p> If the property LOG_DATE_FORMAT is set, then it is interpreted
31 * as a format string for java.text.SimpleDateFormat and used to
32 * format the log timestamps. Default value: HH:mm:ss.SSS
33 *
34 * <p> If LOG_TIMEZONE is set, it is used to set the timezone of the log date
35 * format, otherwise GMT is used.
36 *
37 * @see org.mortbay.util.Log
38 * @version $Id: OutputStreamLogSink.java,v 1.17 2003/04/24 21:57:52 bretts Exp $
39 * @author Greg Wilkins (gregw)
40 */
41 public class OutputStreamLogSink
42 implements LogSink
43 {
44 /*-------------------------------------------------------------------*/
45 public final static char OPT_TIMESTAMP = 't';
46 public final static char OPT_LABEL = 'L';
47 public final static char OPT_TAG = 'T';
48 public final static char OPT_STACKSIZE = 's';
49 public final static char OPT_STACKTRACE = 'S';
50 public final static char OPT_ONELINE = 'O';
51
52 /* ------------------------------------------------------------ */
53 private final static String __lineSeparator =
54 System.getProperty("line.separator");
55
56 /*-------------------------------------------------------------------*/
57 private int _retainDays =Integer.getInteger("LOG_FILE_RETAIN_DAYS",31).intValue();
58
59 protected DateCache _dateFormat=
60 new DateCache(System.getProperty("LOG_DATE_FORMAT","HH:mm:ss.SSS"));
61 protected String _logTimezone=
62 System.getProperty("LOG_TIME_ZONE");
63 {
64 if (_logTimezone!=null)
65 _dateFormat.getFormat().setTimeZone(TimeZone.getTimeZone(_logTimezone));
66 }
67
68 /* ------------------------------------------------------------ */
69 protected boolean _logTimeStamps=true;
70 protected boolean _logLabels=true;
71 protected boolean _logTags=true;
72 protected boolean _logStackSize=true;
73 protected boolean _logStackTrace=false;
74 protected boolean _logOneLine=false;
75
76 /*-------------------------------------------------------------------*/
77 private String _filename;
78 private boolean _append=true;
79 protected boolean _flushOn=true;
80 protected int _bufferSize=2048;
81 protected boolean _reopen=false;
82
83 protected transient boolean _started;
84 protected transient OutputStream _out;
85 protected transient ByteArrayISO8859Writer _buffer;
86
87 /* ------------------------------------------------------------ */
88 /** Constructor.
89 */
90 public OutputStreamLogSink()
91 throws IOException
92 {
93 _filename=System.getProperty("LOG_FILE");
94 if (_filename==null)
95 _out=System.err;
96 }
97
98 /* ------------------------------------------------------------ */
99 public OutputStreamLogSink(String filename)
100 {
101 _filename=filename;
102 }
103
104 /* ------------------------------------------------------------ */
105 public void setOptions(String logOptions)
106 {
107 setOptions((logOptions.indexOf(OPT_TIMESTAMP) >= 0),
108 (logOptions.indexOf(OPT_LABEL) >= 0),
109 (logOptions.indexOf(OPT_TAG) >= 0),
110 (logOptions.indexOf(OPT_STACKSIZE) >= 0),
111 (logOptions.indexOf(OPT_STACKTRACE) >= 0),
112 (logOptions.indexOf(OPT_ONELINE) >= 0));
113 }
114
115 /* ------------------------------------------------------------ */
116 public String getOptions()
117 {
118 return
119 (_logTimeStamps?"t":"")+
120 (_logLabels?"L":"")+
121 (_logTags?"T":"")+
122 (_logStackSize?"s":"")+
123 (_logStackTrace?"S":"")+
124 (_logOneLine?"O":"");
125 }
126
127
128 /*-------------------------------------------------------------------*/
129 /** Set the log options.
130 */
131 public void setOptions(boolean logTimeStamps,
132 boolean logLabels,
133 boolean logTags,
134 boolean logStackSize,
135 boolean logStackTrace,
136 boolean logOneLine)
137 {
138 _logTimeStamps = logTimeStamps;
139 _logLabels = logLabels;
140 _logTags = logTags;
141 _logStackSize = logStackSize;
142 _logStackTrace = logStackTrace;
143 _logOneLine = logOneLine;
144 }
145
146 /* ------------------------------------------------------------ */
147 public String getLogDateFormat()
148 {
149 return _dateFormat.getFormatString();
150 }
151
152 /* ------------------------------------------------------------ */
153 public void setLogDateFormat(String logDateFormat)
154 {
155 _dateFormat = new DateCache(logDateFormat);
156 if (_logTimezone!=null)
157 _dateFormat.getFormat().setTimeZone(TimeZone.getTimeZone(_logTimezone));
158 }
159
160
161 /* ------------------------------------------------------------ */
162 /**
163 * @deprecated Use getLogTimeZone()
164 */
165 public String getLogTimezone()
166 {
167 return _logTimezone;
168 }
169
170 /* ------------------------------------------------------------ */
171 /**
172 * @deprecated Use setLogTimeZone(String)
173 */
174 public void setLogTimezone(String logTimezone)
175 {
176 _logTimezone=logTimezone;
177 if (_dateFormat!=null && _logTimezone!=null)
178 _dateFormat.getFormat().setTimeZone(TimeZone.getTimeZone(_logTimezone));
179 }
180
181 /* ------------------------------------------------------------ */
182 public String getLogTimeZone()
183 {
184 return _logTimezone;
185 }
186
187 /* ------------------------------------------------------------ */
188 public void setLogTimeZone(String logTimezone)
189 {
190 _logTimezone=logTimezone;
191 if (_dateFormat!=null && _logTimezone!=null)
192 _dateFormat.getFormat().setTimeZone(TimeZone.getTimeZone(_logTimezone));
193 }
194
195 /* ------------------------------------------------------------ */
196 public boolean isLogTimeStamps()
197 {
198 return _logTimeStamps;
199 }
200 /* ------------------------------------------------------------ */
201 public void setLogTimeStamps(boolean logTimeStamps)
202 {
203 _logTimeStamps = logTimeStamps;
204 }
205 /* ------------------------------------------------------------ */
206 public boolean isLogLabels()
207 {
208 return _logLabels;
209 }
210 /* ------------------------------------------------------------ */
211 public void setLogLabels(boolean logLabels)
212 {
213 _logLabels = logLabels;
214 }
215 /* ------------------------------------------------------------ */
216 public boolean isLogTags()
217 {
218 return _logTags;
219 }
220 /* ------------------------------------------------------------ */
221 public void setLogTags(boolean logTags)
222 {
223 _logTags = logTags;
224 }
225 /* ------------------------------------------------------------ */
226 public boolean isLogStackSize()
227 {
228 return _logStackSize;
229 }
230 /* ------------------------------------------------------------ */
231 public void setLogStackSize(boolean logStackSize)
232 {
233 _logStackSize = logStackSize;
234 }
235 /* ------------------------------------------------------------ */
236 public boolean isLogStackTrace()
237 {
238 return _logStackTrace;
239 }
240 /* ------------------------------------------------------------ */
241 public void setLogStackTrace(boolean logStackTrace)
242 {
243 _logStackTrace = logStackTrace;
244 }
245 /* ------------------------------------------------------------ */
246 public boolean isLogOneLine()
247 {
248 return _logOneLine;
249 }
250 /* ------------------------------------------------------------ */
251 public void setLogOneLine(boolean logOneLine)
252 {
253 _logOneLine = logOneLine;
254 }
255
256 /* ------------------------------------------------------------ */
257 public boolean isAppend()
258 {
259 return _append;
260 }
261
262 /* ------------------------------------------------------------ */
263 public void setAppend(boolean a)
264 {
265 _append=a;
266 }
267
268 /* ------------------------------------------------------------ */
269 public synchronized void setOutputStream(OutputStream out)
270 {
271 _reopen=isStarted() && out!=out;
272 _filename=null;
273 if (_buffer!=null)
274 _buffer.resetWriter();
275 _out=out;
276 }
277
278 /* ------------------------------------------------------------ */
279 public OutputStream getOutputStream()
280 {
281 return _out;
282 }
283
284 /* ------------------------------------------------------------ */
285 public synchronized void setFilename(String filename)
286 {
287 if (filename!=null)
288 {
289 filename=filename.trim();
290 if (filename.length()==0)
291 filename=null;
292 }
293 _reopen=isStarted() &&
294 ((_filename==null && filename!=null)||
295 (_filename!=null && !_filename.equals(filename)));
296 _filename=filename;
297
298 if (!isStarted() && _filename!=null)
299 _out=null;
300 }
301
302 /* ------------------------------------------------------------ */
303 public String getFilename()
304 {
305 return _filename;
306 }
307
308 /* ------------------------------------------------------------ */
309 public String getDatedFilename()
310 {
311 if (_filename==null)
312 return null;
313
314 if (_out==null || ! (_out instanceof RolloverFileOutputStream))
315 return null;
316
317 return ((RolloverFileOutputStream)_out).getDatedFilename();
318 }
319
320 /* ------------------------------------------------------------ */
321 public int getRetainDays()
322 {
323 return _retainDays;
324 }
325
326 /* ------------------------------------------------------------ */
327 public void setRetainDays(int retainDays)
328 {
329 _reopen=isStarted() && _retainDays!=retainDays;
330 _retainDays = retainDays;
331 }
332
333 /* ------------------------------------------------------------ */
334 /**
335 * @param on If true, log is flushed on every log.
336 */
337 public void setFlushOn(boolean on)
338 {
339 _flushOn=on;
340 if (on && _out!=null)
341 {
342 try{_out.flush();}
343 catch(IOException e){e.printStackTrace();}
344 }
345
346 }
347
348 /* ------------------------------------------------------------ */
349 /**
350 * @return true, log is flushed on every log.
351 */
352 public boolean getFlushOn()
353 {
354 return _flushOn;
355 }
356
357 /* ------------------------------------------------------------ */
358 /** Log a message.
359 * This method formats the log information as a string and calls
360 * log(String). It should only be specialized by a derived
361 * implementation if the format of the logged messages is to be changed.
362 *
363 * @param tag Tag for type of log
364 * @param msg The message
365 * @param frame The frame that generated the message.
366 * @param time The time stamp of the message.
367 */
368 public synchronized void log(String tag,
369 Object msg,
370 Frame frame,
371 long time)
372 {
373 StringBuffer buf = new StringBuffer(160);
374
375 // Log the time stamp
376 if (_logTimeStamps)
377 {
378 buf.append(_dateFormat.format(time));
379 buf.append(' ');
380 }
381
382
383 // Log the tag
384 if (_logTags)
385 buf.append(tag);
386
387 // Log the label
388 if (_logLabels && frame != null)
389 {
390 buf.append(frame.toString());
391 }
392
393 // Log the stack depth.
394 if (_logStackSize && frame != null)
395 {
396 if (frame.getDepth()<10)
397 buf.append('0');
398 buf.append(Integer.toString(frame.getDepth()));
399 buf.append("> ");
400 }
401
402 // Determine the indent string for the message and append it
403 // to the buffer. Only put a newline in the buffer if the first
404 // line is not blank
405 String nl=__lineSeparator;
406
407 if (_logLabels && !_logOneLine && _buffer.size() > 0)
408 buf.append(nl);
409
410 // Log indented message
411 String smsg=(msg==null)
412 ?"???"
413 :((msg instanceof String)?((String)msg):msg.toString());
414
415 if (_logOneLine)
416 {
417 smsg=StringUtil.replace(smsg,"\015\012","<|");
418 smsg=StringUtil.replace(smsg,"\015","<");
419 smsg=StringUtil.replace(smsg,"\012","|");
420 }
421 else
422 {
423 smsg=StringUtil.replace(smsg,"\015\012","<|");
424 smsg=StringUtil.replace(smsg,"\015","<|");
425 smsg=StringUtil.replace(smsg,"\012","<|");
426 smsg=StringUtil.replace(smsg,"<|",nl);
427 }
428 buf.append(smsg);
429
430 // Add stack frame to message
431 if (_logStackTrace && frame != null)
432 {
433 buf.append(nl);
434 buf.append(frame.getStack());
435 }
436
437 log(buf.toString());
438 }
439
440 /* ------------------------------------------------------------ */
441 /** Log a message.
442 * The formatted log string is written to the log sink. The default
443 * implementation writes the message to an outputstream.
444 * @param formattedLog
445 */
446 public synchronized void log(String formattedLog)
447 {
448 if (_reopen)
449 {
450 stop();
451 start();
452 }
453 try
454 {
455 _buffer.write(formattedLog);
456 _buffer.write(StringUtil.__LINE_SEPARATOR);
457 if (_flushOn || _buffer.size()>_bufferSize)
458 {
459 _buffer.writeTo(_out);
460 _buffer.resetWriter();
461 _out.flush();
462 }
463 }
464 catch(IOException e){e.printStackTrace();}
465 }
466
467
468 /* ------------------------------------------------------------ */
469 /** Start a log sink.
470 * The default implementation does nothing
471 */
472 public synchronized void start()
473 {
474 _buffer=new ByteArrayISO8859Writer(_bufferSize);
475 _reopen=false;
476 if (_started)
477 return;
478
479 if (_out==null && _filename!=null)
480 {
481 try
482 {
483 RolloverFileOutputStream rfos=
484 new RolloverFileOutputStream(_filename,_append,_retainDays);
485 _out=rfos;
486 }
487 catch(IOException e){e.printStackTrace();}
488 }
489
490 if (_out==null)
491 _out=System.err;
492
493 _started=true;
494 }
495
496
497 /* ------------------------------------------------------------ */
498 /** Stop a log sink.
499 * An opportunity for subclasses to clean up. The default
500 * implementation does nothing
501 */
502 public synchronized void stop()
503 {
504 _started=false;
505
506 if (_out!=null)
507 {
508 try
509 {
510 if (_buffer.size()>0)
511 {
512 _buffer.writeTo(_out);
513 }
514 _out.flush();
515 _buffer=null;
516 }
517 catch(Exception e){if (Code.debug())e.printStackTrace();}
518 Thread.yield();
519 }
520
521 if (_out!=null && _out!=System.err)
522 {
523 try{_out.close();}
524 catch(Exception e){if (Code.debug())e.printStackTrace();}
525 }
526
527 if (_filename!=null)
528 _out=null;
529 }
530
531 /* ------------------------------------------------------------ */
532 public boolean isStarted()
533 {
534 return _started;
535 }
536 };