blob: 9944f20ff75a70d4d4d9f8b66bf836c11189327e [file] [log] [blame]
The Android Open Source Projectb5de22c2012-04-01 00:00:00 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package java.util.logging;
19
20import java.io.IOException;
21import java.io.ObjectInputStream;
22import java.io.ObjectOutputStream;
23import java.io.Serializable;
24import java.util.MissingResourceException;
25import java.util.ResourceBundle;
26
27/**
28 * A {@code LogRecord} object represents a logging request. It is passed between
29 * the logging framework and individual logging handlers. Client applications
30 * should not modify a {@code LogRecord} object that has been passed into the
31 * logging framework.
32 * <p>
33 * The {@code LogRecord} class will infer the source method name and source
34 * class name the first time they are accessed if the client application didn't
35 * specify them explicitly. This automatic inference is based on the analysis of
36 * the call stack and is not guaranteed to be precise. Client applications
37 * should force the initialization of these two fields by calling
38 * {@code getSourceClassName} or {@code getSourceMethodName} if they expect to
39 * use them after passing the {@code LogRecord} object to another thread or
40 * transmitting it over RMI.
41 */
42public class LogRecord implements Serializable {
43
44 private static final long serialVersionUID = 5372048053134512534L;
45
46 // The major byte used in serialization.
47 private static final int MAJOR = 1;
48
49 // The minor byte used in serialization.
50 private static final int MINOR = 4;
51
52 // Store the current value for the sequence number.
53 private static long currentSequenceNumber = 0;
54
55 // Store the id for each thread.
56 private static ThreadLocal<Integer> currentThreadId = new ThreadLocal<Integer>();
57
58 // The base id as the starting point for thread ID allocation.
59 private static int initThreadId = 0;
60
61 /**
62 * The logging level.
63 *
64 * @serial
65 */
66 private Level level;
67
68 /**
69 * The sequence number.
70 *
71 * @serial
72 */
73 private long sequenceNumber;
74
75 /**
76 * The name of the class that issued the logging call.
77 *
78 * @serial
79 */
80 private String sourceClassName;
81
82 /**
83 * The name of the method that issued the logging call.
84 *
85 * @serial
86 */
87 private String sourceMethodName;
88
89 /**
90 * The original message text.
91 *
92 * @serial
93 */
94 private String message;
95
96 /**
97 * The ID of the thread that issued the logging call.
98 *
99 * @serial
100 */
101 private int threadID;
102
103 /**
104 * The time that the event occurred, in milliseconds since 1970.
105 *
106 * @serial
107 */
108 private long millis;
109
110 /**
111 * The associated {@code Throwable} object if any.
112 *
113 * @serial
114 */
115 private Throwable thrown;
116
117 /**
118 * The name of the source logger.
119 *
120 * @serial
121 */
122 private String loggerName;
123
124 /**
125 * The name of the resource bundle used to localize the log message.
126 *
127 * @serial
128 */
129 private String resourceBundleName;
130
131 // The associated resource bundle if any.
132 private transient ResourceBundle resourceBundle;
133
134 // The parameters.
135 private transient Object[] parameters;
136
137 // If the source method and source class has been initialized
138 private transient boolean sourceInitialized;
139
140 /**
141 * Constructs a {@code LogRecord} object using the supplied the logging
142 * level and message. The millis property is set to the current time. The
143 * sequence property is set to a new unique value, allocated in increasing
144 * order within the VM. The thread ID is set to a unique value
145 * for the current thread. All other properties are set to {@code null}.
146 *
147 * @param level
148 * the logging level, may not be {@code null}.
149 * @param msg
150 * the raw message.
151 * @throws NullPointerException
152 * if {@code level} is {@code null}.
153 */
154 public LogRecord(Level level, String msg) {
155 if (level == null) {
156 throw new NullPointerException("level == null");
157 }
158 this.level = level;
159 this.message = msg;
160 this.millis = System.currentTimeMillis();
161
162 synchronized (LogRecord.class) {
163 this.sequenceNumber = currentSequenceNumber++;
164 Integer id = currentThreadId.get();
165 if (id == null) {
166 this.threadID = initThreadId;
167 currentThreadId.set(Integer.valueOf(initThreadId++));
168 } else {
169 this.threadID = id.intValue();
170 }
171 }
172
173 this.sourceClassName = null;
174 this.sourceMethodName = null;
175 this.loggerName = null;
176 this.parameters = null;
177 this.resourceBundle = null;
178 this.resourceBundleName = null;
179 this.thrown = null;
180 }
181
182 /**
183 * Gets the logging level.
184 *
185 * @return the logging level.
186 */
187 public Level getLevel() {
188 return level;
189 }
190
191 /**
192 * Sets the logging level.
193 *
194 * @param level
195 * the level to set.
196 * @throws NullPointerException
197 * if {@code level} is {@code null}.
198 */
199 public void setLevel(Level level) {
200 if (level == null) {
201 throw new NullPointerException("level == null");
202 }
203 this.level = level;
204 }
205
206 /**
207 * Gets the name of the logger.
208 *
209 * @return the logger name.
210 */
211 public String getLoggerName() {
212 return loggerName;
213 }
214
215 /**
216 * Sets the name of the logger.
217 *
218 * @param loggerName
219 * the logger name to set.
220 */
221 public void setLoggerName(String loggerName) {
222 this.loggerName = loggerName;
223 }
224
225 /**
226 * Gets the raw message.
227 *
228 * @return the raw message, may be {@code null}.
229 */
230 public String getMessage() {
231 return message;
232 }
233
234 /**
235 * Sets the raw message. When this record is formatted by a logger that has
236 * a localization resource bundle that contains an entry for {@code message},
237 * then the raw message is replaced with its localized version.
238 *
239 * @param message
240 * the raw message to set, may be {@code null}.
241 */
242 public void setMessage(String message) {
243 this.message = message;
244 }
245
246 /**
247 * Gets the time when this event occurred, in milliseconds since 1970.
248 *
249 * @return the time when this event occurred, in milliseconds since 1970.
250 */
251 public long getMillis() {
252 return millis;
253 }
254
255 /**
256 * Sets the time when this event occurred, in milliseconds since 1970.
257 *
258 * @param millis
259 * the time when this event occurred, in milliseconds since 1970.
260 */
261 public void setMillis(long millis) {
262 this.millis = millis;
263 }
264
265 /**
266 * Gets the parameters.
267 *
268 * @return the array of parameters or {@code null} if there are no
269 * parameters.
270 */
271 public Object[] getParameters() {
272 return parameters;
273 }
274
275 /**
276 * Sets the parameters.
277 *
278 * @param parameters
279 * the array of parameters to set, may be {@code null}.
280 */
281 public void setParameters(Object[] parameters) {
282 this.parameters = parameters;
283 }
284
285 /**
286 * Gets the resource bundle used to localize the raw message during
287 * formatting.
288 *
289 * @return the associated resource bundle, {@code null} if none is
290 * available or the message is not localizable.
291 */
292 public ResourceBundle getResourceBundle() {
293 return resourceBundle;
294 }
295
296 /**
297 * Sets the resource bundle used to localize the raw message during
298 * formatting.
299 *
300 * @param resourceBundle
301 * the resource bundle to set, may be {@code null}.
302 */
303 public void setResourceBundle(ResourceBundle resourceBundle) {
304 this.resourceBundle = resourceBundle;
305 }
306
307 /**
308 * Gets the name of the resource bundle.
309 *
310 * @return the name of the resource bundle, {@code null} if none is
311 * available or the message is not localizable.
312 */
313 public String getResourceBundleName() {
314 return resourceBundleName;
315 }
316
317 /**
318 * Sets the name of the resource bundle.
319 *
320 * @param resourceBundleName
321 * the name of the resource bundle to set.
322 */
323 public void setResourceBundleName(String resourceBundleName) {
324 this.resourceBundleName = resourceBundleName;
325 }
326
327 /**
328 * Gets the sequence number.
329 *
330 * @return the sequence number.
331 */
332 public long getSequenceNumber() {
333 return sequenceNumber;
334 }
335
336 /**
337 * Sets the sequence number. It is usually not necessary to call this method
338 * to change the sequence number because the number is allocated when this
339 * instance is constructed.
340 *
341 * @param sequenceNumber
342 * the sequence number to set.
343 */
344 public void setSequenceNumber(long sequenceNumber) {
345 this.sequenceNumber = sequenceNumber;
346 }
347
348 /**
349 * Gets the name of the class that is the source of this log record. This
350 * information can be changed, may be {@code null} and is untrusted.
351 *
352 * @return the name of the source class of this log record (possiblity {@code null})
353 */
354 public String getSourceClassName() {
355 initSource();
356 return sourceClassName;
357 }
358
359 /*
360 * Init the sourceClass and sourceMethod fields.
361 */
362 private void initSource() {
363 if (sourceInitialized) {
364 return;
365 }
366
367 boolean sawLogger = false;
368 for (StackTraceElement element : new Throwable().getStackTrace()) {
369 String current = element.getClassName();
370 if (current.startsWith(Logger.class.getName())) {
371 sawLogger = true;
372 } else if (sawLogger) {
373 this.sourceClassName = element.getClassName();
374 this.sourceMethodName = element.getMethodName();
375 break;
376 }
377 }
378
379 sourceInitialized = true;
380 }
381
382 /**
383 * Sets the name of the class that is the source of this log record.
384 *
385 * @param sourceClassName
386 * the name of the source class of this log record, may be
387 * {@code null}.
388 */
389 public void setSourceClassName(String sourceClassName) {
390 sourceInitialized = true;
391 this.sourceClassName = sourceClassName;
392 }
393
394 /**
395 * Gets the name of the method that is the source of this log record.
396 *
397 * @return the name of the source method of this log record.
398 */
399 public String getSourceMethodName() {
400 initSource();
401 return sourceMethodName;
402 }
403
404 /**
405 * Sets the name of the method that is the source of this log record.
406 *
407 * @param sourceMethodName
408 * the name of the source method of this log record, may be
409 * {@code null}.
410 */
411 public void setSourceMethodName(String sourceMethodName) {
412 sourceInitialized = true;
413 this.sourceMethodName = sourceMethodName;
414 }
415
416 /**
417 * Gets a unique ID of the thread originating the log record. Every thread
418 * becomes a different ID.
419 * <p>
420 * Notice : the ID doesn't necessary map the OS thread ID
421 * </p>
422 *
423 * @return the ID of the thread originating this log record.
424 */
425 public int getThreadID() {
426 return threadID;
427 }
428
429 /**
430 * Sets the ID of the thread originating this log record.
431 *
432 * @param threadID
433 * the new ID of the thread originating this log record.
434 */
435 public void setThreadID(int threadID) {
436 this.threadID = threadID;
437 }
438
439 /**
440 * Gets the {@code Throwable} object associated with this log record.
441 *
442 * @return the {@code Throwable} object associated with this log record.
443 */
444 public Throwable getThrown() {
445 return thrown;
446 }
447
448 /**
449 * Sets the {@code Throwable} object associated with this log record.
450 *
451 * @param thrown
452 * the new {@code Throwable} object to associate with this log
453 * record.
454 */
455 public void setThrown(Throwable thrown) {
456 this.thrown = thrown;
457 }
458
459 /*
460 * Customized serialization.
461 */
462 private void writeObject(ObjectOutputStream out) throws IOException {
463 out.defaultWriteObject();
464 out.writeByte(MAJOR);
465 out.writeByte(MINOR);
466 if (parameters == null) {
467 out.writeInt(-1);
468 } else {
469 out.writeInt(parameters.length);
470 for (Object element : parameters) {
471 out.writeObject((element == null) ? null : element.toString());
472 }
473 }
474 }
475
476 /*
477 * Customized deserialization.
478 */
479 private void readObject(ObjectInputStream in) throws IOException,
480 ClassNotFoundException {
481 in.defaultReadObject();
482 byte major = in.readByte();
483 byte minor = in.readByte();
484 // only check MAJOR version
485 if (major != MAJOR) {
486 throw new IOException("Different version " + Byte.valueOf(major) + "." + Byte.valueOf(minor));
487 }
488
489 int length = in.readInt();
490 if (length >= 0) {
491 parameters = new Object[length];
492 for (int i = 0; i < parameters.length; i++) {
493 parameters[i] = in.readObject();
494 }
495 }
496 if (resourceBundleName != null) {
497 try {
498 resourceBundle = Logger.loadResourceBundle(resourceBundleName);
499 } catch (MissingResourceException e) {
500 // Cannot find the specified resource bundle
501 resourceBundle = null;
502 }
503 }
504 }
505}