| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.oozie.util; |
| |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * The <code>XLog</code> class extends the functionality of the Apache common-logging <code>Log</code> interface. <p> |
| * It provides common prefix support, message templating with variable parameters and selective tee logging to multiple |
| * logs. <p> It provides also the LogFactory functionality. |
| */ |
| public class XLog implements Log { |
| |
| public static final String INSTRUMENTATION_LOG_NAME = "oozieinstrumentation"; |
| |
| /** |
| * <code>LogInfo</code> stores contextual information to create log prefixes. <p> <code>LogInfo</code> uses a |
| * <code>ThreadLocal</code> to propagate the context. <p> <code>LogInfo</code> context parameters are configurable |
| * singletons. |
| */ |
| public static class Info { |
| private static String template = ""; |
| private String prefix = ""; |
| private static List<String> parameterNames = new ArrayList<String>(); |
| |
| private static ThreadLocal<Info> tlLogInfo = new ThreadLocal<Info>() { |
| @Override |
| protected Info initialValue() { |
| return new Info(); |
| } |
| |
| }; |
| |
| /** |
| * Define a <code>LogInfo</code> context parameter. <p> The parameter name and its contextual value will be |
| * used to create all prefixes. |
| * |
| * @param name name of the context parameter. |
| */ |
| public static void defineParameter(String name) { |
| ParamChecker.notEmpty(name, "name"); |
| int count = parameterNames.size(); |
| if (count > 0) { |
| template += " "; |
| } |
| template += name + "[{" + count + "}]"; |
| parameterNames.add(name); |
| } |
| |
| /** |
| * Remove all defined context parameters. <p> |
| */ |
| public static void reset() { |
| template = ""; |
| parameterNames.clear(); |
| } |
| |
| /** |
| * Return the <code>LogInfo</code> instance in context. |
| * |
| * @return The thread local instance of LogInfo |
| */ |
| public static Info get() { |
| return tlLogInfo.get(); |
| } |
| |
| /** |
| * Remove the <code>LogInfo</code> instance in context. |
| */ |
| public static void remove() { |
| tlLogInfo.remove(); |
| } |
| |
| private Map<String, String> parameters = new HashMap<String, String>(); |
| |
| /** |
| * Constructs an empty LogInfo. |
| */ |
| public Info() { |
| } |
| |
| |
| /** |
| * Construct a new LogInfo object from an existing one. |
| * |
| * @param logInfo LogInfo object to copy parameters from. |
| */ |
| public Info(Info logInfo) { |
| setParameters(logInfo); |
| } |
| |
| /** |
| * Clear all parameters set in this logInfo instance. |
| */ |
| public void clear() { |
| parameters.clear(); |
| resetPrefix(); |
| } |
| |
| /** |
| * Set a parameter value in the <code>LogInfo</code> context. |
| * |
| * @param name parameter name. |
| * @param value parameter value. |
| */ |
| public void setParameter(String name, String value) { |
| if (!parameterNames.contains(name)) { |
| throw new IllegalArgumentException(format("Parameter[{0}] not defined", name)); |
| } |
| parameters.put(name, value); |
| } |
| |
| /** |
| * Returns the specified parameter. |
| * |
| * @param name parameter name. |
| * @return the parameter value. |
| */ |
| public String getParameter(String name) { |
| return parameters.get(name); |
| } |
| |
| /** |
| * Clear a parameter value from the <code>LogInfo</code> context. |
| * |
| * @param name parameter name. |
| */ |
| public void clearParameter(String name) { |
| if (!parameterNames.contains(name)) { |
| throw new IllegalArgumentException(format("Parameter[{0}] not defined", name)); |
| } |
| parameters.remove(name); |
| } |
| |
| /** |
| * Set all the parameter values from the given <code>LogInfo</code>. |
| * |
| * @param logInfo <code>LogInfo</code> to copy all parameter values from. |
| */ |
| public void setParameters(Info logInfo) { |
| parameters.clear(); |
| parameters.putAll(logInfo.parameters); |
| } |
| |
| /** |
| * Create the <code>LogInfo</code> prefix using the current parameter values. |
| * |
| * @return the <code>LogInfo</code> prefix. |
| */ |
| public String createPrefix() { |
| String[] params = new String[parameterNames.size()]; |
| for (int i = 0; i < params.length; i++) { |
| params[i] = parameters.get(parameterNames.get(i)); |
| if (params[i] == null) { |
| params[i] = "-"; |
| } |
| } |
| return MessageFormat.format(template, (Object[]) params); |
| } |
| |
| public String resetPrefix() { |
| return prefix = createPrefix(); |
| } |
| |
| public String getPrefix() { |
| return prefix; |
| } |
| |
| |
| } |
| |
| /** |
| * Return the named logger. |
| * |
| * @param name logger name. |
| * @return the named logger. |
| */ |
| public static XLog getLog(String name) { |
| return new XLog(LogFactory.getLog(name)); |
| } |
| |
| /** |
| * Return the named logger. |
| * |
| * @param clazz from which the logger name will be derived. |
| * @return the named logger. |
| */ |
| public static XLog getLog(Class clazz) { |
| return new XLog(LogFactory.getLog(clazz)); |
| } |
| |
| /** |
| * Reset the logger prefix |
| * |
| * @param log the named logger |
| * @return the named logger with reset prefix |
| */ |
| public static XLog resetPrefix(XLog log) { |
| log.setMsgPrefix(Info.get().createPrefix()); |
| return log; |
| } |
| |
| /** |
| * Mask for logging to the standard log. |
| */ |
| public static final int STD = 1; |
| |
| /** |
| * Mask for tee logging to the OPS log. |
| */ |
| public static final int OPS = 4; |
| |
| private static final int ALL = STD | OPS; |
| |
| private static final int[] LOGGER_MASKS = {STD, OPS}; |
| |
| //package private for testing purposes. |
| Log[] loggers; |
| |
| private String prefix = null; |
| |
| /** |
| * Create a <code>XLog</code> with no prefix. |
| * |
| * @param log Log instance to use for logging. |
| */ |
| public XLog(Log log) { |
| loggers = new Log[2]; |
| loggers[0] = log; |
| loggers[1] = LogFactory.getLog("oozieops"); |
| } |
| |
| /** |
| * Return the common prefix. |
| * |
| * @return the common prefix. |
| */ |
| public String getMsgPrefix() { |
| return prefix; |
| } |
| |
| /** |
| * Set the common prefix. |
| * |
| * @param prefix the common prefix to set. |
| */ |
| public void setMsgPrefix(String prefix) { |
| this.prefix = prefix; |
| } |
| |
| //All the methods from the commonsLogging Log interface will log to the default logger only. |
| |
| /** |
| * Log a debug message to the common <code>Log</code>. |
| * |
| * @param o message. |
| */ |
| @Override |
| public void debug(Object o) { |
| log(Level.DEBUG, STD, "{0}", o); |
| } |
| |
| /** |
| * Log a debug message and <code>Exception</code> to the common <code>Log</code>. |
| * |
| * @param o message. |
| * @param throwable exception. |
| */ |
| @Override |
| public void debug(Object o, Throwable throwable) { |
| log(Level.DEBUG, STD, "{0}", o, throwable); |
| } |
| |
| /** |
| * Log a error message to the common <code>Log</code>. |
| * |
| * @param o message. |
| */ |
| @Override |
| public void error(Object o) { |
| log(Level.ERROR, STD, "{0}", o); |
| } |
| |
| /** |
| * Log a error message and <code>Exception</code> to the common <code>Log</code>. |
| * |
| * @param o message. |
| * @param throwable exception. |
| */ |
| @Override |
| public void error(Object o, Throwable throwable) { |
| log(Level.ERROR, STD, "{0}", o, throwable); |
| } |
| |
| /** |
| * Log a fatal message to the common <code>Log</code>. |
| * |
| * @param o message. |
| */ |
| @Override |
| public void fatal(Object o) { |
| log(Level.FATAL, STD, "{0}", o); |
| } |
| |
| /** |
| * Log a fatal message and <code>Exception</code> to the common <code>Log</code>. |
| * |
| * @param o message. |
| * @param throwable exception. |
| */ |
| @Override |
| public void fatal(Object o, Throwable throwable) { |
| log(Level.FATAL, STD, "{0}", o, throwable); |
| } |
| |
| /** |
| * Log a info message to the common <code>Log</code>. |
| * |
| * @param o message. |
| */ |
| @Override |
| public void info(Object o) { |
| log(Level.INFO, STD, "{0}", o); |
| } |
| |
| /** |
| * Log a info message and <code>Exception</code> to the common <code>Log</code>. |
| * |
| * @param o message. |
| * @param throwable exception. |
| */ |
| @Override |
| public void info(Object o, Throwable throwable) { |
| log(Level.INFO, STD, "{0}", o, throwable); |
| } |
| |
| /** |
| * Log a trace message to the common <code>Log</code>. |
| * |
| * @param o message. |
| */ |
| @Override |
| public void trace(Object o) { |
| log(Level.TRACE, STD, "{0}", o); |
| } |
| |
| /** |
| * Log a trace message and <code>Exception</code> to the common <code>Log</code>. |
| * |
| * @param o message. |
| * @param throwable exception. |
| */ |
| @Override |
| public void trace(Object o, Throwable throwable) { |
| log(Level.TRACE, STD, "{0}", o, throwable); |
| } |
| |
| /** |
| * Log a warn message to the common <code>Log</code>. |
| * |
| * @param o message. |
| */ |
| @Override |
| public void warn(Object o) { |
| log(Level.WARN, STD, "{0}", o); |
| } |
| |
| /** |
| * Log a warn message and <code>Exception</code> to the common <code>Log</code>. |
| * |
| * @param o message. |
| * @param throwable exception. |
| */ |
| @Override |
| public void warn(Object o, Throwable throwable) { |
| log(Level.WARN, STD, "{0}", o, throwable); |
| } |
| |
| /** |
| * Return if debug logging is enabled. |
| * |
| * @return <code>true</code> if debug logging is enable, <code>false</code> if not. |
| */ |
| @Override |
| public boolean isDebugEnabled() { |
| return isEnabled(Level.DEBUG, ALL); |
| } |
| |
| /** |
| * Return if error logging is enabled. |
| * |
| * @return <code>true</code> if error logging is enable, <code>false</code> if not. |
| */ |
| @Override |
| public boolean isErrorEnabled() { |
| return isEnabled(Level.ERROR, ALL); |
| } |
| |
| /** |
| * Return if fatal logging is enabled. |
| * |
| * @return <code>true</code> if fatal logging is enable, <code>false</code> if not. |
| */ |
| @Override |
| public boolean isFatalEnabled() { |
| return isEnabled(Level.FATAL, ALL); |
| } |
| |
| /** |
| * Return if info logging is enabled. |
| * |
| * @return <code>true</code> if info logging is enable, <code>false</code> if not. |
| */ |
| @Override |
| public boolean isInfoEnabled() { |
| return isEnabled(Level.INFO, ALL); |
| } |
| |
| /** |
| * Return if trace logging is enabled. |
| * |
| * @return <code>true</code> if trace logging is enable, <code>false</code> if not. |
| */ |
| @Override |
| public boolean isTraceEnabled() { |
| return isEnabled(Level.TRACE, ALL); |
| } |
| |
| /** |
| * Return if warn logging is enabled. |
| * |
| * @return <code>true</code> if warn logging is enable, <code>false</code> if not. |
| */ |
| @Override |
| public boolean isWarnEnabled() { |
| return isEnabled(Level.WARN, ALL); |
| } |
| |
| public enum Level { |
| FATAL, ERROR, INFO, WARN, DEBUG, TRACE |
| } |
| |
| private boolean isEnabled(Level level, int loggerMask) { |
| for (int i = 0; i < loggers.length; i++) { |
| if ((LOGGER_MASKS[i] & loggerMask) != 0) { |
| boolean enabled = false; |
| switch (level) { |
| case FATAL: |
| enabled = loggers[i].isFatalEnabled(); |
| break; |
| case ERROR: |
| enabled = loggers[i].isErrorEnabled(); |
| break; |
| case INFO: |
| enabled = loggers[i].isInfoEnabled(); |
| break; |
| case WARN: |
| enabled = loggers[i].isWarnEnabled(); |
| break; |
| case DEBUG: |
| enabled = loggers[i].isDebugEnabled(); |
| break; |
| case TRACE: |
| enabled = loggers[i].isTraceEnabled(); |
| break; |
| } |
| if (enabled) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| |
| private void log(Level level, int loggerMask, String msgTemplate, Object... params) { |
| loggerMask |= STD; |
| if (isEnabled(level, loggerMask)) { |
| String prefix = getMsgPrefix() != null ? getMsgPrefix() : Info.get().getPrefix(); |
| prefix = (prefix != null && prefix.length() > 0) ? prefix + " " : ""; |
| |
| String msg = prefix + format(msgTemplate, params); |
| Throwable throwable = getCause(params); |
| |
| for (int i = 0; i < LOGGER_MASKS.length; i++) { |
| if (isEnabled(level, loggerMask & LOGGER_MASKS[i])) { |
| Log log = loggers[i]; |
| switch (level) { |
| case FATAL: |
| log.fatal(msg, throwable); |
| break; |
| case ERROR: |
| log.error(msg, throwable); |
| break; |
| case INFO: |
| log.info(msg, throwable); |
| break; |
| case WARN: |
| log.warn(msg, throwable); |
| break; |
| case DEBUG: |
| log.debug(msg, throwable); |
| break; |
| case TRACE: |
| log.trace(msg, throwable); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Log a fatal message <code>Exception</code> to the common <code>Log</code>. |
| * |
| * @param msgTemplate message template. |
| * @param params parameters for the message template. If the last parameter is an exception it is logged as such. |
| */ |
| public void fatal(String msgTemplate, Object... params) { |
| log(Level.FATAL, STD, msgTemplate, params); |
| } |
| |
| /** |
| * Log a error message <code>Exception</code> to the common <code>Log</code>. |
| * |
| * @param msgTemplate message template. |
| * @param params parameters for the message template. If the last parameter is an exception it is logged as such. |
| */ |
| public void error(String msgTemplate, Object... params) { |
| log(Level.ERROR, STD, msgTemplate, params); |
| } |
| |
| /** |
| * Log a info message <code>Exception</code> to the common <code>Log</code>. |
| * |
| * @param msgTemplate message template. |
| * @param params parameters for the message template. If the last parameter is an exception it is logged as such. |
| */ |
| public void info(String msgTemplate, Object... params) { |
| log(Level.INFO, STD, msgTemplate, params); |
| } |
| |
| /** |
| * Log a warn message <code>Exception</code> to the common <code>Log</code>. |
| * |
| * @param msgTemplate message template. |
| * @param params parameters for the message template. If the last parameter is an exception it is logged as such. |
| */ |
| public void warn(String msgTemplate, Object... params) { |
| log(Level.WARN, STD, msgTemplate, params); |
| } |
| |
| /** |
| * Log a debug message <code>Exception</code> to the common <code>Log</code>. |
| * |
| * @param msgTemplate message template. |
| * @param params parameters for the message template. If the last parameter is an exception it is logged as such. |
| */ |
| public void debug(String msgTemplate, Object... params) { |
| log(Level.DEBUG, STD, msgTemplate, params); |
| } |
| |
| /** |
| * Log a trace message <code>Exception</code> to the common <code>Log</code>. |
| * |
| * @param msgTemplate message template. |
| * @param params parameters for the message template. If the last parameter is an exception it is logged as such. |
| */ |
| public void trace(String msgTemplate, Object... params) { |
| log(Level.TRACE, STD, msgTemplate, params); |
| } |
| |
| /** |
| * Tee Log a fatal message <code>Exception</code> to the common log and specified <code>Log</code>s. |
| * |
| * @param loggerMask log mask, it is a bit mask, possible values are <code>APP</code> and <code>OPS</code>. |
| * @param msgTemplate message template. |
| * @param params parameters for the message template. |
| */ |
| public void fatal(int loggerMask, String msgTemplate, Object... params) { |
| log(Level.FATAL, loggerMask, msgTemplate, params); |
| } |
| |
| /** |
| * Tee Log a error message <code>Exception</code> to the common log and specified <code>Log</code>s. |
| * |
| * @param loggerMask log mask, it is a bit mask, possible values are <code>APP</code> and <code>OPS</code>. |
| * @param msgTemplate message template. |
| * @param params parameters for the message template. |
| */ |
| public void error(int loggerMask, String msgTemplate, Object... params) { |
| log(Level.ERROR, loggerMask, msgTemplate, params); |
| } |
| |
| /** |
| * Tee Log a info message <code>Exception</code> to the common log and specified <code>Log</code>s. |
| * |
| * @param loggerMask log mask, it is a bit mask, possible values are <code>APP</code> and <code>OPS</code>. |
| * @param msgTemplate message template. |
| * @param params parameters for the message template. |
| */ |
| public void info(int loggerMask, String msgTemplate, Object... params) { |
| log(Level.INFO, loggerMask, msgTemplate, params); |
| } |
| |
| /** |
| * Tee Log a warn message <code>Exception</code> to the common log and specified <code>Log</code>s. |
| * |
| * @param loggerMask log mask, it is a bit mask, possible values are <code>APP</code> and <code>OPS</code>. |
| * @param msgTemplate message template. |
| * @param params parameters for the message template. |
| */ |
| public void warn(int loggerMask, String msgTemplate, Object... params) { |
| log(Level.WARN, loggerMask, msgTemplate, params); |
| } |
| |
| /** |
| * Tee Log a debug message <code>Exception</code> to the common log and specified <code>Log</code>s. |
| * |
| * @param loggerMask log mask, it is a bit mask, possible values are <code>APP</code> and <code>OPS</code>. |
| * @param msgTemplate message template. |
| * @param params parameters for the message template. |
| */ |
| public void debug(int loggerMask, String msgTemplate, Object... params) { |
| log(Level.DEBUG, loggerMask, msgTemplate, params); |
| } |
| |
| /** |
| * Tee Log a trace message <code>Exception</code> to the common log and specified <code>Log</code>s. |
| * |
| * @param loggerMask log mask, it is a bit mask, possible values are <code>APP</code> and <code>OPS</code>. |
| * @param msgTemplate message template. |
| * @param params parameters for the message template. |
| */ |
| public void trace(int loggerMask, String msgTemplate, Object... params) { |
| log(Level.TRACE, loggerMask, msgTemplate, params); |
| } |
| |
| /** |
| * Utility method that does uses the <code>StringFormat</code> to format the message template using the provided |
| * parameters. <p> In addition to the <code>StringFormat</code> syntax for message templates, it supports |
| * <code>{E}</code> for ENTER. <p> The last parameter is ignored for the formatting if it is an Exception. |
| * |
| * @param msgTemplate message template. |
| * @param params paramaters to use in the template. If the last parameter is an Exception, it is ignored. |
| * @return formatted message. |
| */ |
| public static String format(String msgTemplate, Object... params) { |
| ParamChecker.notEmpty(msgTemplate, "msgTemplate"); |
| msgTemplate = msgTemplate.replace("{E}", System.getProperty("line.separator")); |
| if (params != null && params.length > 0) { |
| msgTemplate = MessageFormat.format(msgTemplate, params); |
| } |
| return msgTemplate; |
| } |
| |
| /** |
| * Utility method that extracts the <code>Throwable</code>, if present, from the parameters. |
| * |
| * @param params parameters. |
| * @return a <code>Throwable</code> instance if it is the last parameter, <code>null</code> otherwise. |
| */ |
| public static Throwable getCause(Object... params) { |
| Throwable throwable = null; |
| if (params != null && params.length > 0 && params[params.length - 1] instanceof Throwable) { |
| throwable = (Throwable) params[params.length - 1]; |
| } |
| return throwable; |
| } |
| |
| } |