/** * Copyright (C) 2010 Orbeon, Inc. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU Lesser General Public License as published by the Free Software Foundation; either version * 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html */ package org.orbeon.oxf.util; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.orbeon.exception.OrbeonFormatter; import java.util.Stack; /** * Abstraction over log4j, which provides: * * - start/end operation with parameters * - indenting depending on current nesting of operations * - custom handling of debug level */ public class IndentedLogger { private final Logger logger; private final Indentation indentation; private final boolean debugEnabled; private Stack<Operation> stack = new Stack<Operation>(); public IndentedLogger(Logger logger) { this(logger, logger.isDebugEnabled(), new Indentation()); } public IndentedLogger(Logger logger, boolean isDebugEnabled) { this(logger, isDebugEnabled, new Indentation()); } public IndentedLogger(Logger logger, boolean debugEnabled, Indentation indentation) { this.logger = logger; this.debugEnabled = debugEnabled; this.indentation = indentation; } public IndentedLogger(IndentedLogger indentedLogger, Indentation indentation, boolean isDebugEnabled) { this(indentedLogger.logger, isDebugEnabled, indentation); } public Logger getLogger() { return logger; } public Indentation getIndentation() { return indentation; } public final boolean isDebugEnabled() { return debugEnabled; } public final boolean isInfoEnabled() { return logger.isInfoEnabled(); } public void startHandleOperation(String type, String message) { if (debugEnabled) { stack.push(new Operation(type, message)); logDebug(type, "start " + message); indentation.indentation++; } } public void startHandleOperation(String type, String message, String... parameters) { if (debugEnabled) { stack.push(new Operation(type, message)); logDebug(type, "start " + message, parameters); indentation.indentation++; } } public void endHandleOperation() { if (debugEnabled) { final String[] resultParameters = stack.peek().resultParameters; if (resultParameters != null) { // Case where parameters were set with setDebugResults endHandleOperation(resultParameters); } else { // No parameters indentation.indentation--; final Operation operation = stack.pop(); if (operation != null) { logDebug(operation.type, "end " + operation.message, "time (ms)", Long.toString(operation.getTimeElapsed())); } } } } public void endHandleOperation(String... parameters) { if (debugEnabled) { indentation.indentation--; final Operation operation = stack.pop(); if (operation != null) { final String[] newParameters = new String[parameters.length + 2]; newParameters[0] = "time (ms)"; newParameters[1] = Long.toString(operation.getTimeElapsed()); System.arraycopy(parameters, 0, newParameters, 2, parameters.length); logDebug(operation.type, "end " + operation.message, newParameters); } } } public void setDebugResults(String... parameters) { stack.peek().resultParameters = parameters; } private static String getLogIndentSpaces(int level) { final StringBuilder sb = new StringBuilder(); for (int i = 0; i < level; i++) sb.append(" "); return sb.toString(); } public void log(Level level, String type, String message, String... parameters) { log(level, indentation.indentation, type, message, parameters); } public void logDebug(String type, String message) { log(Level.DEBUG, indentation.indentation, type, message, (String[]) null); } public void logDebug(String type, String message, String... parameters) { log(Level.DEBUG, indentation.indentation, type, message, parameters); } public void logDebug(String type, String message, Throwable throwable) { log(Level.DEBUG, indentation.indentation, type, message, "throwable", OrbeonFormatter.format(throwable)); } public void logWarning(String type, String message, String... parameters) { log(Level.WARN, indentation.indentation, type, message, parameters); } public void logInfo(String type, String message) { log(Level.INFO, indentation.indentation, type, message, (String[]) null); } public void logInfo(String type, String message, String... parameters) { log(Level.INFO, indentation.indentation, type, message, parameters); } public void logInfo(String type, String message, Throwable throwable) { log(Level.INFO, indentation.indentation, type, message, "throwable", OrbeonFormatter.format(throwable)); } public void logWarning(String type, String message, Throwable throwable) { log(Level.WARN, indentation.indentation, type, message, "throwable", OrbeonFormatter.format(throwable)); } public void logError(String type, String message, String... parameters) { log(Level.ERROR, indentation.indentation, type, message, parameters); } public void logError(String type, String message, Throwable throwable) { log(Level.ERROR, indentation.indentation, type, message, "throwable", OrbeonFormatter.format(throwable)); } private void log(Level level, int indentLevel, String type, String message, String... parameters) { if (!(level == Level.DEBUG && !debugEnabled)) // handle DEBUG level locally, everything else goes through log(logger, level, getActualIndentLevel(indentLevel), type, message, parameters); } private int getActualIndentLevel(int indentLevel) { return indentLevel; } private static void log(Logger logger, Level level, int indentLevel, String type, String message, String... parameters) { final String parametersString; if (parameters != null && parameters.length > 0) { final StringBuilder sb = new StringBuilder(" {"); boolean first = true; for (int i = 0; i < parameters.length; i += 2) { final String paramName = parameters[i]; final String paramValue = parameters[i + 1]; if (paramName != null && paramValue != null) { if (!first) sb.append(", "); sb.append(paramName); sb.append(": \""); sb.append(paramValue); sb.append('\"'); first = false; } } sb.append('}'); parametersString = sb.toString(); } else { parametersString = ""; } final String text = (StringUtils.isNotEmpty(type) ? (type + " - ") : "") + message + parametersString; final String indentation = getLogIndentSpaces(indentLevel); logger.log(level, indentation + text); } public static class Indentation { public int indentation; public Indentation() { this(0); } public Indentation(int indentation) { this.indentation = indentation; } } private class Operation { public String type; public String message; public final long startTime; public String[] resultParameters; public Operation() { if (isDebugEnabled()) { startTime = System.currentTimeMillis(); } else { startTime = 0; } } public Operation(String type, String message) { this(); this.type = type; this.message = message; } public long getTimeElapsed() { return System.currentTimeMillis() - startTime; } } }