/* * Copyright (c) 2016, Metron, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Metron, Inc. nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.metsci.glimpse.util.logging; import static com.metsci.glimpse.util.io.StreamOpener.fileThenResourceOpener; import static java.lang.String.format; import static java.util.logging.Level.CONFIG; import static java.util.logging.Level.FINE; import static java.util.logging.Level.FINER; import static java.util.logging.Level.FINEST; import static java.util.logging.Level.INFO; import static java.util.logging.Level.SEVERE; import static java.util.logging.Level.WARNING; import static java.util.logging.LogManager.getLogManager; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InvalidObjectException; import java.io.ObjectStreamException; import java.io.PrintStream; import java.util.logging.ConsoleHandler; import java.util.logging.FileHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; import com.metsci.glimpse.util.io.StreamOpener; import com.metsci.glimpse.util.logging.format.Formatter; import com.metsci.glimpse.util.logging.format.TimestampingMethodNameLogFormatter; /** * @author moskowitz */ public class LoggerUtils { /** * Convenience wrapper around {@link Logger#getLogger(String)}. Uses * the fully qualified classname of the specified class as the logger * name. */ public static Logger getLogger( Class<?> clazz ) { return Logger.getLogger( clazz.getName( ) ); } /** * Traverses parents to find effective log level. */ public static Level getLevelRecursive( Logger logger ) { Level level = logger.getLevel( ); while ( level == null ) { logger = logger.getParent( ); if ( logger != null ) { level = logger.getLevel( ); } else { return null; } } return level; } /** * Prints own and parents' log levels to standard out (for debugging). */ public void dumpAncestry( Logger logger ) { Logger loggerAncestor = logger; int nUp = 0; while ( loggerAncestor != null ) { String name = loggerAncestor.getName( ); if ( name.isEmpty( ) ) { name = "<root>"; } System.out.printf( "logger ancestor %d: %s level=%s%n", nUp, name, loggerAncestor.getLevel( ) ); loggerAncestor = loggerAncestor.getParent( ); nUp++; } System.out.println( "recursive level = " + getLevelRecursive( logger ) ); } /** * Initialize Java logging to use "logging.properties" as the configuration * file and re-read the logging configuration from this file. * <p> * Note: Similar to setting * -Djava.util.logging.config.file=logging.properties on java command line. * </p> */ public static void initializeLogging( ) { initializeLogging( "logging.properties" ); } /** * Initialize Java logging to use given configuration file and re-read the * logging configuration from this file. * <p> * Note: Similar to setting * -Djava.util.logging.config.file=configurationFilename on java command * line. * </p> */ public static void initializeLogging( String configurationFilename ) { initializeLogging( configurationFilename, fileThenResourceOpener ); } /** * Initialize Java logging to use given configuration file and re-read the * logging configuration from this file. * <p> * Note: Similar to setting * -Djava.util.logging.config.file=configurationFilename on java command * line. * </p> */ public static void initializeLogging( String configurationFilename, StreamOpener streamOpener ) { try { InputStream stream = null; try { stream = streamOpener.openForRead( configurationFilename ); getLogManager( ).readConfiguration( stream ); System.setProperty( "java.util.logging.config.file", configurationFilename ); Logger logger = getLogger( LoggerUtils.class ); logger.info( "Loaded logging configuration from " + configurationFilename ); } finally { if ( stream != null ) stream.close( ); } } catch ( IOException e ) { System.err.println( LoggerUtils.class.getSimpleName( ) + ".initializeLogging: IO exception - " + e.toString( ) ); e.printStackTrace( System.err ); } } /** * In cases where a logging.properties file is too cumbersome, sets terse * formatter for console handler. * * @param level maximum logging level; set on root logger */ public static final void setTerseConsoleLogger( Level level ) { java.util.logging.Logger logger = Logger.getLogger( "" ); if ( logger == null ) return; Handler[] handlers = logger.getHandlers( ); for ( Handler h : handlers ) if ( h instanceof ConsoleHandler ) logger.removeHandler( h ); ConsoleHandler handler = new ConsoleHandler( ) { Formatter formatter = new TimestampingMethodNameLogFormatter( ); @Override public Formatter getFormatter( ) { return formatter; } }; handler.setLevel( level ); logger.addHandler( handler ); } /** * In cases where a logging.properties file is too cumbersome, sets terse * formatter for file handler. * * @param level maximum logging level; set on root logger * @throws IOException * @throws SecurityException */ public static final void addTerseFileLogger( Level level, String filename ) throws SecurityException, IOException { java.util.logging.Logger logger = Logger.getLogger( "" ); if ( logger == null ) return; FileHandler handler = new FileHandler( filename ) { Formatter formatter = new TimestampingMethodNameLogFormatter( ); @Override public Formatter getFormatter( ) { return formatter; } }; handler.setLevel( level ); logger.addHandler( handler ); } public static final void setLoggerLevel( final Level level ) { java.util.logging.Logger logger = Logger.getLogger( "" ); if ( logger == null ) return; logger.setLevel( level ); } public static void sendStdoutToLog( ) { Logger logger = Logger.getLogger( "stdout" ); LoggingOutputStream los = new LoggingOutputStream( logger, StdOutErrLevel.STDOUT ); System.setOut( new PrintStream( los, true ) ); } public static void sendStderrToLog( ) { Logger logger = Logger.getLogger( "stderr" ); LoggingOutputStream los = new LoggingOutputStream( logger, StdOutErrLevel.STDERR ); System.setErr( new PrintStream( los, true ) ); } /** * An OutputStream that writes contents to a Logger upon each call to flush() * * Original URL: https://blogs.oracle.com/nickstephen/entry/java_redirecting_system_out_and * Author gives permission for free use in blog comments section. */ public static class LoggingOutputStream extends ByteArrayOutputStream { private String lineSeparator; private Logger logger; private Level level; /** * Constructor * * @param logger Logger to write to * @param level Level at which to write the log message */ public LoggingOutputStream( Logger logger, Level level ) { super( ); this.logger = logger; this.level = level; lineSeparator = System.getProperty( "line.separator" ); } /** * upon flush() write the existing contents of the OutputStream to the * logger as a log record. * * @throws java.io.IOException in case of error */ public void flush( ) throws IOException { String record; synchronized ( this ) { super.flush( ); record = this.toString( ); super.reset( ); if ( record.length( ) == 0 || record.equals( lineSeparator ) ) { // avoid empty records return; } logger.logp( level, "", "", record ); } } } /** * Class defining 2 new Logging levels, one for STDOUT, one for STDERR, used * when multiplexing STDOUT and STDERR into the same rolling log file via * the Java Logging APIs.<br><br> * * From: http://blogs.sun.com/nickstephen/entry/java_redirecting_system_out_and */ public static class StdOutErrLevel extends Level { private static final long serialVersionUID = 2386782470168630460L; /** * Private constructor */ private StdOutErrLevel( String name, int value ) { super( name, value ); } /** * Level for STDOUT activity. */ public static Level STDOUT = new StdOutErrLevel( "STDOUT", Level.INFO.intValue( ) + 53 ); /** * Level for STDERR activity */ public static Level STDERR = new StdOutErrLevel( "STDERR", Level.INFO.intValue( ) + 54 ); /** * Method to avoid creating duplicate instances when deserializing the * object. * * @return the singleton instance of this <code>Level</code> value in * this classloader * @throws ObjectStreamException If unable to deserialize */ protected Object readResolve( ) throws ObjectStreamException { if ( this.intValue( ) == STDOUT.intValue( ) ) return STDOUT; if ( this.intValue( ) == STDERR.intValue( ) ) return STDERR; throw new InvalidObjectException( "Unknown instance :" + this ); } } /////////////// /////////////// Java logger call wrappers without throwable argument. /////////////// /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void log( Logger logger, Level level, String format, Object... args ) { if ( logger.isLoggable( level ) ) { logger.log( level, format( format, args ) ); } } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logFinest( Logger logger, String format, Object... args ) { log( logger, FINEST, format, args ); } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logFiner( Logger logger, String format, Object... args ) { log( logger, FINER, format, args ); } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logFine( Logger logger, String format, Object... args ) { log( logger, FINE, format, args ); } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logConfig( Logger logger, String format, Object... args ) { log( logger, CONFIG, format, args ); } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logInfo( Logger logger, String format, Object... args ) { log( logger, INFO, format, args ); } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logWarning( Logger logger, String format, Object... args ) { log( logger, WARNING, format, args ); } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logSevere( Logger logger, String format, Object... args ) { log( logger, SEVERE, format, args ); } /////////////// /////////////// Java logger call wrappers with throwable argument. /////////////// /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void log( Logger logger, Level level, String format, Throwable thrown, Object... args ) { if ( logger.isLoggable( level ) ) { logger.log( level, format( format, args ), thrown ); } } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logFinest( Logger logger, String format, Throwable thrown, Object... args ) { log( logger, FINEST, format, thrown, args ); } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logFiner( Logger logger, String format, Throwable thrown, Object... args ) { log( logger, FINER, format, thrown, args ); } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logFine( Logger logger, String format, Throwable thrown, Object... args ) { log( logger, FINE, format, thrown, args ); } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logConfig( Logger logger, String format, Throwable thrown, Object... args ) { log( logger, CONFIG, format, thrown, args ); } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logInfo( Logger logger, String format, Throwable thrown, Object... args ) { log( logger, INFO, format, thrown, args ); } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logWarning( Logger logger, String format, Throwable thrown, Object... args ) { log( logger, WARNING, format, thrown, args ); } /** * Wraps call to Java logger with varargs and performance optimization: no argument formatting * if unneeded. */ public static void logSevere( Logger logger, String format, Throwable thrown, Object... args ) { log( logger, SEVERE, format, thrown, args ); } }