1/* 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} 506