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