1/*
2 * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26
27package sun.util.logging;
28
29import java.lang.ref.WeakReference;
30import java.util.Arrays;
31import java.util.HashMap;
32import java.util.Map;
33import java.util.ResourceBundle;
34import java.util.function.Supplier;
35import jdk.internal.logger.LazyLoggers;
36import jdk.internal.logger.LoggerWrapper;
37
38/**
39 * Platform logger provides an API for the JRE components to log
40 * messages.  This enables the runtime components to eliminate the
41 * static dependency of the logging facility and also defers the
42 * java.util.logging initialization until it is enabled.
43 * In addition, the PlatformLogger API can be used if the logging
44 * module does not exist.
45 *
46 * If the logging facility is not enabled, the platform loggers
47 * will output log messages per the default logging configuration
48 * (see below). In this implementation, it does not log the
49 * the stack frame information issuing the log message.
50 *
51 * When the logging facility is enabled (at startup or runtime),
52 * the backend logger will be created for each platform
53 * logger and all log messages will be forwarded to the Logger
54 * to handle.
55 *
56 * The PlatformLogger uses an underlying PlatformLogger.Bridge instance
57 * obtained by calling {@link PlatformLogger.Bridge#convert PlatformLogger.Bridge.convert(}
58 * {@link jdk.internal.logger.LazyLoggers#getLazyLogger(java.lang.String, java.lang.Class)
59 * jdk.internal.logger.LazyLoggers#getLazyLogger(name, PlatformLogger.class))}.
60 *
61 * Logging facility is "enabled" when one of the following
62 * conditions is met:
63 * 1) ServiceLoader.load({@link java.lang.System.LoggerFinder LoggerFinder.class},
64 *    ClassLoader.getSystemClassLoader()).iterator().hasNext().
65 * 2) ServiceLoader.loadInstalled({@link jdk.internal.logger.DefaultLoggerFinder}).iterator().hasNext(),
66 *    and 2.1) a system property "java.util.logging.config.class" or
67 *             "java.util.logging.config.file" is set
68 *     or  2.2) java.util.logging.LogManager or java.util.logging.Logger
69 *              is referenced that will trigger the logging initialization.
70 *
71 * Default logging configuration:
72 *
73 *   No LoggerFinder service implementation declared
74 *   global logging level = INFO
75 *   handlers = java.util.logging.ConsoleHandler
76 *   java.util.logging.ConsoleHandler.level = INFO
77 *   java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
78 *
79 * Limitation:
80 * {@code <JAVA_HOME>/conf/logging.properties} is the system-wide logging
81 * configuration defined in the specification and read in the
82 * default case to configure any java.util.logging.Logger instances.
83 * Platform loggers will not detect if {@code <JAVA_HOME>/conf/logging.properties}
84 * is modified. In other words, unless the java.util.logging API
85 * is used at runtime or the logging system properties is set,
86 * the platform loggers will use the default setting described above.
87 * The platform loggers are designed for JDK developers use and
88 * this limitation can be workaround with setting
89 * -Djava.util.logging.config.file system property.
90 * <br>
91 * Calling PlatformLogger.setLevel will not work when there is a custom
92 * LoggerFinder installed - and as a consequence {@link #setLevel setLevel}
93 * is now deprecated.
94 *
95 * @since 1.7
96 */
97public class PlatformLogger {
98
99    /**
100     * PlatformLogger logging levels.
101     */
102    public static enum Level {
103        // The name and value must match that of {@code java.util.logging.Level}s.
104        // Declare in ascending order of the given value for binary search.
105        ALL(System.Logger.Level.ALL),
106        FINEST(System.Logger.Level.TRACE),
107        FINER(System.Logger.Level.TRACE),
108        FINE(System.Logger.Level.DEBUG),
109        CONFIG(System.Logger.Level.DEBUG),
110        INFO(System.Logger.Level.INFO),
111        WARNING(System.Logger.Level.WARNING),
112        SEVERE(System.Logger.Level.ERROR),
113        OFF(System.Logger.Level.OFF);
114
115        final System.Logger.Level systemLevel;
116        Level(System.Logger.Level systemLevel) {
117            this.systemLevel = systemLevel;
118        }
119
120        // The integer values must match that of {@code java.util.logging.Level}
121        // objects.
122        private static final int SEVERITY_OFF     = Integer.MAX_VALUE;
123        private static final int SEVERITY_SEVERE  = 1000;
124        private static final int SEVERITY_WARNING = 900;
125        private static final int SEVERITY_INFO    = 800;
126        private static final int SEVERITY_CONFIG  = 700;
127        private static final int SEVERITY_FINE    = 500;
128        private static final int SEVERITY_FINER   = 400;
129        private static final int SEVERITY_FINEST  = 300;
130        private static final int SEVERITY_ALL     = Integer.MIN_VALUE;
131
132        // ascending order for binary search matching the list of enum constants
133        private static final int[] LEVEL_VALUES = new int[] {
134            SEVERITY_ALL, SEVERITY_FINEST, SEVERITY_FINER,
135            SEVERITY_FINE, SEVERITY_CONFIG, SEVERITY_INFO,
136            SEVERITY_WARNING, SEVERITY_SEVERE, SEVERITY_OFF
137        };
138
139        public System.Logger.Level systemLevel() {
140            return systemLevel;
141        }
142
143        public int intValue() {
144            return LEVEL_VALUES[this.ordinal()];
145        }
146
147        /**
148         * Maps a severity value to an effective logger level.
149         * @param level The severity of the messages that should be
150         *        logged with a logger set to the returned level.
151         * @return The effective logger level, which is the nearest Level value
152         *         whose severity is greater or equal to the given level.
153         *         For level > SEVERE (OFF excluded), return SEVERE.
154         */
155        public static Level valueOf(int level) {
156            switch (level) {
157                // ordering per the highest occurrences in the jdk source
158                // finest, fine, finer, info first
159                case SEVERITY_FINEST  : return Level.FINEST;
160                case SEVERITY_FINE    : return Level.FINE;
161                case SEVERITY_FINER   : return Level.FINER;
162                case SEVERITY_INFO    : return Level.INFO;
163                case SEVERITY_WARNING : return Level.WARNING;
164                case SEVERITY_CONFIG  : return Level.CONFIG;
165                case SEVERITY_SEVERE  : return Level.SEVERE;
166                case SEVERITY_OFF     : return Level.OFF;
167                case SEVERITY_ALL     : return Level.ALL;
168            }
169            // return the nearest Level value >= the given level,
170            // for level > SEVERE, return SEVERE and exclude OFF
171            int i = Arrays.binarySearch(LEVEL_VALUES, 0, LEVEL_VALUES.length-2, level);
172            return values()[i >= 0 ? i : (-i-1)];
173        }
174    }
175
176    /**
177     *
178     * The PlatformLogger.Bridge interface is implemented by the System.Logger
179     * objects returned by our default JUL provider - so that JRE classes using
180     * PlatformLogger see no difference when JUL is the actual backend.
181     *
182     * PlatformLogger is now only a thin adaptation layer over the same
183     * loggers than returned by java.lang.System.getLogger(String name).
184     *
185     * The recommendation for JRE classes going forward is to use
186     * java.lang.System.getLogger(String name), which will
187     * use Lazy Loggers when possible and necessary.
188     *
189     */
190    public static interface Bridge {
191
192        /**
193         * Gets the name for this platform logger.
194         * @return the name of the platform logger.
195         */
196        public String getName();
197
198        /**
199         * Returns true if a message of the given level would actually
200         * be logged by this logger.
201         * @param level the level
202         * @return whether a message of that level would be logged
203         */
204        public boolean isLoggable(Level level);
205        public boolean isEnabled();
206
207        public void log(Level level, String msg);
208        public void log(Level level, String msg, Throwable thrown);
209        public void log(Level level, String msg, Object... params);
210        public void log(Level level, Supplier<String> msgSupplier);
211        public void log(Level level, Throwable thrown, Supplier<String> msgSupplier);
212        public void logp(Level level, String sourceClass, String sourceMethod, String msg);
213        public void logp(Level level, String sourceClass, String sourceMethod,
214                         Supplier<String> msgSupplier);
215        public void logp(Level level, String sourceClass, String sourceMethod,
216                                                    String msg, Object... params);
217        public void logp(Level level, String sourceClass, String sourceMethod,
218                         String msg, Throwable thrown);
219        public void logp(Level level, String sourceClass, String sourceMethod,
220                         Throwable thrown, Supplier<String> msgSupplier);
221        public void logrb(Level level, String sourceClass, String sourceMethod,
222                          ResourceBundle bundle, String msg, Object... params);
223        public void logrb(Level level, String sourceClass, String sourceMethod,
224                          ResourceBundle bundle, String msg, Throwable thrown);
225        public void logrb(Level level, ResourceBundle bundle, String msg,
226                Object... params);
227        public void logrb(Level level, ResourceBundle bundle, String msg,
228                Throwable thrown);
229
230
231        public static Bridge convert(System.Logger logger) {
232            if (logger instanceof PlatformLogger.Bridge) {
233                return (Bridge) logger;
234            } else {
235                return new LoggerWrapper<>(logger);
236            }
237        }
238    }
239
240    /**
241     * The {@code PlatformLogger.ConfigurableBridge} interface is used to
242     * implement the deprecated {@link PlatformLogger#setLevel} method.
243     *
244     * PlatformLogger is now only a thin adaptation layer over the same
245     * loggers than returned by java.lang.System.getLogger(String name).
246     *
247     * The recommendation for JRE classes going forward is to use
248     * java.lang.System.getLogger(String name), which will
249     * use Lazy Loggers when possible and necessary.
250     *
251     */
252    public static interface ConfigurableBridge {
253
254        public abstract class LoggerConfiguration {
255            public abstract Level getPlatformLevel();
256            public abstract void setPlatformLevel(Level level);
257        }
258
259        public default LoggerConfiguration getLoggerConfiguration() {
260            return null;
261        }
262
263        public static LoggerConfiguration getLoggerConfiguration(PlatformLogger.Bridge logger) {
264            if (logger instanceof PlatformLogger.ConfigurableBridge) {
265                return ((ConfigurableBridge) logger).getLoggerConfiguration();
266            } else {
267                return null;
268            }
269        }
270    }
271
272    // Table of known loggers.  Maps names to PlatformLoggers.
273    private static final Map<String,WeakReference<PlatformLogger>> loggers =
274        new HashMap<>();
275
276    /**
277     * Returns a PlatformLogger of a given name.
278     * @param name the name of the logger
279     * @return a PlatformLogger
280     */
281    public static synchronized PlatformLogger getLogger(String name) {
282        PlatformLogger log = null;
283        WeakReference<PlatformLogger> ref = loggers.get(name);
284        if (ref != null) {
285            log = ref.get();
286        }
287        if (log == null) {
288            log = new PlatformLogger(PlatformLogger.Bridge.convert(
289                    // We pass PlatformLogger.class.getModule() (java.base)
290                    // rather than the actual module of the caller
291                    // because we want PlatformLoggers to be system loggers: we
292                    // won't need to resolve any resource bundles anyway.
293                    // Note: Many unit tests depend on the fact that
294                    //       PlatformLogger.getLoggerFromFinder is not caller
295                    //       sensitive, and this strategy ensure that the tests
296                    //       still pass.
297                    LazyLoggers.getLazyLogger(name, PlatformLogger.class.getModule())));
298            loggers.put(name, new WeakReference<>(log));
299        }
300        return log;
301    }
302
303    // The system loggerProxy returned by LazyLoggers
304    // This may be a lazy logger - see jdk.internal.logger.LazyLoggers,
305    // or may be a Logger instance (or a wrapper thereof).
306    //
307    private final PlatformLogger.Bridge loggerProxy;
308    private PlatformLogger(PlatformLogger.Bridge loggerProxy) {
309        this.loggerProxy = loggerProxy;
310    }
311
312    /**
313     * A convenience method to test if the logger is turned off.
314     * (i.e. its level is OFF).
315     * @return whether the logger is turned off.
316     */
317    public boolean isEnabled() {
318        return loggerProxy.isEnabled();
319    }
320
321    /**
322     * Gets the name for this platform logger.
323     * @return the name of the platform logger.
324     */
325    public String getName() {
326        return loggerProxy.getName();
327    }
328
329    /**
330     * Returns true if a message of the given level would actually
331     * be logged by this logger.
332     * @param level the level
333     * @return whether a message of that level would be logged
334     */
335    public boolean isLoggable(Level level) {
336        if (level == null) {
337            throw new NullPointerException();
338        }
339
340        return loggerProxy.isLoggable(level);
341    }
342
343    /**
344     * Get the log level that has been specified for this PlatformLogger.
345     * The result may be null, which means that this logger's
346     * effective level will be inherited from its parent.
347     *
348     * @return  this PlatformLogger's level
349     */
350    public Level level() {
351        final ConfigurableBridge.LoggerConfiguration spi =
352                PlatformLogger.ConfigurableBridge.getLoggerConfiguration(loggerProxy);
353        return spi == null ? null : spi.getPlatformLevel();
354    }
355
356    /**
357     * Set the log level specifying which message levels will be
358     * logged by this logger.  Message levels lower than this
359     * value will be discarded.  The level value {@link Level#OFF}
360     * can be used to turn off logging.
361     * <p>
362     * If the new level is null, it means that this node should
363     * inherit its level from its nearest ancestor with a specific
364     * (non-null) level value.
365     *
366     * @param newLevel the new value for the log level (may be null)
367     * @deprecated Platform Loggers should not be configured programmatically.
368     *             This method will not work if a custom {@link
369     *             java.lang.System.LoggerFinder} is installed.
370     */
371    @Deprecated
372    public void setLevel(Level newLevel) {
373        final ConfigurableBridge.LoggerConfiguration spi =
374                PlatformLogger.ConfigurableBridge.getLoggerConfiguration(loggerProxy);;
375        if (spi != null) {
376            spi.setPlatformLevel(newLevel);
377        }
378    }
379
380    /**
381     * Logs a SEVERE message.
382     * @param msg the message
383     */
384    public void severe(String msg) {
385        loggerProxy.log(Level.SEVERE, msg, (Object[])null);
386    }
387
388    public void severe(String msg, Throwable t) {
389        loggerProxy.log(Level.SEVERE, msg, t);
390    }
391
392    public void severe(String msg, Object... params) {
393        loggerProxy.log(Level.SEVERE, msg, params);
394    }
395
396    /**
397     * Logs a WARNING message.
398     * @param msg the message
399     */
400    public void warning(String msg) {
401        loggerProxy.log(Level.WARNING, msg, (Object[])null);
402    }
403
404    public void warning(String msg, Throwable t) {
405        loggerProxy.log(Level.WARNING, msg, t);
406    }
407
408    public void warning(String msg, Object... params) {
409        loggerProxy.log(Level.WARNING, msg, params);
410    }
411
412    /**
413     * Logs an INFO message.
414     * @param msg the message
415     */
416    public void info(String msg) {
417        loggerProxy.log(Level.INFO, msg, (Object[])null);
418    }
419
420    public void info(String msg, Throwable t) {
421        loggerProxy.log(Level.INFO, msg, t);
422    }
423
424    public void info(String msg, Object... params) {
425        loggerProxy.log(Level.INFO, msg, params);
426    }
427
428    /**
429     * Logs a CONFIG message.
430     * @param msg the message
431     */
432    public void config(String msg) {
433        loggerProxy.log(Level.CONFIG, msg, (Object[])null);
434    }
435
436    public void config(String msg, Throwable t) {
437        loggerProxy.log(Level.CONFIG, msg, t);
438    }
439
440    public void config(String msg, Object... params) {
441        loggerProxy.log(Level.CONFIG, msg, params);
442    }
443
444    /**
445     * Logs a FINE message.
446     * @param msg the message
447     */
448    public void fine(String msg) {
449        loggerProxy.log(Level.FINE, msg, (Object[])null);
450    }
451
452    public void fine(String msg, Throwable t) {
453        loggerProxy.log(Level.FINE, msg, t);
454    }
455
456    public void fine(String msg, Object... params) {
457        loggerProxy.log(Level.FINE, msg, params);
458    }
459
460    /**
461     * Logs a FINER message.
462     * @param msg the message
463     */
464    public void finer(String msg) {
465        loggerProxy.log(Level.FINER, msg, (Object[])null);
466    }
467
468    public void finer(String msg, Throwable t) {
469        loggerProxy.log(Level.FINER, msg, t);
470    }
471
472    public void finer(String msg, Object... params) {
473        loggerProxy.log(Level.FINER, msg, params);
474    }
475
476    /**
477     * Logs a FINEST message.
478     * @param msg the message
479     */
480    public void finest(String msg) {
481        loggerProxy.log(Level.FINEST, msg, (Object[])null);
482    }
483
484    public void finest(String msg, Throwable t) {
485        loggerProxy.log(Level.FINEST, msg, t);
486    }
487
488    public void finest(String msg, Object... params) {
489        loggerProxy.log(Level.FINEST, msg, params);
490    }
491
492    // ------------------------------------
493    // Maps used for Level conversion
494    // ------------------------------------
495
496    // This map is indexed by java.util.spi.Logger.Level.ordinal() and returns
497    // a PlatformLogger.Level
498    //
499    // ALL, TRACE, DEBUG, INFO, WARNING, ERROR, OFF
500    private static final Level[] spi2platformLevelMapping = {
501            Level.ALL,     // mapped from ALL
502            Level.FINER,   // mapped from TRACE
503            Level.FINE,    // mapped from DEBUG
504            Level.INFO,    // mapped from INFO
505            Level.WARNING, // mapped from WARNING
506            Level.SEVERE,  // mapped from ERROR
507            Level.OFF      // mapped from OFF
508    };
509
510    public static Level toPlatformLevel(java.lang.System.Logger.Level level) {
511        if (level == null) return null;
512        assert level.ordinal() < spi2platformLevelMapping.length;
513        return spi2platformLevelMapping[level.ordinal()];
514    }
515
516}
517