| /* |
| * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| |
| package sun.util.logging; |
| |
| import java.lang.ref.WeakReference; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.io.PrintStream; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.text.MessageFormat; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.Map; |
| import sun.misc.JavaLangAccess; |
| import sun.misc.SharedSecrets; |
| |
| /** |
| * Platform logger provides an API for the JRE components to log |
| * messages. This enables the runtime components to eliminate the |
| * static dependency of the logging facility and also defers the |
| * java.util.logging initialization until it is enabled. |
| * In addition, the PlatformLogger API can be used if the logging |
| * module does not exist. |
| * |
| * If the logging facility is not enabled, the platform loggers |
| * will output log messages per the default logging configuration |
| * (see below). In this implementation, it does not log the |
| * the stack frame information issuing the log message. |
| * |
| * When the logging facility is enabled (at startup or runtime), |
| * the java.util.logging.Logger will be created for each platform |
| * logger and all log messages will be forwarded to the Logger |
| * to handle. |
| * |
| * Logging facility is "enabled" when one of the following |
| * conditions is met: |
| * 1) a system property "java.util.logging.config.class" or |
| * "java.util.logging.config.file" is set |
| * 2) java.util.logging.LogManager or java.util.logging.Logger |
| * is referenced that will trigger the logging initialization. |
| * |
| * Default logging configuration: |
| * global logging level = INFO |
| * handlers = java.util.logging.ConsoleHandler |
| * java.util.logging.ConsoleHandler.level = INFO |
| * java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter |
| * |
| * Limitation: |
| * <JAVA_HOME>/lib/logging.properties is the system-wide logging |
| * configuration defined in the specification and read in the |
| * default case to configure any java.util.logging.Logger instances. |
| * Platform loggers will not detect if <JAVA_HOME>/lib/logging.properties |
| * is modified. In other words, unless the java.util.logging API |
| * is used at runtime or the logging system properties is set, |
| * the platform loggers will use the default setting described above. |
| * The platform loggers are designed for JDK developers use and |
| * this limitation can be workaround with setting |
| * -Djava.util.logging.config.file system property. |
| * |
| * @since 1.7 |
| */ |
| public class PlatformLogger { |
| // Same values as java.util.logging.Level for easy mapping |
| public static final int OFF = Integer.MAX_VALUE; |
| public static final int SEVERE = 1000; |
| public static final int WARNING = 900; |
| public static final int INFO = 800; |
| public static final int CONFIG = 700; |
| public static final int FINE = 500; |
| public static final int FINER = 400; |
| public static final int FINEST = 300; |
| public static final int ALL = Integer.MIN_VALUE; |
| |
| private static final int defaultLevel = INFO; |
| private static boolean loggingEnabled; |
| static { |
| loggingEnabled = AccessController.doPrivileged( |
| new PrivilegedAction<Boolean>() { |
| public Boolean run() { |
| String cname = System.getProperty("java.util.logging.config.class"); |
| String fname = System.getProperty("java.util.logging.config.file"); |
| return (cname != null || fname != null); |
| } |
| }); |
| } |
| |
| // Table of known loggers. Maps names to PlatformLoggers. |
| private static Map<String,WeakReference<PlatformLogger>> loggers = |
| new HashMap<String,WeakReference<PlatformLogger>>(); |
| |
| /** |
| * Returns a PlatformLogger of a given name. |
| */ |
| public static synchronized PlatformLogger getLogger(String name) { |
| PlatformLogger log = null; |
| WeakReference<PlatformLogger> ref = loggers.get(name); |
| if (ref != null) { |
| log = ref.get(); |
| } |
| if (log == null) { |
| log = new PlatformLogger(name); |
| loggers.put(name, new WeakReference<PlatformLogger>(log)); |
| } |
| return log; |
| } |
| |
| /** |
| * Initialize java.util.logging.Logger objects for all platform loggers. |
| * This method is called from LogManager.readPrimordialConfiguration(). |
| */ |
| public static synchronized void redirectPlatformLoggers() { |
| if (loggingEnabled || !LoggingSupport.isAvailable()) return; |
| |
| loggingEnabled = true; |
| for (Map.Entry<String, WeakReference<PlatformLogger>> entry : loggers.entrySet()) { |
| WeakReference<PlatformLogger> ref = entry.getValue(); |
| PlatformLogger plog = ref.get(); |
| if (plog != null) { |
| plog.newJavaLogger(); |
| } |
| } |
| } |
| |
| /** |
| * Creates a new JavaLogger that the platform logger uses |
| */ |
| private void newJavaLogger() { |
| logger = new JavaLogger(logger.name, logger.effectiveLevel); |
| } |
| |
| // logger may be replaced with a JavaLogger object |
| // when the logging facility is enabled |
| private volatile LoggerProxy logger; |
| |
| private PlatformLogger(String name) { |
| if (loggingEnabled) { |
| this.logger = new JavaLogger(name); |
| } else { |
| this.logger = new LoggerProxy(name); |
| } |
| } |
| |
| /** |
| * A convenience method to test if the logger is turned off. |
| * (i.e. its level is OFF). |
| */ |
| public boolean isEnabled() { |
| return logger.isEnabled(); |
| } |
| |
| /** |
| * Gets the name for this platform logger. |
| */ |
| public String getName() { |
| return logger.name; |
| } |
| |
| /** |
| * Returns true if a message of the given level would actually |
| * be logged by this logger. |
| */ |
| public boolean isLoggable(int level) { |
| return logger.isLoggable(level); |
| } |
| |
| /** |
| * Gets the current log level. Returns 0 if the current effective level |
| * is not set (equivalent to Logger.getLevel() returns null). |
| */ |
| public int getLevel() { |
| return logger.getLevel(); |
| } |
| |
| /** |
| * Sets the log level. |
| */ |
| public void setLevel(int newLevel) { |
| logger.setLevel(newLevel); |
| } |
| |
| /** |
| * Logs a SEVERE message. |
| */ |
| public void severe(String msg) { |
| logger.doLog(SEVERE, msg); |
| } |
| |
| public void severe(String msg, Throwable t) { |
| logger.doLog(SEVERE, msg, t); |
| } |
| |
| public void severe(String msg, Object... params) { |
| logger.doLog(SEVERE, msg, params); |
| } |
| |
| /** |
| * Logs a WARNING message. |
| */ |
| public void warning(String msg) { |
| logger.doLog(WARNING, msg); |
| } |
| |
| public void warning(String msg, Throwable t) { |
| logger.doLog(WARNING, msg, t); |
| } |
| |
| public void warning(String msg, Object... params) { |
| logger.doLog(WARNING, msg, params); |
| } |
| |
| /** |
| * Logs an INFO message. |
| */ |
| public void info(String msg) { |
| logger.doLog(INFO, msg); |
| } |
| |
| public void info(String msg, Throwable t) { |
| logger.doLog(INFO, msg, t); |
| } |
| |
| public void info(String msg, Object... params) { |
| logger.doLog(INFO, msg, params); |
| } |
| |
| /** |
| * Logs a CONFIG message. |
| */ |
| public void config(String msg) { |
| logger.doLog(CONFIG, msg); |
| } |
| |
| public void config(String msg, Throwable t) { |
| logger.doLog(CONFIG, msg, t); |
| } |
| |
| public void config(String msg, Object... params) { |
| logger.doLog(CONFIG, msg, params); |
| } |
| |
| /** |
| * Logs a FINE message. |
| */ |
| public void fine(String msg) { |
| logger.doLog(FINE, msg); |
| } |
| |
| public void fine(String msg, Throwable t) { |
| logger.doLog(FINE, msg, t); |
| } |
| |
| public void fine(String msg, Object... params) { |
| logger.doLog(FINE, msg, params); |
| } |
| |
| /** |
| * Logs a FINER message. |
| */ |
| public void finer(String msg) { |
| logger.doLog(FINER, msg); |
| } |
| |
| public void finer(String msg, Throwable t) { |
| logger.doLog(FINER, msg, t); |
| } |
| |
| public void finer(String msg, Object... params) { |
| logger.doLog(FINER, msg, params); |
| } |
| |
| /** |
| * Logs a FINEST message. |
| */ |
| public void finest(String msg) { |
| logger.doLog(FINEST, msg); |
| } |
| |
| public void finest(String msg, Throwable t) { |
| logger.doLog(FINEST, msg, t); |
| } |
| |
| public void finest(String msg, Object... params) { |
| logger.doLog(FINEST, msg, params); |
| } |
| |
| /** |
| * Default platform logging support - output messages to |
| * System.err - equivalent to ConsoleHandler with SimpleFormatter. |
| */ |
| static class LoggerProxy { |
| private static final PrintStream defaultStream = System.err; |
| private static final String lineSeparator = AccessController.doPrivileged( |
| new PrivilegedAction<String>() { |
| public String run() { |
| return System.getProperty("line.separator"); |
| } |
| }); |
| |
| final String name; |
| volatile int levelValue; |
| volatile int effectiveLevel = 0; // current effective level value |
| |
| LoggerProxy(String name) { |
| this(name, defaultLevel); |
| } |
| |
| LoggerProxy(String name, int level) { |
| this.name = name; |
| this.levelValue = level == 0 ? defaultLevel : level; |
| } |
| |
| boolean isEnabled() { |
| return levelValue != OFF; |
| } |
| |
| int getLevel() { |
| return effectiveLevel; |
| } |
| |
| void setLevel(int newLevel) { |
| levelValue = newLevel; |
| effectiveLevel = newLevel; |
| } |
| |
| void doLog(int level, String msg) { |
| if (level < levelValue || levelValue == OFF) { |
| return; |
| } |
| defaultStream.println(format(level, msg, null)); |
| } |
| |
| void doLog(int level, String msg, Throwable thrown) { |
| if (level < levelValue || levelValue == OFF) { |
| return; |
| } |
| defaultStream.println(format(level, msg, thrown)); |
| } |
| |
| void doLog(int level, String msg, Object... params) { |
| if (level < levelValue || levelValue == OFF) { |
| return; |
| } |
| String newMsg = formatMessage(msg, params); |
| defaultStream.println(format(level, newMsg, null)); |
| } |
| |
| public boolean isLoggable(int level) { |
| if (level < levelValue || levelValue == OFF) { |
| return false; |
| } |
| return true; |
| } |
| |
| private static final String format = "{0,date} {0,time}"; |
| |
| private Object args[] = new Object[1]; |
| private MessageFormat formatter; |
| private Date dat; |
| |
| // Copied from java.util.logging.Formatter.formatMessage |
| private String formatMessage(String format, Object... parameters) { |
| // Do the formatting. |
| try { |
| if (parameters == null || parameters.length == 0) { |
| // No parameters. Just return format string. |
| return format; |
| } |
| // Is it a java.text style format? |
| // Ideally we could match with |
| // Pattern.compile("\\{\\d").matcher(format).find()) |
| // However the cost is 14% higher, so we cheaply check for |
| // 1 of the first 4 parameters |
| if (format.indexOf("{0") >= 0 || format.indexOf("{1") >=0 || |
| format.indexOf("{2") >=0|| format.indexOf("{3") >=0) { |
| return java.text.MessageFormat.format(format, parameters); |
| } |
| return format; |
| } catch (Exception ex) { |
| // Formatting failed: use format string. |
| return format; |
| } |
| } |
| |
| private synchronized String format(int level, String msg, Throwable thrown) { |
| StringBuffer sb = new StringBuffer(); |
| // Minimize memory allocations here. |
| if (dat == null) { |
| dat = new Date(); |
| formatter = new MessageFormat(format); |
| } |
| dat.setTime(System.currentTimeMillis()); |
| args[0] = dat; |
| StringBuffer text = new StringBuffer(); |
| formatter.format(args, text, null); |
| sb.append(text); |
| sb.append(" "); |
| sb.append(getCallerInfo()); |
| sb.append(lineSeparator); |
| sb.append(PlatformLogger.getLevelName(level)); |
| sb.append(": "); |
| sb.append(msg); |
| if (thrown != null) { |
| try { |
| StringWriter sw = new StringWriter(); |
| PrintWriter pw = new PrintWriter(sw); |
| thrown.printStackTrace(pw); |
| pw.close(); |
| sb.append(sw.toString()); |
| } catch (Exception ex) { |
| throw new AssertionError(ex); |
| } |
| } |
| |
| return sb.toString(); |
| } |
| |
| // Returns the caller's class and method's name; best effort |
| // if cannot infer, return the logger's name. |
| private String getCallerInfo() { |
| String sourceClassName = null; |
| String sourceMethodName = null; |
| |
| JavaLangAccess access = SharedSecrets.getJavaLangAccess(); |
| Throwable throwable = new Throwable(); |
| int depth = access.getStackTraceDepth(throwable); |
| |
| String logClassName = "sun.util.logging.PlatformLogger"; |
| boolean lookingForLogger = true; |
| for (int ix = 0; ix < depth; ix++) { |
| // Calling getStackTraceElement directly prevents the VM |
| // from paying the cost of building the entire stack frame. |
| StackTraceElement frame = |
| access.getStackTraceElement(throwable, ix); |
| String cname = frame.getClassName(); |
| if (lookingForLogger) { |
| // Skip all frames until we have found the first logger frame. |
| if (cname.equals(logClassName)) { |
| lookingForLogger = false; |
| } |
| } else { |
| if (!cname.equals(logClassName)) { |
| // We've found the relevant frame. |
| sourceClassName = cname; |
| sourceMethodName = frame.getMethodName(); |
| break; |
| } |
| } |
| } |
| |
| if (sourceClassName != null) { |
| return sourceClassName + " " + sourceMethodName; |
| } else { |
| return name; |
| } |
| } |
| } |
| |
| /** |
| * JavaLogger forwards all the calls to its corresponding |
| * java.util.logging.Logger object. |
| */ |
| static class JavaLogger extends LoggerProxy { |
| private static final Map<Integer, Object> levelObjects = |
| new HashMap<Integer, Object>(); |
| |
| static { |
| if (LoggingSupport.isAvailable()) { |
| // initialize the map to Level objects |
| getLevelObjects(); |
| } |
| } |
| |
| private static void getLevelObjects() { |
| // get all java.util.logging.Level objects |
| int[] levelArray = new int[] {OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL}; |
| for (int l : levelArray) { |
| Object level = LoggingSupport.parseLevel(getLevelName(l)); |
| levelObjects.put(l, level); |
| } |
| } |
| |
| private final Object javaLogger; |
| JavaLogger(String name) { |
| this(name, 0); |
| } |
| |
| JavaLogger(String name, int level) { |
| super(name, level); |
| this.javaLogger = LoggingSupport.getLogger(name); |
| if (level != 0) { |
| // level has been updated and so set the Logger's level |
| LoggingSupport.setLevel(javaLogger, levelObjects.get(level)); |
| } |
| } |
| |
| /** |
| * Let Logger.log() do the filtering since if the level of a |
| * platform logger is altered directly from |
| * java.util.logging.Logger.setLevel(), the levelValue will |
| * not be updated. |
| */ |
| void doLog(int level, String msg) { |
| LoggingSupport.log(javaLogger, levelObjects.get(level), msg); |
| } |
| |
| void doLog(int level, String msg, Throwable t) { |
| LoggingSupport.log(javaLogger, levelObjects.get(level), msg, t); |
| } |
| |
| void doLog(int level, String msg, Object... params) { |
| int paramsNumber = (params != null) ? params.length : 0; |
| for (int i = 0; i < paramsNumber; i++) { |
| params[i] = String.valueOf(params[i]); |
| } |
| LoggingSupport.log(javaLogger, levelObjects.get(level), msg, params); |
| } |
| |
| boolean isEnabled() { |
| Object level = LoggingSupport.getLevel(javaLogger); |
| return level == null || level.equals(levelObjects.get(OFF)) == false; |
| } |
| |
| int getLevel() { |
| Object level = LoggingSupport.getLevel(javaLogger); |
| if (level != null) { |
| for (Map.Entry<Integer, Object> l : levelObjects.entrySet()) { |
| if (level == l.getValue()) { |
| return l.getKey(); |
| } |
| } |
| } |
| return 0; |
| } |
| |
| void setLevel(int newLevel) { |
| levelValue = newLevel; |
| LoggingSupport.setLevel(javaLogger, levelObjects.get(newLevel)); |
| } |
| |
| public boolean isLoggable(int level) { |
| return LoggingSupport.isLoggable(javaLogger, levelObjects.get(level)); |
| } |
| } |
| |
| private static String getLevelName(int level) { |
| switch (level) { |
| case OFF : return "OFF"; |
| case SEVERE : return "SEVERE"; |
| case WARNING : return "WARNING"; |
| case INFO : return "INFO"; |
| case CONFIG : return "CONFIG"; |
| case FINE : return "FINE"; |
| case FINER : return "FINER"; |
| case FINEST : return "FINEST"; |
| case ALL : return "ALL"; |
| default : return "UNKNOWN"; |
| } |
| } |
| |
| } |