001 /*
002 * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.util.logging;
027
028 import java.util.*;
029 import java.io.*;
030
031 /**
032 * LogRecord objects are used to pass logging requests between
033 * the logging framework and individual log Handlers.
034 * <p>
035 * When a LogRecord is passed into the logging framework it
036 * logically belongs to the framework and should no longer be
037 * used or updated by the client application.
038 * <p>
039 * Note that if the client application has not specified an
040 * explicit source method name and source class name, then the
041 * LogRecord class will infer them automatically when they are
042 * first accessed (due to a call on getSourceMethodName or
043 * getSourceClassName) by analyzing the call stack. Therefore,
044 * if a logging Handler wants to pass off a LogRecord to another
045 * thread, or to transmit it over RMI, and if it wishes to subsequently
046 * obtain method name or class name information it should call
047 * one of getSourceClassName or getSourceMethodName to force
048 * the values to be filled in.
049 * <p>
050 * <b> Serialization notes:</b>
051 * <ul>
052 * <li>The LogRecord class is serializable.
053 *
054 * <li> Because objects in the parameters array may not be serializable,
055 * during serialization all objects in the parameters array are
056 * written as the corresponding Strings (using Object.toString).
057 *
058 * <li> The ResourceBundle is not transmitted as part of the serialized
059 * form, but the resource bundle name is, and the recipient object's
060 * readObject method will attempt to locate a suitable resource bundle.
061 *
062 * </ul>
063 *
064 * @version 1.31, 05/09/07
065 * @since 1.4
066 */
067
068 public class LogRecord implements java.io.Serializable {
069 private static long globalSequenceNumber;
070 private static int nextThreadId = 10;
071 private static ThreadLocal<Integer> threadIds = new ThreadLocal<Integer>();
072
073 /**
074 * @serial Logging message level
075 */
076 private Level level;
077
078 /**
079 * @serial Sequence number
080 */
081 private long sequenceNumber;
082
083 /**
084 * @serial Class that issued logging call
085 */
086 private String sourceClassName;
087
088 /**
089 * @serial Method that issued logging call
090 */
091 private String sourceMethodName;
092
093 /**
094 * @serial Non-localized raw message text
095 */
096 private String message;
097
098 /**
099 * @serial Thread ID for thread that issued logging call.
100 */
101 private int threadID;
102
103 /**
104 * @serial Event time in milliseconds since 1970
105 */
106 private long millis;
107
108 /**
109 * @serial The Throwable (if any) associated with log message
110 */
111 private Throwable thrown;
112
113 /**
114 * @serial Name of the source Logger.
115 */
116 private String loggerName;
117
118 /**
119 * @serial Resource bundle name to localized log message.
120 */
121 private String resourceBundleName;
122
123 private transient boolean needToInferCaller;
124 private transient Object parameters[];
125 private transient ResourceBundle resourceBundle;
126
127 /**
128 * Construct a LogRecord with the given level and message values.
129 * <p>
130 * The sequence property will be initialized with a new unique value.
131 * These sequence values are allocated in increasing order within a VM.
132 * <p>
133 * The millis property will be initialized to the current time.
134 * <p>
135 * The thread ID property will be initialized with a unique ID for
136 * the current thread.
137 * <p>
138 * All other properties will be initialized to "null".
139 *
140 * @param level a logging level value
141 * @param msg the raw non-localized logging message (may be null)
142 */
143 public LogRecord(Level level, String msg) {
144 // Make sure level isn't null, by calling random method.
145 level.getClass();
146 this .level = level;
147 message = msg;
148 // Assign a thread ID and a unique sequence number.
149 synchronized (LogRecord.class) {
150 sequenceNumber = globalSequenceNumber++;
151 Integer id = threadIds.get();
152 if (id == null) {
153 id = new Integer(nextThreadId++);
154 threadIds.set(id);
155 }
156 threadID = id.intValue();
157 }
158 millis = System.currentTimeMillis();
159 needToInferCaller = true;
160 }
161
162 /**
163 * Get the source Logger name's
164 *
165 * @return source logger name (may be null)
166 */
167 public String getLoggerName() {
168 return loggerName;
169 }
170
171 /**
172 * Set the source Logger name.
173 *
174 * @param name the source logger name (may be null)
175 */
176 public void setLoggerName(String name) {
177 loggerName = name;
178 }
179
180 /**
181 * Get the localization resource bundle
182 * <p>
183 * This is the ResourceBundle that should be used to localize
184 * the message string before formatting it. The result may
185 * be null if the message is not localizable, or if no suitable
186 * ResourceBundle is available.
187 */
188 public ResourceBundle getResourceBundle() {
189 return resourceBundle;
190 }
191
192 /**
193 * Set the localization resource bundle.
194 *
195 * @param bundle localization bundle (may be null)
196 */
197 public void setResourceBundle(ResourceBundle bundle) {
198 resourceBundle = bundle;
199 }
200
201 /**
202 * Get the localization resource bundle name
203 * <p>
204 * This is the name for the ResourceBundle that should be
205 * used to localize the message string before formatting it.
206 * The result may be null if the message is not localizable.
207 */
208 public String getResourceBundleName() {
209 return resourceBundleName;
210 }
211
212 /**
213 * Set the localization resource bundle name.
214 *
215 * @param name localization bundle name (may be null)
216 */
217 public void setResourceBundleName(String name) {
218 resourceBundleName = name;
219 }
220
221 /**
222 * Get the logging message level, for example Level.SEVERE.
223 * @return the logging message level
224 */
225 public Level getLevel() {
226 return level;
227 }
228
229 /**
230 * Set the logging message level, for example Level.SEVERE.
231 * @param level the logging message level
232 */
233 public void setLevel(Level level) {
234 if (level == null) {
235 throw new NullPointerException();
236 }
237 this .level = level;
238 }
239
240 /**
241 * Get the sequence number.
242 * <p>
243 * Sequence numbers are normally assigned in the LogRecord
244 * constructor, which assigns unique sequence numbers to
245 * each new LogRecord in increasing order.
246 * @return the sequence number
247 */
248 public long getSequenceNumber() {
249 return sequenceNumber;
250 }
251
252 /**
253 * Set the sequence number.
254 * <p>
255 * Sequence numbers are normally assigned in the LogRecord constructor,
256 * so it should not normally be necessary to use this method.
257 */
258 public void setSequenceNumber(long seq) {
259 sequenceNumber = seq;
260 }
261
262 /**
263 * Get the name of the class that (allegedly) issued the logging request.
264 * <p>
265 * Note that this sourceClassName is not verified and may be spoofed.
266 * This information may either have been provided as part of the
267 * logging call, or it may have been inferred automatically by the
268 * logging framework. In the latter case, the information may only
269 * be approximate and may in fact describe an earlier call on the
270 * stack frame.
271 * <p>
272 * May be null if no information could be obtained.
273 *
274 * @return the source class name
275 */
276 public String getSourceClassName() {
277 if (needToInferCaller) {
278 inferCaller();
279 }
280 return sourceClassName;
281 }
282
283 /**
284 * Set the name of the class that (allegedly) issued the logging request.
285 *
286 * @param sourceClassName the source class name (may be null)
287 */
288 public void setSourceClassName(String sourceClassName) {
289 this .sourceClassName = sourceClassName;
290 needToInferCaller = false;
291 }
292
293 /**
294 * Get the name of the method that (allegedly) issued the logging request.
295 * <p>
296 * Note that this sourceMethodName is not verified and may be spoofed.
297 * This information may either have been provided as part of the
298 * logging call, or it may have been inferred automatically by the
299 * logging framework. In the latter case, the information may only
300 * be approximate and may in fact describe an earlier call on the
301 * stack frame.
302 * <p>
303 * May be null if no information could be obtained.
304 *
305 * @return the source method name
306 */
307 public String getSourceMethodName() {
308 if (needToInferCaller) {
309 inferCaller();
310 }
311 return sourceMethodName;
312 }
313
314 /**
315 * Set the name of the method that (allegedly) issued the logging request.
316 *
317 * @param sourceMethodName the source method name (may be null)
318 */
319 public void setSourceMethodName(String sourceMethodName) {
320 this .sourceMethodName = sourceMethodName;
321 needToInferCaller = false;
322 }
323
324 /**
325 * Get the "raw" log message, before localization or formatting.
326 * <p>
327 * May be null, which is equivalent to the empty string "".
328 * <p>
329 * This message may be either the final text or a localization key.
330 * <p>
331 * During formatting, if the source logger has a localization
332 * ResourceBundle and if that ResourceBundle has an entry for
333 * this message string, then the message string is replaced
334 * with the localized value.
335 *
336 * @return the raw message string
337 */
338 public String getMessage() {
339 return message;
340 }
341
342 /**
343 * Set the "raw" log message, before localization or formatting.
344 *
345 * @param message the raw message string (may be null)
346 */
347 public void setMessage(String message) {
348 this .message = message;
349 }
350
351 /**
352 * Get the parameters to the log message.
353 *
354 * @return the log message parameters. May be null if
355 * there are no parameters.
356 */
357 public Object[] getParameters() {
358 return parameters;
359 }
360
361 /**
362 * Set the parameters to the log message.
363 *
364 * @param parameters the log message parameters. (may be null)
365 */
366 public void setParameters(Object parameters[]) {
367 this .parameters = parameters;
368 }
369
370 /**
371 * Get an identifier for the thread where the message originated.
372 * <p>
373 * This is a thread identifier within the Java VM and may or
374 * may not map to any operating system ID.
375 *
376 * @return thread ID
377 */
378 public int getThreadID() {
379 return threadID;
380 }
381
382 /**
383 * Set an identifier for the thread where the message originated.
384 * @param threadID the thread ID
385 */
386 public void setThreadID(int threadID) {
387 this .threadID = threadID;
388 }
389
390 /**
391 * Get event time in milliseconds since 1970.
392 *
393 * @return event time in millis since 1970
394 */
395 public long getMillis() {
396 return millis;
397 }
398
399 /**
400 * Set event time.
401 *
402 * @param millis event time in millis since 1970
403 */
404 public void setMillis(long millis) {
405 this .millis = millis;
406 }
407
408 /**
409 * Get any throwable associated with the log record.
410 * <p>
411 * If the event involved an exception, this will be the
412 * exception object. Otherwise null.
413 *
414 * @return a throwable
415 */
416 public Throwable getThrown() {
417 return thrown;
418 }
419
420 /**
421 * Set a throwable associated with the log event.
422 *
423 * @param thrown a throwable (may be null)
424 */
425 public void setThrown(Throwable thrown) {
426 this .thrown = thrown;
427 }
428
429 private static final long serialVersionUID = 5372048053134512534L;
430
431 /**
432 * @serialData Default fields, followed by a two byte version number
433 * (major byte, followed by minor byte), followed by information on
434 * the log record parameter array. If there is no parameter array,
435 * then -1 is written. If there is a parameter array (possible of zero
436 * length) then the array length is written as an integer, followed
437 * by String values for each parameter. If a parameter is null, then
438 * a null String is written. Otherwise the output of Object.toString()
439 * is written.
440 */
441 private void writeObject(ObjectOutputStream out) throws IOException {
442 // We have to call defaultWriteObject first.
443 out.defaultWriteObject();
444
445 // Write our version number.
446 out.writeByte(1);
447 out.writeByte(0);
448 if (parameters == null) {
449 out.writeInt(-1);
450 return;
451 }
452 out.writeInt(parameters.length);
453 // Write string values for the parameters.
454 for (int i = 0; i < parameters.length; i++) {
455 if (parameters[i] == null) {
456 out.writeObject(null);
457 } else {
458 out.writeObject(parameters[i].toString());
459 }
460 }
461 }
462
463 private void readObject(ObjectInputStream in) throws IOException,
464 ClassNotFoundException {
465 // We have to call defaultReadObject first.
466 in.defaultReadObject();
467
468 // Read version number.
469 byte major = in.readByte();
470 byte minor = in.readByte();
471 if (major != 1) {
472 throw new IOException("LogRecord: bad version: " + major
473 + "." + minor);
474 }
475 int len = in.readInt();
476 if (len == -1) {
477 parameters = null;
478 } else {
479 parameters = new Object[len];
480 for (int i = 0; i < parameters.length; i++) {
481 parameters[i] = in.readObject();
482 }
483 }
484 // If necessary, try to regenerate the resource bundle.
485 if (resourceBundleName != null) {
486 try {
487 resourceBundle = ResourceBundle
488 .getBundle(resourceBundleName);
489 } catch (MissingResourceException ex) {
490 // This is not a good place to throw an exception,
491 // so we simply leave the resourceBundle null.
492 resourceBundle = null;
493 }
494 }
495
496 needToInferCaller = false;
497 }
498
499 // Private method to infer the caller's class and method names
500 private void inferCaller() {
501 needToInferCaller = false;
502 // Get the stack trace.
503 StackTraceElement stack[] = (new Throwable()).getStackTrace();
504 // First, search back to a method in the Logger class.
505 int ix = 0;
506 while (ix < stack.length) {
507 StackTraceElement frame = stack[ix];
508 String cname = frame.getClassName();
509 if (cname.equals("java.util.logging.Logger")) {
510 break;
511 }
512 ix++;
513 }
514 // Now search for the first frame before the "Logger" class.
515 while (ix < stack.length) {
516 StackTraceElement frame = stack[ix];
517 String cname = frame.getClassName();
518 if (!cname.equals("java.util.logging.Logger")) {
519 // We've found the relevant frame.
520 setSourceClassName(cname);
521 setSourceMethodName(frame.getMethodName());
522 return;
523 }
524 ix++;
525 }
526 // We haven't found a suitable frame, so just punt. This is
527 // OK as we are only committed to making a "best effort" here.
528 }
529 }
|