Commit 44d1f5e0 authored by guus's avatar guus

Replacing Openfire custom logging implementation with SLF4J and Log4j (OF-53)

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@11195 b35dd754-fafc-0310-a699-88a17e54d16e
parent 7b16dc87
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="debug-out" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="../logs/debug.log" />
<param name="MaxFileSize" value="1024KB"/>
<param name="MaxBackupIndex" value="5"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy.MM.dd HH:mm:ss} %m%n" />
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMax" value="debug" />
<param name="AcceptOnMatch" value="true" />
</filter>
</appender>
<appender name="info-out" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="../logs/info.log" />
<param name="MaxFileSize" value="1024KB"/>
<param name="MaxBackupIndex" value="5"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy.MM.dd HH:mm:ss} %m%n" />
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMax" value="info" />
<param name="LevelMin" value="info" />
<param name="AcceptOnMatch" value="true" />
</filter>
</appender>
<appender name="warn-out" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="../logs/warn.log" />
<param name="MaxFileSize" value="1024KB"/>
<param name="MaxBackupIndex" value="5"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy.MM.dd HH:mm:ss} %m%n" />
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMax" value="warn" />
<param name="LevelMin" value="warn" />
<param name="AcceptOnMatch" value="true" />
</filter>
</appender>
<appender name="error-out" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="../logs/error.log" />
<param name="MaxFileSize" value="1024KB"/>
<param name="MaxBackupIndex" value="5"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy.MM.dd HH:mm:ss} %m%n" />
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="error" />
<param name="AcceptOnMatch" value="true" />
</filter>
</appender>
<root>
<level value="info" />
<appender-ref ref="debug-out" />
<appender-ref ref="info-out" />
<appender-ref ref="warn-out" />
<appender-ref ref="error-out" />
</root>
</log4j:configuration>
......@@ -313,7 +313,7 @@ public class SASLAuthentication {
}
}
else {
Log.fatal("SaslServer is null, should be valid object instead.");
Log.error("SaslServer is null, should be valid object instead.");
authenticationFailed(session);
status = Status.failed;
}
......
......@@ -12,331 +12,245 @@
package org.jivesoftware.util;
import org.jivesoftware.util.log.Hierarchy;
import org.jivesoftware.util.log.LogTarget;
import org.jivesoftware.util.log.Logger;
import org.jivesoftware.util.log.Priority;
import org.jivesoftware.util.log.format.ExtendedPatternFormatter;
import org.jivesoftware.util.log.output.io.StreamTarget;
import org.jivesoftware.util.log.output.io.rotate.RevolvingFileStrategy;
import org.jivesoftware.util.log.output.io.rotate.RotateStrategyBySize;
import org.jivesoftware.util.log.output.io.rotate.RotatingFileTarget;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
/**
* Simple wrapper to the incorporated LogKit to log under a single logging name.
* Openfire makes use of a logging facade (slf4j) to manage its log output. The
* facade is backed up by Log4j by default. This class provides utility methods.
* <p>
* Additionally, this class provides methods that can be used to record logging
* statements. These methods are exact duplicates of the previous Log
* implementation of Openfire and are kept for backwards-compatibility (the are
* deprecated). These methods will delegate logging functionality to slf4j.
* Instead of these methods, slf4j logging functionality should be used
* directly.
*
* @author Bruce Ritchie
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
* @see <a href="http://www.slf4j.org/">http://www.slf4j.org/</a>
*/
public class Log {
private static final Logger debugLog = Hierarchy.getDefaultHierarchy().getLoggerFor("Jive-DEBUG");
private static final Logger infoLog = Hierarchy.getDefaultHierarchy().getLoggerFor("Jive-INFO");
private static final Logger warnLog = Hierarchy.getDefaultHierarchy().getLoggerFor("Jive-WARN");
private static final Logger errorLog = Hierarchy.getDefaultHierarchy().getLoggerFor("Jive-ERR");
private static String logNameDebug = null;
private static String logNameInfo = null;
private static String logNameWarn = null;
private static String logNameError = null;
private static String debugPattern = null;
private static String infoPattern = null;
private static String warnPattern = null;
private static String errorPattern = null;
private static String logDirectory = null;
private static long maxDebugSize = 1024;
private static long maxInfoSize = 1024;
private static long maxWarnSize = 1024;
private static long maxErrorSize = 1024;
private static final org.slf4j.Logger Logger = org.slf4j.LoggerFactory.getLogger(Log.class);
private static boolean debugEnabled;
static {
initLog();
}
private Log() { }
// TODO deprecate these properties
// JiveGlobals.getXMLProperty("log.debug.format");
// JiveGlobals.getXMLProperty("log.info.format");
// JiveGlobals.getXMLProperty("log.warn.format");
// JiveGlobals.getXMLProperty("log.error.format");
// JiveGlobals.getXMLProperty("log.debug.size");
// JiveGlobals.getXMLProperty("log.info.size");
// JiveGlobals.getXMLProperty("log.warn.size");
// JiveGlobals.getXMLProperty("log.error.size");
// JiveGlobals.getXMLProperty("log.debug.enabled");
/**
* This method is used to initialize the Log class. For normal operations this method
* should <b>never</b> be called, rather it's only publically available so that the class
* can be reset by the setup process once the home directory has been specified.
* @deprecated replaced by {@link org.slf4j.Logger#isErrorEnabled()}.
* Functionality of this method is delegated there.
*/
public static void initLog() {
try {
logDirectory = JiveGlobals.getXMLProperty("log.directory");
if (logDirectory == null) {
if (JiveGlobals.getHomeDirectory() != null) {
File openfireHome = new File(JiveGlobals.getHomeDirectory());
if (openfireHome.exists() && openfireHome.canWrite()) {
logDirectory = (new File(openfireHome, "logs")).toString();
}
}
}
if (!logDirectory.endsWith(File.separator)) {
logDirectory = logDirectory + File.separator;
}
// Make sure the logs directory exists. If not, make it:
File logDir = new File(logDirectory);
if (!logDir.exists()) {
logDir.mkdir();
}
logNameDebug = logDirectory + "debug.log";
logNameInfo = logDirectory + "info.log";
logNameWarn = logDirectory + "warn.log";
logNameError = logDirectory + "error.log";
debugPattern = JiveGlobals.getXMLProperty("log.debug.format");
infoPattern = JiveGlobals.getXMLProperty("log.info.format");
warnPattern = JiveGlobals.getXMLProperty("log.warn.format");
errorPattern = JiveGlobals.getXMLProperty("log.error.format");
try { maxDebugSize = Long.parseLong(JiveGlobals.getXMLProperty("log.debug.size")); }
catch (NumberFormatException e) { /* ignore */ }
try { maxInfoSize = Long.parseLong(JiveGlobals.getXMLProperty("log.info.size")); }
catch (NumberFormatException e) { /* ignore */ }
try { maxWarnSize = Long.parseLong(JiveGlobals.getXMLProperty("log.warn.size")); }
catch (NumberFormatException e) { /* ignore */ }
try { maxErrorSize = Long.parseLong(JiveGlobals.getXMLProperty("log.error.size")); }
catch (NumberFormatException e) { /* ignore */ }
debugEnabled = "true".equals(JiveGlobals.getXMLProperty("log.debug.enabled"));
}
catch (Exception e) {
// we'll get an exception if home isn't setup yet - we ignore that since
// it's sure to be logged elsewhere :)
}
if (debugPattern == null) {
debugPattern = "%{time:yyyy.MM.dd HH:mm:ss} %{message}\\n%{throwable}";
}
if (infoPattern == null) {
infoPattern = "%{time:yyyy.MM.dd HH:mm:ss} %{message}\\n%{throwable}";
}
if (warnPattern == null) {
warnPattern = "%{time:yyyy.MM.dd HH:mm:ss} %{message}\\n%{throwable}";
}
if (errorPattern == null) {
errorPattern = "%{time:yyyy.MM.dd HH:mm:ss} [%{method}] %{message}\\n%{throwable}";
}
createLogger(debugPattern, logNameDebug, maxDebugSize, debugLog, Priority.DEBUG);
createLogger(infoPattern, logNameInfo, maxInfoSize, infoLog, Priority.INFO);
createLogger(warnPattern, logNameWarn, maxWarnSize, warnLog, Priority.WARN);
createLogger(errorPattern, logNameError, maxErrorSize, errorLog, Priority.ERROR);
// set up the ties into jdk logging
Handler jdkLogHandler = new JiveLogHandler();
jdkLogHandler.setLevel(Level.ALL);
java.util.logging.Logger.getLogger("").addHandler(jdkLogHandler);
}
private static void createLogger(String pattern, String logName, long maxLogSize,
Logger logger, Priority priority)
{
// debug log file
ExtendedPatternFormatter formatter = new ExtendedPatternFormatter(pattern);
StreamTarget target = null;
Exception ioe = null;
try {
// home was not setup correctly
if (logName == null) {
throw new IOException("LogName was null - OpenfireHome not set?");
}
else {
RevolvingFileStrategy fileStrategy = new RevolvingFileStrategy(logName, 5);
RotateStrategyBySize rotateStrategy = new RotateStrategyBySize(maxLogSize * 1024);
target = new RotatingFileTarget(formatter, rotateStrategy, fileStrategy);
}
}
catch (IOException e) {
ioe = e;
// can't log to file, log to stderr
target = new StreamTarget(System.err, formatter);
}
logger.setLogTargets(new LogTarget[] { target } );
logger.setPriority(priority);
if (ioe != null) {
logger.debug("Error occurred opening log file: " + ioe.getMessage());
}
}
public static void setProductName(String productName) {
debugPattern = productName + " " + debugPattern;
infoPattern = productName + " " + infoPattern;
warnPattern = productName + " " + warnPattern;
errorPattern = productName + " " + errorPattern;
createLogger(debugPattern, logNameDebug, maxDebugSize, debugLog, Priority.DEBUG);
createLogger(infoPattern, logNameInfo, maxInfoSize, infoLog, Priority.INFO);
createLogger(warnPattern, logNameWarn, maxWarnSize, warnLog, Priority.WARN);
createLogger(errorPattern, logNameError, maxErrorSize, errorLog, Priority.ERROR);
}
@Deprecated()
public static boolean isErrorEnabled() {
return errorLog.isErrorEnabled();
}
public static boolean isFatalEnabled() {
return errorLog.isFatalErrorEnabled();
return Logger.isErrorEnabled();
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#isDebugEnabled()}.
* Functionality of this method is delegated there.
*/
public static boolean isDebugEnabled() {
return debugEnabled;
return Logger.isDebugEnabled();
}
public static void setDebugEnabled(boolean enabled) {
JiveGlobals.setXMLProperty("log.debug.enabled", Boolean.toString(enabled));
debugEnabled = enabled;
// SLF4J doesn't provide a hook into the logging implementation. We'll have to do this 'direct', bypassing slf4j.
final org.apache.log4j.Level newLevel;
if (enabled) {
newLevel = org.apache.log4j.Level.ALL;
} else {
newLevel = org.apache.log4j.Level.INFO;
}
org.apache.log4j.LogManager.getRootLogger().setLevel(newLevel);
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#isInfoEnabled()}.
* Functionality of this method is delegated there.
*/
public static boolean isInfoEnabled() {
return infoLog.isInfoEnabled();
return Logger.isInfoEnabled();
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#isWarnEnabled()}.
* Functionality of this method is delegated there.
*/
public static boolean isWarnEnabled() {
return warnLog.isWarnEnabled();
return Logger.isWarnEnabled();
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#debug(String)}.
* Functionality of this method is delegated there.
*/
public static void debug(String s) {
if (isDebugEnabled()) {
debugLog.debug(s);
Logger.debug(s);
}
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#debug(String, Throwable)}.
* Functionality of this method is delegated there.
*/
public static void debug(Throwable throwable) {
if (isDebugEnabled()) {
debugLog.debug("", throwable);
Logger.debug("", throwable);
}
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#debug(String, Throwable)}.
* Functionality of this method is delegated there.
*/
public static void debug(String s, Throwable throwable) {
if (isDebugEnabled()) {
debugLog.debug(s, throwable);
Logger.debug(s, throwable);
}
}
public static void markDebugLogFile(String username) {
RotatingFileTarget target = (RotatingFileTarget) debugLog.getLogTargets()[0];
markLogFile(username, target);
String message = getMarkMessage(username);
debug(message);
}
public static void rotateDebugLogFile() {
RotatingFileTarget target = (RotatingFileTarget) debugLog.getLogTargets()[0];
try {
target.rotate();
}
catch (IOException e) {
System.err.println("Warning: There was an error rotating the Jive debug log file. " +
"Logging may not work correctly until a restart happens.");
}
// SLF4J doesn't provide a hook into the logging implementation. We'll have to do this 'direct', bypassing slf4j.
File logFile = new File(Log.getLogDirectory(), "debug.log");
emptyFile(logFile);
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#info(String)}.
* Functionality of this method is delegated there.
*/
public static void info(String s) {
if (isInfoEnabled()) {
infoLog.info(s);
Logger.info(s);
}
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#info(String, Throwable)}.
* Functionality of this method is delegated there.
*/
public static void info(Throwable throwable) {
if (isInfoEnabled()) {
infoLog.info("", throwable);
Logger.info("", throwable);
}
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#info(String, Throwable)}.
* Functionality of this method is delegated there.
*/
public static void info(String s, Throwable throwable) {
if (isInfoEnabled()) {
infoLog.info(s, throwable);
Logger.info(s, throwable);
}
}
public static void markInfoLogFile(String username) {
RotatingFileTarget target = (RotatingFileTarget) infoLog.getLogTargets()[0];
markLogFile(username, target);
String message = getMarkMessage(username);
info(message);
}
public static void rotateInfoLogFile() {
RotatingFileTarget target = (RotatingFileTarget) infoLog.getLogTargets()[0];
try {
target.rotate();
}
catch (IOException e) {
System.err.println("Warning: There was an error rotating the Jive info log file. " +
"Logging may not work correctly until a restart happens.");
}
// SLF4J doesn't provide a hook into the logging implementation. We'll have to do this 'direct', bypassing slf4j.
File logFile = new File(Log.getLogDirectory(), "info.log");
emptyFile(logFile);
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#warn(String)}.
* Functionality of this method is delegated there.
*/
public static void warn(String s) {
if (isWarnEnabled()) {
warnLog.warn(s);
Logger.warn(s);
}
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#warn(String, Throwable)}.
* Functionality of this method is delegated there.
*/
public static void warn(Throwable throwable) {
if (isWarnEnabled()) {
warnLog.warn("", throwable);
Logger.warn("", throwable);
}
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#debug(String, Throwable)}.
* Functionality of this method is delegated there.
*/
public static void warn(String s, Throwable throwable) {
if (isWarnEnabled()) {
warnLog.warn(s, throwable);
Logger.warn(s, throwable);
}
}
public static void markWarnLogFile(String username) {
RotatingFileTarget target = (RotatingFileTarget) warnLog.getLogTargets()[0];
markLogFile(username, target);
String message = getMarkMessage(username);
warn(message);
}
public static void rotateWarnLogFile() {
RotatingFileTarget target = (RotatingFileTarget) warnLog.getLogTargets()[0];
try {
target.rotate();
}
catch (IOException e) {
System.err.println("Warning: There was an error rotating the Jive warn log file. " +
"Logging may not work correctly until a restart happens.");
}
// SLF4J doesn't provide a hook into the logging implementation. We'll have to do this 'direct', bypassing slf4j.
File logFile = new File(Log.getLogDirectory(), "warn.log");
emptyFile(logFile);
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#error(String)}.
* Functionality of this method is delegated there.
*/
public static void error(String s) {
if (isErrorEnabled()) {
errorLog.error(s);
Logger.error(s);
if (isDebugEnabled()) {
printToStdErr(s, null);
}
}
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#error(String, Throwable)}.
* Functionality of this method is delegated there.
*/
public static void error(Throwable throwable) {
if (isErrorEnabled()) {
errorLog.error("", throwable);
Logger.error("", throwable);
if (isDebugEnabled()) {
printToStdErr(null, throwable);
}
}
}
/**
* @deprecated replaced by {@link org.slf4j.Logger#error(String, Throwable)}.
* Functionality of this method is delegated there.
*/
public static void error(String s, Throwable throwable) {
if (isErrorEnabled()) {
errorLog.error(s, throwable);
Logger.error(s, throwable);
if (isDebugEnabled()) {
printToStdErr(s, throwable);
}
......@@ -344,46 +258,14 @@ public class Log {
}
public static void markErrorLogFile(String username) {
RotatingFileTarget target = (RotatingFileTarget) errorLog.getLogTargets()[0];
markLogFile(username, target);
String message = getMarkMessage(username);
error(message);
}
public static void rotateErrorLogFile() {
RotatingFileTarget target = (RotatingFileTarget) errorLog.getLogTargets()[0];
try {
target.rotate();
}
catch (IOException e) {
System.err.println("Warning: There was an error rotating the Jive error log file. " +
"Logging may not work correctly until a restart happens.");
}
}
public static void fatal(String s) {
if (isFatalEnabled()) {
errorLog.fatalError(s);
if (isDebugEnabled()) {
printToStdErr(s, null);
}
}
}
public static void fatal(Throwable throwable) {
if (isFatalEnabled()) {
errorLog.fatalError("", throwable);
if (isDebugEnabled()) {
printToStdErr(null, throwable);
}
}
}
public static void fatal(String s, Throwable throwable) {
if (isFatalEnabled()) {
errorLog.fatalError(s, throwable);
if (isDebugEnabled()) {
printToStdErr(s, throwable);
}
}
// SLF4J doesn't provide a hook into the logging implementation. We'll have to do this 'direct', bypassing slf4j.
File logFile = new File(Log.getLogDirectory(), "error.log");
emptyFile(logFile);
}
/**
......@@ -393,14 +275,22 @@ public class Log {
* @return the directory that log files exist in.
*/
public static String getLogDirectory() {
return logDirectory;
// SLF4J doesn't provide a hook into the logging implementation. We'll have to do this 'direct', bypassing slf4j.
final StringBuilder sb = new StringBuilder();
sb.append(JiveGlobals.getHomeDirectory());
if (!sb.substring(sb.length()-1).startsWith(File.separator)) {
sb.append(File.separator);
}
sb.append("logs");
sb.append(File.separator);
return sb.toString();
}
private static void markLogFile(String username, RotatingFileTarget target) {
List args = new ArrayList();
private static String getMarkMessage(String username) {
final List<String> args = new ArrayList<String>();
args.add(username);
args.add(JiveGlobals.formatDateTime(new java.util.Date()));
target.write(LocaleUtils.getLocalizedString("log.marker_inserted_by", args) + "\n");
return LocaleUtils.getLocalizedString("log.marker_inserted_by", args);
}
private static void printToStdErr(String s, Throwable throwable) {
......@@ -416,65 +306,21 @@ public class Log {
}
}
private static final class JiveLogHandler extends Handler {
public void publish(LogRecord record) {
Level level = record.getLevel();
Throwable throwable = record.getThrown();
if (Level.SEVERE.equals(level)) {
if (throwable != null) {
Log.error(record.getMessage(), throwable);
}
else {
Log.error(record.getMessage());
}
}
else if (Level.WARNING.equals(level)) {
if (throwable != null) {
Log.warn(record.getMessage(), throwable);
}
else {
Log.warn(record.getMessage());
}
}
else if (Level.INFO.equals(level)) {
if (throwable != null) {
Log.info(record.getMessage(), throwable);
}
else {
Log.info(record.getMessage());
}
}
else {
// else FINE,FINER,FINEST
if (throwable != null) {
Log.debug(record.getMessage(), throwable);
}
else {
Log.debug(record.getMessage());
}
}
private static void emptyFile(File logFile) {
BufferedWriter out = null;
try {
out = new BufferedWriter(new FileWriter(logFile));
out.write("");
} catch (IOException ex) {
Log.warn("Could not empty file " + logFile.getName(), ex);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException ex) {
Log.warn("Could not close file.", ex);
}
public void flush() {
// do nothing
}
public void close() throws SecurityException {
// do nothing
}
}
}
\ No newline at end of file
/**
* Copyright (C) 2004-2008 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution, or a commercial license
* agreement with Jive.
*/
package org.jivesoftware.util;
/**
* A simple logging service for components. Four log levels are provided:<ul>
*
* <li>Error -- an error occured in the component.
* <li>Warn -- a condition occured that an administrator should be warned about.
* <li>Info -- used to send information messages, such as a version or license notice.
* <li>Debug -- used to send debugging information. Most Log implementations will
* disable debug output by default.
* </ul>
*
* Log implementations will attempt use the native logging service of the component host
* server. However, this may not be possible in some cases -- for example, when using an
* external component that is not currently connected to the server.
*
* @author Matt Tucker
*/
public interface Logger {
/**
* Logs an error.
*
* @param message the error message.
*/
public void error(String message);
/**
* Logs an error.
*
* @param message the error message.
* @param throwable the Throwable that caused the error.
*/
public void error(String message, Throwable throwable);
/**
* Logs an error.
*
* @param throwable the Throwable that caused the error.
*/
public void error(Throwable throwable);
/**
* Logs a warning.
*
* @param message the warning message.
*/
public void warn(String message);
/**
* Logs a warning.
*
* @param message the warning message.
* @param throwable the Throwable that caused the error.
*/
public void warn(String message, Throwable throwable);
/**
* Logs a warning.
*
* @param throwable the Throwable that caused the error.
*/
public void warn(Throwable throwable);
/**
* Logs an info message.
*
* @param message the info message.
*/
public void info(String message);
/**
* Logs an info message.
*
* @param message the info message.
* @param throwable the Throwable that caused the info message.
*/
public void info(String message, Throwable throwable);
/**
* Logs an info message.
*
* @param throwable the Throwable that caused the info message.
*/
public void info(Throwable throwable);
/**
* Logs a debug message.
*
* @param message the debug message.
*/
public void debug(String message);
/**
* Logs a debug message.
*
* @param message the debug message.
* @param throwable the Throwable that caused the debug message.
*/
public void debug(String message, Throwable throwable);
/**
* Logs a debug message.
*
* @param throwable the Throwable the caused the debug message.
*/
public void debug(Throwable throwable);
}
\ No newline at end of file
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* The ContextMap contains non-hierarchical context information
* relevent to a particular LogEvent. It may include information
* such as;
* <p/>
* <ul>
* <li>user -&gt;fred</li>
* <li>hostname -&gt;helm.realityforge.org</li>
* <li>ipaddress -&gt;1.2.3.4</li>
* <li>interface -&gt;127.0.0.1</li>
* <li>caller -&gt;com.biz.MyCaller.method(MyCaller.java:18)</li>
* <li>source -&gt;1.6.3.2:33</li>
* </ul>
* The context is bound to a thread (and inherited by sub-threads) but
* it can also be added to by LogTargets.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public final class ContextMap implements Serializable {
///Thread local for holding instance of map associated with current thread
private static final ThreadLocal c_context = new InheritableThreadLocal();
private final ContextMap m_parent;
///Container to hold map of elements
private Map m_map = Collections.synchronizedMap(new HashMap());
///Flag indicating whether this map should be readonly
private transient boolean m_readOnly;
/**
* Get the Current ContextMap.
* This method returns a ContextMap associated with current thread. If the
* thread doesn't have a ContextMap associated with it then a new
* ContextMap is created.
*
* @return the current ContextMap
*/
public final static ContextMap getCurrentContext() {
return getCurrentContext(true);
}
/**
* Get the Current ContextMap.
* This method returns a ContextMap associated with current thread.
* If the thread doesn't have a ContextMap associated with it and
* autocreate is true then a new ContextMap is created.
*
* @param autocreate true if a ContextMap is to be created if it doesn't exist
* @return the current ContextMap
*/
public final static ContextMap getCurrentContext(final boolean autocreate) {
//Check security permission here???
ContextMap context = (ContextMap)c_context.get();
if (null == context && autocreate) {
context = new ContextMap();
c_context.set(context);
}
return context;
}
/**
* Bind a particular ContextMap to current thread.
*
* @param context the context map (may be null)
*/
public final static void bind(final ContextMap context) {
//Check security permission here??
c_context.set(context);
}
/**
* Default constructor.
*/
public ContextMap() {
this(null);
}
/**
* Constructor that sets parent contextMap.
*
* @param parent the parent ContextMap
*/
public ContextMap(final ContextMap parent) {
m_parent = parent;
}
/**
* Make the context read-only.
* This makes it safe to allow untrusted code reference
* to ContextMap.
*/
public void makeReadOnly() {
m_readOnly = true;
}
/**
* Determine if context is read-only.
*
* @return true if Context is read only, false otherwise
*/
public boolean isReadOnly() {
return m_readOnly;
}
/**
* Empty the context map.
*/
public void clear() {
checkReadable();
m_map.clear();
}
/**
* Get an entry from the context.
*
* @param key the key to map
* @param defaultObject a default object to return if key does not exist
* @return the object in context
*/
public Object get(final String key, final Object defaultObject) {
final Object object = get(key);
if (null != object)
return object;
else
return defaultObject;
}
/**
* Get an entry from the context.
*
* @param key the key to map
* @return the object in context or null if none with specified key
*/
public Object get(final String key) {
final Object result = m_map.get(key);
if (null == result && null != m_parent) {
return m_parent.get(key);
}
return result;
}
/**
* Set a value in context
*
* @param key the key
* @param value the value (may be null)
*/
public void set(final String key, final Object value) {
checkReadable();
if (value == null) {
m_map.remove(key);
}
else {
m_map.put(key, value);
}
}
/**
* Get the number of contexts in map.
*
* @return the number of contexts in map
*/
public int getSize() {
return m_map.size();
}
/**
* Helper method that sets context to read-only after de-serialization.
*
* @return the corrected object version
* @throws ObjectStreamException if an error occurs
*/
private Object readResolve() throws ObjectStreamException {
makeReadOnly();
return this;
}
/**
* Utility method to verify that Context is read-only.
*/
private void checkReadable() {
if (isReadOnly()) {
throw new IllegalStateException("ContextMap is read only and can not be modified");
}
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log;
/**
* Interface implemented by components that wish to
* delegate ErrorHandling to an ErrorHandler.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public interface ErrorAware {
/**
* Provide component with ErrorHandler.
*
* @param errorHandler the errorHandler
*/
void setErrorHandler(ErrorHandler errorHandler);
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log;
/**
* Handle unrecoverable errors that occur during logging.
* Based on Log4js notion of ErrorHandlers.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public interface ErrorHandler {
/**
* Log an unrecoverable error.
*
* @param message the error message
* @param throwable the exception associated with error (may be null)
* @param event the LogEvent that caused error, if any (may be null)
*/
void error(String message, Throwable throwable, LogEvent event);
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log;
/**
* A Log target which will do filtering and then pass it
* onto targets further along in chain.
* <p/>
* <p>Filtering can mena that not all LogEvents get passed
* along chain or that the LogEvents passed alongare modified
* in some manner.</p>
*
* @author <a href="mailto:donaldp@apache.org">Peter Donald</a>
*/
public interface FilterTarget extends LogTarget {
/**
* Add a target to output chain.
*
* @param target the log target
*/
void addTarget(LogTarget target);
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log;
import org.jivesoftware.util.log.format.PatternFormatter;
import org.jivesoftware.util.log.output.io.StreamTarget;
import org.jivesoftware.util.log.util.DefaultErrorHandler;
/**
* This class encapsulates a basic independent log hierarchy.
* The hierarchy is essentially a safe wrapper around root logger.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public class Hierarchy {
///Format of default formatter
private static final String FORMAT =
"%7.7{priority} %5.5{time} [%8.8{category}] (%{context}): %{message}\\n%{throwable}";
///The instance of default hierarchy
private static final Hierarchy c_hierarchy = new Hierarchy();
///Error Handler associated with hierarchy
private ErrorHandler m_errorHandler;
///The root logger which contains all Loggers in this hierarchy
private Logger m_rootLogger;
/**
* Retrieve the default hierarchy.
* <p/>
* <p>In most cases the default LogHierarchy is the only
* one used in an application. However when security is
* a concern or multiple independent applications will
* be running in same JVM it is advantageous to create
* new Hierarchies rather than reuse default.</p>
*
* @return the default Hierarchy
*/
public static Hierarchy getDefaultHierarchy() {
return c_hierarchy;
}
/**
* Create a hierarchy object.
* The default LogTarget writes to stdout.
*/
public Hierarchy() {
m_errorHandler = new DefaultErrorHandler();
m_rootLogger = new Logger(new InnerErrorHandler(), "", null, null);
//Setup default output target to print to console
final PatternFormatter formatter = new PatternFormatter(FORMAT);
final StreamTarget target = new StreamTarget(System.out, formatter);
setDefaultLogTarget(target);
}
/**
* Set the default log target for hierarchy.
* This is the target inherited by loggers if no other target is specified.
*
* @param target the default target
*/
public void setDefaultLogTarget(final LogTarget target) {
if (null == target) {
throw new IllegalArgumentException("Can not set DefaultLogTarget to null");
}
final LogTarget[] targets = new LogTarget[]{target};
getRootLogger().setLogTargets(targets);
}
/**
* Set the default log targets for this hierarchy.
* These are the targets inherited by loggers if no other targets are specified
*
* @param targets the default targets
*/
public void setDefaultLogTargets(final LogTarget[] targets) {
if (null == targets || 0 == targets.length) {
throw new IllegalArgumentException("Can not set DefaultLogTargets to null");
}
for (int i = 0; i < targets.length; i++) {
if (null == targets[i]) {
throw new IllegalArgumentException("Can not set DefaultLogTarget element to null");
}
}
getRootLogger().setLogTargets(targets);
}
/**
* Set the default priority for hierarchy.
* This is the priority inherited by loggers if no other priority is specified.
*
* @param priority the default priority
*/
public void setDefaultPriority(final Priority priority) {
if (null == priority) {
throw new IllegalArgumentException("Can not set default Hierarchy Priority to null");
}
getRootLogger().setPriority(priority);
}
/**
* Set the ErrorHandler associated with hierarchy.
*
* @param errorHandler the ErrorHandler
*/
public void setErrorHandler(final ErrorHandler errorHandler) {
if (null == errorHandler) {
throw new IllegalArgumentException("Can not set default Hierarchy ErrorHandler to null");
}
m_errorHandler = errorHandler;
}
/**
* Retrieve a logger for named category.
*
* @param category the context
* @return the Logger
*/
public Logger getLoggerFor(final String category) {
return getRootLogger().getChildLogger(category);
}
// /**
// * Logs an error message to error handler.
// * Default Error Handler is stderr.
// *
// * @param message a message to log
// * @param throwable a Throwable to log
// * @deprecated Logging components should use ErrorHandler rather than Hierarchy.log()
// */
// public void log(final String message, final Throwable throwable) {
// m_errorHandler.error(message, throwable, null);
// }
//
// /**
// * Logs an error message to error handler.
// * Default Error Handler is stderr.
// *
// * @param message a message to log
// * @deprecated Logging components should use ErrorHandler rather than Hierarchy.log()
// */
// public void log(final String message) {
// log(message, null);
// }
private class InnerErrorHandler
implements ErrorHandler {
/**
* Log an unrecoverable error.
*
* @param message the error message
* @param throwable the exception associated with error (may be null)
* @param event the LogEvent that caused error, if any (may be null)
*/
public void error(final String message,
final Throwable throwable,
final LogEvent event) {
m_errorHandler.error(message, throwable, event);
}
}
/**
* Utility method to retrieve logger for hierarchy.
* This method is intended for use by sub-classes
* which can take responsibility for manipulating
* Logger directly.
*
* @return the Logger
*/
protected final Logger getRootLogger() {
return m_rootLogger;
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log;
import java.io.ObjectStreamException;
import java.io.Serializable;
/**
* This class encapsulates each individual log event.
* LogEvents usually originate at a Logger and are routed
* to LogTargets.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public final class LogEvent
implements Serializable {
//A Constant used when retrieving time relative to start of applicaiton start
private final static long START_TIME = System.currentTimeMillis();
///The category that this LogEvent concerns. (Must not be null)
private String m_category;
///The message to be logged. (Must not be null)
private String m_message;
///The exception that caused LogEvent if any. (May be null)
private Throwable m_throwable;
///The time in millis that LogEvent occurred
private long m_time;
///The priority of LogEvent. (Must not be null)
private Priority m_priority;
///The context map associated with LogEvent. (May be null).
private ContextMap m_contextMap;
/**
* Get Priority for LogEvent.
*
* @return the LogEvent Priority
*/
public final Priority getPriority() {
return m_priority;
}
/**
* Set the priority of LogEvent.
*
* @param priority the new LogEvent priority
*/
public final void setPriority(final Priority priority) {
m_priority = priority;
}
/**
* Get ContextMap associated with LogEvent
*
* @return the ContextMap
*/
public final ContextMap getContextMap() {
return m_contextMap;
}
/**
* Set the ContextMap for this LogEvent.
*
* @param contextMap the context map
*/
public final void setContextMap(final ContextMap contextMap) {
m_contextMap = contextMap;
}
// /**
// * Get ContextStack associated with LogEvent
// *
// * @return the ContextStack
// * @deprecated ContextStack has been deprecated and thus so has this method
// */
// public final ContextStack getContextStack()
// {
// return m_contextStack;
// }
// /**
// * Set the ContextStack for this LogEvent.
// * Note that if this LogEvent ever changes threads, the
// * ContextStack must be cloned.
// *
// * @param contextStack the context stack
// * @deprecated ContextStack has been deprecated and thus so has this method
// */
// public final void setContextStack( final ContextStack contextStack )
// {
// m_contextStack = contextStack;
// }
/**
* Get the category that LogEvent relates to.
*
* @return the name of category
*/
public final String getCategory() {
return m_category;
}
/**
* Get the message associated with event.
*
* @return the message
*/
public final String getMessage() {
return m_message;
}
/**
* Get throwabe instance associated with event.
*
* @return the Throwable
*/
public final Throwable getThrowable() {
return m_throwable;
}
/**
* Get the absolute time of the log event.
*
* @return the absolute time
*/
public final long getTime() {
return m_time;
}
/**
* Get the time of the log event relative to start of application.
*
* @return the time
*/
public final long getRelativeTime() {
return m_time - START_TIME;
}
/**
* Set the LogEvent category.
*
* @param category the category
*/
public final void setCategory(final String category) {
m_category = category;
}
/**
* Set the message for LogEvent.
*
* @param message the message
*/
public final void setMessage(final String message) {
m_message = message;
}
/**
* Set the throwable for LogEvent.
*
* @param throwable the instance of Throwable
*/
public final void setThrowable(final Throwable throwable) {
m_throwable = throwable;
}
/**
* Set the absolute time of LogEvent.
*
* @param time the time
*/
public final void setTime(final long time) {
m_time = time;
}
/**
* Helper method that replaces deserialized priority with correct singleton.
*
* @return the singleton version of object
* @throws ObjectStreamException if an error occurs
*/
private Object readResolve()
throws ObjectStreamException {
if (null == m_category) m_category = "";
if (null == m_message) m_message = "";
String priorityName = "";
if (null != m_priority) {
priorityName = m_priority.getName();
}
m_priority = Priority.getPriorityForName(priorityName);
return this;
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log;
/**
* LogTarget is a class to encapsulate outputting LogEvent's.
* This provides the base for all output and filter targets.
* <p/>
* Warning: If performance becomes a problem then this
* interface will be rewritten as a abstract class.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public interface LogTarget {
/**
* Process a log event.
* In NO case should this method ever throw an exception/error.
* The reason is that logging is usually added for debugging/auditing
* purposes and it would be unnaceptable to have your debugging
* code cause more errors.
*
* @param event the event
*/
void processEvent(LogEvent event);
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log;
/**
* The object interacted with by client objects to perform logging.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public class Logger {
///Separator character use to separate different categories
public final static char CATEGORY_SEPARATOR = '.';
///The ErrorHandler associated with Logger
private final ErrorHandler m_errorHandler;
///Logger to inherit logtargets and priorities from
private final Logger m_parent;
///the fully qualified name of category
private final String m_category;
///The list of child loggers associated with this logger
private Logger[] m_children;
///The log-targets this logger writes to
private LogTarget[] m_logTargets;
///Indicate that logTargets were set with setLogTargets() rather than inherited
private boolean m_logTargetsForceSet;
///The priority threshold associated with logger
private Priority m_priority;
///Indicate that priority was set with setPriority() rather than inherited
private boolean m_priorityForceSet;
/**
* True means LogEvents will be sent to parents LogTargets
* aswell as the ones set for this Logger.
*/
private boolean m_additivity;
/**
* Protected constructor for use inside the logging toolkit.
* You should not be using this constructor directly.
*
* @param errorHandler the ErrorHandler logger uses to log errors
* @param category the fully qualified name of category
* @param logTargets the LogTargets associated with logger
* @param parent the parent logger (used for inheriting from)
*/
Logger(final ErrorHandler errorHandler,
final String category,
final LogTarget[] logTargets,
final Logger parent) {
m_errorHandler = errorHandler;
m_category = category;
m_logTargets = logTargets;
m_parent = parent;
if (null == m_logTargets) {
unsetLogTargets();
}
unsetPriority();
}
/**
* Determine if messages of priority DEBUG will be logged.
*
* @return true if DEBUG messages will be logged
*/
public final boolean isDebugEnabled() {
return m_priority.isLowerOrEqual(Priority.DEBUG);
}
/**
* Log a debug priority event.
*
* @param message the message
* @param throwable the throwable
*/
public final void debug(final String message, final Throwable throwable) {
if (isDebugEnabled()) {
output(Priority.DEBUG, message, throwable);
}
}
/**
* Log a debug priority event.
*
* @param message the message
*/
public final void debug(final String message) {
if (isDebugEnabled()) {
output(Priority.DEBUG, message, null);
}
}
/**
* Determine if messages of priority INFO will be logged.
*
* @return true if INFO messages will be logged
*/
public final boolean isInfoEnabled() {
return m_priority.isLowerOrEqual(Priority.INFO);
}
/**
* Log a info priority event.
*
* @param message the message
*/
public final void info(final String message, final Throwable throwable) {
if (isInfoEnabled()) {
output(Priority.INFO, message, throwable);
}
}
/**
* Log a info priority event.
*
* @param message the message
*/
public final void info(final String message) {
if (isInfoEnabled()) {
output(Priority.INFO, message, null);
}
}
/**
* Determine if messages of priority WARN will be logged.
*
* @return true if WARN messages will be logged
*/
public final boolean isWarnEnabled() {
return m_priority.isLowerOrEqual(Priority.WARN);
}
/**
* Log a warn priority event.
*
* @param message the message
* @param throwable the throwable
*/
public final void warn(final String message, final Throwable throwable) {
if (isWarnEnabled()) {
output(Priority.WARN, message, throwable);
}
}
/**
* Log a warn priority event.
*
* @param message the message
*/
public final void warn(final String message) {
if (isWarnEnabled()) {
output(Priority.WARN, message, null);
}
}
/**
* Determine if messages of priority ERROR will be logged.
*
* @return true if ERROR messages will be logged
*/
public final boolean isErrorEnabled() {
return m_priority.isLowerOrEqual(Priority.ERROR);
}
/**
* Log a error priority event.
*
* @param message the message
* @param throwable the throwable
*/
public final void error(final String message, final Throwable throwable) {
if (isErrorEnabled()) {
output(Priority.ERROR, message, throwable);
}
}
/**
* Log a error priority event.
*
* @param message the message
*/
public final void error(final String message) {
if (isErrorEnabled()) {
output(Priority.ERROR, message, null);
}
}
/**
* Determine if messages of priority FATAL_ERROR will be logged.
*
* @return true if FATAL_ERROR messages will be logged
*/
public final boolean isFatalErrorEnabled() {
return m_priority.isLowerOrEqual(Priority.FATAL_ERROR);
}
/**
* Log a fatalError priority event.
*
* @param message the message
* @param throwable the throwable
*/
public final void fatalError(final String message, final Throwable throwable) {
if (isFatalErrorEnabled()) {
output(Priority.FATAL_ERROR, message, throwable);
}
}
/**
* Log a fatalError priority event.
*
* @param message the message
*/
public final void fatalError(final String message) {
if (isFatalErrorEnabled()) {
output(Priority.FATAL_ERROR, message, null);
}
}
/**
* Make this logger additive, which means send all log events to parent
* loggers LogTargets regardless of whether or not the
* LogTargets have been overidden.
* <p/>
* This is derived from Log4js notion of Additivity.
*
* @param additivity true to make logger additive, false otherwise
*/
public final void setAdditivity(final boolean additivity) {
m_additivity = additivity;
}
/**
* Determine if messages of priority will be logged.
*
* @return true if messages will be logged
*/
public final boolean isPriorityEnabled(final Priority priority) {
return m_priority.isLowerOrEqual(priority);
}
/**
* Log a event at specific priority with a certain message and throwable.
*
* @param message the message
* @param priority the priority
* @param throwable the throwable
*/
public final void log(final Priority priority,
final String message,
final Throwable throwable) {
if (m_priority.isLowerOrEqual(priority)) {
output(priority, message, throwable);
}
}
/**
* Log a event at specific priority with a certain message.
*
* @param message the message
* @param priority the priority
*/
public final void log(final Priority priority, final String message) {
log(priority, message, null);
}
/**
* Set the priority for this logger.
*
* @param priority the priority
*/
public synchronized void setPriority(final Priority priority) {
m_priority = priority;
m_priorityForceSet = true;
resetChildPriorities(false);
}
/**
* Unset the priority of Logger.
* (Thus it will use it's parent's priority or DEBUG if no parent.
*/
public synchronized void unsetPriority() {
unsetPriority(false);
}
/**
* Unset the priority of Logger.
* (Thus it will use it's parent's priority or DEBUG if no parent.
* If recursive is true unset priorities of all child loggers.
*
* @param recursive true to unset priority of all child loggers
*/
public synchronized void unsetPriority(final boolean recursive) {
if (null != m_parent)
m_priority = m_parent.m_priority;
else
m_priority = Priority.DEBUG;
m_priorityForceSet = false;
resetChildPriorities(recursive);
}
/**
* Set the log targets for this logger.
*
* @param logTargets the Log Targets
*/
public synchronized void setLogTargets(final LogTarget[] logTargets) {
m_logTargets = logTargets;
setupErrorHandlers();
m_logTargetsForceSet = true;
resetChildLogTargets(false);
}
/**
* Unset the logtargets for this logger.
* This logger (and thus all child loggers who don't specify logtargets) will
* inherit from the parents LogTargets.
*/
public synchronized void unsetLogTargets() {
unsetLogTargets(false);
}
/**
* Unset the logtargets for this logger and all child loggers if recursive is set.
* The loggers unset (and all child loggers who don't specify logtargets) will
* inherit from the parents LogTargets.
*/
public synchronized void unsetLogTargets(final boolean recursive) {
if (null != m_parent)
m_logTargets = m_parent.safeGetLogTargets();
else
m_logTargets = null;
m_logTargetsForceSet = false;
resetChildLogTargets(recursive);
}
/**
* Get all the child Loggers of current logger.
*
* @return the child loggers
*/
public synchronized Logger[] getChildren() {
if (null == m_children) return new Logger[0];
final Logger[] children = new Logger[m_children.length];
for (int i = 0; i < children.length; i++) {
children[i] = m_children[i];
}
return children;
}
/**
* Create a new child logger.
* The category of child logger is [current-category].subcategory
*
* @param subCategory the subcategory of this logger
* @return the new logger
* @throws IllegalArgumentException if subCategory has an empty element name
*/
public synchronized Logger getChildLogger(final String subCategory)
throws IllegalArgumentException {
final int end = subCategory.indexOf(CATEGORY_SEPARATOR);
String nextCategory = null;
String remainder = null;
if (-1 == end)
nextCategory = subCategory;
else {
if (end == 0) {
throw new IllegalArgumentException("Logger categories MUST not have empty elements");
}
nextCategory = subCategory.substring(0, end);
remainder = subCategory.substring(end + 1);
}
//Get FQN for category
String category = null;
if (m_category.equals(""))
category = nextCategory;
else {
category = m_category + CATEGORY_SEPARATOR + nextCategory;
}
//Check existing children to see if they
//contain next Logger for step in category
if (null != m_children) {
for (int i = 0; i < m_children.length; i++) {
if (m_children[i].m_category.equals(category)) {
if (null == remainder)
return m_children[i];
else
return m_children[i].getChildLogger(remainder);
}
}
}
//Create new logger
final Logger child = new Logger(m_errorHandler, category, null, this);
//Add new logger to child list
if (null == m_children) {
m_children = new Logger[]{child};
}
else {
final Logger[] children = new Logger[m_children.length + 1];
System.arraycopy(m_children, 0, children, 0, m_children.length);
children[m_children.length] = child;
m_children = children;
}
if (null == remainder)
return child;
else
return child.getChildLogger(remainder);
}
/**
* Retrieve priority associated with Logger.
*
* @return the loggers priority
* @deprecated This method violates Inversion of Control principle.
* It will downgraded to protected access in a future
* release. When user needs to check priority it is advised
* that they use the is[Priority]Enabled() functions.
*/
public final Priority getPriority() {
return m_priority;
}
/**
* Retrieve category associated with logger.
*
* @return the Category
* @deprecated This method violates Inversion of Control principle.
* If you are relying on its presence then there may be
* something wrong with the design of your system
*/
public final String getCategory() {
return m_category;
}
/**
* Get a copy of log targets for this logger.
*
* @return the child loggers
*/
public LogTarget[] getLogTargets() {
// Jive change - we ignore the deprecated warning above and just return the log targets
// since it's a closed system for us anyways
return m_logTargets;
}
/**
* Internal method to do actual outputting.
*
* @param priority the priority
* @param message the message
* @param throwable the throwable
*/
private final void output(final Priority priority,
final String message,
final Throwable throwable) {
final LogEvent event = new LogEvent();
event.setCategory(m_category);
// event.setContextStack( ContextStack.getCurrentContext( false ) );
event.setContextMap(ContextMap.getCurrentContext(false));
if (null != message) {
event.setMessage(message);
}
else {
event.setMessage("");
}
event.setThrowable(throwable);
event.setPriority(priority);
//this next line can kill performance. It may be wise to
//disable it sometimes and use a more granular approach
event.setTime(System.currentTimeMillis());
output(event);
}
private final void output(final LogEvent event) {
//cache a copy of targets for thread safety
//It is now possible for another thread
//to replace m_logTargets
final LogTarget[] targets = m_logTargets;
if (null == targets) {
final String message = "LogTarget is null for category '" + m_category + "'";
m_errorHandler.error(message, null, event);
}
else if (!m_additivity) {
fireEvent(event, targets);
}
else {
//If log targets were not inherited, additivity is true
//then fire an event to local targets
if (m_logTargetsForceSet) {
fireEvent(event, targets);
}
//if we have a parent Logger then send log event to parent
if (null != m_parent) {
m_parent.output(event);
}
}
}
private final void fireEvent(final LogEvent event, final LogTarget[] targets) {
for (int i = 0; i < targets.length; i++) {
//No need to clone array as addition of a log-target
//will result in changin whole array
targets[i].processEvent(event);
}
}
/**
* Update priority of children if any.
*/
private synchronized void resetChildPriorities(final boolean recursive) {
if (null == m_children) return;
final Logger[] children = m_children;
for (int i = 0; i < children.length; i++) {
children[i].resetPriority(recursive);
}
}
/**
* Update priority of this Logger.
* If this loggers priority was manually set then ignore
* otherwise get parents priority and update all children's priority.
*/
private synchronized void resetPriority(final boolean recursive) {
if (recursive) {
m_priorityForceSet = false;
}
else if (m_priorityForceSet) {
return;
}
m_priority = m_parent.m_priority;
resetChildPriorities(recursive);
}
/**
* Retrieve logtarget array contained in logger.
* This method is provided so that child Loggers can access a
* copy of parents LogTargets.
*
* @return the array of LogTargets
*/
private synchronized LogTarget[] safeGetLogTargets() {
if (null == m_logTargets) {
if (null == m_parent)
return new LogTarget[0];
else
return m_parent.safeGetLogTargets();
}
else {
final LogTarget[] logTargets = new LogTarget[m_logTargets.length];
for (int i = 0; i < logTargets.length; i++) {
logTargets[i] = m_logTargets[i];
}
return logTargets;
}
}
/**
* Update logTargets of children if any.
*/
private synchronized void resetChildLogTargets(final boolean recursive) {
if (null == m_children) return;
for (int i = 0; i < m_children.length; i++) {
m_children[i].resetLogTargets(recursive);
}
}
/**
* Set ErrorHandlers of LogTargets if necessary.
*/
private synchronized void setupErrorHandlers() {
if (null == m_logTargets) return;
for (int i = 0; i < m_logTargets.length; i++) {
final LogTarget target = m_logTargets[i];
if (target instanceof ErrorAware) {
((ErrorAware)target).setErrorHandler(m_errorHandler);
}
}
}
/**
* Update logTarget of this Logger.
* If this loggers logTarget was manually set then ignore
* otherwise get parents logTarget and update all children's logTarget.
*/
private synchronized void resetLogTargets(final boolean recursive) {
if (recursive) {
m_logTargetsForceSet = false;
}
else if (m_logTargetsForceSet) {
return;
}
m_logTargets = m_parent.safeGetLogTargets();
resetChildLogTargets(recursive);
}
}
\ No newline at end of file
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log;
import java.io.ObjectStreamException;
import java.io.Serializable;
/**
* Class representing and holding constants for priority.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public final class Priority implements Serializable {
/**
* Developer orientated messages, usually used during development of product.
*/
public final static Priority DEBUG = new Priority("DEBUG", 5);
/**
* Useful information messages such as state changes, client connection, user login etc.
*/
public final static Priority INFO = new Priority("INFO", 10);
/**
* A problem or conflict has occurred but it may be recoverable, then
* again it could be the start of the system failing.
*/
public final static Priority WARN = new Priority("WARN", 15);
/**
* A problem has occurred but it is not fatal. The system will still function.
*/
public final static Priority ERROR = new Priority("ERROR", 20);
/**
* Something caused whole system to fail. This indicates that an administrator
* should restart the system and try to fix the problem that caused the failure.
*/
public final static Priority FATAL_ERROR = new Priority("FATAL_ERROR", 25);
private final String m_name;
private final int m_priority;
/**
* Retrieve a Priority object for the name parameter.
*
* @param priority the priority name
* @return the Priority for name
*/
public static Priority getPriorityForName(final String priority) {
if (Priority.DEBUG.getName().equals(priority))
return Priority.DEBUG;
else if (Priority.INFO.getName().equals(priority))
return Priority.INFO;
else if (Priority.WARN.getName().equals(priority))
return Priority.WARN;
else if (Priority.ERROR.getName().equals(priority))
return Priority.ERROR;
else if (Priority.FATAL_ERROR.getName().equals(priority))
return Priority.FATAL_ERROR;
else
return Priority.DEBUG;
}
/**
* Private Constructor to block instantiation outside class.
*
* @param name the string name of priority
* @param priority the numerical code of priority
*/
private Priority(final String name, final int priority) {
m_name = name;
m_priority = priority;
}
/**
* Overidden string to display Priority in human readable form.
*
* @return the string describing priority
*/
public String toString() {
return "Priority[" + getName() + "/" + getValue() + "]";
}
/**
* Get numerical value associated with priority.
*
* @return the numerical value
*/
public int getValue() {
return m_priority;
}
/**
* Get name of priority.
*
* @return the priorities name
*/
public String getName() {
return m_name;
}
/**
* Test whether this priority is greater than other priority.
*
* @param other the other Priority
*/
public boolean isGreater(final Priority other) {
return m_priority > other.getValue();
}
/**
* Test whether this priority is lower than other priority.
*
* @param other the other Priority
*/
public boolean isLower(final Priority other) {
return m_priority < other.getValue();
}
/**
* Test whether this priority is lower or equal to other priority.
*
* @param other the other Priority
*/
public boolean isLowerOrEqual(final Priority other) {
return m_priority <= other.getValue();
}
/**
* Helper method that replaces deserialized object with correct singleton.
*
* @return the singleton version of object
* @throws ObjectStreamException if an error occurs
*/
private Object readResolve()
throws ObjectStreamException {
return getPriorityForName(m_name);
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.filter;
import org.jivesoftware.util.log.FilterTarget;
import org.jivesoftware.util.log.LogEvent;
import org.jivesoftware.util.log.LogTarget;
/**
* Abstract implementation of FilterTarget.
* A concrete implementation has to implement filter method.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public abstract class AbstractFilterTarget
implements FilterTarget, LogTarget {
//Log targets in filter chain
private LogTarget m_targets[];
/**
* Add a new target to output chain.
*
* @param target the target
*/
public void addTarget(final LogTarget target) {
if (null == m_targets) {
m_targets = new LogTarget[]{target};
}
else {
final LogTarget oldTargets[] = m_targets;
m_targets = new LogTarget[oldTargets.length + 1];
System.arraycopy(oldTargets, 0, m_targets, 0, oldTargets.length);
m_targets[m_targets.length - 1] = target;
}
}
/**
* Filter the log event.
*
* @param event the event
* @return return true to discard event, false otherwise
*/
protected abstract boolean filter(LogEvent event);
/**
* Process a log event
*
* @param event the log event
*/
public void processEvent(final LogEvent event) {
if (null == m_targets || filter(event))
return;
else {
for (int i = 0; i < m_targets.length; i++) {
m_targets[i].processEvent(event);
}
}
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.filter;
import org.jivesoftware.util.log.LogEvent;
import org.jivesoftware.util.log.Priority;
/**
* Filters log events based on priority.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public class PriorityFilter extends AbstractFilterTarget {
///Priority to filter against
private Priority m_priority;
/**
* Constructor that sets the priority that is filtered against.
*
* @param priority the Priority
*/
public PriorityFilter(final Priority priority) {
m_priority = priority;
}
/**
* Set priority used to filter.
*
* @param priority the priority to filter on
*/
public void setPriority(final Priority priority) {
m_priority = priority;
}
/**
* Filter the log event based on priority.
* <p/>
* If LogEvent has a Lower priroity then discard it.
*
* @param event the event
* @return return true to discard event, false otherwise
*/
protected boolean filter(final LogEvent event) {
return (!m_priority.isLower(event.getPriority()));
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.format;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.log.ContextMap;
import org.jivesoftware.util.log.LogEvent;
import org.jivesoftware.util.log.util.StackIntrospector;
/**
* Formatter especially designed for debugging applications.
* <p/>
* This formatter extends the standard PatternFormatter to add
* two new possible expansions. These expansions are %{method}
* and %{thread}. In both cases the context map is first checked
* for values with specified key. This is to facilitate passing
* information about caller/thread when threads change (as in
* AsyncLogTarget). They then attempt to determine appropriate
* information dynamically.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version CVS $Revision$ $Date$
*/
public class ExtendedPatternFormatter extends PatternFormatter {
private final static int TYPE_METHOD = MAX_TYPE + 1;
private final static int TYPE_THREAD = MAX_TYPE + 2;
private final static String TYPE_METHOD_STR = "method";
private final static String TYPE_THREAD_STR = "thread";
public ExtendedPatternFormatter(final String format) {
super(format);
}
/**
* Retrieve the type-id for a particular string.
*
* @param type the string
* @return the type-id
*/
protected int getTypeIdFor(final String type) {
if (type.equalsIgnoreCase(TYPE_METHOD_STR))
return TYPE_METHOD;
else if (type.equalsIgnoreCase(TYPE_THREAD_STR))
return TYPE_THREAD;
else {
return super.getTypeIdFor(type);
}
}
/**
* Formats a single pattern run (can be extended in subclasses).
*
* @param run the pattern run to format.
* @return the formatted result.
*/
protected String formatPatternRun(final LogEvent event, final PatternRun run) {
switch (run.m_type) {
case TYPE_METHOD:
return getMethod(event, run.m_format);
case TYPE_THREAD:
return getThread(event, run.m_format);
default:
return super.formatPatternRun(event, run);
}
}
/**
* Utility method to format category.
*
* @param event
* @param format ancilliary format parameter - allowed to be null
* @return the formatted string
*/
private String getMethod(final LogEvent event, final String format) {
final ContextMap map = event.getContextMap();
if (null != map) {
final Object object = map.get("method");
if (null != object) {
return object.toString();
}
}
// final String result = StackIntrospector.getCallerMethod(Logger.class);
final String result = StackIntrospector.getCallerMethod(Log.class);
if (null == result) {
return "UnknownMethod";
}
return result;
}
/**
* Utility thread to format category.
*
* @param event
* @param format ancilliary format parameter - allowed to be null
* @return the formatted string
*/
private String getThread(final LogEvent event, final String format) {
final ContextMap map = event.getContextMap();
if (null != map) {
final Object object = map.get("thread");
if (null != object) {
return object.toString();
}
}
return Thread.currentThread().getName();
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.format;
import org.jivesoftware.util.log.LogEvent;
/**
* This defines the interface for components that wish to serialize
* LogEvents into Strings.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public interface Formatter {
/**
* Serialize log event into string.
*
* @param event the event
* @return the formatted string
*/
String format(LogEvent event);
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.format;
import org.jivesoftware.util.FastDateFormat;
import org.jivesoftware.util.log.ContextMap;
import org.jivesoftware.util.log.LogEvent;
import org.jivesoftware.util.log.Priority;
import java.io.StringWriter;
import java.util.Date;
import java.util.Stack;
/**
* This formater formats the LogEvents according to a input pattern
* string.
* <p/>
* The format of each pattern element can be %[+|-][#[.#]]{field:subformat}.
* </p>
* <ul>
* <li>The +|- indicates left or right justify.
* </li>
* <li>The #.# indicates the minimum and maximum size of output.<br>
* You may omit the values and the field will be formatted without size
* restriction.<br>
* You may specify '#', or '#.' to define an minimum size, only.</br>
* You may specify '.#' to define an maximum size only.
* </li>
* <li>
* 'field' indicates which field is to be output and must be one of
* properties of LogEvent.<br>
* Currently following fields are supported:
* <dl>
* <dt>category</dt>
* <dd>Category value of the logging event.</dd>
* <dt>context</dt>
* <dd>Context value of the logging event.</dd>
* <dt>message</dt>
* <dd>Message value of the logging event.</dd>
* <dt>time</dt>
* <dd>Time value of the logging event.</dd>
* <dt>rtime</dt>
* <dd>Relative time value of the logging event.</dd>
* <dt>throwable</dt>
* <dd>Throwable value of the logging event.</dd>
* <dt>priority</dt>
* <dd>Priority value of the logging event.</dd>
* </dl>
* </li>
* <li>'subformat' indicates a particular subformat and is currently only used
* for category context to specify the context map parameter name.
* </li>
* </ul>
* <p>A simple example of a typical PatternFormatter format:
* </p>
* <pre><code>%{time} %5.5{priority}[%-10.10{category}]: %{message}
* </pre></code>
* <p/>
* This format string will format a log event printing first time value of
* of log event with out size restriction, next priority with minum and maximum size 5,
* next category right justified having minmum and maximum size of 10,
* at last the message of the log event without size restriction.
* </p>
* <p>A formatted sample message of the above pattern format:
* </p>
* <pre><code>1000928827905 DEBUG [ junit]: Sample message
* </pre><code>
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
* @version CVS $Revision$ $Date$
*/
public class PatternFormatter implements Formatter {
private final static int TYPE_TEXT = 1;
private final static int TYPE_CATEGORY = 2;
private final static int TYPE_CONTEXT = 3;
private final static int TYPE_MESSAGE = 4;
private final static int TYPE_TIME = 5;
private final static int TYPE_RELATIVE_TIME = 6;
private final static int TYPE_THROWABLE = 7;
private final static int TYPE_PRIORITY = 8;
/**
* The maximum value used for TYPEs. Subclasses can define their own TYPEs
* starting at <code>MAX_TYPE + 1</code>.
*/
protected final static int MAX_TYPE = TYPE_PRIORITY;
private final static String TYPE_CATEGORY_STR = "category";
private final static String TYPE_CONTEXT_STR = "context";
private final static String TYPE_MESSAGE_STR = "message";
private final static String TYPE_TIME_STR = "time";
private final static String TYPE_RELATIVE_TIME_STR = "rtime";
private final static String TYPE_THROWABLE_STR = "throwable";
private final static String TYPE_PRIORITY_STR = "priority";
private final static String SPACE_16 = " ";
private final static String SPACE_8 = " ";
private final static String SPACE_4 = " ";
private final static String SPACE_2 = " ";
private final static String SPACE_1 = " ";
private final static String EOL = System.getProperty("line.separator", "\n");
protected static class PatternRun {
public String m_data;
public boolean m_rightJustify;
public int m_minSize;
public int m_maxSize;
public int m_type;
public String m_format;
}
private PatternRun m_formatSpecification[];
private FastDateFormat m_simpleDateFormat;
private final Date m_date = new Date();
/**
* @deprecated Use constructor PatternFormatter(String pattern) as this does not
* correctly initialize object
*/
public PatternFormatter() {
}
public PatternFormatter(final String pattern) {
parse(pattern);
}
/**
* Extract and build a pattern from input string.
*
* @param stack the stack on which to place patterns
* @param pattern the input string
* @param index the start of pattern run
* @return the number of characters in pattern run
*/
private int addPatternRun(final Stack stack,
final char pattern[],
int index) {
final PatternRun run = new PatternRun();
final int start = index++;
//first check for a +|- sign
if ('+' == pattern[index])
index++;
else if ('-' == pattern[index]) {
run.m_rightJustify = true;
index++;
}
if (Character.isDigit(pattern[index])) {
int total = 0;
while (Character.isDigit(pattern[index])) {
total = total * 10 + (pattern[index] - '0');
index++;
}
run.m_minSize = total;
}
//check for . sign indicating a maximum is to follow
if (index < pattern.length && '.' == pattern[index]) {
index++;
if (Character.isDigit(pattern[index])) {
int total = 0;
while (Character.isDigit(pattern[index])) {
total = total * 10 + (pattern[index] - '0');
index++;
}
run.m_maxSize = total;
}
}
if (index >= pattern.length || '{' != pattern[index]) {
throw
new IllegalArgumentException("Badly formed pattern at character " +
index);
}
int typeStart = index;
while (index < pattern.length &&
pattern[index] != ':' && pattern[index] != '}') {
index++;
}
int typeEnd = index - 1;
final String type =
new String(pattern, typeStart + 1, typeEnd - typeStart);
run.m_type = getTypeIdFor(type);
if (index < pattern.length && pattern[index] == ':') {
index++;
while (index < pattern.length && pattern[index] != '}') index++;
final int length = index - typeEnd - 2;
if (0 != length) {
run.m_format = new String(pattern, typeEnd + 2, length);
}
}
if (index >= pattern.length || '}' != pattern[index]) {
throw new
IllegalArgumentException("Unterminated type in pattern at character "
+ index);
}
index++;
stack.push(run);
return index - start;
}
/**
* Extract and build a text run from input string.
* It does special handling of '\n' and '\t' replaceing
* them with newline and tab.
*
* @param stack the stack on which to place runs
* @param pattern the input string
* @param index the start of the text run
* @return the number of characters in run
*/
private int addTextRun(final Stack stack,
final char pattern[],
int index) {
final PatternRun run = new PatternRun();
final int start = index;
boolean escapeMode = false;
if ('%' == pattern[index]) index++;
final StringBuffer sb = new StringBuffer();
while (index < pattern.length && pattern[index] != '%') {
if (escapeMode) {
if ('n' == pattern[index])
sb.append(EOL);
else if ('t' == pattern[index])
sb.append('\t');
else
sb.append(pattern[index]);
escapeMode = false;
}
else if ('\\' == pattern[index])
escapeMode = true;
else
sb.append(pattern[index]);
index++;
}
run.m_data = sb.toString();
run.m_type = TYPE_TEXT;
stack.push(run);
return index - start;
}
/**
* Utility to append a string to buffer given certain constraints.
*
* @param sb the StringBuffer
* @param minSize the minimum size of output (0 to ignore)
* @param maxSize the maximum size of output (0 to ignore)
* @param rightJustify true if the string is to be right justified in it's box.
* @param output the input string
*/
private void append(final StringBuffer sb,
final int minSize,
final int maxSize,
final boolean rightJustify,
final String output) {
final int size = output.length();
if (size < minSize) {
//assert( minSize > 0 );
if (rightJustify) {
appendWhiteSpace(sb, minSize - size);
sb.append(output);
}
else {
sb.append(output);
appendWhiteSpace(sb, minSize - size);
}
}
else if (maxSize > 0 && maxSize < size) {
if (rightJustify) {
sb.append(output.substring(size - maxSize));
}
else {
sb.append(output.substring(0, maxSize));
}
}
else {
sb.append(output);
}
}
/**
* Append a certain number of whitespace characters to a StringBuffer.
*
* @param sb the StringBuffer
* @param length the number of spaces to append
*/
private void appendWhiteSpace(final StringBuffer sb, int length) {
while (length >= 16) {
sb.append(SPACE_16);
length -= 16;
}
if (length >= 8) {
sb.append(SPACE_8);
length -= 8;
}
if (length >= 4) {
sb.append(SPACE_4);
length -= 4;
}
if (length >= 2) {
sb.append(SPACE_2);
length -= 2;
}
if (length >= 1) {
sb.append(SPACE_1);
length -= 1;
}
}
/**
* Format the event according to the pattern.
*
* @param event the event
* @return the formatted output
*/
public String format(final LogEvent event) {
final StringBuffer sb = new StringBuffer();
for (int i = 0; i < m_formatSpecification.length; i++) {
final PatternRun run = m_formatSpecification[i];
//treat text differently as it doesn't need min/max padding
if (run.m_type == TYPE_TEXT) {
sb.append(run.m_data);
}
else {
final String data = formatPatternRun(event, run);
if (null != data) {
append(sb, run.m_minSize, run.m_maxSize, run.m_rightJustify, data);
}
}
}
return sb.toString();
}
/**
* Formats a single pattern run (can be extended in subclasses).
*
* @param run the pattern run to format.
* @return the formatted result.
*/
protected String formatPatternRun(final LogEvent event, final PatternRun run) {
switch (run.m_type) {
case TYPE_RELATIVE_TIME:
return getRTime(event.getRelativeTime(), run.m_format);
case TYPE_TIME:
return getTime(event.getTime(), run.m_format);
case TYPE_THROWABLE:
return getStackTrace(event.getThrowable(), run.m_format);
case TYPE_MESSAGE:
return getMessage(event.getMessage(), run.m_format);
case TYPE_CATEGORY:
return getCategory(event.getCategory(), run.m_format);
case TYPE_PRIORITY:
return getPriority(event.getPriority(), run.m_format);
case TYPE_CONTEXT:
// if( null == run.m_format ||
// run.m_format.startsWith( "stack" ) )
// {
// //Print a warning out to stderr here
// //to indicate you are using a deprecated feature?
// return getContext( event.getContextStack(), run.m_format );
// }
// else
// {
return getContextMap(event.getContextMap(), run.m_format);
// }
default:
throw new IllegalStateException("Unknown Pattern specification." + run.m_type);
}
}
/**
* Utility method to format category.
*
* @param category the category string
* @param format ancilliary format parameter - allowed to be null
* @return the formatted string
*/
protected String getCategory(final String category, final String format) {
return category;
}
/**
* Get formatted priority string.
*/
protected String getPriority(final Priority priority, final String format) {
return priority.getName();
}
// /**
// * Utility method to format context.
// *
// * @param context the context string
// * @param format ancilliary format parameter - allowed to be null
// * @return the formatted string
// * @deprecated Use getContextStack rather than this method
// */
// protected String getContext( final ContextStack stack, final String format )
// {
// return getContextStack( stack, format );
// }
// /**
// * Utility method to format context.
// *
// * @param context the context string
// * @param format ancilliary format parameter - allowed to be null
// * @return the formatted string
// */
// protected String getContextStack( final ContextStack stack, final String format )
// {
// if( null == stack ) return "";
// return stack.toString( Integer.MAX_VALUE );
// }
/**
* Utility method to format context map.
*
* @param map the context map
* @param format ancilliary format parameter - allowed to be null
* @return the formatted string
*/
protected String getContextMap(final ContextMap map, final String format) {
if (null == map) return "";
return map.get(format, "").toString();
}
/**
* Utility method to format message.
*
* @param message the message string
* @param format ancilliary format parameter - allowed to be null
* @return the formatted string
*/
protected String getMessage(final String message, final String format) {
return message;
}
/**
* Utility method to format stack trace.
*
* @param throwable the throwable instance
* @param format ancilliary format parameter - allowed to be null
* @return the formatted string
*/
protected String getStackTrace(final Throwable throwable, final String format) {
if (null == throwable) return "";
final StringWriter sw = new StringWriter();
throwable.printStackTrace(new java.io.PrintWriter(sw));
return sw.toString();
}
/**
* Utility method to format relative time.
*
* @param time the time
* @param format ancilliary format parameter - allowed to be null
* @return the formatted string
*/
protected String getRTime(final long time, final String format) {
return getTime(time, format);
}
/**
* Utility method to format time.
*
* @param time the time
* @param format ancilliary format parameter - allowed to be null
* @return the formatted string
*/
protected String getTime(final long time, final String format) {
if (null == format) {
return Long.toString(time);
}
else {
synchronized (m_date) {
if (null == m_simpleDateFormat) {
m_simpleDateFormat = FastDateFormat.getInstance(format);
}
m_date.setTime(time);
return m_simpleDateFormat.format(m_date);
}
}
}
/**
* Retrieve the type-id for a particular string.
*
* @param type the string
* @return the type-id
*/
protected int getTypeIdFor(final String type) {
if (type.equalsIgnoreCase(TYPE_CATEGORY_STR))
return TYPE_CATEGORY;
else if (type.equalsIgnoreCase(TYPE_CONTEXT_STR))
return TYPE_CONTEXT;
else if (type.equalsIgnoreCase(TYPE_MESSAGE_STR))
return TYPE_MESSAGE;
else if (type.equalsIgnoreCase(TYPE_PRIORITY_STR))
return TYPE_PRIORITY;
else if (type.equalsIgnoreCase(TYPE_TIME_STR))
return TYPE_TIME;
else if (type.equalsIgnoreCase(TYPE_RELATIVE_TIME_STR))
return TYPE_RELATIVE_TIME;
else if (type.equalsIgnoreCase(TYPE_THROWABLE_STR)) {
return TYPE_THROWABLE;
}
else {
throw new IllegalArgumentException("Unknown Type in pattern - " +
type);
}
}
/**
* Parse the input pattern and build internal data structures.
*
* @param patternString the pattern
*/
protected final void parse(final String patternString) {
final Stack stack = new Stack();
final int size = patternString.length();
final char pattern[] = new char[size];
int index = 0;
patternString.getChars(0, size, pattern, 0);
while (index < size) {
if (pattern[index] == '%' &&
!(index != size - 1 && pattern[index + 1] == '%')) {
index += addPatternRun(stack, pattern, index);
}
else {
index += addTextRun(stack, pattern, index);
}
}
final int elementCount = stack.size();
m_formatSpecification = new PatternRun[elementCount];
for (int i = 0; i < elementCount; i++) {
m_formatSpecification[i] = (PatternRun)stack.elementAt(i);
}
}
/**
* Set the string description that the format is extracted from.
*
* @param format the string format
* @deprecated Parse format in via constructor rather than use this method
*/
public void setFormat(final String format) {
parse(format);
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output;
import org.jivesoftware.util.log.LogEvent;
import org.jivesoftware.util.log.format.Formatter;
/**
* Abstract output target.
* Any new output target that is writing to a single connected
* resource should extend this class directly or indirectly.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public abstract class AbstractOutputTarget
extends AbstractTarget {
/**
* Formatter for target.
*/
private Formatter m_formatter;
/**
* Parameterless constructor.
*/
public AbstractOutputTarget() {
}
public AbstractOutputTarget(final Formatter formatter) {
m_formatter = formatter;
}
/**
* Retrieve the associated formatter.
*
* @return the formatter
* @deprecated Access to formatter is not advised and this method will be removed
* in future iterations. It remains only for backwards compatability.
*/
public synchronized Formatter getFormatter() {
return m_formatter;
}
/**
* Set the formatter.
*
* @param formatter the formatter
* @deprecated In future this method will become protected access.
*/
public synchronized void setFormatter(final Formatter formatter) {
writeTail();
m_formatter = formatter;
writeHead();
}
/**
* Abstract method to send data.
*
* @param data the data to be output
*/
protected void write(final String data) {
output(data);
}
/**
* Abstract method that will output event.
*
* @param data the data to be output
* @deprecated User should overide send() instead of output(). Output exists
* for backwards compatability and will be removed in future.
*/
protected void output(final String data) {
}
protected void doProcessEvent(LogEvent event) {
final String data = format(event);
write(data);
}
/**
* Startup log session.
*/
protected synchronized void open() {
if (!isOpen()) {
super.open();
writeHead();
}
}
/**
* Shutdown target.
* Attempting to send to target after close() will cause errors to be logged.
*/
public synchronized void close() {
if (isOpen()) {
writeTail();
super.close();
}
}
/**
* Helper method to format an event into a string, using the formatter if available.
*
* @param event the LogEvent
* @return the formatted string
*/
private String format(final LogEvent event) {
if (null != m_formatter) {
return m_formatter.format(event);
}
else {
return event.toString();
}
}
/**
* Helper method to send out log head.
* The head initiates a session of logging.
*/
private void writeHead() {
if (!isOpen()) return;
final String head = getHead();
if (null != head) {
write(head);
}
}
/**
* Helper method to send out log tail.
* The tail completes a session of logging.
*/
private void writeTail() {
if (!isOpen()) return;
final String tail = getTail();
if (null != tail) {
write(tail);
}
}
/**
* Helper method to retrieve head for log session.
* TODO: Extract from formatter
*
* @return the head string
*/
private String getHead() {
return null;
}
/**
* Helper method to retrieve tail for log session.
* TODO: Extract from formatter
*
* @return the head string
*/
private String getTail() {
return null;
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output;
import org.jivesoftware.util.log.ErrorAware;
import org.jivesoftware.util.log.ErrorHandler;
import org.jivesoftware.util.log.LogEvent;
import org.jivesoftware.util.log.LogTarget;
/**
* Abstract target.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public abstract class AbstractTarget implements LogTarget, ErrorAware {
///ErrorHandler used by target to delegate Error handling
private ErrorHandler m_errorHandler;
///Flag indicating that log session is finished (aka target has been closed)
private boolean m_isOpen;
/**
* Provide component with ErrorHandler.
*
* @param errorHandler the errorHandler
*/
public synchronized void setErrorHandler(final ErrorHandler errorHandler) {
m_errorHandler = errorHandler;
}
protected synchronized boolean isOpen() {
return m_isOpen;
}
/**
* Startup log session.
*/
protected synchronized void open() {
if (!isOpen()) {
m_isOpen = true;
}
}
/**
* Process a log event, via formatting and outputting it.
*
* @param event the log event
*/
public synchronized void processEvent(final LogEvent event) {
if (!isOpen()) {
getErrorHandler().error("Writing event to closed stream.", null, event);
return;
}
try {
doProcessEvent(event);
}
catch (final Throwable throwable) {
getErrorHandler().error("Unknown error writing event.", throwable, event);
}
}
/**
* Process a log event, via formatting and outputting it.
* This should be overidden by subclasses.
*
* @param event the log event
*/
protected abstract void doProcessEvent(LogEvent event)
throws Exception;
/**
* Shutdown target.
* Attempting to send to target after close() will cause errors to be logged.
*/
public synchronized void close() {
if (isOpen()) {
m_isOpen = false;
}
}
/**
* Helper method to retrieve ErrorHandler for subclasses.
*
* @return the ErrorHandler
*/
protected final ErrorHandler getErrorHandler() {
return m_errorHandler;
}
/**
* Helper method to send error messages to error handler.
*
* @param message the error message
* @param throwable the exception if any
* @deprecated Use getErrorHandler().error(...) directly
*/
protected final void error(final String message, final Throwable throwable) {
getErrorHandler().error(message, throwable, null);
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output;
import org.jivesoftware.util.log.ErrorAware;
import org.jivesoftware.util.log.ErrorHandler;
import org.jivesoftware.util.log.LogEvent;
import org.jivesoftware.util.log.LogTarget;
import java.util.LinkedList;
/**
* An asynchronous LogTarget that sends entries on in another thread.
* It is the responsibility of the user of this class to start
* the thread etc.
* <p/>
* <pre>
* LogTarget mySlowTarget = ...;
* AsyncLogTarget asyncTarget = new AsyncLogTarget( mySlowTarget );
* Thread thread = new Thread( asyncTarget );
* thread.setPriority( Thread.MIN_PRIORITY );
* thread.start();
* <p/>
* logger.setLogTargets( new LogTarget[] { asyncTarget } );
* </pre>
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public class AsyncLogTarget extends AbstractTarget implements Runnable {
private final LinkedList m_list;
private final int m_queueSize;
private final LogTarget m_logTarget;
public AsyncLogTarget(final LogTarget logTarget) {
this(logTarget, 15);
}
public AsyncLogTarget(final LogTarget logTarget, final int queueSize) {
m_logTarget = logTarget;
m_list = new LinkedList();
m_queueSize = queueSize;
open();
}
/**
* Provide component with ErrorHandler.
*
* @param errorHandler the errorHandler
*/
public synchronized void setErrorHandler(final ErrorHandler errorHandler) {
super.setErrorHandler(errorHandler);
if (m_logTarget instanceof ErrorAware) {
((ErrorAware)m_logTarget).setErrorHandler(errorHandler);
}
}
/**
* Process a log event by adding it to queue.
*
* @param event the log event
*/
public void doProcessEvent(final LogEvent event) {
synchronized (m_list) {
final int size = m_list.size();
while (m_queueSize <= size) {
try {
m_list.wait();
}
catch (final InterruptedException ie) {
//This really should not occur ...
//Maybe we should log it though for
//now lets ignore it
}
}
m_list.addFirst(event);
if (size == 0) {
//tell the "server" thread to wake up
//if it is waiting for a queue to contain some items
m_list.notify();
}
}
}
public void run() {
//set this variable when thread is interupted
//so we know we can shutdown thread soon.
boolean interupted = false;
while (true) {
LogEvent event = null;
synchronized (m_list) {
while (null == event) {
final int size = m_list.size();
if (size > 0) {
event = (LogEvent)m_list.removeLast();
if (size == m_queueSize) {
//tell the "client" thread to wake up
//if it is waiting for a queue position to open up
m_list.notify();
}
}
else if (interupted || Thread.interrupted()) {
//ie there is nothing in queue and thread is interrupted
//thus we stop thread
return;
}
else {
try {
m_list.wait();
}
catch (final InterruptedException ie) {
//Ignore this and let it be dealt in next loop
//Need to set variable as the exception throw cleared status
interupted = true;
}
}
}
}
try {
//actually process an event
m_logTarget.processEvent(event);
}
catch (final Throwable throwable) {
getErrorHandler().error("Unknown error writing event.", throwable, event);
}
}
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output.io;
import org.jivesoftware.util.log.format.Formatter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* A basic target that writes to a File.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public class FileTarget extends StreamTarget {
///File we are writing to
private File m_file;
///Flag indicating whether or not file should be appended to
private boolean m_append;
/**
* Construct file target to send to a file with a formatter.
*
* @param file the file to send to
* @param append true if file is to be appended to, false otherwise
* @param formatter the Formatter
* @throws IOException if an error occurs
*/
public FileTarget(final File file, final boolean append, final Formatter formatter)
throws IOException {
super(null, formatter);
if (null != file) {
setFile(file, append);
openFile();
}
}
/**
* Set the file for this target.
*
* @param file the file to send to
* @param append true if file is to be appended to, false otherwise
* @throws IOException if directories can not be created or file can not be opened
*/
protected synchronized void setFile(final File file, final boolean append)
throws IOException {
if (null == file) {
throw new NullPointerException("file property must not be null");
}
if (isOpen()) {
throw new IOException("target must be closed before " +
"file property can be set");
}
m_append = append;
m_file = file;
}
/**
* Open underlying file and allocate resources.
* This method will attempt to create directories below file and
* append to it if specified.
*/
protected synchronized void openFile()
throws IOException {
if (isOpen()) close();
final File file = getFile().getCanonicalFile();
final File parent = file.getParentFile();
if (null != parent && !parent.exists()) {
parent.mkdir();
}
final FileOutputStream outputStream =
new FileOutputStream(file.getPath(), m_append);
setOutputStream(outputStream);
open();
}
/**
* Retrieve file associated with target.
* This allows subclasses to access file object.
*
* @return the output File
*/
protected synchronized File getFile() {
return m_file;
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output.io;
import org.jivesoftware.util.log.format.Formatter;
import org.jivesoftware.util.log.output.AbstractOutputTarget;
import java.io.IOException;
import java.io.OutputStream;
/**
* A basic target that writes to an OutputStream.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public class StreamTarget extends AbstractOutputTarget {
///OutputStream we are writing to
private OutputStream m_outputStream;
/**
* Constructor that writes to a stream and uses a particular formatter.
*
* @param outputStream the OutputStream to send to
* @param formatter the Formatter to use
*/
public StreamTarget(final OutputStream outputStream, final Formatter formatter) {
super(formatter);
if (null != outputStream) {
setOutputStream(outputStream);
open();
}
}
/**
* Set the output stream.
* Close down old stream and send tail if appropriate.
*
* @param outputStream the new OutputStream
*/
protected synchronized void setOutputStream(final OutputStream outputStream) {
if (null == outputStream) {
throw new NullPointerException("outputStream property must not be null");
}
m_outputStream = outputStream;
}
/**
* Abstract method that will output event.
*
* @param data the data to be output
*/
protected synchronized void write(final String data) {
//Cache method local version
//so that can be replaced in another thread
final OutputStream outputStream = m_outputStream;
if (null == outputStream) {
final String message = "Attempted to send data '" + data + "' to Null OutputStream";
getErrorHandler().error(message, null, null);
return;
}
try {
//TODO: We should be able to specify encoding???
outputStream.write(data.getBytes("UTF-8"));
outputStream.flush();
}
catch (final IOException ioe) {
final String message = "Error writing data '" + data + "' to OutputStream";
getErrorHandler().error(message, ioe, null);
}
}
/**
* Shutdown target.
* Attempting to send to target after close() will cause errors to be logged.
*/
public synchronized void close() {
super.close();
shutdownStream();
}
/**
* Shutdown output stream.
*/
protected synchronized void shutdownStream() {
final OutputStream outputStream = m_outputStream;
m_outputStream = null;
try {
if (null != outputStream) {
outputStream.close();
}
}
catch (final IOException ioe) {
getErrorHandler().error("Error closing OutputStream", ioe, null);
}
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output.io;
import org.jivesoftware.util.log.format.Formatter;
import org.jivesoftware.util.log.output.AbstractOutputTarget;
import java.io.IOException;
import java.io.Writer;
/**
* This target outputs to a writer.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public class WriterTarget extends AbstractOutputTarget {
private Writer m_output;
/**
* Construct target with a specific writer and formatter.
*
* @param writer the writer
* @param formatter the formatter
*/
public WriterTarget(final Writer writer, final Formatter formatter) {
super(formatter);
if (null != writer) {
setWriter(writer);
open();
}
}
/**
* Set the writer.
* Close down writer and send tail if appropriate.
*
* @param writer the new writer
*/
protected synchronized void setWriter(final Writer writer) {
if (null == writer) {
throw new NullPointerException("writer property must not be null");
}
m_output = writer;
}
/**
* Concrete implementation of output that writes out to underlying writer.
*
* @param data the data to output
*/
protected void write(final String data) {
try {
m_output.write(data);
m_output.flush();
}
catch (final IOException ioe) {
getErrorHandler().error("Caught an IOException", ioe, null);
}
}
/**
* Shutdown target.
* Attempting to send to target after close() will cause errors to be logged.
*/
public synchronized void close() {
super.close();
shutdownWriter();
}
/**
* Shutdown Writer.
*/
protected synchronized void shutdownWriter() {
final Writer writer = m_output;
m_output = null;
try {
if (null != writer) {
writer.close();
}
}
catch (final IOException ioe) {
getErrorHandler().error("Error closing Writer", ioe, null);
}
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output.io.rotate;
import java.io.File;
/**
* strategy for naming log files based on appending revolving suffix.
* <p/>
* Heavily odified by Bruce Ritchie (Jive Software) to rotate along
* the following strategy:
* <p/>
* current log file will always be the base File name
* the next oldest file will be the _1 file
* the next oldest file will be the _2 file
* etc.
*
* @author <a href="mailto:bh22351@i-one.at">Bernhard Huber</a>
*/
public class ExpandingFileStrategy implements FileStrategy {
///the base file name.
private String baseFileName;
public ExpandingFileStrategy(final String baseFileName) {
this.baseFileName = baseFileName;
}
public File currentFile() {
return new File(baseFileName);
}
/**
* Calculate the real file name from the base filename.
*
* @return File the calculated file name
*/
public File nextFile() {
// go through all the possible filenames and delete/rename as necessary
for (int i = 0; true; i++) {
File test = new File(baseFileName.substring(0, baseFileName.lastIndexOf('.')) +
"_" + i + baseFileName.substring(baseFileName.lastIndexOf('.')));
if (test.exists()) {
continue;
}
else {
return test;
}
}
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output.io.rotate;
import java.io.File;
/**
* Strategy for naming log files.
* For a given base file name an implementation calculates
* the real file name.
*
* @author <a href="mailto:bh22351@i-one.at">Bernhard Huber</a>
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public interface FileStrategy {
/**
* Get the current logfile
*/
File currentFile();
/**
* Get the next log file to rotate to.
*
* @return the file to rotate to
*/
File nextFile();
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output.io.rotate;
import java.io.File;
/**
* Hierarchical Rotation stragety.
* This object is initialised with several rotation strategy objects.
* The <code>isRotationNeeded</code> method checks the first rotation
* strategy object. If a rotation is needed, this result is returned.
* If not the next rotation strategy object is asked and so on.
*
* @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
*/
public class OrRotateStrategy
implements RotateStrategy {
private RotateStrategy[] m_strategies;
/**
* The rotation strategy used. This marker is required for the reset()
* method.
*/
private int m_usedRotation = -1;
/**
* Constructor
*/
public OrRotateStrategy(final RotateStrategy[] strategies) {
this.m_strategies = strategies;
}
/**
* reset.
*/
public void reset() {
if (-1 != m_usedRotation) {
m_strategies[m_usedRotation].reset();
m_usedRotation = -1;
}
}
/**
* check if now a log rotation is neccessary.
* This object is initialised with several rotation strategy objects.
* The <code>isRotationNeeded</code> method checks the first rotation
* strategy object. If a rotation is needed, this result is returned.
* If not the next rotation strategy object is asked and so on.
*
* @param data the last message written to the log system
* @return boolean return true if log rotation is neccessary, else false
*/
public boolean isRotationNeeded(final String data, final File file) {
m_usedRotation = -1;
if (null != m_strategies) {
final int length = m_strategies.length;
for (int i = 0; i < length; i++) {
if (true == m_strategies[i].isRotationNeeded(data, file)) {
m_usedRotation = i;
return true;
}
}
}
return false;
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output.io.rotate;
import java.io.File;
/**
* strategy for naming log files based on appending revolving suffix.
* <p/>
* Heavily odified by Bruce Ritchie (Jive Software) to rotate along
* the following strategy:
* <p/>
* current log file will always be the base File name
* the next oldest file will be the _1 file
* the next oldest file will be the _2 file
* etc.
*
* @author <a href="mailto:bh22351@i-one.at">Bernhard Huber</a>
*/
public class RevolvingFileStrategy implements FileStrategy {
///max file prefix count
private int maxCount;
///the base file name.
private String baseFileName;
public RevolvingFileStrategy(final String baseFileName, final int maxCount) {
this.baseFileName = baseFileName;
this.maxCount = maxCount;
if (-1 == this.maxCount) {
this.maxCount = 5;
}
}
public File currentFile() {
return new File(baseFileName);
}
/**
* Calculate the real file name from the base filename.
*
* @return File the calculated file name
*/
public File nextFile() {
// go through all the possible filenames and delete/rename as necessary
for (int i = maxCount; i > 0; i--) {
File test = new File(baseFileName.substring(0, baseFileName.lastIndexOf('.')) +
"_" + i + baseFileName.substring(baseFileName.lastIndexOf('.')));
if (i == maxCount && test.exists()) {
test.delete();
}
if (test.exists()) {
File r = new File(baseFileName.substring(0, baseFileName.lastIndexOf('.')) +
"_" + (i + 1) + baseFileName.substring(baseFileName.lastIndexOf('.')));
test.renameTo(r);
}
}
// rename the current file
File current = new File(baseFileName);
File first = new File(baseFileName.substring(0, baseFileName.lastIndexOf('.')) +
"_1" + baseFileName.substring(baseFileName.lastIndexOf('.')));
current.renameTo(first);
// return the base filename
return new File(baseFileName);
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output.io.rotate;
import java.io.File;
/**
* Strategy that checks condition under which file rotation is needed.
*
* @author <a href="mailto:bh22351@i-one.at">Bernhard Huber</a>
*/
public interface RotateStrategy {
/**
* reset cumulative rotation history data.
* Called after rotation.
*/
void reset();
/**
* Check if a log rotation is neccessary at this time.
*
* @param data the serialized version of last message written to the log system
* @param file the File that we are writing to
* @return boolean return true if log rotation is neccessary, else false
*/
boolean isRotationNeeded(String data, File file);
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output.io.rotate;
import java.io.File;
/**
* Rotation stragety based on size written to log file.
*
* @author <a href="mailto:bh22351@i-one.at">Bernhard Huber</a>
*/
public class RotateStrategyBySize
implements RotateStrategy {
private long m_maxSize;
private long m_currentSize;
/**
* Rotate logs by size.
* By default do log rotation after writing approx. 1MB of messages
*/
public RotateStrategyBySize() {
this(1024 * 1024);
}
/**
* Rotate logs by size.
*
* @param maxSize rotate after writing max_size [byte] of messages
*/
public RotateStrategyBySize(final long maxSize) {
m_currentSize = 0;
m_maxSize = maxSize;
}
/**
* reset log size written so far.
*/
public void reset() {
m_currentSize = 0;
}
/**
* Check if now a log rotation is neccessary.
*
* @param data the last message written to the log system
* @return boolean return true if log rotation is neccessary, else false
*/
public boolean isRotationNeeded(final String data, final File file) {
m_currentSize += data.length();
if (m_currentSize >= m_maxSize) {
m_currentSize = 0;
return true;
}
else {
return false;
}
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output.io.rotate;
import java.io.File;
/**
* rotation stragety based when log writting started.
*
* @author <a href="mailto:bh22351@i-one.at">Bernhard Huber</a>
*/
public class RotateStrategyByTime
implements RotateStrategy {
///time interval when rotation is triggered.
private long m_timeInterval;
///time when logging started.
private long m_startingTime;
///rotation count.
private long m_currentRotation;
/**
* Rotate logs by time.
* By default do log rotation every 24 hours
*/
public RotateStrategyByTime() {
this(1000 * 60 * 60 * 24);
}
/**
* Rotate logs by time.
*
* @param timeInterval rotate after time-interval [ms] has expired
*/
public RotateStrategyByTime(final long timeInterval) {
m_startingTime = System.currentTimeMillis();
m_currentRotation = 0;
m_timeInterval = timeInterval;
}
/**
* reset interval history counters.
*/
public void reset() {
m_startingTime = System.currentTimeMillis();
m_currentRotation = 0;
}
/**
* Check if now a log rotation is neccessary.
* If
* <code>(current_time - m_startingTime) / m_timeInterval &gt; m_currentRotation </code>
* rotation is needed.
*
* @param data the last message written to the log system
* @return boolean return true if log rotation is neccessary, else false
*/
public boolean isRotationNeeded(final String data, final File file) {
final long newRotation =
(System.currentTimeMillis() - m_startingTime) / m_timeInterval;
if (newRotation > m_currentRotation) {
m_currentRotation = newRotation;
return true;
}
else {
return false;
}
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output.io.rotate;
import org.jivesoftware.util.log.format.Formatter;
import org.jivesoftware.util.log.output.io.FileTarget;
import java.io.File;
import java.io.IOException;
/**
* This is a basic Output log target that writes to rotating files.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @author <a href="mailto:mcconnell@osm.net">Stephen McConnell</a>
* @author <a href="mailto:bh22351@i-one.at">Bernhard Huber</a>
*/
public class RotatingFileTarget extends FileTarget {
///The rotation strategy to be used.
private RotateStrategy m_rotateStrategy;
///The file strategy to be used.
private FileStrategy m_fileStrategy;
/**
* Construct RotatingFileTarget object.
*
* @param formatter Formatter to be used
*/
public RotatingFileTarget(final Formatter formatter,
final RotateStrategy rotateStrategy,
final FileStrategy fileStrategy)
throws IOException {
super(null, false, formatter);
m_rotateStrategy = rotateStrategy;
m_fileStrategy = fileStrategy;
getInitialFile();
}
public synchronized void rotate()
throws IOException {
close();
final File file = m_fileStrategy.nextFile();
setFile(file, false);
openFile();
}
/**
* Output the log message, and check if rotation is needed.
*/
public synchronized void write(final String data) {
// send the log message
super.write(data);
// if rotation is needed, close old File, create new File
final boolean rotate =
m_rotateStrategy.isRotationNeeded(data, getFile());
if (rotate) {
try {
rotate();
}
catch (final IOException ioe) {
getErrorHandler().error("Error rotating file", ioe, null);
}
}
}
private void getInitialFile() throws IOException {
close();
boolean rotate = m_rotateStrategy.isRotationNeeded("", m_fileStrategy.currentFile());
if (rotate) {
setFile(m_fileStrategy.nextFile(), false);
}
else {
setFile(m_fileStrategy.currentFile(), true);
}
openFile();
}
}
\ No newline at end of file
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.output.io.rotate;
import org.jivesoftware.util.FastDateFormat;
import java.io.File;
import java.util.Date;
/**
* Strategy for naming log files based on appending time suffix.
* A file name can be based on simply appending the number of miliseconds
* since (not really sure) 1/1/1970.
* Other constructors accept a pattern of a <code>SimpleDateFormat</code>
* to form the appended string to the base file name as well as a suffix
* which should be appended last.
* <p/>
* A <code>new UniqueFileStrategy( new File("foo.", "yyyy-MM-dd", ".log" )</code>
* object will return <code>File</code> objects with file names like
* <code>foo.2001-12-24.log</code>
*
* @author <a href="mailto:bh22351@i-one.at">Bernhard Huber</a>
* @author <a href="mailto:giacomo@apache.org">Giacomo Pati</a>
*/
public class UniqueFileStrategy
implements FileStrategy {
private File m_baseFile;
private File m_currentFile;
private FastDateFormat m_formatter;
private String m_suffix;
public UniqueFileStrategy(final File baseFile) {
m_baseFile = baseFile;
}
public UniqueFileStrategy(final File baseFile, String pattern) {
this(baseFile);
m_formatter = FastDateFormat.getInstance(pattern);
}
public UniqueFileStrategy(final File baseFile, String pattern, String suffix) {
this(baseFile, pattern);
m_suffix = suffix;
}
public File currentFile() {
return m_currentFile;
}
/**
* Calculate the real file name from the base filename.
*
* @return File the calculated file name
*/
public File nextFile() {
final StringBuilder sb = new StringBuilder();
sb.append(m_baseFile);
if (m_formatter == null) {
sb.append(System.currentTimeMillis());
}
else {
final String dateString = m_formatter.format(new Date());
sb.append(dateString);
}
if (m_suffix != null) {
sb.append(m_suffix);
}
m_currentFile = new File(sb.toString());
return m_currentFile;
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.util;
import org.jivesoftware.util.log.ErrorHandler;
import org.jivesoftware.util.log.LogEvent;
/**
* Handle unrecoverable errors that occur during logging by
* writing to standard error.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public class DefaultErrorHandler
implements ErrorHandler {
/**
* Log an unrecoverable error.
*
* @param message the error message
* @param throwable the exception associated with error (may be null)
* @param event the LogEvent that caused error, if any (may be null)
*/
public void error(final String message,
final Throwable throwable,
final LogEvent event) {
System.err.println("Logging Error: " + message);
if (null != throwable) {
throwable.printStackTrace();
}
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.util;
import org.jivesoftware.util.log.Logger;
import org.jivesoftware.util.log.Priority;
import java.io.EOFException;
import java.io.IOException;
import java.io.OutputStream;
/**
* Redirect an output stream to a logger.
* This class is useful to redirect standard output or
* standard error to a Logger. An example use is
* <p/>
* <pre>
* final LoggerOutputStream outputStream =
* new LoggerOutputStream( logger, Priority.DEBUG );
* final PrintStream output = new PrintStream( outputStream, true );
* <p/>
* System.setOut( output );
* </pre>
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public class LoggerOutputStream
extends OutputStream {
///Logger that we log to
private final Logger m_logger;
///Log level we log to
private final Priority m_priority;
///The buffered output so far
private final StringBuffer m_output = new StringBuffer();
///Flag set to true once stream closed
private boolean m_closed;
/**
* Construct OutputStreamLogger to send to a particular logger at a particular priority.
*
* @param logger the logger to send to
* @param priority the priority at which to log
*/
public LoggerOutputStream(final Logger logger,
final Priority priority) {
m_logger = logger;
m_priority = priority;
}
/**
* Shutdown stream.
*/
public void close()
throws IOException {
flush();
super.close();
m_closed = true;
}
/**
* Write a single byte of data to output stream.
*
* @param data the byte of data
* @throws IOException if an error occurs
*/
public void write(final int data)
throws IOException {
checkValid();
//Should we properly convert char using locales etc??
m_output.append((char)data);
if ('\n' == data) {
flush();
}
}
/**
* Flush data to underlying logger.
*
* @throws IOException if an error occurs
*/
public synchronized void flush()
throws IOException {
checkValid();
m_logger.log(m_priority, m_output.toString());
m_output.setLength(0);
}
/**
* Make sure stream is valid.
*
* @throws IOException if an error occurs
*/
private void checkValid()
throws IOException {
if (true == m_closed) {
throw new EOFException("OutputStreamLogger closed");
}
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.util;
import org.jivesoftware.util.log.Logger;
import org.jivesoftware.util.log.Priority;
/**
* Redirect an output stream to a logger.
* This class is useful to redirect standard output or
* standard error to a Logger. An example use is
* <p/>
* <pre>
* final OutputStreamLogger outputStream =
* new OutputStreamLogger( logger, Priority.DEBUG );
* final PrintStream output = new PrintStream( outputStream, true );
* <p/>
* System.setOut( output );
* </pre>
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @deprecated Use LoggerOutputStream as this class was misnamed.
*/
public class OutputStreamLogger
extends LoggerOutputStream {
/**
* Construct logger to send to a particular logger at a particular priority.
*
* @param logger the logger to send to
* @param priority the priority at which to log
* @deprecated Use LoggerOutputStream as this class was misnamed.
*/
public OutputStreamLogger(final Logger logger,
final Priority priority) {
super(logger, priority);
}
}
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.jivesoftware.util.log.util;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* A set of utilities to inspect current stack frame.
*
* @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
* @author <a href="mailto:stuart.roebuck@adolos.com">Stuart Roebuck</a>
*/
public final class StackIntrospector {
/**
* Hack to get the call stack as an array of classes. The
* SecurityManager class provides it as a protected method, so
* change it to public through a new method !
*/
private final static class CallStack
extends SecurityManager {
/**
* Returns the current execution stack as an array of classes.
* The length of the array is the number of methods on the execution
* stack. The element at index 0 is the class of the currently executing
* method, the element at index 1 is the class of that method's caller,
* and so on.
*/
public Class[] get() {
return getClassContext();
}
}
///Method to cache CallStack hack as needed
private static CallStack c_callStack;
/**
* Private constructor to block instantiation.
*/
private StackIntrospector() {
}
/**
* Create Hack SecurityManager to get CallStack.
*
* @return the CallStack object
* @throws SecurityException if an existing SecurityManager disallows construction
* of another SecurityManager
*/
private synchronized static CallStack getCallStack()
throws SecurityException {
if (null == c_callStack) {
//Lazily create CallStack accessor as appropriate
c_callStack = new CallStack();
}
return c_callStack;
}
/**
* Find the caller of the passed in Class.
* May return null if caller not found on execution stack
*
* @param clazz the Class to search for on stack to find caller of
* @return the Class of object that called parrameter class
* @throws SecurityException if an existing SecurityManager disallows construction
* of another SecurityManager and thus blocks method results
*/
public final static Class getCallerClass(final Class clazz)
throws SecurityException {
final Class[] stack = getCallStack().get();
// Traverse the call stack in reverse order until we find clazz
for (int i = stack.length - 1; i >= 0; i--) {
if (clazz.isAssignableFrom(stack[i])) {
// Found : the caller is the previous stack element
return stack[i + 1];
}
}
//Unable to locate class in call stack
return null;
}
/**
* Get the method path name for the method from which the LogEvent was
* created, this includes the path name and the source filename and line
* number if the source was compiled with debugging on.
*
* @return The method path name in the form "the.package.path.Method"
*/
public final static String getCallerMethod(final Class clazz) {
final String className = clazz.getName();
//Extract stack into a StringBuffer
final StringWriter sw = new StringWriter();
final Throwable throwable = new Throwable();
throwable.printStackTrace(new PrintWriter(sw, true));
final StringBuffer buffer = sw.getBuffer();
//Cache vars used in loop
final StringBuffer line = new StringBuffer();
final int length = buffer.length();
//setup state
boolean found = false;
int state = 0;
//parse line
for (int i = 0; i < length; i++) {
final char ch = buffer.charAt(i);
switch (state) {
case 0:
//Strip the first line from input
if ('\n' == ch) state = 1;
break;
case 1:
//strip 't' from 'at'
if ('t' == ch) state = 2;
break;
case 2:
//Strip space after 'at'
line.setLength(0);
state = 3;
break;
case 3:
//accumulate all characters to end of line
if ('\n' != ch)
line.append(ch);
else {
//At this stage you have the line that looks like
//com.biz.SomeClass.someMethod(SomeClass.java:22)
final String method = line.toString();
///Determine if line is a match for class
final boolean match = method.startsWith(className);
if (!found && match) {
//If this is the first time we cound class then
//set found to true and look for caller into class
found = true;
}
else if (found && !match) {
//We have now located caller of Clazz
return method;
}
//start parsing from start of line again
state = 1;
}
}
}
return "";
}
/**
* Return the current call stack as a String, starting with the first call
* in the stack after a reference to the <code>clazz</code> class, and then
* display <code>entries</code> entries.
* <p/>
* <p>This can be useful for debugging code to determine where calls to a
* method are coming from.</p>
*
* @param clazz the last class on the stack you are <i>not</i> interested in!
* @param entries the number of stack lines to return.
* @return The method path name in the form "the.package.path.Method"
*/
public final static String getRecentStack(final Class clazz, int entries) {
final String className = clazz.getName();
//Extract stack into a StringBuffer
final StringWriter sw = new StringWriter();
final Throwable throwable = new Throwable();
throwable.printStackTrace(new PrintWriter(sw, true));
final StringBuffer buffer = sw.getBuffer();
//Cache vars used in loop
final StringBuffer line = new StringBuffer();
final StringBuffer stack = new StringBuffer();
final int length = buffer.length();
//setup state
boolean found = false;
int state = 0;
//parse line
for (int i = 0; i < length; i++) {
final char ch = buffer.charAt(i);
switch (state) {
case 0:
//Strip the first line from input
if ('\n' == ch) state = 1;
break;
case 1:
//strip 't' from 'at'
if ('t' == ch) state = 2;
break;
case 2:
//Strip space after 'at'
line.setLength(0);
state = 3;
break;
case 3:
//accumulate all characters to end of line
if ('\n' != ch)
line.append(ch);
else {
//At this stage you have the line that looks like
//com.biz.SomeClass.someMethod(SomeClass.java:22)
final String method = line.toString();
///Determine if line is a match for class
final boolean match = method.startsWith(className);
if (!found && match) {
//If this is the first time we cound class then
//set found to true and look for caller into class
found = true;
}
else if (found && !match) {
//We are looking at the callers of Clazz
stack.append(method);
entries--;
if (entries == 0) return stack.toString();
stack.append("\n");
}
//start parsing from start of line again
state = 1;
}
}
}
return "";
}
}
......@@ -97,6 +97,9 @@
String filename = log + ".log";
File logFile = new File(logDir, filename);
String lines[] = new String[0];
int start = 0;
try {
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(logFile), "UTF-8"));
String line;
int totalNumLines = 0;
......@@ -108,10 +111,10 @@
if ("All".equals(numLinesParam)) {
numLines = totalNumLines;
}
String[] lines = new String[numLines];
lines = new String[numLines];
in = new BufferedReader(new InputStreamReader(new FileInputStream(logFile), "UTF-8"));
// skip lines
int start = totalNumLines - numLines;
start = totalNumLines - numLines;
if (start < 0) { start = 0; }
for (int i=0; i<start; i++) {
in.readLine();
......@@ -137,6 +140,9 @@
}
}
numLines = start + i;
} catch (FileNotFoundException ex) {
Log.info("Could not open (log)file.", ex);
}
%>
<html>
......
......@@ -134,7 +134,6 @@
if (log != null) {
log = StringUtils.escapeHTMLTags(log);
}
debugEnabled = Log.isDebugEnabled();
User pageUser = admin.getUser();
......@@ -286,6 +285,7 @@ IFRAME {
</tr>
</tbody>
</table>
</div>
<% ByteFormat byteFormatter = new ByteFormat();
Date lastMod = new Date(logFile.lastModified());
......@@ -324,11 +324,11 @@ IFRAME {
<% for (String aLINES : LINES) {
String selected = (aLINES.equals(numLinesParam)) ? " selected" : "";
%>
<option value="<%= aLINES %>"<%= selected %>><%= aLINES %>
<option value="<%= aLINES %>"<%= selected %>><%= aLINES %></option>
<% } %>
<option value="All"<%= (("All".equals(numLinesParam))?" selected":"") %>
><fmt:message key="logviewer.all" />
><fmt:message key="logviewer.all" /></option>
</select>
</td>
</tr>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment