1. /*
  2. * @(#)LogRecord.java 1.23 04/01/12
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.util.logging;
  8. import java.util.*;
  9. import java.io.*;
  10. /**
  11. * LogRecord objects are used to pass logging requests between
  12. * the logging framework and individual log Handlers.
  13. * <p>
  14. * When a LogRecord is passed into the logging framework it
  15. * logically belongs to the framework and should no longer be
  16. * used or updated by the client application.
  17. * <p>
  18. * Note that if the client application has not specified an
  19. * explicit source method name and source class name, then the
  20. * LogRecord class will infer them automatically when they are
  21. * first accessed (due to a call on getSourceMethodName or
  22. * getSourceClassName) by analyzing the call stack. Therefore,
  23. * if a logging Handler wants to pass off a LogRecord to another
  24. * thread, or to transmit it over RMI, and if it wishes to subsequently
  25. * obtain method name or class name information it should call
  26. * one of getSourceClassName or getSourceMethodName to force
  27. * the values to be filled in.
  28. * <p>
  29. * <b> Serialization notes:</b>
  30. * <ul>
  31. * <li>The LogRecord class is serializable.
  32. *
  33. * <li> Because objects in the parameters array may not be serializable,
  34. * during serialization all objects in the parameters array are
  35. * written as the corresponding Strings (using Object.toString).
  36. *
  37. * <li> The ResourceBundle is not transmitted as part of the serialized
  38. * form, but the resource bundle name is, and the recipient object's
  39. * readObject method will attempt to locate a suitable resource bundle.
  40. *
  41. * </ul>
  42. *
  43. * @version 1.23, 01/12/04
  44. * @since 1.4
  45. */
  46. public class LogRecord implements java.io.Serializable {
  47. private static long globalSequenceNumber;
  48. private static int nextThreadId=10;
  49. private static ThreadLocal threadIds = new ThreadLocal();
  50. /**
  51. * @serial Logging message level
  52. */
  53. private Level level;
  54. /**
  55. * @serial Sequence number
  56. */
  57. private long sequenceNumber;
  58. /**
  59. * @serial Class that issued logging call
  60. */
  61. private String sourceClassName;
  62. /**
  63. * @serial Method that issued logging call
  64. */
  65. private String sourceMethodName;
  66. /**
  67. * @serial Non-localized raw message text
  68. */
  69. private String message;
  70. /**
  71. * @serial Thread ID for thread that issued logging call.
  72. */
  73. private int threadID;
  74. /**
  75. * @serial Event time in milliseconds since 1970
  76. */
  77. private long millis;
  78. /**
  79. * @serial The Throwable (if any) associated with log message
  80. */
  81. private Throwable thrown;
  82. /**
  83. * @serial Name of the source Logger.
  84. */
  85. private String loggerName;
  86. /**
  87. * @serial Resource bundle name to localized log message.
  88. */
  89. private String resourceBundleName;
  90. private transient boolean needToInferCaller;
  91. private transient Object parameters[];
  92. private transient ResourceBundle resourceBundle;
  93. /**
  94. * Construct a LogRecord with the given level and message values.
  95. * <p>
  96. * The sequence property will be initialized with a new unique value.
  97. * These sequence values are allocated in increasing order within a VM.
  98. * <p>
  99. * The millis property will be initialized to the current time.
  100. * <p>
  101. * The thread ID property will be initialized with a unique ID for
  102. * the current thread.
  103. * <p>
  104. * All other properties will be initialized to "null".
  105. *
  106. * @param level a logging level value
  107. * @param msg the raw non-localized logging message (may be null)
  108. */
  109. public LogRecord(Level level, String msg) {
  110. // Make sure level isn't null, by calling random method.
  111. level.getClass();
  112. this.level = level;
  113. message = msg;
  114. // Assign a thread ID and a unique sequence number.
  115. synchronized (LogRecord.class) {
  116. sequenceNumber = globalSequenceNumber++;
  117. Integer id = (Integer)threadIds.get();
  118. if (id == null) {
  119. id = new Integer(nextThreadId++);
  120. threadIds.set(id);
  121. }
  122. threadID = id.intValue();
  123. }
  124. millis = System.currentTimeMillis();
  125. needToInferCaller = true;
  126. }
  127. /**
  128. * Get the source Logger name's
  129. *
  130. * @return source logger name (may be null)
  131. */
  132. public String getLoggerName() {
  133. return loggerName;
  134. }
  135. /**
  136. * Set the source Logger name.
  137. *
  138. * @param name the source logger name (may be null)
  139. */
  140. public void setLoggerName(String name) {
  141. loggerName = name;
  142. }
  143. /**
  144. * Get the localization resource bundle
  145. * <p>
  146. * This is the ResourceBundle that should be used to localize
  147. * the message string before formatting it. The result may
  148. * be null if the message is not localizable, or if no suitable
  149. * ResourceBundle is available.
  150. */
  151. public ResourceBundle getResourceBundle() {
  152. return resourceBundle;
  153. }
  154. /**
  155. * Set the localization resource bundle.
  156. *
  157. * @param bundle localization bundle (may be null)
  158. */
  159. public void setResourceBundle(ResourceBundle bundle) {
  160. resourceBundle = bundle;
  161. }
  162. /**
  163. * Get the localization resource bundle name
  164. * <p>
  165. * This is the name for the ResourceBundle that should be
  166. * used to localize the message string before formatting it.
  167. * The result may be null if the message is not localizable.
  168. */
  169. public String getResourceBundleName() {
  170. return resourceBundleName;
  171. }
  172. /**
  173. * Set the localization resource bundle name.
  174. *
  175. * @param name localization bundle name (may be null)
  176. */
  177. public void setResourceBundleName(String name) {
  178. resourceBundleName = name;
  179. }
  180. /**
  181. * Get the logging message level, for example Level.SEVERE.
  182. * @return the logging message level
  183. */
  184. public Level getLevel() {
  185. return level;
  186. }
  187. /**
  188. * Set the logging message level, for example Level.SEVERE.
  189. * @param level the logging message level
  190. */
  191. public void setLevel(Level level) {
  192. if (level == null) {
  193. throw new NullPointerException();
  194. }
  195. this.level = level;
  196. }
  197. /**
  198. * Get the sequence number.
  199. * <p>
  200. * Sequence numbers are normally assigned in the LogRecord
  201. * constructor, which assigns unique sequence numbers to
  202. * each new LogRecord in increasing order.
  203. * @return the sequence number
  204. */
  205. public long getSequenceNumber() {
  206. return sequenceNumber;
  207. }
  208. /**
  209. * Set the sequence number.
  210. * <p>
  211. * Sequence numbers are normally assigned in the LogRecord constructor,
  212. * so it should not normally be necessary to use this method.
  213. */
  214. public void setSequenceNumber(long seq) {
  215. sequenceNumber = seq;
  216. }
  217. /**
  218. * Get the name of the class that (allegedly) issued the logging request.
  219. * <p>
  220. * Note that this sourceClassName is not verified and may be spoofed.
  221. * This information may either have been provided as part of the
  222. * logging call, or it may have been inferred automatically by the
  223. * logging framework. In the latter case, the information may only
  224. * be approximate and may in fact describe an earlier call on the
  225. * stack frame.
  226. * <p>
  227. * May be null if no information could be obtained.
  228. *
  229. * @return the source class name
  230. */
  231. public String getSourceClassName() {
  232. if (needToInferCaller) {
  233. inferCaller();
  234. }
  235. return sourceClassName;
  236. }
  237. /**
  238. * Set the name of the class that (allegedly) issued the logging request.
  239. *
  240. * @param sourceClassName the source class name (may be null)
  241. */
  242. public void setSourceClassName(String sourceClassName) {
  243. this.sourceClassName = sourceClassName;
  244. needToInferCaller = false;
  245. }
  246. /**
  247. * Get the name of the method that (allegedly) issued the logging request.
  248. * <p>
  249. * Note that this sourceMethodName is not verified and may be spoofed.
  250. * This information may either have been provided as part of the
  251. * logging call, or it may have been inferred automatically by the
  252. * logging framework. In the latter case, the information may only
  253. * be approximate and may in fact describe an earlier call on the
  254. * stack frame.
  255. * <p>
  256. * May be null if no information could be obtained.
  257. *
  258. * @return the source method name
  259. */
  260. public String getSourceMethodName() {
  261. if (needToInferCaller) {
  262. inferCaller();
  263. }
  264. return sourceMethodName;
  265. }
  266. /**
  267. * Set the name of the method that (allegedly) issued the logging request.
  268. *
  269. * @param sourceMethodName the source method name (may be null)
  270. */
  271. public void setSourceMethodName(String sourceMethodName) {
  272. this.sourceMethodName = sourceMethodName;
  273. needToInferCaller = false;
  274. }
  275. /**
  276. * Get the "raw" log message, before localization or formatting.
  277. * <p>
  278. * May be null, which is equivalent to the empty string "".
  279. * <p>
  280. * This message may be either the final text or a localization key.
  281. * <p>
  282. * During formatting, if the source logger has a localization
  283. * ResourceBundle and if that ResourceBundle has an entry for
  284. * this message string, then the message string is replaced
  285. * with the localized value.
  286. *
  287. * @return the raw message string
  288. */
  289. public String getMessage() {
  290. return message;
  291. }
  292. /**
  293. * Set the "raw" log message, before localization or formatting.
  294. *
  295. * @param message the raw message string (may be null)
  296. */
  297. public void setMessage(String message) {
  298. this.message = message;
  299. }
  300. /**
  301. * Get the parameters to the log message.
  302. *
  303. * @return the log message parameters. May be null if
  304. * there are no parameters.
  305. */
  306. public Object[] getParameters() {
  307. return parameters;
  308. }
  309. /**
  310. * Set the parameters to the log message.
  311. *
  312. * @param parameters the log message parameters. (may be null)
  313. */
  314. public void setParameters(Object parameters[]) {
  315. this.parameters = parameters;
  316. }
  317. /**
  318. * Get an identifier for the thread where the message originated.
  319. * <p>
  320. * This is a thread identifier within the Java VM and may or
  321. * may not map to any operating system ID.
  322. *
  323. * @return thread ID
  324. */
  325. public int getThreadID() {
  326. return threadID;
  327. }
  328. /**
  329. * Set an identifier for the thread where the message originated.
  330. * @param threadID the thread ID
  331. */
  332. public void setThreadID(int threadID) {
  333. this.threadID = threadID;
  334. }
  335. /**
  336. * Get event time in milliseconds since 1970.
  337. *
  338. * @return event time in millis since 1970
  339. */
  340. public long getMillis() {
  341. return millis;
  342. }
  343. /**
  344. * Set event time.
  345. *
  346. * @param millis event time in millis since 1970
  347. */
  348. public void setMillis(long millis) {
  349. this.millis = millis;
  350. }
  351. /**
  352. * Get any throwable associated with the log record.
  353. * <p>
  354. * If the event involved an exception, this will be the
  355. * exception object. Otherwise null.
  356. *
  357. * @return a throwable
  358. */
  359. public Throwable getThrown() {
  360. return thrown;
  361. }
  362. /**
  363. * Set a throwable associated with the log event.
  364. *
  365. * @param thrown a throwable (may be null)
  366. */
  367. public void setThrown(Throwable thrown) {
  368. this.thrown = thrown;
  369. }
  370. private static final long serialVersionUID = 5372048053134512534L;
  371. /**
  372. * @serialData Default fields, followed by a two byte version number
  373. * (major byte, followed by minor byte), followed by information on
  374. * the log record parameter array. If there is no parameter array,
  375. * then -1 is written. If there is a parameter array (possible of zero
  376. * length) then the array length is written as an integer, followed
  377. * by String values for each parameter. If a parameter is null, then
  378. * a null String is written. Otherwise the output of Object.toString()
  379. * is written.
  380. */
  381. private void writeObject(ObjectOutputStream out) throws IOException {
  382. // We have to call defaultWriteObject first.
  383. out.defaultWriteObject();
  384. // Write our version number.
  385. out.writeByte(1);
  386. out.writeByte(0);
  387. if (parameters == null) {
  388. out.writeInt(-1);
  389. return;
  390. }
  391. out.writeInt(parameters.length);
  392. // Write string values for the parameters.
  393. for (int i = 0; i < parameters.length; i++) {
  394. if (parameters[i] == null) {
  395. out.writeObject(null);
  396. } else {
  397. out.writeObject(parameters[i].toString());
  398. }
  399. }
  400. }
  401. private void readObject(ObjectInputStream in)
  402. throws IOException, ClassNotFoundException {
  403. // We have to call defaultReadObject first.
  404. in.defaultReadObject();
  405. // Read version number.
  406. byte major = in.readByte();
  407. byte minor = in.readByte();
  408. if (major != 1) {
  409. throw new IOException("LogRecord: bad version: " + major + "." + minor);
  410. }
  411. int len = in.readInt();
  412. if (len == -1) {
  413. parameters = null;
  414. } else {
  415. parameters = new Object[len];
  416. for (int i = 0; i < parameters.length; i++) {
  417. parameters[i] = in.readObject();
  418. }
  419. }
  420. // If necessary, try to regenerate the resource bundle.
  421. if (resourceBundleName != null) {
  422. try {
  423. resourceBundle = ResourceBundle.getBundle(resourceBundleName);
  424. } catch (MissingResourceException ex) {
  425. // This is not a good place to throw an exception,
  426. // so we simply leave the resourceBundle null.
  427. resourceBundle = null;
  428. }
  429. }
  430. needToInferCaller = false;
  431. }
  432. // Private method to infer the caller's class and method names
  433. private void inferCaller() {
  434. needToInferCaller = false;
  435. // Get the stack trace.
  436. StackTraceElement stack[] = (new Throwable()).getStackTrace();
  437. // First, search back to a method in the Logger class.
  438. int ix = 0;
  439. while (ix < stack.length) {
  440. StackTraceElement frame = stack[ix];
  441. String cname = frame.getClassName();
  442. if (cname.equals("java.util.logging.Logger")) {
  443. break;
  444. }
  445. ix++;
  446. }
  447. // Now search for the first frame before the "Logger" class.
  448. while (ix < stack.length) {
  449. StackTraceElement frame = stack[ix];
  450. String cname = frame.getClassName();
  451. if (!cname.equals("java.util.logging.Logger")) {
  452. // We've found the relevant frame.
  453. setSourceClassName(cname);
  454. setSourceMethodName(frame.getMethodName());
  455. return;
  456. }
  457. ix++;
  458. }
  459. // We haven't found a suitable frame, so just punt. This is
  460. // OK as we are only committed to making a "best effort" here.
  461. }
  462. }