Java tutorial
/** * 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 com.ayovel.nian.exception; 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"; public void debug(Object o) { } public void debug(Object o, Throwable throwable) { } public void error(Object o) { } public void error(Object o, Throwable throwable) { } public void fatal(Object o) { } public void fatal(Object o, Throwable throwable) { } public void info(Object o) { } public void info(Object o, Throwable throwable) { } public boolean isDebugEnabled() { return false; } public boolean isErrorEnabled() { return false; } public boolean isFatalEnabled() { return false; } public boolean isInfoEnabled() { return false; } public boolean isTraceEnabled() { return false; } public boolean isWarnEnabled() { return false; } public void trace(Object o) { } public void trace(Object o, Throwable throwable) { } public void warn(Object o) { } public void warn(Object o, Throwable throwable) { } /** * <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) { 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; } 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) { 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; } }