/* * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * muCommander is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * muCommander 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.mucommander.utils; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.*; import ch.qos.logback.core.encoder.LayoutWrappingEncoder; import com.mucommander.conf.MuConfigurations; import com.mucommander.conf.MuPreference; import com.mucommander.conf.MuPreferences; import com.mucommander.ui.dialog.debug.DebugConsoleAppender; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; /** * This class manages logging issues within mucommander * * @author Maxence Bernard, Arik Hadas */ public class MuLogging { /** Levels of log printings */ public enum LogLevel { OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST; /** * This method maps logback levels to mucommander log levels * * @param logbackLevel logback log level * @return <code>LogLevel</code> corresponding to the given logback log level */ public static LogLevel valueOf(Level logbackLevel) { switch(logbackLevel.toInt()) { case ch.qos.logback.classic.Level.OFF_INT: return LogLevel.OFF; case ch.qos.logback.classic.Level.ERROR_INT: return LogLevel.SEVERE; case ch.qos.logback.classic.Level.WARN_INT: return LogLevel.WARNING; case ch.qos.logback.classic.Level.INFO_INT: return LogLevel.INFO; case ch.qos.logback.classic.Level.DEBUG_INT: return LogLevel.FINE; case ch.qos.logback.classic.Level.TRACE_INT: return LogLevel.FINEST; default: return LogLevel.OFF; } } /** * This method maps mucommander log levels to logback levels * * @return logback level corresponding to this <code>LogLevel</code> */ public Level toLogbackLevel() { switch (this) { case SEVERE: return ch.qos.logback.classic.Level.ERROR; case WARNING: return ch.qos.logback.classic.Level.WARN; case INFO: case CONFIG: return ch.qos.logback.classic.Level.INFO; case FINE: case FINER: return ch.qos.logback.classic.Level.DEBUG; case FINEST: return ch.qos.logback.classic.Level.TRACE; case OFF: default: return ch.qos.logback.classic.Level.OFF; } } } /** Appender that writes log printings to the standard console */ private static ConsoleAppender<ILoggingEvent> consoleAppender; /** Appender that writes log printings to the debug console dialog */ private static DebugConsoleAppender debugConsoleAppender; /** * Sets the level of all muCommander loggers. * * @param level the new log level */ private static void updateLogLevel(LogLevel level) { ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); logger.setLevel(level.toLogbackLevel()); } /** * Returns the log level, in mucommander terms, that match the level of a given logback logging event * * @param loggingEvent logback logging event * @return log level, in mucommander terms, that match the level of the given logback logging event */ public static LogLevel getLevel(ILoggingEvent loggingEvent) { return LogLevel.valueOf(loggingEvent.getLevel()); } /** * Returns the current log level used by all <code>org.slf4j</code> loggers. * * @return the current log level used by all <code>org.slf4j</code> loggers. */ public static LogLevel getLogLevel() { return LogLevel.valueOf(MuConfigurations.getPreferences().getVariable(MuPreference.LOG_LEVEL, MuPreferences.DEFAULT_LOG_LEVEL)); } /** * Sets the new log level to be used by all <code>org.slf4j</code> loggers, and persists it in the * application preferences. * * @param level the new log level to be used by all <code>org.slf4j</code> loggers. */ public static void setLogLevel(LogLevel level) { MuConfigurations.getPreferences().setVariable(MuPreference.LOG_LEVEL, level.toString()); updateLogLevel(level); } public static DebugConsoleAppender getDebugConsoleAppender() { return debugConsoleAppender; } public static ConsoleAppender<ILoggingEvent> getConsoleAppender() { return consoleAppender; } public static void configureLogging() throws IOException { // We're no longer using LogManager and a logging.properties file to initialize java.util.logging, because of // a limitation with Webstart limiting the use of handlers and formatters residing in the system's classpath, // i.e. built-in ones. // Get root logger ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); // we are not interested in auto-configuration LoggerContext loggerContext = rootLogger.getLoggerContext(); loggerContext.reset(); // Remove default appenders rootLogger.detachAndStopAllAppenders(); // and add ours Appender<ILoggingEvent>[] appenders = createAppenders(loggerContext); for (Appender<ILoggingEvent> appender : appenders) rootLogger.addAppender(appender); // Set the log level to the value defined in the configuration updateLogLevel(getLogLevel()); } private static Appender<ILoggingEvent>[] createAppenders(LoggerContext loggerContext) { Layout<ILoggingEvent> layout = new CustomLoggingLayout(); consoleAppender = createConsoleAppender(loggerContext, layout); debugConsoleAppender = createDebugConsoleAppender(loggerContext, layout); return new Appender[] { consoleAppender, debugConsoleAppender }; } private static ConsoleAppender<ILoggingEvent> createConsoleAppender(LoggerContext loggerContext, Layout<ILoggingEvent> layout) { ConsoleAppender<ILoggingEvent> consoleAppender = new ConsoleAppender<ILoggingEvent>(); LayoutWrappingEncoder<ILoggingEvent> encoder = new LayoutWrappingEncoder<ILoggingEvent>(); encoder.setContext(loggerContext); encoder.setLayout(layout); encoder.start(); consoleAppender.setContext(loggerContext); consoleAppender.setEncoder(encoder); consoleAppender.start(); return consoleAppender; } private static DebugConsoleAppender createDebugConsoleAppender(LoggerContext loggerContext, Layout<ILoggingEvent> layout) { DebugConsoleAppender debugConsoleAppender = new DebugConsoleAppender(layout); debugConsoleAppender.setContext(loggerContext); debugConsoleAppender.start(); return debugConsoleAppender; } private static class CustomLoggingLayout extends LayoutBase<ILoggingEvent> { private final static SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); public String doLayout(ILoggingEvent event) { StackTraceElement stackTraceElement = event.getCallerData()[0]; StringBuilder sbuf = new StringBuilder(128); sbuf.append("["); sbuf.append(SIMPLE_DATE_FORMAT.format(new Date(event.getTimeStamp()))); sbuf.append("] "); sbuf.append(getLevel(event)); sbuf.append(" "); sbuf.append(stackTraceElement.getFileName()); sbuf.append("#"); sbuf.append(stackTraceElement.getMethodName()); sbuf.append(","); sbuf.append(stackTraceElement.getLineNumber()); sbuf.append(" "); sbuf.append(event.getFormattedMessage()); sbuf.append(CoreConstants.LINE_SEPARATOR); return sbuf.toString(); } } }