/* * Copyright 2004 - 2008 Christian Sprajc. All rights reserved. * * This file is part of PowerFolder. * * PowerFolder 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. * * PowerFolder 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 PowerFolder. If not, see <http://www.gnu.org/licenses/>. * * $Id: LoggingManager.java 4734 2008-07-28 03:14:24Z harry $ */ package de.dal33t.powerfolder.util.logging; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.logging.FileHandler; import java.util.logging.Filter; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import javax.swing.text.StyledDocument; import de.dal33t.powerfolder.Constants; import de.dal33t.powerfolder.Controller; import de.dal33t.powerfolder.PreferencesEntry; import de.dal33t.powerfolder.util.FileUtils; import de.dal33t.powerfolder.util.Reject; import de.dal33t.powerfolder.util.logging.handlers.BufferedHandler; import de.dal33t.powerfolder.util.logging.handlers.ConsoleHandler; import de.dal33t.powerfolder.util.logging.handlers.DocumentHandler; /** * Class to manage logging handler. This maintains up to three handlers; * document, file and console. The file handler is only constructed when * required. root logging is adjusted to the minimum required by the handlers. * This allows Logger.isLoggable() to optimize based on root logging level. Root * logging level is never set above SEVERE, so that runtime exceptions get * handled. */ public class LoggingManager { /** Default debug directory */ private static final String DEBUG_DIR = "logs"; /** File Logging file prefix */ private static String LOGFILE_PREFIX = Constants.MISC_DIR_NAME; // "PowerFolder" private static String LOGFILE_SUFFIX = "-log.txt"; /** The document handler for the DebugPanel */ private static final DocumentHandler documentHandler; /** The console handler */ private static final ConsoleHandler consoleHandler; /** The buffer handler */ private static final BufferedHandler bufferedHandler; /** The file handler */ private static FileHandler fileHandler; /** Lock object when creating file handler */ private static final Object fileHandlerLock = new Object(); /** The document logging level */ private static Level documentLoggingLevel; /** The console logging level */ private static Level consoleLoggingLevel; /** The file logging level */ private static Level fileLoggingLevel; /** The name of the file logging file */ private static String fileLoggingFileName; /** #2585 */ private static boolean fileRotate; /** The buffered logging level */ private static Level bufferedLoggingLevel; /** * The default filter for the handlers */ private static Filter DEFAULT_FILTER = new Filter() { public boolean isLoggable(LogRecord record) { String loggerName = record.getLoggerName(); if (loggerName == null) { return false; } // Special HACK for EhCache if (loggerName.contains("EhCacheProvider") && record.getMessage() != null && record.getMessage().contains("Could not find configuration")) { return false; } if ((loggerName.startsWith("org.hibernate") || loggerName .startsWith("com.mchange")) && record.getLevel().intValue() > Level.FINE.intValue()) { if (record.getLevel() == Level.INFO) { record.setLevel(Level.FINE); } return true; } return loggerName.startsWith("de.dal33t") || loggerName.startsWith("net.sf.webdav"); } }; static { Logger rootLogger = getRootLogger(); // Switch logging nearly off until one of the handlers is configured. rootLogger.setLevel(Level.SEVERE); // Remove any default log handlers; we do our own logging as required. for (Handler handler : rootLogger.getHandlers()) { handler.flush(); handler.close(); rootLogger.removeHandler(handler); } // Create loggers, thread-safe in the static initializer. consoleHandler = new ConsoleHandler(); documentHandler = new DocumentHandler(); bufferedHandler = new BufferedHandler(200); rootLogger.setFilter(DEFAULT_FILTER); consoleHandler.setFilter(DEFAULT_FILTER); documentHandler.setFilter(DEFAULT_FILTER); bufferedHandler.setFilter(DEFAULT_FILTER); } /** * Set the console handler level. Add handler to root logger if this is the * first time. * * @param level */ public static void setConsoleLogging(Level level) { if (consoleLoggingLevel == null) { getRootLogger().addHandler(consoleHandler); } consoleLoggingLevel = level; consoleHandler.setLevel(level); setMinimumBaseLoggingLevel(); } /** * Set the document handler level. Add handler to root logger if this is the * first time. * * @param level * @param controller */ public static void setDocumentLogging(Level level, Controller controller) { if (documentLoggingLevel == null) { getRootLogger().addHandler(documentHandler); } documentLoggingLevel = level; documentHandler.setLevel(level); PreferencesEntry.DOCUMENT_LOGGING.setValue(controller, level.getName()); setMinimumBaseLoggingLevel(); } /** * Set the file handler level. Add handler to root logger if this is the * first time. Create the file handler inside a synchronized block to stop * other threads trying to access it during construction. * * @param level * @param rotate * to rotate the file every day */ public static void setFileLogging(Level level, boolean rotate) { fileLoggingLevel = level; fileRotate = rotate; if (fileHandler == null) { createFileHandler(); } if (fileHandler != null) { fileHandler.setLevel(fileLoggingLevel); } setMinimumBaseLoggingLevel(); } /** * Set the console handler level. Add handler to root logger if this is the * first time. * * @param level */ public static void setBufferedLogging(Level level) { if (bufferedLoggingLevel == null) { getRootLogger().addHandler(bufferedHandler); } bufferedLoggingLevel = level; bufferedHandler.setLevel(level); setMinimumBaseLoggingLevel(); } public static void clearBuffer() { if (bufferedHandler != null) { bufferedHandler.clear(); } } /** * Physically create the file handler. * * @param level */ private static void createFileHandler() { // Make sure nothing else tries to create the file handler concurrently. synchronized (fileHandlerLock) { try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String logFilename = LOGFILE_PREFIX; if (fileRotate) { logFilename += '-'; logFilename += sdf.format(new Date()); } logFilename += LOGFILE_SUFFIX; fileLoggingFileName = new File(getDebugDir(), FileUtils.removeInvalidFilenameChars(logFilename)) .getAbsolutePath(); fileHandler = new FileHandler(fileLoggingFileName, true); fileHandler.setFormatter(new LoggingFormatter(!fileRotate)); getRootLogger().addHandler(fileHandler); fileHandler.setFilter(DEFAULT_FILTER); } catch (IOException e) { // Duh. No file logger. e.printStackTrace(); } } } /** * @return the document handler document for display in the debug panel. */ public static StyledDocument getLogBuffer() { Reject .ifNull(documentHandler.getLogBuffer(), "DocumentHandler not set"); return documentHandler.getLogBuffer(); } /** * @return the buffered handler. */ public static BufferedHandler getBufferedHandler() { return bufferedHandler; } /** * @return if file logging is enabled. */ public static boolean isLogToFile() { return fileLoggingLevel != null; } /** * @return the document logging level. */ public static Level getDocumentLoggingLevel() { if (documentLoggingLevel == null) { return Level.OFF; } else { return documentLoggingLevel; } } public static Level getMinimumLoggingLevel() { Level min = Level.OFF; if (documentLoggingLevel != null && documentLoggingLevel.intValue() < min.intValue()) { min = documentLoggingLevel; } if (consoleLoggingLevel != null && consoleLoggingLevel.intValue() < min.intValue()) { min = consoleLoggingLevel; } if (fileLoggingLevel != null && fileLoggingLevel.intValue() < min.intValue()) { min = fileLoggingLevel; } if (bufferedLoggingLevel != null && bufferedLoggingLevel.intValue() < min.intValue()) { min = bufferedLoggingLevel; } return min; } /** * Convenience method for getting the root logger. * * @return */ private static Logger getRootLogger() { return Logger.getLogger(""); } /** * @return the directory that the file logging is written to. */ public static File getDebugDir() { File canidate = new File(Controller.getMiscFilesLocation(), DEBUG_DIR); if (canidate.exists() && canidate.isDirectory()) { return canidate; } canidate.mkdirs(); if (canidate.exists() && canidate.isDirectory()) { return canidate; } return null; } /** * Sets the file logging file name prefix. Should be the config name. * * @param prefix */ public static void setPrefix(String prefix) { assert prefix != null; LoggingManager.LOGFILE_PREFIX = prefix; } /** * @return the file logging file name. */ public static String getLoggingFileName() { synchronized (fileHandlerLock) { return fileLoggingFileName; } } /** * Set the root logging level to the highest possible, so that * Logger.isLoggable() has the desired effect in the code. */ private static void setMinimumBaseLoggingLevel() { Level level = Level.SEVERE; if (documentLoggingLevel != null && documentLoggingLevel.intValue() < level.intValue()) { level = documentLoggingLevel; } if (consoleLoggingLevel != null && consoleLoggingLevel.intValue() < level.intValue()) { level = consoleLoggingLevel; } synchronized (fileHandlerLock) { if (fileLoggingLevel != null && fileLoggingLevel.intValue() < level.intValue()) { level = fileLoggingLevel; } } if (bufferedLoggingLevel != null && bufferedLoggingLevel.intValue() < level.intValue()) { level = bufferedLoggingLevel; } getRootLogger().setLevel(level); } public static Level levelForName(String levelName) { if (levelName == null) { return null; } if (levelName.equalsIgnoreCase(Level.ALL.getName())) { return Level.ALL; } else if (levelName.equalsIgnoreCase(Level.CONFIG.getName())) { return Level.CONFIG; } else if (levelName.equalsIgnoreCase(Level.FINE.getName())) { return Level.FINE; } else if (levelName.equalsIgnoreCase(Level.FINER.getName())) { return Level.FINER; } else if (levelName.equalsIgnoreCase(Level.FINEST.getName())) { return Level.FINEST; } else if (levelName.equalsIgnoreCase(Level.INFO.getName())) { return Level.INFO; } else if (levelName.equalsIgnoreCase(Level.OFF.getName())) { return Level.OFF; } else if (levelName.equalsIgnoreCase(Level.SEVERE.getName())) { return Level.SEVERE; } else if (levelName.equalsIgnoreCase(Level.WARNING.getName()) || levelName.equalsIgnoreCase("WARN")) { return Level.WARNING; } return null; } /** * Re-set the file logging, to change the log file to a new date. */ public static void resetFileLogging() { if (fileLoggingLevel != null && fileHandler != null) { // Close off the old one first. fileHandler.flush(); fileHandler.close(); createFileHandler(); } } public static void closeFileLogging() { if (fileLoggingLevel != null && fileHandler != null) { // Close off the old one first. fileHandler.flush(); fileHandler.close(); fileHandler = null; } } /** * PFS-475 * * @param maxAgeDays */ public static void removeOldLogs(final int maxAgeDays) { File[] oldFiles = getDebugDir().listFiles(new FileFilter() { public boolean accept(File file) { if (!file.isFile()) { return false; } if (!file.getName().startsWith(LOGFILE_PREFIX)) { return false; } if (!file.getName().contains(LOGFILE_SUFFIX)) { return false; } long msOld = System.currentTimeMillis() - file.lastModified(); int daysOld = (int) (msOld / 1000 / 60 / 60 / 24); return daysOld >= maxAgeDays; } }); for (File logFile : oldFiles) { logFile.delete(); } } }