package jp.sf.amateras.functions;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;

import jp.sf.amateras.functions.utils.RequestUtils;

import org.apache.log4j.Logger;

/**
 * Log4jを使ってログを出力するための関数を提供します。
 * <p>
 * このクラスが提供する関数を使用して出力したログは以下のルールにしたがって作成された<code>Logger</code>を用いて出力されます。
 * <ol>
 *   <li>HttpServletRequest#getServletPath()でサーブレットパスを取得</li>
 *   <li>サーブレットパスの先頭の / と末尾の .jsp を除去</li>
 *   <li><strong>jsp.&lt;サーブレットパスの / を . に変換した文字列&gt;</strong> をLogger名とする</li>
 * </ol>
 * JSPのパスとLogger名の例を以下に示します。Log4側ではこのLogger名に応じてアペンダを定義することでJSPからのログ出力を制御することができます。
 * <ul>
 *   <li>/index.jsp の場合 jsp.index</li>
 *   <li>/admin/index.jsp の場合 jsp.admin.index</li>
 * </ul>
 * 
 * @author Naoki Takezoe
 */
public class Log4j {
	
	/**
	 * サーブレットパスからLogger名への変換結果をキャッシュしておくマップ。
	 */
	private static Map<String, String> loggerNameMap = new ConcurrentHashMap<String, String>();
	
	/**
	 * サーブレットパスから<code>Logger</code>の名前を取得します。
	 * 
	 * @param servletPath <code>HttpServletRequest#getServletPath()</code>で取得したサーブレットパス。
	 * @return <code>Logger</code>の名前
	 */
	protected static String getLoggerName(String servletPath){
		// Logger名がキャッシュに存在する場合
		if(loggerNameMap.containsKey(servletPath)){
			return loggerNameMap.get(servletPath);
		}
		
		String path = servletPath;
		String loggerName = "";
		
		if(path.endsWith(".jsp")){
			loggerName = "jsp.";
			path = path.replaceFirst("\\.jsp$", "");
		}
		
		path = path.replace('/', '.');
		if(path.startsWith(".")){
			path = path.substring(1);
		}
		
		loggerName = loggerName + path;
		
		// Logger名をキャッシュに保存しておく
		loggerNameMap.put(servletPath, loggerName);
		
		return loggerName;
	}
	
	/**
	 * ログ出力用の各メソッドで使用する<code>Logger</code>を取得します。
	 * 
	 * @return ログ出力用の各メソッドで使用する<code>Logger</code>
	 */
	protected static Logger getLogger(){
		HttpServletRequest request = RequestUtils.getRequest();
		return Logger.getLogger(getLoggerName(request.getServletPath()));
	}
	
	/**
	 * TRACEレベルでを出力します。
	 * 
	 * @param message ログに出力するメッセージ
	 */
	public static void trace(Object message){
		getLogger().trace(message);
	}
	
	/**
	 * DEBUGレベルでログを出力します。
	 * 
	 * @param message ログに出力するメッセージ
	 */
	public static void debug(Object message){
		getLogger().debug(message);
	}
	
	/**
	 * INFOレベルでログを出力します。
	 * 
	 * @param message ログに出力するメッセージ
	 */
	public static void info(Object message){
		getLogger().info(message);
	}
	
	/**
	 * WARNレベルでログを出力します。
	 * 
	 * @param message ログに出力するメッセージ
	 */
	public static void warn(Object message){
		getLogger().warn(message);
	}
	
	/**
	 * ERRORレベルでログを出力します。
	 * 
	 * @param message ログに出力するメッセージ
	 */
	public static void error(Object message){
		getLogger().error(message);
	}
	
	/**
	 * FATALレベルでログを出力します。
	 * 
	 * @param message ログに出力するメッセージ
	 */
	public static void fatal(Object message){
		getLogger().fatal(message);
	}
}
