1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 2009, 2013, 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 
28 package sun.util.logging;
29 
30 import java.lang.ref.WeakReference;
31 import java.io.PrintStream;
32 import java.io.PrintWriter;
33 import java.io.StringWriter;
34 import java.security.AccessController;
35 import java.security.PrivilegedAction;
36 import java.util.Arrays;
37 import java.util.Date;
38 import java.util.HashMap;
39 import java.util.Map;
40 
41 /**
42  * Platform logger provides an API for the JRE components to log
43  * messages.  This enables the runtime components to eliminate the
44  * static dependency of the logging facility and also defers the
45  * java.util.logging initialization until it is enabled.
46  * In addition, the PlatformLogger API can be used if the logging
47  * module does not exist.
48  *
49  * If the logging facility is not enabled, the platform loggers
50  * will output log messages per the default logging configuration
51  * (see below). In this implementation, it does not log the
52  * the stack frame information issuing the log message.
53  *
54  * When the logging facility is enabled (at startup or runtime),
55  * the java.util.logging.Logger will be created for each platform
56  * logger and all log messages will be forwarded to the Logger
57  * to handle.
58  *
59  * Logging facility is "enabled" when one of the following
60  * conditions is met:
61  * 1) a system property "java.util.logging.config.class" or
62  *    "java.util.logging.config.file" is set
63  * 2) java.util.logging.LogManager or java.util.logging.Logger
64  *    is referenced that will trigger the logging initialization.
65  *
66  * Default logging configuration:
67  *   global logging level = INFO
68  *   handlers = java.util.logging.ConsoleHandler
69  *   java.util.logging.ConsoleHandler.level = INFO
70  *   java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
71  *
72  * Limitation:
73  * <JAVA_HOME>/lib/logging.properties is the system-wide logging
74  * configuration defined in the specification and read in the
75  * default case to configure any java.util.logging.Logger instances.
76  * Platform loggers will not detect if <JAVA_HOME>/lib/logging.properties
77  * is modified. In other words, unless the java.util.logging API
78  * is used at runtime or the logging system properties is set,
79  * the platform loggers will use the default setting described above.
80  * The platform loggers are designed for JDK developers use and
81  * this limitation can be workaround with setting
82  * -Djava.util.logging.config.file system property.
83  *
84  * @since 1.7
85  */
86 public class PlatformLogger {
87 
88     // The integer values must match that of {@code java.util.logging.Level}
89     // objects.
90     private static final int OFF     = Integer.MAX_VALUE;
91     private static final int SEVERE  = 1000;
92     private static final int WARNING = 900;
93     private static final int INFO    = 800;
94     private static final int CONFIG  = 700;
95     private static final int FINE    = 500;
96     private static final int FINER   = 400;
97     private static final int FINEST  = 300;
98     private static final int ALL     = Integer.MIN_VALUE;
99 
100     /**
101      * PlatformLogger logging levels.
102      */
103     public static enum Level {
104         // The name and value must match that of {@code java.util.logging.Level}s.
105         // Declare in ascending order of the given value for binary search.
106         ALL,
107         FINEST,
108         FINER,
109         FINE,
110         CONFIG,
111         INFO,
112         WARNING,
113         SEVERE,
114         OFF;
115 
116         /**
117          * Associated java.util.logging.Level lazily initialized in
118          * JavaLoggerProxy's static initializer only once
119          * when java.util.logging is available and enabled.
120          * Only accessed by JavaLoggerProxy.
121          */
122         /* java.util.logging.Level */ Object javaLevel;
123 
124         // ascending order for binary search matching the list of enum constants
125         private static final int[] LEVEL_VALUES = new int[] {
126             PlatformLogger.ALL, PlatformLogger.FINEST, PlatformLogger.FINER,
127             PlatformLogger.FINE, PlatformLogger.CONFIG, PlatformLogger.INFO,
128             PlatformLogger.WARNING, PlatformLogger.SEVERE, PlatformLogger.OFF
129         };
130 
intValue()131         public int intValue() {
132             return LEVEL_VALUES[this.ordinal()];
133         }
134 
valueOf(int level)135         static Level valueOf(int level) {
136             switch (level) {
137                 // ordering per the highest occurrences in the jdk source
138                 // finest, fine, finer, info first
139                 case PlatformLogger.FINEST  : return Level.FINEST;
140                 case PlatformLogger.FINE    : return Level.FINE;
141                 case PlatformLogger.FINER   : return Level.FINER;
142                 case PlatformLogger.INFO    : return Level.INFO;
143                 case PlatformLogger.WARNING : return Level.WARNING;
144                 case PlatformLogger.CONFIG  : return Level.CONFIG;
145                 case PlatformLogger.SEVERE  : return Level.SEVERE;
146                 case PlatformLogger.OFF     : return Level.OFF;
147                 case PlatformLogger.ALL     : return Level.ALL;
148             }
149             // return the nearest Level value >= the given level,
150             // for level > SEVERE, return SEVERE and exclude OFF
151             int i = Arrays.binarySearch(LEVEL_VALUES, 0, LEVEL_VALUES.length-2, level);
152             return values()[i >= 0 ? i : (-i-1)];
153         }
154     }
155 
156     private static final Level DEFAULT_LEVEL = Level.INFO;
157     private static boolean loggingEnabled;
158     static {
159         loggingEnabled = AccessController.doPrivileged(
160             new PrivilegedAction<Boolean>() {
161                 public Boolean run() {
162                     String cname = System.getProperty("java.util.logging.config.class");
163                     String fname = System.getProperty("java.util.logging.config.file");
164                     return (cname != null || fname != null);
165                 }
166             });
167 
168         // Android-removed: JavaLoggerProxy is unneeded, complicates obfuscated releases.
169         /*
170         // force loading of all JavaLoggerProxy (sub)classes to make JIT de-optimizations
171         // less probable.  Don't initialize JavaLoggerProxy class since
172         // java.util.logging may not be enabled.
173         try {
174             Class.forName("sun.util.logging.PlatformLogger$DefaultLoggerProxy",
175                           false,
176                           PlatformLogger.class.getClassLoader());
177             Class.forName("sun.util.logging.PlatformLogger$JavaLoggerProxy",
178                           false,   // do not invoke class initializer
179                           PlatformLogger.class.getClassLoader());
180         } catch (ClassNotFoundException ex) {
181             throw new InternalError(ex);
182         }
183         */
184     }
185 
186     // Table of known loggers.  Maps names to PlatformLoggers.
187     private static Map<String,WeakReference<PlatformLogger>> loggers =
188         new HashMap<>();
189 
190     /**
191      * Returns a PlatformLogger of a given name.
192      */
getLogger(String name)193     public static synchronized PlatformLogger getLogger(String name) {
194         PlatformLogger log = null;
195         WeakReference<PlatformLogger> ref = loggers.get(name);
196         if (ref != null) {
197             log = ref.get();
198         }
199         if (log == null) {
200             log = new PlatformLogger(name);
201             loggers.put(name, new WeakReference<>(log));
202         }
203         return log;
204     }
205 
206     /**
207      * Initialize java.util.logging.Logger objects for all platform loggers.
208      * This method is called from LogManager.readPrimordialConfiguration().
209      */
redirectPlatformLoggers()210     public static synchronized void redirectPlatformLoggers() {
211         if (loggingEnabled || !LoggingSupport.isAvailable()) return;
212 
213         loggingEnabled = true;
214         for (Map.Entry<String, WeakReference<PlatformLogger>> entry : loggers.entrySet()) {
215             WeakReference<PlatformLogger> ref = entry.getValue();
216             PlatformLogger plog = ref.get();
217             if (plog != null) {
218                 plog.redirectToJavaLoggerProxy();
219             }
220         }
221     }
222 
223     /**
224      * Creates a new JavaLoggerProxy and redirects the platform logger to it
225      */
redirectToJavaLoggerProxy()226     private void redirectToJavaLoggerProxy() {
227         DefaultLoggerProxy lp = DefaultLoggerProxy.class.cast(this.loggerProxy);
228         JavaLoggerProxy jlp = new JavaLoggerProxy(lp.name, lp.level);
229         // the order of assignments is important
230         this.javaLoggerProxy = jlp;   // isLoggable checks javaLoggerProxy if set
231         this.loggerProxy = jlp;
232     }
233 
234     // DefaultLoggerProxy may be replaced with a JavaLoggerProxy object
235     // when the java.util.logging facility is enabled
236     private volatile LoggerProxy loggerProxy;
237     // javaLoggerProxy is only set when the java.util.logging facility is enabled
238     private volatile JavaLoggerProxy javaLoggerProxy;
PlatformLogger(String name)239     private PlatformLogger(String name) {
240         if (loggingEnabled) {
241             this.loggerProxy = this.javaLoggerProxy = new JavaLoggerProxy(name);
242         } else {
243             this.loggerProxy = new DefaultLoggerProxy(name);
244         }
245     }
246 
247     /**
248      * A convenience method to test if the logger is turned off.
249      * (i.e. its level is OFF).
250      */
isEnabled()251     public boolean isEnabled() {
252         return loggerProxy.isEnabled();
253     }
254 
255     /**
256      * Gets the name for this platform logger.
257      */
getName()258     public String getName() {
259         return loggerProxy.name;
260     }
261 
262     /**
263      * Returns true if a message of the given level would actually
264      * be logged by this logger.
265      */
isLoggable(Level level)266     public boolean isLoggable(Level level) {
267         if (level == null) {
268             throw new NullPointerException();
269         }
270         // performance-sensitive method: use two monomorphic call-sites
271         JavaLoggerProxy jlp = javaLoggerProxy;
272         return jlp != null ? jlp.isLoggable(level) : loggerProxy.isLoggable(level);
273     }
274 
275     /**
276      * Get the log level that has been specified for this PlatformLogger.
277      * The result may be null, which means that this logger's
278      * effective level will be inherited from its parent.
279      *
280      * @return  this PlatformLogger's level
281      */
level()282     public Level level() {
283         return loggerProxy.getLevel();
284     }
285 
286     /**
287      * Set the log level specifying which message levels will be
288      * logged by this logger.  Message levels lower than this
289      * value will be discarded.  The level value {@link #OFF}
290      * can be used to turn off logging.
291      * <p>
292      * If the new level is null, it means that this node should
293      * inherit its level from its nearest ancestor with a specific
294      * (non-null) level value.
295      *
296      * @param newLevel the new value for the log level (may be null)
297      */
setLevel(Level newLevel)298     public void setLevel(Level newLevel) {
299         loggerProxy.setLevel(newLevel);
300     }
301 
302     /**
303      * Logs a SEVERE message.
304      */
severe(String msg)305     public void severe(String msg) {
306         loggerProxy.doLog(Level.SEVERE, msg);
307     }
308 
severe(String msg, Throwable t)309     public void severe(String msg, Throwable t) {
310         loggerProxy.doLog(Level.SEVERE, msg, t);
311     }
312 
severe(String msg, Object... params)313     public void severe(String msg, Object... params) {
314         loggerProxy.doLog(Level.SEVERE, msg, params);
315     }
316 
317     /**
318      * Logs a WARNING message.
319      */
warning(String msg)320     public void warning(String msg) {
321         loggerProxy.doLog(Level.WARNING, msg);
322     }
323 
warning(String msg, Throwable t)324     public void warning(String msg, Throwable t) {
325         loggerProxy.doLog(Level.WARNING, msg, t);
326     }
327 
warning(String msg, Object... params)328     public void warning(String msg, Object... params) {
329         loggerProxy.doLog(Level.WARNING, msg, params);
330     }
331 
332     /**
333      * Logs an INFO message.
334      */
info(String msg)335     public void info(String msg) {
336         loggerProxy.doLog(Level.INFO, msg);
337     }
338 
info(String msg, Throwable t)339     public void info(String msg, Throwable t) {
340         loggerProxy.doLog(Level.INFO, msg, t);
341     }
342 
info(String msg, Object... params)343     public void info(String msg, Object... params) {
344         loggerProxy.doLog(Level.INFO, msg, params);
345     }
346 
347     /**
348      * Logs a CONFIG message.
349      */
config(String msg)350     public void config(String msg) {
351         loggerProxy.doLog(Level.CONFIG, msg);
352     }
353 
config(String msg, Throwable t)354     public void config(String msg, Throwable t) {
355         loggerProxy.doLog(Level.CONFIG, msg, t);
356     }
357 
config(String msg, Object... params)358     public void config(String msg, Object... params) {
359         loggerProxy.doLog(Level.CONFIG, msg, params);
360     }
361 
362     /**
363      * Logs a FINE message.
364      */
fine(String msg)365     public void fine(String msg) {
366         loggerProxy.doLog(Level.FINE, msg);
367     }
368 
fine(String msg, Throwable t)369     public void fine(String msg, Throwable t) {
370         loggerProxy.doLog(Level.FINE, msg, t);
371     }
372 
fine(String msg, Object... params)373     public void fine(String msg, Object... params) {
374         loggerProxy.doLog(Level.FINE, msg, params);
375     }
376 
377     /**
378      * Logs a FINER message.
379      */
finer(String msg)380     public void finer(String msg) {
381         loggerProxy.doLog(Level.FINER, msg);
382     }
383 
finer(String msg, Throwable t)384     public void finer(String msg, Throwable t) {
385         loggerProxy.doLog(Level.FINER, msg, t);
386     }
387 
finer(String msg, Object... params)388     public void finer(String msg, Object... params) {
389         loggerProxy.doLog(Level.FINER, msg, params);
390     }
391 
392     /**
393      * Logs a FINEST message.
394      */
finest(String msg)395     public void finest(String msg) {
396         loggerProxy.doLog(Level.FINEST, msg);
397     }
398 
finest(String msg, Throwable t)399     public void finest(String msg, Throwable t) {
400         loggerProxy.doLog(Level.FINEST, msg, t);
401     }
402 
finest(String msg, Object... params)403     public void finest(String msg, Object... params) {
404         loggerProxy.doLog(Level.FINEST, msg, params);
405     }
406 
407     /**
408      * Abstract base class for logging support, defining the API and common field.
409      */
410     private static abstract class LoggerProxy {
411         final String name;
412 
LoggerProxy(String name)413         protected LoggerProxy(String name) {
414             this.name = name;
415         }
416 
isEnabled()417         abstract boolean isEnabled();
418 
getLevel()419         abstract Level getLevel();
setLevel(Level newLevel)420         abstract void setLevel(Level newLevel);
421 
doLog(Level level, String msg)422         abstract void doLog(Level level, String msg);
doLog(Level level, String msg, Throwable thrown)423         abstract void doLog(Level level, String msg, Throwable thrown);
doLog(Level level, String msg, Object... params)424         abstract void doLog(Level level, String msg, Object... params);
425 
isLoggable(Level level)426         abstract boolean isLoggable(Level level);
427     }
428 
429 
430     private static final class DefaultLoggerProxy extends LoggerProxy {
431         /**
432          * Default platform logging support - output messages to System.err -
433          * equivalent to ConsoleHandler with SimpleFormatter.
434          */
outputStream()435         private static PrintStream outputStream() {
436             return System.err;
437         }
438 
439         volatile Level effectiveLevel; // effective level (never null)
440         volatile Level level;          // current level set for this node (may be null)
441 
DefaultLoggerProxy(String name)442         DefaultLoggerProxy(String name) {
443             super(name);
444             this.effectiveLevel = deriveEffectiveLevel(null);
445             this.level = null;
446         }
447 
isEnabled()448         boolean isEnabled() {
449             return effectiveLevel != Level.OFF;
450         }
451 
getLevel()452         Level getLevel() {
453             return level;
454         }
455 
setLevel(Level newLevel)456         void setLevel(Level newLevel) {
457             Level oldLevel = level;
458             if (oldLevel != newLevel) {
459                 level = newLevel;
460                 effectiveLevel = deriveEffectiveLevel(newLevel);
461             }
462         }
463 
doLog(Level level, String msg)464         void doLog(Level level, String msg) {
465             if (isLoggable(level)) {
466                 outputStream().print(format(level, msg, null));
467             }
468         }
469 
doLog(Level level, String msg, Throwable thrown)470         void doLog(Level level, String msg, Throwable thrown) {
471             if (isLoggable(level)) {
472                 outputStream().print(format(level, msg, thrown));
473             }
474         }
475 
doLog(Level level, String msg, Object... params)476         void doLog(Level level, String msg, Object... params) {
477             if (isLoggable(level)) {
478                 String newMsg = formatMessage(msg, params);
479                 outputStream().print(format(level, newMsg, null));
480             }
481         }
482 
isLoggable(Level level)483         boolean isLoggable(Level level) {
484             Level effectiveLevel = this.effectiveLevel;
485             return level.intValue() >= effectiveLevel.intValue() && effectiveLevel != Level.OFF;
486         }
487 
488         // derive effective level (could do inheritance search like j.u.l.Logger)
deriveEffectiveLevel(Level level)489         private Level deriveEffectiveLevel(Level level) {
490             return level == null ? DEFAULT_LEVEL : level;
491         }
492 
493         // Copied from java.util.logging.Formatter.formatMessage
formatMessage(String format, Object... parameters)494         private String formatMessage(String format, Object... parameters) {
495             // Do the formatting.
496             try {
497                 if (parameters == null || parameters.length == 0) {
498                     // No parameters.  Just return format string.
499                     return format;
500                 }
501                 // Is it a java.text style format?
502                 // Ideally we could match with
503                 // Pattern.compile("\\{\\d").matcher(format).find())
504                 // However the cost is 14% higher, so we cheaply check for
505                 // 1 of the first 4 parameters
506                 if (format.indexOf("{0") >= 0 || format.indexOf("{1") >=0 ||
507                             format.indexOf("{2") >=0|| format.indexOf("{3") >=0) {
508                     return java.text.MessageFormat.format(format, parameters);
509                 }
510                 return format;
511             } catch (Exception ex) {
512                 // Formatting failed: use format string.
513                 return format;
514             }
515         }
516 
517         private static final String formatString =
518             LoggingSupport.getSimpleFormat(false); // don't check logging.properties
519 
520         // minimize memory allocation
521         private Date date = new Date();
format(Level level, String msg, Throwable thrown)522         private synchronized String format(Level level, String msg, Throwable thrown) {
523             date.setTime(System.currentTimeMillis());
524             String throwable = "";
525             if (thrown != null) {
526                 StringWriter sw = new StringWriter();
527                 PrintWriter pw = new PrintWriter(sw);
528                 pw.println();
529                 thrown.printStackTrace(pw);
530                 pw.close();
531                 throwable = sw.toString();
532             }
533 
534             return String.format(formatString,
535                                  date,
536                                  getCallerInfo(),
537                                  name,
538                                  level.name(),
539                                  msg,
540                                  throwable);
541         }
542 
543         // Returns the caller's class and method's name; best effort
544         // if cannot infer, return the logger's name.
getCallerInfo()545         private String getCallerInfo() {
546             String sourceClassName = null;
547             String sourceMethodName = null;
548 
549             Throwable throwable = new Throwable();
550 
551             String logClassName = "sun.util.logging.PlatformLogger";
552             boolean lookingForLogger = true;
553             // Android-changed: Different way to access throwable.getStackTrace()
554             // OpenJDK's faster way via SharedSecrets.getJavaLangAccess()
555             // is not available on Android.
556             /*
557             for (int ix = 0; ix < depth; ix++) {
558                 // Calling getStackTraceElement directly prevents the VM
559                 // from paying the cost of building the entire stack frame.
560                 StackTraceElement frame =
561                     access.getStackTraceElement(throwable, ix);
562             */
563             for (StackTraceElement frame : throwable.getStackTrace()) {
564                 String cname = frame.getClassName();
565                 if (lookingForLogger) {
566                     // Skip all frames until we have found the first logger frame.
567                     if (cname.equals(logClassName)) {
568                         lookingForLogger = false;
569                     }
570                 } else {
571                     if (!cname.equals(logClassName)) {
572                         // We've found the relevant frame.
573                         sourceClassName = cname;
574                         sourceMethodName = frame.getMethodName();
575                         break;
576                     }
577                 }
578             }
579 
580             if (sourceClassName != null) {
581                 return sourceClassName + " " + sourceMethodName;
582             } else {
583                 return name;
584             }
585         }
586     }
587 
588     /**
589      * JavaLoggerProxy forwards all the calls to its corresponding
590      * java.util.logging.Logger object.
591      */
592     private static final class JavaLoggerProxy extends LoggerProxy {
593         // initialize javaLevel fields for mapping from Level enum -> j.u.l.Level object
594         static {
595             for (Level level : Level.values()) {
596                 level.javaLevel = LoggingSupport.parseLevel(level.name());
597             }
598         }
599 
600         private final /* java.util.logging.Logger */ Object javaLogger;
601 
JavaLoggerProxy(String name)602         JavaLoggerProxy(String name) {
603             this(name, null);
604         }
605 
JavaLoggerProxy(String name, Level level)606         JavaLoggerProxy(String name, Level level) {
607             super(name);
608             this.javaLogger = LoggingSupport.getLogger(name);
609             if (level != null) {
610                 // level has been updated and so set the Logger's level
611                 LoggingSupport.setLevel(javaLogger, level.javaLevel);
612             }
613         }
614 
doLog(Level level, String msg)615         void doLog(Level level, String msg) {
616             LoggingSupport.log(javaLogger, level.javaLevel, msg);
617         }
618 
doLog(Level level, String msg, Throwable t)619         void doLog(Level level, String msg, Throwable t) {
620             LoggingSupport.log(javaLogger, level.javaLevel, msg, t);
621         }
622 
doLog(Level level, String msg, Object... params)623         void doLog(Level level, String msg, Object... params) {
624             if (!isLoggable(level)) {
625                 return;
626             }
627             // only pass String objects to the j.u.l.Logger which may
628             // be created by untrusted code
629             int len = (params != null) ? params.length : 0;
630             Object[] sparams = new String[len];
631             for (int i = 0; i < len; i++) {
632                 sparams [i] = String.valueOf(params[i]);
633             }
634             LoggingSupport.log(javaLogger, level.javaLevel, msg, sparams);
635         }
636 
isEnabled()637         boolean isEnabled() {
638             return LoggingSupport.isLoggable(javaLogger, Level.OFF.javaLevel);
639         }
640 
641         /**
642          * Returns the PlatformLogger.Level mapped from j.u.l.Level
643          * set in the logger.  If the j.u.l.Logger is set to a custom Level,
644          * this method will return the nearest Level.
645          */
getLevel()646         Level getLevel() {
647             Object javaLevel = LoggingSupport.getLevel(javaLogger);
648             if (javaLevel == null) return null;
649 
650             try {
651                 return Level.valueOf(LoggingSupport.getLevelName(javaLevel));
652             } catch (IllegalArgumentException e) {
653                 return Level.valueOf(LoggingSupport.getLevelValue(javaLevel));
654             }
655         }
656 
setLevel(Level level)657         void setLevel(Level level) {
658             LoggingSupport.setLevel(javaLogger, level == null ? null : level.javaLevel);
659         }
660 
isLoggable(Level level)661         boolean isLoggable(Level level) {
662             return LoggingSupport.isLoggable(javaLogger, level.javaLevel);
663         }
664     }
665 }
666