/****************************************************************************** * Copyright © 2013-2016 The Nxt Core Developers. * * * * See the AUTHORS.txt, DEVELOPER-AGREEMENT.txt and LICENSE.txt files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * Nxt software, including this file, may be copied, modified, propagated, * * or distributed except according to the terms contained in the LICENSE.txt * * file. * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ package nxt.util; import nxt.Nxt; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Properties; import java.util.logging.LogManager; /** * Handle logging for the Nxt node server */ public final class Logger { /** Log event types */ public enum Event { MESSAGE, EXCEPTION } /** Log levels */ public enum Level { DEBUG, INFO, WARN, ERROR } /** Message listeners */ private static final Listeners<String, Event> messageListeners = new Listeners<>(); /** Exception listeners */ private static final Listeners<Throwable, Event> exceptionListeners = new Listeners<>(); /** Our logger instance */ private static final org.slf4j.Logger log; /** Enable stack traces */ private static final boolean enableStackTraces; /** Enable log traceback */ private static final boolean enableLogTraceback; /** * No constructor */ private Logger() {} /** * Logger initialization * * The existing Java logging configuration will be used if the Java logger has already * been initialized. Otherwise, we will configure our own log manager and log handlers. * The nxt/conf/logging-default.properties and nxt/conf/logging.properties configuration * files will be used. Entries in logging.properties will override entries in * logging-default.properties. */ static { String oldManager = System.getProperty("java.util.logging.manager"); System.setProperty("java.util.logging.manager", "nxt.util.NxtLogManager"); if (!(LogManager.getLogManager() instanceof NxtLogManager)) { System.setProperty("java.util.logging.manager", (oldManager != null ? oldManager : "java.util.logging.LogManager")); } if (! Boolean.getBoolean("nxt.doNotConfigureLogging")) { try { Properties loggingProperties = new Properties(); Nxt.loadProperties(loggingProperties, "logging-default.properties", true); Nxt.loadProperties(loggingProperties, "logging.properties", false); Nxt.updateLogFileHandler(loggingProperties); if (loggingProperties.size() > 0) { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); loggingProperties.store(outStream, "logging properties"); ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); java.util.logging.LogManager.getLogManager().readConfiguration(inStream); inStream.close(); outStream.close(); } BriefLogFormatter.init(); } catch (IOException e) { throw new RuntimeException("Error loading logging properties", e); } } log = org.slf4j.LoggerFactory.getLogger(nxt.Nxt.class); enableStackTraces = Nxt.getBooleanProperty("nxt.enableStackTraces"); enableLogTraceback = Nxt.getBooleanProperty("nxt.enableLogTraceback"); logInfoMessage("logging enabled"); } /** * Logger initialization */ public static void init() {} /** * Logger shutdown */ public static void shutdown() { if (LogManager.getLogManager() instanceof NxtLogManager) { ((NxtLogManager) LogManager.getLogManager()).nxtShutdown(); } } /** * Set the log level * * @param level Desired log level */ public static void setLevel(Level level) { java.util.logging.Logger jdkLogger = java.util.logging.Logger.getLogger(log.getName()); switch (level) { case DEBUG: jdkLogger.setLevel(java.util.logging.Level.FINE); break; case INFO: jdkLogger.setLevel(java.util.logging.Level.INFO); break; case WARN: jdkLogger.setLevel(java.util.logging.Level.WARNING); break; case ERROR: jdkLogger.setLevel(java.util.logging.Level.SEVERE); break; } } /** * Add a message listener * * @param listener Listener * @param eventType Notification event type * @return TRUE if listener added */ public static boolean addMessageListener(Listener<String> listener, Event eventType) { return messageListeners.addListener(listener, eventType); } /** * Add an exception listener * * @param listener Listener * @param eventType Notification event type * @return TRUE if listener added */ public static boolean addExceptionListener(Listener<Throwable> listener, Event eventType) { return exceptionListeners.addListener(listener, eventType); } /** * Remove a message listener * * @param listener Listener * @param eventType Notification event type * @return TRUE if listener removed */ public static boolean removeMessageListener(Listener<String> listener, Event eventType) { return messageListeners.removeListener(listener, eventType); } /** * Remove an exception listener * * @param listener Listener * @param eventType Notification event type * @return TRUE if listener removed */ public static boolean removeExceptionListener(Listener<Throwable> listener, Event eventType) { return exceptionListeners.removeListener(listener, eventType); } /** * Log a message (map to INFO) * * @param message Message */ public static void logMessage(String message) { doLog(Level.INFO, message, null); } /** * Log an exception (map to ERROR) * * @param message Message * @param exc Exception */ public static void logMessage(String message, Exception exc) { doLog(Level.ERROR, message, exc); } public static void logShutdownMessage(String message) { if (LogManager.getLogManager() instanceof NxtLogManager) { logMessage(message); } else { System.out.println(message); } } public static void logShutdownMessage(String message, Exception e) { if (LogManager.getLogManager() instanceof NxtLogManager) { logMessage(message, e); } else { System.out.println(message); System.out.println(e.toString()); } } public static boolean isErrorEnabled() { return log.isErrorEnabled(); } /** * Log an ERROR message * * @param message Message */ public static void logErrorMessage(String message) { doLog(Level.ERROR, message, null); } /** * Log an ERROR exception * * @param message Message * @param exc Exception */ public static void logErrorMessage(String message, Throwable exc) { doLog(Level.ERROR, message, exc); } public static boolean isWarningEnabled() { return log.isWarnEnabled(); } /** * Log a WARNING message * * @param message Message */ public static void logWarningMessage(String message) { doLog(Level.WARN, message, null); } /** * Log a WARNING exception * * @param message Message * @param exc Exception */ public static void logWarningMessage(String message, Throwable exc) { doLog(Level.WARN, message, exc); } public static boolean isInfoEnabled() { return log.isInfoEnabled(); } /** * Log an INFO message * * @param message Message */ public static void logInfoMessage(String message) { doLog(Level.INFO, message, null); } /** * Log an INFO message * * @param format Message format * @param args Message args */ public static void logInfoMessage(String format, Object ... args) { doLog(Level.INFO, String.format(format, args), null); } /** * Log an INFO exception * * @param message Message * @param exc Exception */ public static void logInfoMessage(String message, Throwable exc) { doLog(Level.INFO, message, exc); } public static boolean isDebugEnabled() { return log.isDebugEnabled(); } /** * Log a debug message * * @param message Message */ public static void logDebugMessage(String message) { doLog(Level.DEBUG, message, null); } /** * Log a debug message * * @param format Message format * @param args Message args */ public static void logDebugMessage(String format, Object ... args) { doLog(Level.DEBUG, String.format(format, args), null); } /** * Log a debug exception * * @param message Message * @param exc Exception */ public static void logDebugMessage(String message, Throwable exc) { doLog(Level.DEBUG, message, exc); } /** * Log the event * * @param level Level * @param message Message * @param exc Exception */ private static void doLog(Level level, String message, Throwable exc) { String logMessage = message; Throwable e = exc; // // Add caller class and method if enabled // if (enableLogTraceback) { StackTraceElement caller = Thread.currentThread().getStackTrace()[3]; String className = caller.getClassName(); int index = className.lastIndexOf('.'); if (index != -1) className = className.substring(index+1); logMessage = className + "." + caller.getMethodName() + ": " + logMessage; } // // Format the stack trace if enabled // if (e != null) { if (!enableStackTraces) { logMessage = logMessage + "\n" + exc.toString(); e = null; } } // // Log the event // switch (level) { case DEBUG: log.debug(logMessage, e); break; case INFO: log.info(logMessage, e); break; case WARN: log.warn(logMessage, e); break; case ERROR: log.error(logMessage, e); break; } // // Notify listeners // if (exc != null) exceptionListeners.notify(exc, Event.EXCEPTION); else messageListeners.notify(message, Event.MESSAGE); } }