/* * (C) Copyright 2011-2015 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Julien Carsique * */ package org.nuxeo.log4j; import java.io.File; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Enumeration; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.log4j.Appender; import org.apache.log4j.FileAppender; import org.apache.log4j.Hierarchy; import org.apache.log4j.Level; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; import org.apache.log4j.spi.DefaultRepositorySelector; import org.apache.log4j.spi.Filter; import org.apache.log4j.spi.LoggerRepository; import org.apache.log4j.spi.RootLogger; import org.apache.log4j.varia.LevelRangeFilter; import org.apache.log4j.xml.DOMConfigurator; /** * Provides helper methods for working with log4j * * @author jcarsique * @since 5.4.2 */ public class Log4JHelper { private static final Log log = LogFactory.getLog(Log4JHelper.class); public static final String CONSOLE_APPENDER_NAME = "CONSOLE"; protected static final String FULL_PATTERN_LAYOUT = "%d{HH:mm:ss,SSS} %-5p [%l] %m%n"; protected static final String LIGHT_PATTERN_LAYOUT = "%m%n"; /** * Returns list of files produced by {@link FileAppender}s defined in a given {@link LoggerRepository}. There's no * need for the log4j configuration corresponding to this repository of being active. * * @param loggerRepository {@link LoggerRepository} to browse looking for {@link FileAppender} * @return {@link FileAppender}s configured in loggerRepository */ public static ArrayList<String> getFileAppendersFiles(LoggerRepository loggerRepository) { ArrayList<String> logFiles = new ArrayList<>(); for (Enumeration<Appender> appenders = loggerRepository.getRootLogger().getAllAppenders(); appenders.hasMoreElements();) { Appender appender = appenders.nextElement(); if (appender instanceof FileAppender) { FileAppender fileAppender = (FileAppender) appender; logFiles.add(fileAppender.getFile()); } } Enumeration<Logger> currentLoggers = loggerRepository.getCurrentLoggers(); while (currentLoggers.hasMoreElements()) { Logger logger = (currentLoggers.nextElement()); for (Enumeration<Appender> appenders = logger.getAllAppenders(); appenders.hasMoreElements();) { Appender appender = appenders.nextElement(); if (appender instanceof FileAppender) { FileAppender fileAppender = (FileAppender) appender; logFiles.add(fileAppender.getFile()); } } } return logFiles; } /** * Creates a {@link LoggerRepository} initialized with given log4j configuration file without making this * configuration active. * * @param log4jConfigurationFile XML Log4J configuration file to load. * @return {@link LoggerRepository} initialized with log4jConfigurationFile */ public static LoggerRepository getNewLoggerRepository(File log4jConfigurationFile) { LoggerRepository loggerRepository = null; try { loggerRepository = new DefaultRepositorySelector(new Hierarchy(new RootLogger(Level.DEBUG))).getLoggerRepository(); if (log4jConfigurationFile == null || !log4jConfigurationFile.exists()) { log.error("Missing Log4J configuration: " + log4jConfigurationFile); } else { new DOMConfigurator().doConfigure(log4jConfigurationFile.toURI().toURL(), loggerRepository); log.debug("Log4j configuration " + log4jConfigurationFile + " successfully loaded."); } } catch (MalformedURLException e) { log.error("Could not load " + log4jConfigurationFile, e); } return loggerRepository; } /** * @see #getFileAppendersFiles(LoggerRepository) * @param log4jConfigurationFile * @return {@link FileAppender}s defined in log4jConfigurationFile */ public static ArrayList<String> getFileAppendersFiles(File log4jConfigurationFile) { return getFileAppendersFiles(getNewLoggerRepository(log4jConfigurationFile)); } /** * Set DEBUG level on the given category and the children categories. Also change the pattern layout of the given * appenderName. * * @since 5.6 * @param categories Log4J categories for which to switch debug log level (comma separated values) * @param debug set debug log level to true or false * @param includeChildren Also set/unset debug mode on children categories * @param appenderNames Appender names on which to set a detailed pattern layout. Ignored if null. */ public static void setDebug(String categories, boolean debug, boolean includeChildren, String[] appenderNames) { setDebug(categories.split(","), debug, includeChildren, appenderNames); } /** * @param categories * @param debug * @param includeChildren * @param appenderNames * @since 7.4 */ public static void setDebug(String[] categories, boolean debug, boolean includeChildren, String[] appenderNames) { Level newLevel = debug ? Level.DEBUG : Level.INFO; // Manage categories for (String category : categories) { // Create non existing loggers Logger logger = Logger.getLogger(category); logger.setLevel(newLevel); log.info("Log level set to " + newLevel + " for: " + logger.getName()); } if (includeChildren) { // Also change children categories' level for (Enumeration<Logger> loggers = LogManager.getCurrentLoggers(); loggers.hasMoreElements();) { Logger logger = loggers.nextElement(); if (logger.getLevel() == newLevel) { continue; } for (String category : categories) { if (logger.getName().startsWith(category)) { logger.setLevel(newLevel); log.info("Log level set to " + newLevel + " for: " + logger.getName()); break; } } } } // Manage appenders if (ArrayUtils.isEmpty(appenderNames)) { return; } for (String appenderName : appenderNames) { Appender consoleAppender = Logger.getRootLogger().getAppender(appenderName); if (consoleAppender != null) { Filter filter = consoleAppender.getFilter(); while (filter != null && !(filter instanceof LevelRangeFilter)) { filter = filter.getNext(); } if (filter != null) { LevelRangeFilter levelRangeFilter = (LevelRangeFilter) filter; levelRangeFilter.setLevelMin(newLevel); log.debug(String.format("Log level filter set to %s for appender %s", newLevel, appenderName)); } String patternLayout = debug ? FULL_PATTERN_LAYOUT : LIGHT_PATTERN_LAYOUT; consoleAppender.setLayout(new PatternLayout(patternLayout)); log.info(String.format("Set pattern layout of %s to %s", appenderName, patternLayout)); } } } /** * Set DEBUG level on the given category and change pattern layout of {@link #CONSOLE_APPENDER_NAME} if defined. * Children categories are unchanged. * * @since 5.5 * @param category Log4J category for which to switch debug log level * @param debug set debug log level to true or false * @see #setDebug(String, boolean, boolean, String[]) */ public static void setDebug(String category, boolean debug) { setDebug(category, debug, false, new String[] { CONSOLE_APPENDER_NAME }); } /** * Set "quiet" mode: set log level to WARN for the given Log4J appender. * * @param appenderName Log4J appender to switch to WARN * @since 5.5 */ public static void setQuiet(String appenderName) { Appender appender = Logger.getRootLogger().getAppender(appenderName); if (appender == null) { return; } Filter filter = appender.getFilter(); while (filter != null && !(filter instanceof LevelRangeFilter)) { filter = filter.getNext(); } if (filter != null) { LevelRangeFilter levelRangeFilter = (LevelRangeFilter) filter; levelRangeFilter.setLevelMin(Level.WARN); log.debug("Log level filter set to WARN for appender " + appenderName); } } }