/**
* eAdventure (formerly <e-Adventure> and <e-Game>) is a research project of the
* <e-UCM> research group.
*
* Copyright 2005-2010 <e-UCM> research group.
*
* You can access a list of all the contributors to eAdventure at:
* http://e-adventure.e-ucm.es/contributors
*
* <e-UCM> is a research group of the Department of Software Engineering
* and Artificial Intelligence at the Complutense University of Madrid
* (School of Computer Science).
*
* C Profesor Jose Garcia Santesmases sn,
* 28040 Madrid (Madrid), Spain.
*
* For more info please visit: <http://e-adventure.e-ucm.es> or
* <http://www.e-ucm.es>
*
* ****************************************************************************
*
* This file is part of eAdventure, version 2.0
*
* eAdventure 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 3 of the License, or
* (at your option) any later version.
*
* eAdventure 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with eAdventure. If not, see <http://www.gnu.org/licenses/>.
*/
package es.eucm.ead.editor.util;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.LinkedList;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.WriterAppender;
/**
* Simple configuration for log4j logging. Should be called from "main" files or
* unit-tests in this directory. A single check for log4j existence is performed
* during initial class-loading, to bail out cleanly if it is not present.
*
* @author mfreire
*/
public class Log4jConfig {
static private final boolean isLog4jPresent;
static private final Object mutex = new Object();
static private LinkedList<String> outputBuffer = new LinkedList<String>();
static private int totalBufferLength = 0;
static private int maxBufferLength = 10 * (1 << 10);
static ArrayList<LogListener> listeners = new ArrayList<LogListener>();
static {
boolean found = false;
try {
found = (null != Log4jConfig.class.getClassLoader().loadClass(
"org.apache.log4j.Logger"));
} catch (ClassNotFoundException e) {
// loading of a sample log4j class failed: no log4j for you
System.err.println("[WARNING] Log4j not available: " + e);
}
isLog4jPresent = found;
}
static public void subscribe(LogListener ll) {
listeners.add(ll);
}
static public void unsubscribe(LogListener ll) {
listeners.remove(ll);
}
static private void writeInBuffer(String text) {
synchronized (mutex) {
while (!outputBuffer.isEmpty()
&& totalBufferLength + text.length() > maxBufferLength) {
int removed = outputBuffer.pop().length();
totalBufferLength -= removed;
}
outputBuffer.push(text);
for (LogListener ll : listeners) {
ll.logChanged(text);
}
totalBufferLength += text.length();
}
}
static public String getBuffer() {
StringBuilder sb = new StringBuilder();
synchronized (mutex) {
for (String s : outputBuffer) {
sb.append(s);
}
}
return sb.toString();
}
public static interface LogListener {
void logChanged(String change);
}
/**
* Decouples slf4j levels from log4j levels. This avoids adding any mention
* to log4j in the calling class, or to rely on their being basically the
* same.
*/
public enum Slf4jLevel {
// very detailed, generally inactive
Trace("TRACE"), Debug("DEBUG"),
// generally active
Info("INFO"),
// always active
Warn("WARN"), Error("ERROR"),
// critical - only before crash
Fatal("FATAL");
private String log4jLevelName;
Slf4jLevel(String log4jLevelName) {
this.log4jLevelName = log4jLevelName;
}
public Level getLog4jLevel() {
return Level.toLevel(log4jLevelName);
}
public String toString() {
switch (this) {
case Trace:
return "trace";
case Debug:
return "debug";
case Info:
return "info";
case Warn:
return "warn";
case Error:
return "error";
case Fatal:
return "fatal";
}
return "trace";
}
}
public static void pushNDC(String value) {
NDC.push(value);
}
public static void popNDC() {
NDC.pop();
}
public static void configForConsole(final Slf4jLevel defaultLevel) {
configForConsole(defaultLevel, new Object[] {});
}
/**
* Initial log4j configuration. Fails if no log4j present; should be called
* only from main(), but can be called repeatedly without ill effects (only
* the 1st call will set the pattern, though).
*
* @param defaultLevel
* @param otherLevels
* if you want loggers "A" and "B" to use Debug, and logger "C"
* to use Warn, you would pass in new Object[] {"A", Debug, "B",
* Debug, "C", Warn}; null is also valid.
*/
public static void configForConsole(final Slf4jLevel defaultLevel,
final Object[] otherLevels) {
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel",
defaultLevel.toString());
if (!isLog4jPresent) {
System.err
.println("Log4j is not present. Configuration request ignored");
} else {
new Runnable() {
@Override
public void run() {
Logger root = Logger.getRootLogger();
if (!root.getAllAppenders().hasMoreElements()) {
root.setLevel(defaultLevel.getLog4jLevel());
root.addAppender(new ConsoleAppender(new PatternLayout(
"%-5p %x [%c{1}|%t]: %m%n")));
root.addAppender(new WriterAppender(new PatternLayout(
"%-5p %x [%c{1}|%t]: %m%n"), new Writer() {
@Override
public void write(char[] cbuf, int off, int len)
throws IOException {
writeInBuffer(new String(cbuf, off, len));
}
@Override
public void flush() throws IOException {
}
@Override
public void close() throws IOException {
}
}));
} else {
root.setLevel(defaultLevel.getLog4jLevel());
}
if (otherLevels != null) {
for (int i = 0; i < otherLevels.length; i += 2) {
String loggerName = (otherLevels[i] instanceof Class) ? ((Class) otherLevels[i])
.getName()
: (String) otherLevels[i];
Slf4jLevel level = (Slf4jLevel) otherLevels[i + 1];
setLevel(loggerName, level);
}
}
}
}.run();
}
}
public static void setLevel(final String loggerName, final String level) {
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", level);
if (!isLog4jPresent) {
System.err
.println("Log4j is not present. Configuration request ignored");
} else {
new Runnable() {
@Override
public void run() {
Logger.getLogger(loggerName).setLevel(Level.toLevel(level));
}
}.run();
}
}
/**
* Sets a logger to a level.
*
* @param loggerName
* @param level
*/
public static void setLevel(final String loggerName, final Slf4jLevel level) {
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", level
.toString());
if (!isLog4jPresent) {
System.err
.println("Log4j is not present. Configuration request ignored");
} else {
new Runnable() {
@Override
public void run() {
Logger.getLogger(loggerName)
.setLevel(level.getLog4jLevel());
}
}.run();
}
}
}