/* * Copyright 2004-2012 the Seasar Foundation and the Others. * * 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.seasar.util.log; import java.util.Map; import org.seasar.util.exception.SIllegalArgumentException; import org.seasar.util.message.MessageFormatter; import org.seasar.util.misc.Disposable; import org.seasar.util.misc.DisposableUtil; import static org.seasar.util.collection.ArrayUtil.*; import static org.seasar.util.collection.CollectionsUtil.*; import static org.seasar.util.misc.AssertionUtil.*; /** * ログ出力を提供するクラスです。 * * @author higa */ public class Logger { /** * ログの出力レベルです。 */ public enum LogLevel { /** デバッグ */ DEBUG, /** 情報 */ INFO, /** 警告 */ WARN, /** エラー */ ERROR, /** 致命的 */ FATAL, } /** ロガーアダプタのファクトリ */ protected static final LoggerAdapterFactory factory = getLoggerAdapterFactory(); /** クラスをキーとするロガー のマップ */ protected static final Map<Class<?>, Logger> loggers = newHashMap(); /** 初期化済みを示すフラグ */ private static boolean initialized; /** ロガーアダプタ */ private final LoggerAdapter log; /** * {@link Logger}を返します。 * * @param clazz * ロガーのカテゴリとなるクラス。{@literal null}であってはいけません * @return {@link Logger} */ public static synchronized Logger getLogger(final Class<?> clazz) { assertArgumentNotNull("clazz", clazz); if (!initialized) { initialize(); } Logger logger = loggers.get(clazz); if (logger == null) { logger = new Logger(clazz); loggers.put(clazz, logger); } return logger; } /** * フォーマットされたメッセージ文字列を返します。 * * @param messageCode * メッセージコード。{@literal null}や空文字列であってはいけません * @param args * 引数 * @return フォーマットされたメッセージ文字列 */ public static LogMessage format(final String messageCode, final Object... args) { assertArgumentNotEmpty("messageCode", messageCode); final char messageType = messageCode.charAt(0); final String message = MessageFormatter.getSimpleMessage(messageCode, args); switch (messageType) { case 'D': return new LogMessage(LogLevel.DEBUG, message); case 'I': return new LogMessage(LogLevel.INFO, message); case 'W': return new LogMessage(LogLevel.WARN, message); case 'E': return new LogMessage(LogLevel.ERROR, message); case 'F': return new LogMessage(LogLevel.FATAL, message); default: throw new SIllegalArgumentException( "messageCode", "EUTL0009", asArray(messageCode, "messageCode : " + messageCode)); } } /** * {@link Logger}を初期化します。 */ protected static synchronized void initialize() { DisposableUtil.addFirst(new Disposable() { @Override public void dispose() { initialized = false; loggers.clear(); factory.releaseAll(); } }); initialized = true; } /** * ログアダプタのファクトリを返します。 * <p> * Commons Loggingが使える場合はCommons Loggingを利用するためのファクトリを返します。 * 使えない場合はjava.util.loggingロガーを利用するためのファクトリを返します。 * </p> * * @return ログアダプタのファクトリ */ protected static LoggerAdapterFactory getLoggerAdapterFactory() { try { Class.forName("org.apache.commons.logging.LogFactory"); return new JclLoggerAdapterFactory(); } catch (final Throwable ignore) { return new JulLoggerAdapterFactory(); } } /** * インスタンスを構築します。 * * @param clazz * ログ出力のカテゴリとなるクラス */ protected Logger(final Class<?> clazz) { log = factory.getLoggerAdapter(clazz); } /** * DEBUG情報が出力されるかどうかを返します。 * * @return DEBUG情報が出力されるかどうか */ public boolean isDebugEnabled() { return log.isDebugEnabled(); } /** * DEBUG情報を出力します。 * * @param message * メッセージ * @param throwable * 例外 */ public void debug(final Object message, final Throwable throwable) { if (isDebugEnabled()) { log.debug(toString(message), throwable); } } /** * DEBUG情報を出力します。 * * @param message * メッセージ */ public void debug(final Object message) { if (isDebugEnabled()) { log.debug(toString(message)); } } /** * INFO情報が出力されるかどうかを返します。 * * @return INFO情報が出力されるかどうか */ public boolean isInfoEnabled() { return log.isInfoEnabled(); } /** * INFO情報を出力します。 * * @param message * メッセージ * @param throwable * 例外 */ public void info(final Object message, final Throwable throwable) { if (isInfoEnabled()) { log.info(toString(message), throwable); } } /** * INFO情報を出力します。 * * @param message * メッセージ */ public void info(final Object message) { if (isInfoEnabled()) { log.info(toString(message)); } } /** * WARN情報を出力します。 * * @param message * メッセージ * @param throwable * 例外 */ public void warn(final Object message, final Throwable throwable) { log.warn(toString(message), throwable); } /** * WARN情報を出力します。 * * @param message * メッセージ */ public void warn(final Object message) { log.warn(message.toString()); } /** * ERROR情報を出力します。 * * @param message * メッセージ * @param throwable * 例外 */ public void error(final Object message, final Throwable throwable) { log.error(message.toString(), throwable); } /** * ERROR情報を出力します。 * * @param message * メッセージ */ public void error(final Object message) { log.error(message.toString()); } /** * FATAL情報を出力します。 * * @param message * メッセージ * @param throwable * 例外 */ public void fatal(final Object message, final Throwable throwable) { log.fatal(message.toString(), throwable); } /** * FATAL情報を出力します。 * * @param message * メッセージ */ public void fatal(final Object message) { log.fatal(message.toString()); } /** * ログを出力します。 * * @param throwable * 例外。{@literal null}であってはいけません */ public void log(final Throwable throwable) { assertArgumentNotNull("throwable", throwable); error(throwable.getMessage(), throwable); } /** * ログを出力します。 * * @param messageCode * メッセージコード。{@literal null}や空文字列であってはいけません * @param args * 引数 */ public void log(final String messageCode, final Object... args) { assertArgumentNotEmpty("messageCode", messageCode); log(format(messageCode, args)); } /** * ログを出力します。 * <p> * ログメッセージは{@link #format(String, Object...)}メソッドで作成します。 * {@link #format(String, Object...)}を{@literal static import}しておくと便利です。 * </p> * * <pre> * import static org.seasar.util.log.Logger.format; * * Logger logger = Logger.getLogger(Xxx.class); * logger.log(format("DXXX0000", arg1, arg2, arg3)); * </pre> * * @param logMessage * ログメッセージ。{@literal null}であってはいけません */ public void log(final LogMessage logMessage) { assertArgumentNotNull("logMessage", logMessage); log(logMessage, null); } /** * ログを出力します。 * <p> * ログメッセージは{@link #format(String, Object...)}メソッドで作成します。 * {@link #format(String, Object...)}を{@literal static import}しておくと便利です。 * </p> * * <pre> * import static org.seasar.util.log.Logger.format; * * Logger logger = Logger.getLogger(Xxx.class); * logger.log(format("DXXX0000", arg1, arg2, arg3), t); * </pre> * * @param logMessage * ログメッセージ。{@literal null}であってはいけません * @param throwable * 例外 */ public void log(final LogMessage logMessage, final Throwable throwable) { assertArgumentNotNull("logMessage", logMessage); final LogLevel level = logMessage.getLevel(); if (isEnabledFor(level)) { final String message = logMessage.getMessage(); switch (level) { case DEBUG: log.debug(message, throwable); break; case INFO: log.info(message, throwable); break; case WARN: log.warn(message, throwable); break; case ERROR: log.error(message, throwable); break; case FATAL: log.fatal(message, throwable); break; } } } /** * 指定のログレベルが有効なら{@literal true}を返します. * * @param logLevel * ログレベル * @return 指定のログレベルが有効なら{@literal true} */ protected boolean isEnabledFor(final LogLevel logLevel) { switch (logLevel) { case DEBUG: return log.isDebugEnabled(); case INFO: return log.isInfoEnabled(); case WARN: return log.isWarnEnabled(); case ERROR: return log.isErrorEnabled(); case FATAL: return log.isFatalEnabled(); default: throw new SIllegalArgumentException( "logLevel", "EUTL0009", asArray(logLevel, logLevel)); } } /** * メッセージオブジェクトの文字列表現を返します。 * * @param message * メッセージオブジェクト * @return メッセージオブジェクトの文字列表現 */ protected static String toString(final Object message) { if (message == null) { return "null"; } if (message instanceof String) { return (String) message; } return message.toString(); } /** * ログ出力するメッセージです。 * * @author koichik */ public static class LogMessage { /** ログレベル */ protected final LogLevel level; /** ログメッセージ */ protected final String message; /** * インスタンスを構築します。 * * @param level * ログレベル。{@literal null}であってはいけません * @param message * ログメッセージ */ public LogMessage(final LogLevel level, final String message) { assertArgumentNotNull("level", level); this.level = level; this.message = message; } /** * 出力レベルを返します。 * * @return 出力レベル */ public LogLevel getLevel() { return level; } /** * メッセージを返します。 * * @return メッセージ */ public String getMessage() { return message; } } }