Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*-
   * See the file LICENSE for redistribution information.
   *
   * Copyright (c) 2002,2010 Oracle and/or its affiliates.  All rights reserved.
   *
   */
  
  package com.sleepycat.je.utilint;
  
 import java.util.Map;
 
Logging Architecture =========================== JE uses the java.util.logging package. The ability to dynamically specify logging levels per component is important functionality for the system. Logging output is directed to the console, to the je.info files, and in special cases, to a MemoryHandler. The latter is meant for debugging and field support. Logging output from multiple environments may end up going to the same handler, either because a single process is executing multiple environments, or because the output of multiple environments, such as a replication group, is combined in a single display. Because of that, it's important for logging output to be prefixed with an environment id so it can be distinguished by environment. Loggers managed by java.util.logging.LogManager are supposed to be maintained with a weak reference by the LogManager. In our experience, loggers do not seem to be released, and seem to accumulate in memory. Because of that, we create a single logger per JE class, rather than a logger per class instance. The latter would be more convenient, because we wish to use environment specific information, such as the environment name as a prefix, or the location of the je.info file, when creating output. Restricting ourselves to a single per-class logger requires that we keep the logger and its associated handlers and formatters stateless, because the logger may be shared by multiple environments. To compensate for that, we use per-thread state to permit per-environment customization of the logging output (that is the logging prefix) and file handler location. Because we've seen some performance issues with ThreadLocals, we elected instead to maintain a per-thread map to store state information needed by the logger. This state information is: - the enviroment impl from the envMap(from which one can obtain the prefix and the console, file and memory handlers to use) - or if the environment impl is null because the component executes without an environment, the output will go to only a console handler. It will use a particular formatter to prefix the output with a useful id. This is obtained from the formatter map. With this scheme, a JE process has a maximum of - N loggers, where N is the number of classes which get loggers - 3 handlers * number of environments, because each environment creates a Console, File and Memory handler. How To Use Logging in a JE Class ======================================= Creating a Logger: There are three kinds of loggers that a class may chose to use. 1. A class with a reference to EnvironmentImpl or RepImpl should use LoggerUtils.getLogger(Class<?>) to create a logger which prefixes its output with an environment id. When a logger is obtained this way, the logger should not be used directly. Instead, LoggerUtils provides several methods like this: LoggerUtils.severe() equals to logger.severe LoggerUtils.warning() equals to logger.warning etc LoggerUtils.logMsg(Logger, EnvironmentImpl, Level, String) equals to logger.log(Level, String) 2. A class without an EnvironmentImpl which still has some kind of custom information to prepend to the logging output should use LoggerUtils.getFormatterNeeded(). For example, com.sleepycat.je.rep.monitor.Monitor does not have an environment, but does have a NameIdPair, and it can insert that information via a specific Formatter. When using this logger, the class must create and maintain a Formatter instance to pass as a logging parameter. When using this flavor, use: LoggerUtils.logMsg(Logger, Formatter, Level, String) where the formatter is the one created by the using class. 3. A logger without an EnvironmentImpl does not prefix or customize the logging output, and uses LoggerUtils.getLoggerFixedPrefix to create a logger. In this case, use the usual java.util.logging.Logger logging methods. Note: there are some JE classes which only conditionally reference an environment. In that case, the environment must also conditionally create a logger, and then use the wrapper methods which use both an environmentImpl and a formatter. For example: if (envImpl != null) { logger = LoggerUtils.getLogger(getClass()); } else { logger = LoggerUtils.getLoggerFormatterNeeded(); } formatter = new Formatter(.....); Then use LoggerUtils.logMsg(Logger, EnvironmentImpl, Formatter, Level, String) instead of Logger.log(Level, String)
public class LoggerUtils {
    /*
     * Environment state to be used by a logger. Must be set and released
     * per logger call.
     */
    static Map<ThreadEnvironmentImplenvMap =
        new ConcurrentHashMap<ThreadEnvironmentImpl>();
    /*
     * Formatter state to be used by a logger. Must be set and released
     * per logger call. Used by logging calls that do not have an available
     * environment.
     */
    static Map<ThreadFormatterformatterMap =
        new ConcurrentHashMap<ThreadFormatter>();
    public static final String NO_ENV = ".noEnv";
    public static final String FIXED_PREFIX = ".fixedPrefix";
    private static final String PUSH_LEVEL = ".push.level";

    
Get a logger which is configured to use the shared console, memory, and file handlers of an EnvironmentImpl and prefixes all messages with an environment identifier. Use this for classes which have a reference to an EnvironmentImpl (or RepImpl). When a logger is obtained this way, the logger should not be used directly. Instead, the wrapper methods in LoggerUtils which put and remove the environment from the envMap must be used, so that the logging output can be properly prefixed and redirected to the correct environment.
    public static Logger getLogger(Class<?> cl) {
        Logger logger = createLogger(cl.getName());
        /* Check whether the logger already has existing handlers. */
        boolean hasConsoleHandler = false;
        boolean hasFileHandler = false;
        boolean hasConfiguredHandler = false;
        /*
         * [#18277] Add null check of logger.getHandlers() because the Resin
         * app server's implementation of logging can return null instead of an
         * empty array.
         */
        Handler[] handlers = logger.getHandlers();
        if (handlers != null) {
            for (Handler h : handlers) {
                /*
                 * Intentionally check for java.util.logging.ConsoleHandler
                 * rather than ConsoleRedirectHandler, because the loggers that
                 * do not have a custom prefix use the ConsoleHandler
                 * directly. Having ConsoleRedirectHandler extend
                 * ConsoleHandler lets us have a model where the user only have
                 * to set com.sleepycat.je.util.ConsoleHandler in their logging
                 * properties file.
                 */
                if (h instanceof java.util.logging.ConsoleHandler) {
                    hasConsoleHandler = true;
                }
         
                if (h instanceof FileRedirectHandler) {
                    hasFileHandler = true;
                }
                if (h instanceof ConfiguredRedirectHandler) {
                    hasConfiguredHandler = true;
                }
            }
        }
        if (!hasConsoleHandler) {
            logger.addHandler(new ConsoleRedirectHandler());
        }
        if (!hasFileHandler) {
            logger.addHandler(new FileRedirectHandler());
        }
        if (!hasConfiguredHandler) {
            logger.addHandler(new ConfiguredRedirectHandler());
        }
        return logger;
    }

    
Get a logger which only publishes to a console handler. The logging output is prefixed in a custom way, using the formatter map to access the proper state. This should be used by a class that does not have an EnvironmentImpl, but still wishes to prepend some kind of custom prefix to the logging output. When a logger is obtained this way, the logger should not be used directly. Instead, the wrapper methods in LoggerUtils which use a Formatter parameter, and put and remove the environment from the formatterMap must be used, so that the logging output can be properly prefixed and redirected to the correct environment.
    public static Logger getLoggerFormatterNeeded(Class<?> cl) {
        /*
         * By convention, loggers that use redirect handlers are named with the
         * class name. Name logger that don't use redirecting differently, in
         * order to avoid conflicts when a single class uses both redirecting
         * and fixed prefix loggers.
         */
        Logger logger = createLogger(cl.getName() + );
        /* Add a new handler if a console handler does not already exist. */
        if (!hasConsoleHandler(logger)) {
            logger.addHandler(new FormatterRedirectHandler());
        }
        return logger;
    }
    /* Convenience method for getLoggerFixedPrefix. */
    public static Logger getLoggerFixedPrefix(Class<?> cl,
                                              String prefix) {
        return getLoggerFixedPrefix(clprefixnull);
    }

    
Get a logger that uses the generic console handler, with no attempt to use thread local state to customize the message prefix.
    public static Logger getLoggerFixedPrefix(Class<?> cl,
                                              String prefix,
                                              EnvironmentImpl envImpl) {
        /*
         * By convention, loggers that use redirect handlers are named with the
         * class name. Name logger that don't use redirecting differently, in
         * order to avoid conflicts when a single class uses both redirecting
         * and fixed prefix loggers.
         */
        Logger logger = createLogger(cl.getName() + );
        /* Check whether the logger already has this handler. */
        if (!hasConsoleHandler(logger)) {
            logger.addHandler(new com.sleepycat.je.util.ConsoleHandler
                              (new TracerFormatter(prefix), envImpl));
        }
        return logger;
    }
    /*
     * Return true if this logger already has a console handler.
     */
    private static boolean hasConsoleHandler(Logger logger) {
        /*
         * [#18277] Add null check of logger.getHandlers() because the Resin
         * app server's implementation of logging can return null instead of an
         * empty array.
         */
        Handler[] handlers = logger.getHandlers();
        if (handlers == null) {
            return false;
        }
        for (Handler h : handlers) {
            if (h instanceof java.util.logging.ConsoleHandler) {
                return true;
            }
        }
        return false;
    }
    /* Create a logger for the specified class name. */
    private static Logger createLogger(String className) {
        /*
         * No need to set level values explicitly. This is managed in the
         * standard way by java.util.logging.LogManager.
         */
        Logger logger = Logger.getLogger(className);
        /*
         * We've debated permitting the logger to use parental handlers, which
         * would permit using the standard java.util.logging policy of setting
         * tbe property com.sleepycat.je.handlers as a way of customizing
         * handlers. This was not useful because of the need to specify
         * handlers per environment, and also caused a process monitor echo
         * issue within NoSQL DB.
         */
        logger.setUseParentHandlers(false);
        return logger;
    }
    /* Get the value of a specified Logger property. */
    public static String getLoggerProperty(String property) {
        java.util.logging.LogManager mgr =
            java.util.logging.LogManager.getLogManager();
        return mgr.getProperty(property);
    }

    
Get the push level for the MemoryHandler.
    public static Level getPushLevel(String name) {
        String propertyValue = getLoggerProperty(name + );
        Level level = .;
        if (propertyValue != null) {
            level = Level.parse(propertyValue);
        }
        return level;
    }

    
Log a message using this logger. We expect that this logger is one that has been configured to expect an environment. This utility method should be used to ensure that the thread specific context is pushed before logging, and cleared afterwards.
    public static void logMsg(Logger useLogger,
                              EnvironmentImpl envImpl,
                              Level logLevel,
                              String msg) {
        /* Set thread specific context. */
        if (envImpl != null) {
            .put(Thread.currentThread(), envImpl);
        }
        try {
            useLogger.log(logLevelmsg);
        } finally {
            /* Clear thread specific context. */
            .remove(Thread.currentThread());
        }
    }

    
Use the environment logger.
    public static void envLogMsg(Level logLevel,
                                 EnvironmentImpl envImpl,
                                 String msg) {
        logMsg(envImpl.getLogger(), envImpllogLevelmsg);
    }

    
Log a message using this logger. The logger may be either one that expects to use the state in the envMap (obtained via getLogger(), or it may be one that expects to use the state in the formatter map (obtained via getLoggerFormatterNeeded(). This method checks whether the EnvironmentImpl is null or not and choses the appropriate state type to use.
    public static void logMsg(Logger useLogger,
                              EnvironmentImpl envImpl,
                              Formatter formatter,
                              Level logLevel,
                              String msg) {
        if (envImpl != null) {
            logMsg(useLoggerenvImpllogLevelmsg);
        } else {
            logMsg(useLoggerformatterlogLevelmsg);
        }
    }
    /* Some convenience methods. */
    public static void severe(Logger useLogger,
                              EnvironmentImpl envImpl,
                              String msg) {
        logMsg(useLoggerenvImpl.msg);
    }
    public static void warning(Logger useLogger,
                               EnvironmentImpl envImpl,
                               String msg) {
        logMsg(useLoggerenvImpl.msg);
    }
    public static void info(Logger useLogger,
                            EnvironmentImpl envImpl,
                            String msg) {
        logMsg(useLoggerenvImpl.msg);
    }
    public static void fine(Logger useLogger,
                            EnvironmentImpl envImpl,
                            String msg) {
        logMsg(useLoggerenvImpl.msg);
    }
    public static void finer(Logger useLogger,
                             EnvironmentImpl envImpl,
                             String msg) {
        logMsg(useLoggerenvImpl.msg);
    }
    public static void finest(Logger useLogger,
                              EnvironmentImpl envImpl,
                              String msg) {
        logMsg(useLoggerenvImpl.msg);
    }

    
Log a message with this logger. This utility method should be used in tandem with loggers obtained via getLoggerFormatterNeeded() to ensure that the thread specific Formatter is pushed before logging, and cleared afterwards.
    public static void logMsg(Logger useLogger,
                              Formatter formatter,
                              Level logLevel,
                              String msg) {
        /* Set thread specific Formatter. */
        if (formatter != null) {
            .put(Thread.currentThread(), formatter);
        }
        try {
            useLogger.log(logLevelmsg);
        } finally {
            /* Clear thread specific Formatter. */
            .remove(Thread.currentThread());
        }
    }

    
Logger method for recording an exception and stacktrace to both the java.util.logging system and the .jdb files.
    public static void traceAndLogException(EnvironmentImpl envImpl,
                                            String sourceClass,
                                            String sourceMethod,
                                            String msg,
                                            Throwable t) {
        String traceMsg = msg + "\n" + getStackTrace(t);
        .put(Thread.currentThread(), envImpl);
        try {
            envImpl.getLogger().logp
                (.sourceClasssourceMethodtraceMsg);
        } finally {
            .remove(Thread.currentThread());
        }
        Trace.trace(envImpltraceMsg);
    }

    
Records a message both to the java.util.logging loggers and through the trace system which writes to the .jdb files. The logLevel parameter only applies to the java.util.logging system. Trace messages are unconditionally written to the .jdb files. Because of that, this method should be used sparingly, for critical messages.
    public static void traceAndLog(Logger logger,
                                   EnvironmentImpl envImpl,
                                   Level logLevel,
                                   String msg) {
        logMsg(loggerenvImpllogLevelmsg);
        Trace.trace(envImplmsg);
    }

    
Return a String version of a stack trace
    public static String getStackTrace(Throwable t) {
        StringWriter sw = new StringWriter();
        t.printStackTrace(new PrintWriter(sw));
        String stackTrace = sw.toString();
        stackTrace = stackTrace.replaceAll("&lt""<");
        stackTrace = stackTrace.replaceAll("&gt"">");
        return stackTrace;
    }

    
Return the stack trace of the caller, for debugging.
    public static String getStackTrace() {
        Exception e = new Exception();
        return getStackTrace(e);
    }
    /* Get the level for ConsoleHandler and FileHandler. */
    public static Level getHandlerLevel(DbConfigManager configManager,
                                        ConfigParam param,
                                        String levelName) {
        boolean changed = false;
        /* Check if the level params are set. */
        String level = configManager.get(param);
        if (!param.getDefault().equals(level)) {
            changed = true;
        }
        /* Get the level from the java.util.logging configuration system. */
        String propertyLevel = getLoggerProperty(levelName);
        /*
         * If the params are not set, and levels are set in the properties
         * file, then set the level from properties file.
         */
        if (!changed && propertyLevel != null) {
            level = propertyLevel;
        }
        return Level.parse(level);
    }
    public static void fullThreadDump() {
        Logger logger = Logger.getAnonymousLogger();
        if (!logger.isLoggable(.)) {
            return;
        }
        Handler handler = new ConsoleRedirectHandler();
        handler.setFormatter(new TracerFormatter(null));
        logger.addHandler(handler);
        fullThreadDump(logger);
    }
    public static void fullThreadDump(Class<?> cl) {
        fullThreadDump(createLogger(cl.getName()));
    }
    public static void fullThreadDump(Logger logger) {
        fullThreadDump(loggernull);
    }
    
    public static void fullThreadDump(Logger logger,
                                      EnvironmentImpl envImpl) {
        fullThreadDump(loggerenvImpl.);
    }
    public static void fullThreadDump(Logger logger,
                                      EnvironmentImpl envImpl,
                                      Level level) {
        if (!logger.isLoggable(level)) {
            return;
        }
        Map<ThreadStackTraceElement[]> stackTraces =
            Thread.getAllStackTraces();
        for (Map.Entry<ThreadStackTraceElement[]> stme :
                 stackTraces.entrySet()) {
            logMsg(loggerenvImpllevelstme.getKey().toString());
            for (StackTraceElement ste : stme.getValue()) {
                logMsg(loggerenvImpllevel"     " + ste);
            }
        }
    }
New to GrepCode? Check out our FAQ X