All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hsqldb.lib.FrameworkLogger Maven / Gradle / Ivy

There is a newer version: 2.7.4
Show newest version
/* Copyright (c) 2001-2019, The HSQL Development Group
 * 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 the HSQL Development Group 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 HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS 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 org.hsqldb.lib;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.lang.reflect.InvocationTargetException;

/**
 * A logging framework wrapper that supports java.util.logging and log4j.
 * 

* Logger hierarchies are stored at the Class level. * Log4j will be used if the Log4j system (not necessarily config files) are * found in the runtime classpath. * Otherwise, java.util.logging will be used. *

* This is pretty safe because for use cases where multiple hierarchies * are desired, classloader hierarchies will effectively isolate multiple * class-level Logger hierarchies. *

* Sad as it is, the java.util.logging facility lacks the most basic * developer-side and configuration-side capabilities. * Besides having a non-scalable discovery system, the designers didn't * comprehend the need for a level between WARNING and SEVERE! * Since we don't want to require log4j in Classpath, we have to live * with these constraints. *

* As with all the popular logging frameworks, if you want to capture a * stack trace, you must use the two-parameters logging methods. * I.e., you must also pass a String, or only toString() from your * throwable will be captured. *

* Usage example: * *


 * private static FrameworkLogger logger =
 *        FrameworkLogger.getLog(SqlTool.class);
 * ...
 *   logger.finer("Doing something log-worthy");
 * 
* *

* The system level property hsqldb.reconfig_logging=false is * required to avoid configuration of java.util.logging. Otherwise * configuration takes place. * * @author Blaine Simpson (blaine dot simpson at admc dot com) * @version 2.5.0 * @since 1.9.0 */ public class FrameworkLogger { /* * FrameworkLogger coders: It would be convenient to be able to log * states and such at debug level in this class. * I tentatively think that using a logger instance early in the static * lifecycle is too risky, possibly using the underlying plumbing before * the application has had a chance to customize, and perhaps before * classloaders have been re-prioritized, etc. * Could be that it all works out ok, but make sure you consider all * situations before logging with FrameworkLogger instances here. * This is one reason why there are a couple uses of System.err below. */ /** * Utility method for integrators. Returns a string representation of the * active Logger instance keys. * *

Not named similar to 'toString' to avoid ambiguity with instance * method toString.

* * @return String */ public static synchronized String report() { return new StringBuilder().append(loggerInstances.size()).append( " logger instances: ").append( loggerInstances.keySet()).toString(); } static private Map loggerInstances = new HashMap(); static private Map jdkToLog4jLevels = new HashMap(); static private Method log4jGetLogger; static private Method log4jLogMethod; static private boolean callerFqcnAvailable = false; private Object log4jLogger; private Logger jdkLogger; // No need for more than one static, since we have only one console static private boolean noopMode; // If true, then logging calls do nothing static { try { reconfigure(); } catch (SecurityException e) {} } /** * Frees Logger(s), if any, with the specified category, or that begins with * the specified prefix + dot. * *

Note that as of today, this depends on the underlying logging * framework implementation to release the underlying Logger instances. JUL * in Sun's JVM uses weak references, so that should be fine. Log4j as of * today seems to use strong references (and no API hooks to free anything), * so this method will probably have little benefit for Log4j. * * @param prefixToZap String */ public static synchronized void clearLoggers(String prefixToZap) { Set targetKeys = new HashSet(); java.util.Iterator it = loggerInstances.keySet().iterator(); String k; String dottedPrefix = prefixToZap + '.'; while (it.hasNext()) { k = (String) it.next(); if (k.equals(prefixToZap) || k.startsWith(dottedPrefix)) { targetKeys.add(k); } } loggerInstances.keySet().removeAll(targetKeys); } private static synchronized void populateJdkToLog4jLevels(String classString) throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Method log4jToLevel = Class.forName(classString).getMethod( "toLevel", new Class[]{ String.class }); jdkToLog4jLevels.put(Level.ALL, log4jToLevel.invoke(null, new Object[]{ "ALL" })); jdkToLog4jLevels.put(Level.FINER, log4jToLevel.invoke(null, new Object[]{ "DEBUG" })); jdkToLog4jLevels.put(Level.WARNING, log4jToLevel.invoke(null, new Object[]{ "ERROR" })); jdkToLog4jLevels.put(Level.SEVERE, log4jToLevel.invoke(null, new Object[]{ "FATAL" })); jdkToLog4jLevels.put(Level.INFO, log4jToLevel.invoke(null, new Object[]{ "INFO" })); jdkToLog4jLevels.put(Level.OFF, log4jToLevel.invoke(null, new Object[]{ "OFF" })); jdkToLog4jLevels.put(Level.FINEST, log4jToLevel.invoke(null, new Object[]{ "TRACE" })); jdkToLog4jLevels.put(Level.WARNING, log4jToLevel.invoke(null, new Object[]{ "WARN" })); } static void reconfigure() { noopMode = false; Class log4jLoggerClass = null; Class log4jManagerClass = null; loggerInstances.clear(); jdkToLog4jLevels.clear(); log4jGetLogger = null; log4jLogMethod = null; callerFqcnAvailable = false; // Precedence: // 1) Use log4j v2 if available and class initialization succeeds // 2) Use log4j v1 if available and class initialization succeeds // 3) JUL try { // log4j v2 available? log4jLoggerClass = Class.forName( "org.apache.logging.log4j.Logger"); log4jManagerClass = Class.forName( "org.apache.logging.log4j.LogManager"); } catch (Exception e) { // The class will only load successfully if Log4j v2 thinks it is // in usable state. // Intentionally empty. } // Attempt to configure log4j v2 if (log4jLoggerClass != null) { try { populateJdkToLog4jLevels("org.apache.logging.log4j.Level"); log4jLogMethod = log4jLoggerClass.getMethod("log", new Class[] { Class.forName("org.apache.logging.log4j.Level"), Object.class, Throwable.class }); log4jGetLogger = log4jManagerClass.getMethod("getLogger", new Class[]{ String.class }); // This last object is what we toggle on to generate either // Log4j or Jdk Logger objects (to wrap). return; // Success for Log4j v2 } catch (Exception e) { // This is an unexpected problem, because our Log4j try block will // only be attempted if Log4j itself initialized (even if it // successfully initialized with warnings due to bad config). try { System.err.println( " failure " + "instantiating configured Log4j v2 system: " + e); // It's possible we don't have write access to System.err. } catch (Throwable t) { // Intentionally empty. We tried our best to report problem, // but don't want to throw and prevent JUL from working. } } } // Reset log4jLoggerClass = null; log4jManagerClass = null; log4jLogMethod = null; log4jGetLogger = null; jdkToLog4jLevels.clear(); try { // log4j v1 available? log4jLoggerClass = Class.forName("org.apache.log4j.Logger"); log4jManagerClass = log4jLoggerClass; } catch (Exception e) { // The class will only load successfully if Log4j v1 thinks it is // in usable state. // Intentionally empty. } // Attempt to configure log4j v1 if (log4jLoggerClass != null) { try { populateJdkToLog4jLevels("org.apache.log4j.Level"); log4jLogMethod = log4jLoggerClass.getMethod("log", new Class[] { String.class, Class.forName("org.apache.log4j.Priority"), Object.class, Throwable.class }); log4jGetLogger = log4jManagerClass.getMethod("getLogger", new Class[]{ String.class }); // This last object is what we toggle on to generate either // Log4j or Jdk Logger objects (to wrap). callerFqcnAvailable = true; return; // Success for Log4j v1 } catch (Exception e) { // This is an unexpected problem, because our Log4j try block will // only be attempted if Log4j itself initialized (even if it // successfully initialized with warnings due to bad config). try { System.err.println( " failure " + "instantiating configured Log4j v1 system: " + e); // It's possible we don't have write access to System.err. } catch (Throwable t) { // Intentionally empty. We tried our best to report problem, // but don't want to throw and prevent JUL from working. } } } // Reset log4jLoggerClass = null; log4jManagerClass = null; log4jLogMethod = null; log4jGetLogger = null; callerFqcnAvailable = false; jdkToLog4jLevels.clear(); String propVal = System.getProperty("hsqldb.reconfig_logging"); if (propVal != null && propVal.equalsIgnoreCase("false")) { return; } InputStream istream = null; Logger cmdlineLogger; try { LogManager lm = LogManager.getLogManager(); String path = "/org/hsqldb/resources/jdklogging-default.properties"; if (isDefaultJdkConfig()) { lm.reset(); ConsoleHandler consoleHandler = new ConsoleHandler(); consoleHandler.setFormatter( new BasicTextJdkLogFormatter(false)); consoleHandler.setLevel(Level.INFO); istream = FrameworkLogger.class.getResourceAsStream(path); lm.readConfiguration(istream); cmdlineLogger = Logger.getLogger("org.hsqldb.cmdline"); cmdlineLogger.addHandler(consoleHandler); cmdlineLogger.setUseParentHandlers(false); } else { // Do not intervene. Use JDK logging exactly as configured // by user. lm.readConfiguration(); // The only bad thing about doing this is that if the app // has programmatically changed the logging config after // starting the program but before using FrameworkLogger, // we will clobber those customizations. // Set sys srop 'hsqldb.reconfig_logging' to false to // prevent this. } } catch (Exception e) { noopMode = true; System.err.println( " failure initializing JDK logging system. " + "Continuing without Application logging."); e.printStackTrace(); } finally { if (istream != null) { try { istream.close(); } catch (IOException ioe) { System.err.println("Failed to close logging input stream: " + ioe); } } } } /** * User may not use the constructor. * * @param s String */ private FrameworkLogger(String s) { if (!noopMode) { if (log4jGetLogger == null) { jdkLogger = Logger.getLogger(s); } else { try { log4jLogger = log4jGetLogger.invoke(null, new Object[]{ s }); } catch (Exception e) { throw new RuntimeException( "Failed to instantiate Log4j Logger", e); } } } synchronized (FrameworkLogger.class) { loggerInstances.put(s, this); } } /** * User's entry-point into this logging system.

