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