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