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