• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.  Oracle designates this
9  * particular file as subject to the "Classpath" exception as provided
10  * by Oracle in the LICENSE file that accompanied this code.
11  *
12  * This code is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  * version 2 for more details (a copy is included in the LICENSE file that
16  * accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License version
19  * 2 along with this work; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23  * or visit www.oracle.com if you need additional information or have any
24  * questions.
25  */
26 
27 package java.util.logging;
28 import dalvik.system.VMStack;
29 import java.util.*;
30 import java.util.concurrent.atomic.AtomicInteger;
31 import java.util.concurrent.atomic.AtomicLong;
32 import java.io.*;
33 
34 /**
35  * LogRecord objects are used to pass logging requests between
36  * the logging framework and individual log Handlers.
37  * <p>
38  * When a LogRecord is passed into the logging framework it
39  * logically belongs to the framework and should no longer be
40  * used or updated by the client application.
41  * <p>
42  * Note that if the client application has not specified an
43  * explicit source method name and source class name, then the
44  * LogRecord class will infer them automatically when they are
45  * first accessed (due to a call on getSourceMethodName or
46  * getSourceClassName) by analyzing the call stack.  Therefore,
47  * if a logging Handler wants to pass off a LogRecord to another
48  * thread, or to transmit it over RMI, and if it wishes to subsequently
49  * obtain method name or class name information it should call
50  * one of getSourceClassName or getSourceMethodName to force
51  * the values to be filled in.
52  * <p>
53  * <b> Serialization notes:</b>
54  * <ul>
55  * <li>The LogRecord class is serializable.
56  *
57  * <li> Because objects in the parameters array may not be serializable,
58  * during serialization all objects in the parameters array are
59  * written as the corresponding Strings (using Object.toString).
60  *
61  * <li> The ResourceBundle is not transmitted as part of the serialized
62  * form, but the resource bundle name is, and the recipient object's
63  * readObject method will attempt to locate a suitable resource bundle.
64  *
65  * </ul>
66  *
67  * @since 1.4
68  */
69 
70 public class LogRecord implements java.io.Serializable {
71     private static final AtomicLong globalSequenceNumber
72         = new AtomicLong(0);
73 
74     /**
75      * The default value of threadID will be the current thread's
76      * thread id, for ease of correlation, unless it is greater than
77      * MIN_SEQUENTIAL_THREAD_ID, in which case we try harder to keep
78      * our promise to keep threadIDs unique by avoiding collisions due
79      * to 32-bit wraparound.  Unfortunately, LogRecord.getThreadID()
80      * returns int, while Thread.getId() returns long.
81      */
82     private static final int MIN_SEQUENTIAL_THREAD_ID = Integer.MAX_VALUE / 2;
83 
84     private static final AtomicInteger nextThreadId
85         = new AtomicInteger(MIN_SEQUENTIAL_THREAD_ID);
86 
87     private static final ThreadLocal<Integer> threadIds = new ThreadLocal<>();
88 
89     /**
90      * @serial Logging message level
91      */
92     private Level level;
93 
94     /**
95      * @serial Sequence number
96      */
97     private long sequenceNumber;
98 
99     /**
100      * @serial Class that issued logging call
101      */
102     private String sourceClassName;
103 
104     /**
105      * @serial Method that issued logging call
106      */
107     private String sourceMethodName;
108 
109     /**
110      * @serial Non-localized raw message text
111      */
112     private String message;
113 
114     /**
115      * @serial Thread ID for thread that issued logging call.
116      */
117     private int threadID;
118 
119     /**
120      * @serial Event time in milliseconds since 1970
121      */
122     private long millis;
123 
124     /**
125      * @serial The Throwable (if any) associated with log message
126      */
127     private Throwable thrown;
128 
129     /**
130      * @serial Name of the source Logger.
131      */
132     private String loggerName;
133 
134     /**
135      * @serial Resource bundle name to localized log message.
136      */
137     private String resourceBundleName;
138 
139     private transient boolean needToInferCaller;
140     private transient Object parameters[];
141     private transient ResourceBundle resourceBundle;
142 
143     /**
144      * Returns the default value for a new LogRecord's threadID.
145      */
defaultThreadID()146     private int defaultThreadID() {
147         long tid = Thread.currentThread().getId();
148         if (tid < MIN_SEQUENTIAL_THREAD_ID) {
149             return (int) tid;
150         } else {
151             Integer id = threadIds.get();
152             if (id == null) {
153                 id = nextThreadId.getAndIncrement();
154                 threadIds.set(id);
155             }
156             return id;
157         }
158     }
159 
160     /**
161      * Construct a LogRecord with the given level and message values.
162      * <p>
163      * The sequence property will be initialized with a new unique value.
164      * These sequence values are allocated in increasing order within a VM.
165      * <p>
166      * The millis property will be initialized to the current time.
167      * <p>
168      * The thread ID property will be initialized with a unique ID for
169      * the current thread.
170      * <p>
171      * All other properties will be initialized to "null".
172      *
173      * @param level  a logging level value
174      * @param msg  the raw non-localized logging message (may be null)
175      */
LogRecord(Level level, String msg)176     public LogRecord(Level level, String msg) {
177         // Make sure level isn't null, by calling random method.
178         level.getClass();
179         this.level = level;
180         message = msg;
181         // Assign a thread ID and a unique sequence number.
182         sequenceNumber = globalSequenceNumber.getAndIncrement();
183         threadID = defaultThreadID();
184         millis = System.currentTimeMillis();
185         needToInferCaller = true;
186    }
187 
188     /**
189      * Get the source Logger's name.
190      *
191      * @return source logger name (may be null)
192      */
getLoggerName()193     public String getLoggerName() {
194         return loggerName;
195     }
196 
197     /**
198      * Set the source Logger's name.
199      *
200      * @param name   the source logger name (may be null)
201      */
setLoggerName(String name)202     public void setLoggerName(String name) {
203         loggerName = name;
204     }
205 
206     /**
207      * Get the localization resource bundle
208      * <p>
209      * This is the ResourceBundle that should be used to localize
210      * the message string before formatting it.  The result may
211      * be null if the message is not localizable, or if no suitable
212      * ResourceBundle is available.
213      * @return the localization resource bundle
214      */
getResourceBundle()215     public ResourceBundle getResourceBundle() {
216         return resourceBundle;
217     }
218 
219     /**
220      * Set the localization resource bundle.
221      *
222      * @param bundle  localization bundle (may be null)
223      */
setResourceBundle(ResourceBundle bundle)224     public void setResourceBundle(ResourceBundle bundle) {
225         resourceBundle = bundle;
226     }
227 
228     /**
229      * Get the localization resource bundle name
230      * <p>
231      * This is the name for the ResourceBundle that should be
232      * used to localize the message string before formatting it.
233      * The result may be null if the message is not localizable.
234      * @return the localization resource bundle name
235      */
getResourceBundleName()236     public String getResourceBundleName() {
237         return resourceBundleName;
238     }
239 
240     /**
241      * Set the localization resource bundle name.
242      *
243      * @param name  localization bundle name (may be null)
244      */
setResourceBundleName(String name)245     public void setResourceBundleName(String name) {
246         resourceBundleName = name;
247     }
248 
249     /**
250      * Get the logging message level, for example Level.SEVERE.
251      * @return the logging message level
252      */
getLevel()253     public Level getLevel() {
254         return level;
255     }
256 
257     /**
258      * Set the logging message level, for example Level.SEVERE.
259      * @param level the logging message level
260      */
setLevel(Level level)261     public void setLevel(Level level) {
262         if (level == null) {
263             throw new NullPointerException();
264         }
265         this.level = level;
266     }
267 
268     /**
269      * Get the sequence number.
270      * <p>
271      * Sequence numbers are normally assigned in the LogRecord
272      * constructor, which assigns unique sequence numbers to
273      * each new LogRecord in increasing order.
274      * @return the sequence number
275      */
getSequenceNumber()276     public long getSequenceNumber() {
277         return sequenceNumber;
278     }
279 
280     /**
281      * Set the sequence number.
282      * <p>
283      * Sequence numbers are normally assigned in the LogRecord constructor,
284      * so it should not normally be necessary to use this method.
285      * @param seq the sequence number
286      */
setSequenceNumber(long seq)287     public void setSequenceNumber(long seq) {
288         sequenceNumber = seq;
289     }
290 
291     /**
292      * Get the  name of the class that (allegedly) issued the logging request.
293      * <p>
294      * Note that this sourceClassName is not verified and may be spoofed.
295      * This information may either have been provided as part of the
296      * logging call, or it may have been inferred automatically by the
297      * logging framework.  In the latter case, the information may only
298      * be approximate and may in fact describe an earlier call on the
299      * stack frame.
300      * <p>
301      * May be null if no information could be obtained.
302      *
303      * @return the source class name
304      */
getSourceClassName()305     public String getSourceClassName() {
306         if (needToInferCaller) {
307             inferCaller();
308         }
309         return sourceClassName;
310     }
311 
312     /**
313      * Set the name of the class that (allegedly) issued the logging request.
314      *
315      * @param sourceClassName the source class name (may be null)
316      */
setSourceClassName(String sourceClassName)317     public void setSourceClassName(String sourceClassName) {
318         this.sourceClassName = sourceClassName;
319         needToInferCaller = false;
320     }
321 
322     /**
323      * Get the  name of the method that (allegedly) issued the logging request.
324      * <p>
325      * Note that this sourceMethodName is not verified and may be spoofed.
326      * This information may either have been provided as part of the
327      * logging call, or it may have been inferred automatically by the
328      * logging framework.  In the latter case, the information may only
329      * be approximate and may in fact describe an earlier call on the
330      * stack frame.
331      * <p>
332      * May be null if no information could be obtained.
333      *
334      * @return the source method name
335      */
getSourceMethodName()336     public String getSourceMethodName() {
337         if (needToInferCaller) {
338             inferCaller();
339         }
340         return sourceMethodName;
341     }
342 
343     /**
344      * Set the name of the method that (allegedly) issued the logging request.
345      *
346      * @param sourceMethodName the source method name (may be null)
347      */
setSourceMethodName(String sourceMethodName)348     public void setSourceMethodName(String sourceMethodName) {
349         this.sourceMethodName = sourceMethodName;
350         needToInferCaller = false;
351     }
352 
353     /**
354      * Get the "raw" log message, before localization or formatting.
355      * <p>
356      * May be null, which is equivalent to the empty string "".
357      * <p>
358      * This message may be either the final text or a localization key.
359      * <p>
360      * During formatting, if the source logger has a localization
361      * ResourceBundle and if that ResourceBundle has an entry for
362      * this message string, then the message string is replaced
363      * with the localized value.
364      *
365      * @return the raw message string
366      */
getMessage()367     public String getMessage() {
368         return message;
369     }
370 
371     /**
372      * Set the "raw" log message, before localization or formatting.
373      *
374      * @param message the raw message string (may be null)
375      */
setMessage(String message)376     public void setMessage(String message) {
377         this.message = message;
378     }
379 
380     /**
381      * Get the parameters to the log message.
382      *
383      * @return the log message parameters.  May be null if
384      *                  there are no parameters.
385      */
getParameters()386     public Object[] getParameters() {
387         return parameters;
388     }
389 
390     /**
391      * Set the parameters to the log message.
392      *
393      * @param parameters the log message parameters. (may be null)
394      */
setParameters(Object parameters[])395     public void setParameters(Object parameters[]) {
396         this.parameters = parameters;
397     }
398 
399     /**
400      * Get an identifier for the thread where the message originated.
401      * <p>
402      * This is a thread identifier within the Java VM and may or
403      * may not map to any operating system ID.
404      *
405      * @return thread ID
406      */
getThreadID()407     public int getThreadID() {
408         return threadID;
409     }
410 
411     /**
412      * Set an identifier for the thread where the message originated.
413      * @param threadID  the thread ID
414      */
setThreadID(int threadID)415     public void setThreadID(int threadID) {
416         this.threadID = threadID;
417     }
418 
419     /**
420      * Get event time in milliseconds since 1970.
421      *
422      * @return event time in millis since 1970
423      */
getMillis()424     public long getMillis() {
425         return millis;
426     }
427 
428     /**
429      * Set event time.
430      *
431      * @param millis event time in millis since 1970
432      */
setMillis(long millis)433     public void setMillis(long millis) {
434         this.millis = millis;
435     }
436 
437     /**
438      * Get any throwable associated with the log record.
439      * <p>
440      * If the event involved an exception, this will be the
441      * exception object. Otherwise null.
442      *
443      * @return a throwable
444      */
getThrown()445     public Throwable getThrown() {
446         return thrown;
447     }
448 
449     /**
450      * Set a throwable associated with the log event.
451      *
452      * @param thrown  a throwable (may be null)
453      */
setThrown(Throwable thrown)454     public void setThrown(Throwable thrown) {
455         this.thrown = thrown;
456     }
457 
458     private static final long serialVersionUID = 5372048053134512534L;
459 
460     /**
461      * @serialData Default fields, followed by a two byte version number
462      * (major byte, followed by minor byte), followed by information on
463      * the log record parameter array.  If there is no parameter array,
464      * then -1 is written.  If there is a parameter array (possible of zero
465      * length) then the array length is written as an integer, followed
466      * by String values for each parameter.  If a parameter is null, then
467      * a null String is written.  Otherwise the output of Object.toString()
468      * is written.
469      */
writeObject(ObjectOutputStream out)470     private void writeObject(ObjectOutputStream out) throws IOException {
471         // We have to call defaultWriteObject first.
472         out.defaultWriteObject();
473 
474         // Write our version number.
475         out.writeByte(1);
476         out.writeByte(0);
477         if (parameters == null) {
478             out.writeInt(-1);
479             return;
480         }
481         out.writeInt(parameters.length);
482         // Write string values for the parameters.
483         for (int i = 0; i < parameters.length; i++) {
484             if (parameters[i] == null) {
485                 out.writeObject(null);
486             } else {
487                 out.writeObject(parameters[i].toString());
488             }
489         }
490     }
491 
readObject(ObjectInputStream in)492     private void readObject(ObjectInputStream in)
493                         throws IOException, ClassNotFoundException {
494         // We have to call defaultReadObject first.
495         in.defaultReadObject();
496 
497         // Read version number.
498         byte major = in.readByte();
499         byte minor = in.readByte();
500         if (major != 1) {
501             throw new IOException("LogRecord: bad version: " + major + "." + minor);
502         }
503         int len = in.readInt();
504         if (len < -1) {
505             throw new NegativeArraySizeException();
506         } else if (len == -1) {
507             parameters = null;
508         } else if (len < 255) {
509             parameters = new Object[len];
510             for (int i = 0; i < parameters.length; i++) {
511                 parameters[i] = in.readObject();
512             }
513         } else {
514             List<Object> params = new ArrayList<>(Math.min(len, 1024));
515             for (int i = 0; i < len; i++) {
516                 params.add(in.readObject());
517             }
518             parameters = params.toArray(new Object[params.size()]);
519         }
520         // If necessary, try to regenerate the resource bundle.
521         if (resourceBundleName != null) {
522             try {
523                 // use system class loader to ensure the ResourceBundle
524                 // instance is a different instance than null loader uses
525                 final ResourceBundle bundle =
526                         ResourceBundle.getBundle(resourceBundleName,
527                                 Locale.getDefault(),
528                                 ClassLoader.getSystemClassLoader());
529                 resourceBundle = bundle;
530             } catch (MissingResourceException ex) {
531                 // Android-changed: Fall back to context classloader before giving up.
532                 /*
533                 // This is not a good place to throw an exception,
534                 // so we simply leave the resourceBundle null.
535                 resourceBundle = null;
536                 */
537                 try {
538                     resourceBundle = ResourceBundle.getBundle(resourceBundleName, Locale.getDefault(),
539                             Thread.currentThread().getContextClassLoader());
540                 } catch (MissingResourceException innerE){
541                     // This is not a good place to throw an exception,
542                     // so we simply leave the resourceBundle null.
543                     resourceBundle = null;
544                 }
545             }
546         }
547 
548         needToInferCaller = false;
549     }
550 
551     // Private method to infer the caller's class and method names
inferCaller()552     private void inferCaller() {
553         needToInferCaller = false;
554         // BEGIN Android-changed: Use VMStack.getThreadStackTrace.
555         /*
556         JavaLangAccess access = SharedSecrets.getJavaLangAccess();
557         Throwable throwable = new Throwable();
558         int depth = access.getStackTraceDepth(throwable);
559         */
560         StackTraceElement[] stack = VMStack.getThreadStackTrace(Thread.currentThread());
561         int depth = stack.length;
562         // END Android-changed: Use VMStack.getThreadStackTrace.
563 
564         boolean lookingForLogger = true;
565         for (int ix = 0; ix < depth; ix++) {
566             // Calling getStackTraceElement directly prevents the VM
567             // from paying the cost of building the entire stack frame.
568             //
569             // Android-changed: Use value from previous getThreadStackTrace call.
570             // StackTraceElement frame =
571             //     access.getStackTraceElement(throwable, ix);
572             StackTraceElement frame = stack[ix];
573             String cname = frame.getClassName();
574             boolean isLoggerImpl = isLoggerImplFrame(cname);
575             if (lookingForLogger) {
576                 // Skip all frames until we have found the first logger frame.
577                 if (isLoggerImpl) {
578                     lookingForLogger = false;
579                 }
580             } else {
581                 if (!isLoggerImpl) {
582                     // skip reflection call
583                     if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) {
584                        // We've found the relevant frame.
585                        setSourceClassName(cname);
586                        setSourceMethodName(frame.getMethodName());
587                        return;
588                     }
589                 }
590             }
591         }
592         // We haven't found a suitable frame, so just punt.  This is
593         // OK as we are only committed to making a "best effort" here.
594     }
595 
isLoggerImplFrame(String cname)596     private boolean isLoggerImplFrame(String cname) {
597         // the log record could be created for a platform logger
598         return (cname.equals("java.util.logging.Logger") ||
599                 cname.startsWith("java.util.logging.LoggingProxyImpl") ||
600                 cname.startsWith("sun.util.logging."));
601     }
602 }
603