001/* 002 * (C) Copyright 2011-2015 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Julien Carsique 016 * 017 */ 018 019package org.nuxeo.log4j; 020 021import java.io.File; 022import java.net.MalformedURLException; 023import java.util.ArrayList; 024import java.util.Enumeration; 025 026import org.apache.commons.lang.ArrayUtils; 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029import org.apache.log4j.Appender; 030import org.apache.log4j.FileAppender; 031import org.apache.log4j.Hierarchy; 032import org.apache.log4j.Level; 033import org.apache.log4j.LogManager; 034import org.apache.log4j.Logger; 035import org.apache.log4j.PatternLayout; 036import org.apache.log4j.spi.DefaultRepositorySelector; 037import org.apache.log4j.spi.Filter; 038import org.apache.log4j.spi.LoggerRepository; 039import org.apache.log4j.spi.RootLogger; 040import org.apache.log4j.varia.LevelRangeFilter; 041import org.apache.log4j.xml.DOMConfigurator; 042 043/** 044 * Provides helper methods for working with log4j 045 * 046 * @author jcarsique 047 * @since 5.4.2 048 */ 049public class Log4JHelper { 050 private static final Log log = LogFactory.getLog(Log4JHelper.class); 051 052 public static final String CONSOLE_APPENDER_NAME = "CONSOLE"; 053 054 protected static final String FULL_PATTERN_LAYOUT = "%d{HH:mm:ss,SSS} %-5p [%l] %m%n"; 055 056 protected static final String LIGHT_PATTERN_LAYOUT = "%m%n"; 057 058 /** 059 * Returns list of files produced by {@link FileAppender}s defined in a given {@link LoggerRepository}. There's no 060 * need for the log4j configuration corresponding to this repository of being active. 061 * 062 * @param loggerRepository {@link LoggerRepository} to browse looking for {@link FileAppender} 063 * @return {@link FileAppender}s configured in loggerRepository 064 */ 065 public static ArrayList<String> getFileAppendersFiles(LoggerRepository loggerRepository) { 066 ArrayList<String> logFiles = new ArrayList<>(); 067 for (Enumeration<Appender> appenders = loggerRepository.getRootLogger().getAllAppenders(); appenders.hasMoreElements();) { 068 Appender appender = appenders.nextElement(); 069 if (appender instanceof FileAppender) { 070 FileAppender fileAppender = (FileAppender) appender; 071 logFiles.add(fileAppender.getFile()); 072 } 073 } 074 Enumeration<Logger> currentLoggers = loggerRepository.getCurrentLoggers(); 075 while (currentLoggers.hasMoreElements()) { 076 Logger logger = (currentLoggers.nextElement()); 077 for (Enumeration<Appender> appenders = logger.getAllAppenders(); appenders.hasMoreElements();) { 078 Appender appender = appenders.nextElement(); 079 if (appender instanceof FileAppender) { 080 FileAppender fileAppender = (FileAppender) appender; 081 logFiles.add(fileAppender.getFile()); 082 } 083 } 084 } 085 return logFiles; 086 } 087 088 /** 089 * Creates a {@link LoggerRepository} initialized with given log4j configuration file without making this 090 * configuration active. 091 * 092 * @param log4jConfigurationFile XML Log4J configuration file to load. 093 * @return {@link LoggerRepository} initialized with log4jConfigurationFile 094 */ 095 public static LoggerRepository getNewLoggerRepository(File log4jConfigurationFile) { 096 LoggerRepository loggerRepository = null; 097 try { 098 loggerRepository = new DefaultRepositorySelector(new Hierarchy(new RootLogger(Level.DEBUG))).getLoggerRepository(); 099 if (log4jConfigurationFile == null || !log4jConfigurationFile.exists()) { 100 log.error("Missing Log4J configuration: " + log4jConfigurationFile); 101 } else { 102 new DOMConfigurator().doConfigure(log4jConfigurationFile.toURI().toURL(), loggerRepository); 103 log.debug("Log4j configuration " + log4jConfigurationFile + " successfully loaded."); 104 } 105 } catch (MalformedURLException e) { 106 log.error("Could not load " + log4jConfigurationFile, e); 107 } 108 return loggerRepository; 109 } 110 111 /** 112 * @see #getFileAppendersFiles(LoggerRepository) 113 * @param log4jConfigurationFile 114 * @return {@link FileAppender}s defined in log4jConfigurationFile 115 */ 116 public static ArrayList<String> getFileAppendersFiles(File log4jConfigurationFile) { 117 return getFileAppendersFiles(getNewLoggerRepository(log4jConfigurationFile)); 118 } 119 120 /** 121 * Set DEBUG level on the given category and the children categories. Also change the pattern layout of the given 122 * appenderName. 123 * 124 * @since 5.6 125 * @param categories Log4J categories for which to switch debug log level (comma separated values) 126 * @param debug set debug log level to true or false 127 * @param includeChildren Also set/unset debug mode on children categories 128 * @param appenderNames Appender names on which to set a detailed pattern layout. Ignored if null. 129 */ 130 public static void setDebug(String categories, boolean debug, boolean includeChildren, String[] appenderNames) { 131 setDebug(categories.split(","), debug, includeChildren, appenderNames); 132 } 133 134 /** 135 * @param categories 136 * @param debug 137 * @param includeChildren 138 * @param appenderNames 139 * @since 7.4 140 */ 141 public static void setDebug(String[] categories, boolean debug, boolean includeChildren, String[] appenderNames) { 142 Level newLevel = debug ? Level.DEBUG : Level.INFO; 143 144 // Manage categories 145 for (String category : categories) { // Create non existing loggers 146 Logger logger = Logger.getLogger(category); 147 logger.setLevel(newLevel); 148 log.info("Log level set to " + newLevel + " for: " + logger.getName()); 149 } 150 if (includeChildren) { // Also change children categories' level 151 for (Enumeration<Logger> loggers = LogManager.getCurrentLoggers(); loggers.hasMoreElements();) { 152 Logger logger = loggers.nextElement(); 153 if (logger.getLevel() == newLevel) { 154 continue; 155 } 156 for (String category : categories) { 157 if (logger.getName().startsWith(category)) { 158 logger.setLevel(newLevel); 159 log.info("Log level set to " + newLevel + " for: " + logger.getName()); 160 break; 161 } 162 } 163 } 164 } 165 166 // Manage appenders 167 if (ArrayUtils.isEmpty(appenderNames)) { 168 return; 169 } 170 for (String appenderName : appenderNames) { 171 Appender consoleAppender = Logger.getRootLogger().getAppender(appenderName); 172 if (consoleAppender != null) { 173 Filter filter = consoleAppender.getFilter(); 174 while (filter != null && !(filter instanceof LevelRangeFilter)) { 175 filter = filter.getNext(); 176 } 177 if (filter != null) { 178 LevelRangeFilter levelRangeFilter = (LevelRangeFilter) filter; 179 levelRangeFilter.setLevelMin(newLevel); 180 log.debug(String.format("Log level filter set to %s for appender %s", newLevel, appenderName)); 181 } 182 String patternLayout = debug ? FULL_PATTERN_LAYOUT : LIGHT_PATTERN_LAYOUT; 183 consoleAppender.setLayout(new PatternLayout(patternLayout)); 184 log.info(String.format("Set pattern layout of %s to %s", appenderName, patternLayout)); 185 } 186 } 187 } 188 189 /** 190 * Set DEBUG level on the given category and change pattern layout of {@link #CONSOLE_APPENDER_NAME} if defined. 191 * Children categories are unchanged. 192 * 193 * @since 5.5 194 * @param category Log4J category for which to switch debug log level 195 * @param debug set debug log level to true or false 196 * @see #setDebug(String, boolean, boolean, String[]) 197 */ 198 public static void setDebug(String category, boolean debug) { 199 setDebug(category, debug, false, new String[] { CONSOLE_APPENDER_NAME }); 200 } 201 202 /** 203 * Set "quiet" mode: set log level to WARN for the given Log4J appender. 204 * 205 * @param appenderName Log4J appender to switch to WARN 206 * @since 5.5 207 */ 208 public static void setQuiet(String appenderName) { 209 Appender appender = Logger.getRootLogger().getAppender(appenderName); 210 if (appender == null) { 211 return; 212 } 213 Filter filter = appender.getFilter(); 214 while (filter != null && !(filter instanceof LevelRangeFilter)) { 215 filter = filter.getNext(); 216 } 217 if (filter != null) { 218 LevelRangeFilter levelRangeFilter = (LevelRangeFilter) filter; 219 levelRangeFilter.setLevelMin(Level.WARN); 220 log.debug("Log level filter set to WARN for appender " + appenderName); 221 } 222 } 223 224}