/*
* Copyright 2013 NGDATA nv
* Copyright 2007 Outerthought bvba and Schaubroeck nv
*
* 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.
*/
package org.lilyproject.runtime.cli;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.io.File;
import java.lang.management.ManagementFactory;
import java.util.Enumeration;
import org.apache.log4j.Appender;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.jmx.HierarchyDynamicMBean;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.xml.DOMConfigurator;
import org.lilyproject.runtime.LilyRuntime;
public class Logging {
private Logging() {
}
public static void setupLogging(boolean verbose, boolean quiet, boolean classLoadingLog, String logConfLocation,
String consoleLoggingLevel, String consoleLogCategory) {
ConsoleAppender consoleAppender = new ConsoleAppender();
consoleAppender.setName("console appender");
consoleAppender.setLayout(new PatternLayout("[%-5p][%d{ABSOLUTE}][%-10.10t] %c - %m%n"));
consoleAppender.activateOptions();
// Since we set up logging very early on, it is not expected that log4j will already have loaded
// something. But just to avoid possible double configuration, reset the configuration.
LogManager.resetConfiguration();
// If the user did not specify a configuration file, default to log4j.properties in the working dir
if (logConfLocation == null) {
File defaultConf = new File("log4j.properties");
if (defaultConf.exists()) {
logConfLocation = defaultConf.getAbsolutePath();
System.out.println("Using log4j.properties from working directory: " + logConfLocation);
}
}
boolean hasConsoleAppender = false;
if (logConfLocation != null) {
if (logConfLocation.endsWith(".xml")) {
DOMConfigurator.configure(logConfLocation);
} else {
PropertyConfigurator.configure(logConfLocation);
}
// Check if the user's log configuration is already logging to the console, in order to avoid
// double logging in case the user would also use the console logging options (see below)
for (Enumeration it = Logger.getRootLogger().getAllAppenders(); it.hasMoreElements(); ) {
Appender appender = (Appender)it.nextElement();
if (appender instanceof ConsoleAppender) {
hasConsoleAppender = true;
}
}
} else {
// If there is no log configuration specified, and console logging is not enabled,
// then default to printing warn and higher messages to the console
System.out.println("Note: it is recommended to specify a log configuration. Will print error logs to the console.");
Logger logger = Logger.getRootLogger();
logger.setLevel(Level.WARN);
logger.addAppender(consoleAppender);
hasConsoleAppender = true;
}
if (consoleLoggingLevel != null) {
Level level = null;
if (consoleLoggingLevel.equalsIgnoreCase("trace")) {
level = Level.TRACE;
} else if (consoleLoggingLevel.equalsIgnoreCase("debug")) {
level = Level.DEBUG;
} else if (consoleLoggingLevel.equalsIgnoreCase("info")) {
level = Level.INFO;
} else if (consoleLoggingLevel.equalsIgnoreCase("warn")) {
level = Level.WARN;
} else if (consoleLoggingLevel.equalsIgnoreCase("error")) {
level = Level.ERROR;
} else if (consoleLoggingLevel.equalsIgnoreCase("fatal")) {
level = Level.FATAL;
} else {
System.err.println("Unrecognized log level: " + consoleLoggingLevel);
}
if (level != null) {
System.out.println("Setting console output for log level " + level.toString() + " on category " + consoleLogCategory);
Logger logger = consoleLogCategory == null ? Logger.getRootLogger() : Logger.getLogger(consoleLogCategory);
logger.setLevel(level);
if (!hasConsoleAppender) {
Logger rootLogger = Logger.getRootLogger();
rootLogger.addAppender(consoleAppender);
hasConsoleAppender = true;
}
}
}
if (quiet) {
Logger logger = Logger.getLogger("org.lilyproject.runtime");
logger.setLevel(Level.ERROR);
return;
}
if (verbose) {
Logger logger = Logger.getLogger("org.lilyproject.runtime");
logger.setLevel(Level.DEBUG);
if (!hasConsoleAppender) {
logger.addAppender(consoleAppender);
}
return;
}
Logger logger = Logger.getLogger(LilyRuntime.INFO_LOG_CATEGORY);
logger.setLevel(Level.INFO);
if (!hasConsoleAppender) {
logger.addAppender(consoleAppender);
}
if (classLoadingLog) {
logger = Logger.getLogger(LilyRuntime.CLASSLOADING_LOG_CATEGORY);
logger.setLevel(Level.INFO);
if (!hasConsoleAppender) {
logger.addAppender(consoleAppender);
}
logger = Logger.getLogger(LilyRuntime.CLASSLOADING_REPORT_CATEGORY);
logger.setLevel(Level.INFO);
if (!hasConsoleAppender) {
logger.addAppender(consoleAppender);
}
} else {
// Always print classloader warnings
logger = Logger.getLogger(LilyRuntime.CLASSLOADING_LOG_CATEGORY);
if (logger.getEffectiveLevel().toInt() > Level.WARN.toInt()) {
logger.setLevel(Level.WARN);
if (!hasConsoleAppender) {
logger.addAppender(consoleAppender);
}
}
}
}
/**
* This method was copied from Apache ZooKeeper, but it is a pretty common snippet
* that can be found in many places.
*
* Register the log4j JMX mbeans. Set environment variable
* "lilyruntime.jmx.log4j.disable" to true to disable registration.
* http://logging.apache.org/log4j/1.2/apidocs/index.html?org/apache/log4j/jmx/package-summary.html
* @throws javax.management.JMException if registration fails
*/
public static void registerLog4jMBeans() throws JMException {
if (Boolean.getBoolean("lilyruntime.jmx.log4j.disable")) {
return;
}
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
// Create and Register the top level Log4J MBean
HierarchyDynamicMBean hdm = new HierarchyDynamicMBean();
ObjectName mbo = new ObjectName("log4j:hierarchy=default");
mbs.registerMBean(hdm, mbo);
// Add the root logger to the Hierarchy MBean
Logger rootLogger = Logger.getRootLogger();
hdm.addLoggerMBean(rootLogger.getName());
// Get each logger from the Log4J Repository and add it to
// the Hierarchy MBean created above.
LoggerRepository r = LogManager.getLoggerRepository();
Enumeration enumer = r.getCurrentLoggers();
Logger logger;
while (enumer.hasMoreElements()) {
logger = (Logger) enumer.nextElement();
hdm.addLoggerMBean(logger.getName());
}
}
}