/**
* 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;
}
}
}