Source code: org/mortbay/util/Log.java
1 // ========================================================================
2 // Copyright (c) 1997 MortBay Consulting, Sydney
3 // $Id: Log.java,v 1.12 2003/07/01 09:06:26 gregwilkins Exp $
4 // ========================================================================
5
6 package org.mortbay.util;
7
8 import java.util.StringTokenizer;
9
10 /*-----------------------------------------------------------------------*/
11 /** Log formatted and tagged messages.
12 * Multiple LogSinks instances can be configured, but by default a
13 * System.err sink is created.
14 * <p>
15 * The Log log format is controlled by the LOG_OPTIONS property
16 * supplied to the VM.
17 * <p>If LOG_OPTIONS is set, then the default output format is controlled
18 * by the option characters in the string:
19 * <PRE>
20 * t Timestamp log output
21 * T Show the log tag name
22 * L Show log label (thread, method and file names).
23 * s Show indication of stack depth
24 * S Stack trace for each output line (VERY VERBOSE)
25 * O Place each log one One line of output
26 * </PRE>
27 *
28 * <p> If the property LOG_CLASSES is set, it is interpreted as a
29 * semi-colon-separated list of fully-qualified LogSink class names.
30 * An instance of each class, created with a default constructor,
31 * is added to the list of log sinks.
32 *
33 * Some possibilities for LOG_CLASSES are
34 * org.mortbay.util.OutputStreamLogSink - log to System.err, a file whose name is
35 * specified in LOG_FILE, and optionally rollover the logs on a daily basis.
36 * See the javadoc for org.mortbay.util.OutputStreamLogSink for information on the
37 * options LOG_DATE_FORMAT, LOG_FILE_BACKUP_FORMAT, LOG_FILE_RETAIN_DAYS,
38 * LOG_FILE_DATE_FORMAT and LOG_TIME_ZONE.
39 *
40 * <p> If the property LOG_CLASSES is missing, a single OutputStreamLogSink is
41 * used to output to System.err.
42 *
43 * <p> As an alternative to the above behavior, you can create LogSinks
44 * in code and add() them to the Log. If you do this before the first
45 * use of the log, the default initialization will be skipped.
46 *
47 * @see org.mortbay.util.OutputStreamLogSink
48 */
49 public class Log
50 {
51 /*-------------------------------------------------------------------*/
52 public final static String DEBUG= "DEBUG ";
53 public final static String EVENT= "EVENT ";
54 public final static String WARN= "WARN!! ";
55 public final static String ASSERT="ASSERT ";
56 public final static String FAIL= "FAIL!! ";
57
58 /*-------------------------------------------------------------------*/
59 public LogSink[] _sinks = null;
60 public String _logOptions=null;
61 private boolean _initialized = false;
62
63 /*-------------------------------------------------------------------*/
64 private static class Singleton {static final Log __instance = new Log();}
65
66 /*-------------------------------------------------------------------*/
67 public static Log instance()
68 {
69 return Singleton.__instance;
70 }
71
72 /*-------------------------------------------------------------------*/
73 /** Default initialization is used the first time we have to log
74 * unless a sink has been added with add(). _needInit allows us to
75 * distinguish between initial state and disabled state.
76 */
77 private synchronized void defaultInit()
78 {
79 if (!_initialized)
80 {
81 _initialized = true;
82 _logOptions=System.getProperty("LOG_OPTIONS",
83 Code.getDebug()?"stLT":"tT");
84 String sinkClasses = System.getProperty("LOG_CLASSES",
85 "org.mortbay.util.OutputStreamLogSink");
86 StringTokenizer sinkTokens = new StringTokenizer(sinkClasses, ";");
87
88 LogSink sink= null;
89 while (sinkTokens.hasMoreTokens())
90 {
91 String sinkClassName = sinkTokens.nextToken();
92
93 try
94 {
95 Class sinkClass = Loader.loadClass(this.getClass(),sinkClassName);
96 if (org.mortbay.util.LogSink.class.isAssignableFrom(sinkClass)) {
97 sink = (LogSink)sinkClass.newInstance();
98 sink.setOptions(_logOptions);
99 sink.start();
100 Singleton.__instance.add(sink);
101 }
102 else
103 // Can't use Code.fail here, that's what we're setting up
104 System.err.println(sinkClass+" is not a org.mortbay.util.LogSink");
105 }
106 catch (Exception e) {
107 e.printStackTrace();
108 }
109 }
110 }
111 }
112
113 /*-------------------------------------------------------------------*/
114 /** Construct the shared instance of Log that decodes the
115 * options setup in the environments properties.
116 */
117 private Log()
118 {}
119
120
121
122 /* ------------------------------------------------------------ */
123 /** Add a Log Sink.
124 * @param logSinkClass The logsink classname or null for the default.
125 */
126 public synchronized void add(String logSinkClass)
127 {
128 try
129 {
130 if (logSinkClass==null || logSinkClass.length()==0)
131 logSinkClass="org.mortbay.util.OutputStreamLogSink";
132 Class sinkClass = Loader.loadClass(this.getClass(),logSinkClass);
133 LogSink sink=(LogSink)sinkClass.newInstance();
134 add(sink);
135 }
136 catch(Exception e)
137 {
138 Code.warning(e);
139 throw new IllegalArgumentException(e.toString());
140 }
141
142 }
143
144 /* ------------------------------------------------------------ */
145 /** Add a Log Sink.
146 * @param logSink
147 */
148 public synchronized void add(LogSink logSink)
149 {
150 if (_sinks==null)
151 {
152 _sinks=new LogSink[1];
153 _sinks[0]=logSink;
154 }
155 else
156 {
157 boolean slotFree = false;
158 for( int i=_sinks.length; i-->0; )
159 {
160 if( _sinks[i] == null )
161 {
162 slotFree = true;
163 _sinks[i] = logSink;
164 break;
165 }
166 }
167
168 if( !slotFree )
169 {
170 LogSink[] ns = new LogSink[_sinks.length+1];
171 for (int i=_sinks.length;i-->0;)
172 ns[i]=_sinks[i];
173 ns[_sinks.length]=logSink;
174 _sinks=ns;
175 }
176 }
177 _initialized = true;
178 }
179
180 /* ------------------------------------------------------------ */
181 public LogSink[] getLogSinks()
182 {
183 return _sinks;
184 }
185
186 /* ------------------------------------------------------------ */
187 /**
188 */
189 public synchronized void deleteStoppedLogSinks()
190 {
191 if (_sinks!=null)
192 {
193 for (int s=_sinks.length;s-->0;)
194 {
195 if (_sinks[s]==null)
196 continue;
197 if (!_sinks[s].isStarted())
198 _sinks[s]=null;
199 }
200 }
201 }
202
203 /* ------------------------------------------------------------ */
204 /** No logging.
205 * All log sinks are stopped and removed.
206 */
207 public synchronized void disableLog()
208 {
209 if (_sinks!=null) {
210 for (int s=_sinks.length;s-->0;)
211 {
212 try{
213 if (_sinks[s]!=null)
214 _sinks[s].stop();
215 }
216 catch(InterruptedException e)
217 {
218 Code.ignore(e);
219 }
220 }
221 _sinks=null;
222 }
223 _initialized=true;
224 }
225
226 /*-------------------------------------------------------------------*/
227 public static void message(String tag,
228 Object msg,
229 Frame frame)
230 {
231 long time = System.currentTimeMillis();
232 instance().message(tag,msg,frame,time);
233 }
234
235 /* ------------------------------------------------------------ */
236 /** Log an event.
237 */
238 public static void event(Object message, int stackDepth)
239 {
240 Log.message(Log.EVENT,message,new Frame(stackDepth));
241 }
242
243 /* ------------------------------------------------------------ */
244 /** Log an event.
245 */
246 public static void event(Object message)
247 {
248 Log.message(Log.EVENT,message,new Frame(1));
249 }
250
251 /* ------------------------------------------------------------ */
252 /** Log a warning message.
253 * @see org.mortbay.util.Code for warnings of exceptions etc.
254 * @param message the Object to use as a warning message.
255 * @param stackDepth number of levels of stack to ignore.
256 */
257 public static void warning(Object message, int stackDepth)
258 {
259 Log.message(Log.WARN,message,new Frame(stackDepth));
260 }
261
262 /* ------------------------------------------------------------ */
263 /** Log a warning.
264 * @see org.mortbay.util.Code for warnings of exceptions etc.
265 * @param message the Object to use as a warning message.
266 */
267 public static void warning(Object message)
268 {
269 Log.message(Log.WARN,message,new Frame(1));
270 }
271
272 /* ------------------------------------------------------------ */
273 /** Log a message.
274 * @param tag Tag for type of log
275 * @param msg The message
276 * @param frame The frame that generated the message.
277 * @param time The time stamp of the message.
278 */
279 public synchronized void message(String tag,
280 Object msg,
281 Frame frame,
282 long time)
283 {
284 if (!_initialized)
285 defaultInit();
286
287 if (_sinks==null)
288 {
289 System.err.println(time+": "+tag+","+msg+","+frame);
290 return;
291 }
292
293 boolean logged=false;
294 for (int s=_sinks.length;s-->0;)
295 {
296 if (_sinks[s]==null)
297 continue;
298
299 if (_sinks[s].isStarted())
300 {
301 logged=true;
302 _sinks[s].log(tag,msg,frame,time);
303 }
304 }
305
306 if (!logged)
307 System.err.println(time+": "+tag+","+msg+","+frame);
308 }
309
310 /* ------------------------------------------------------------ */
311 /** Log a message.
312 * @param tag Tag for type of log
313 * @param msg The message
314 */
315 public synchronized void message(String tag,
316 String msg)
317 {
318 message(tag,msg,new Frame(1),System.currentTimeMillis());
319 }
320
321
322 /*-------------------------------------------------------------------*/
323 public synchronized void setOptions(String logOptions)
324 {
325 _logOptions=logOptions;
326
327 for (int s=_sinks.length;s-->0;)
328 {
329 if (_sinks[s]==null)
330 continue;
331 _sinks[s].setOptions(logOptions);
332 }
333 }
334
335 /* ------------------------------------------------------------ */
336 public String getOptions()
337 {
338 return _logOptions;
339 }
340 }
341