You normally want to * work with static (class-level) pointers to logger instances, for * performance efficiency. See the class-level JavaDoc for a usage example. * * @see FrameworkLogger * @param c Class * @return FrameworkLogger */ public static FrameworkLogger getLog(Class c) { return getLog(c.getName()); } /** * This method just defers to the getLog(Class) method unless default (no * local configuration) JDK logging is being used; In that case, this method * assures that the returned logger has an associated FileHander using the * supplied String identifier. * * @param c Class * @param contextId String * @return FrameworkLogger */ public static FrameworkLogger getLog(Class c, String contextId) { return (contextId == null) ? getLog(c) : getLog(contextId + '.' + c.getName()); } /** * This method just defers to the getLog(String) method unless default (no * local configuration) JDK logging is being used; In that case, this method * assures that the returned logger has an associated FileHander using the * supplied String identifier. * * @param baseId String * @param contextId String * @return FrameworkLogger */ public static FrameworkLogger getLog(String baseId, String contextId) { return (contextId == null) ? getLog(baseId) : getLog(contextId + '.' + baseId); } /** * Alternative entry-point into this logging system, for cases where you * want to share a single logger instance among multiple classes, or you * want to use multiple logger instances from a single class. * * @see #getLog(Class) * @param s String * @return FrameworkLogger */ public static synchronized FrameworkLogger getLog(String s) { if (loggerInstances.containsKey(s)) { return (FrameworkLogger) loggerInstances.get(s); } return new FrameworkLogger(s); } /** * Just like FrameworkLogger.log(Level, String), * but also logs a stack trace. * * @param level java.util.logging.Level level to filter and log at * @param message Message to be logged * @param t Throwable whose stack trace will be logged. * @see #log(Level, String) * @see Logger#log(Level, String) * @see Level */ public void log(Level level, String message, Throwable t) { privlog(level, message, t, 2, FrameworkLogger.class); } /** * The "priv" prefix is historical. This is for special usage when you need * to modify the reported call stack. If you don't know that you want to do * this, then you should not use this method. * * @param level Level * @param message String * @param t Throwable * @param revertMethods int * @param skipClass Class */ public void privlog(Level level, String message, Throwable t, int revertMethods, Class skipClass) { if (noopMode) { return; } if (log4jLogger == null) { StackTraceElement[] elements = new Throwable().getStackTrace(); String c = ""; String m = ""; if (elements.length > revertMethods) { c = elements[revertMethods].getClassName(); m = elements[revertMethods].getMethodName(); } if (t == null) { jdkLogger.logp(level, c, m, message); } else { jdkLogger.logp(level, c, m, message, t); } } else { try { log4jLogMethod.invoke(log4jLogger, callerFqcnAvailable ? new Object[] { skipClass.getName(), jdkToLog4jLevels.get(level), message, t} : new Object[] { jdkToLog4jLevels.get(level), message, t} ); } catch (Exception e) { throw new RuntimeException( "Logging failed when attempting to log: " + message, e); } } } public void enduserlog(Level level, String message) { /* This method is SqlTool-specific, which is where this class began at. * Need to move this back there, but it needs access to the logging * structures private to this class. Thinking... */ if (noopMode) { return; } if (log4jLogger == null) { String c = FrameworkLogger.class.getName(); String m = "\\l"; jdkLogger.logp(level, c, m, message); } else { try { log4jLogMethod.invoke(log4jLogger, callerFqcnAvailable ? new Object[] { FrameworkLogger.class.getName(), jdkToLog4jLevels.get(level), message, null} : new Object[] { jdkToLog4jLevels.get(level), message, null} ); // Test where SqlFile correct here. } catch (Exception e) { throw new RuntimeException( "Logging failed when attempting to log: " + message, e); } } } // Wrappers /** * @param level java.util.logging.Level level to filter and log at * @param message Message to be logged * @see Logger#log(Level, String) * @see Level */ public void log(Level level, String message) { privlog(level, message, null, 2, FrameworkLogger.class); } /** * @param message Message to be logged * @see Logger#finer(String) */ public void finer(String message) { privlog(Level.FINER, message, null, 2, FrameworkLogger.class); } /** * @param message Message to be logged * @see Logger#warning(String) */ public void warning(String message) { privlog(Level.WARNING, message, null, 2, FrameworkLogger.class); } /** * @param message Message to be logged * @see Logger#severe(String) */ public void severe(String message) { privlog(Level.SEVERE, message, null, 2, FrameworkLogger.class); } /** * @param message Message to be logged * @see Logger#info(String) */ public void info(String message) { privlog(Level.INFO, message, null, 2, FrameworkLogger.class); } /** * @param message Message to be logged * @see Logger#finest(String) */ public void finest(String message) { privlog(Level.FINEST, message, null, 2, FrameworkLogger.class); } /** * This is just a wrapper for FrameworkLogger.warning(), because * java.util.logging lacks a method for this critical purpose. * * @param message Message to be logged * @see #warning(String) */ public void error(String message) { privlog(Level.WARNING, message, null, 2, FrameworkLogger.class); } /** * Just like FrameworkLogger.finer(String), but also logs a stack trace. * * @param message String * @param t Throwable whose stack trace will be logged. * @see #finer(String) */ public void finer(String message, Throwable t) { privlog(Level.FINER, message, t, 2, FrameworkLogger.class); } /** * Just like FrameworkLogger.warning(String), but also logs a stack trace. * * @param message String * @param t Throwable whose stack trace will be logged. * @see #warning(String) */ public void warning(String message, Throwable t) { privlog(Level.WARNING, message, t, 2, FrameworkLogger.class); } /** * Just like FrameworkLogger.severe(String), but also logs a stack trace. * * @param message String * @param t Throwable whose stack trace will be logged. * @see #severe(String) */ public void severe(String message, Throwable t) { privlog(Level.SEVERE, message, t, 2, FrameworkLogger.class); } /** * Just like FrameworkLogger.info(String), but also logs a stack trace. * * @param message String * @param t Throwable whose stack trace will be logged. * @see #info(String) */ public void info(String message, Throwable t) { privlog(Level.INFO, message, t, 2, FrameworkLogger.class); } /** * Just like FrameworkLogger.finest(String), but also logs a stack trace. * * @param message String * @param t Throwable whose stack trace will be logged. * @see #finest(String) */ public void finest(String message, Throwable t) { privlog(Level.FINEST, message, t, 2, FrameworkLogger.class); } /** * Just like FrameworkLogger.error(String), but also logs a stack trace. * * @param message String * @param t Throwable whose stack trace will be logged. * @see #error(String) */ public void error(String message, Throwable t) { privlog(Level.WARNING, message, t, 2, FrameworkLogger.class); } /** * Whether this JVM is configured with java.util.logging defaults. If the * JRE-provided config file is not in the expected place, then we return * false. * * @return boolean */ public static boolean isDefaultJdkConfig() { File globalCfgFile = new File(System.getProperty("java.home"), "lib/logging.properties"); if (!globalCfgFile.isFile()) { return false; } FileInputStream fis = null; LogManager lm = LogManager.getLogManager(); try { fis = new FileInputStream(globalCfgFile); Properties defaultProps = new Properties(); defaultProps.load(fis); Enumeration names = defaultProps.propertyNames(); int i = 0; String name; String liveVal; while (names.hasMoreElements()) { i++; name = (String) names.nextElement(); liveVal = lm.getProperty(name); if (liveVal == null) { return false; } if (!lm.getProperty(name).equals(liveVal)) { return false; } } return true; } catch (IOException ioe) { return false; } finally { if (fis != null) { try { fis.close(); } catch (IOException ioe) { // Intentional no-op } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy