/**
* Copyright 2011-2012 Akiban Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.persistit.logging;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* A template for a Persisit log message. See {@link LogBase} for usage. A
* LogBase instance defines {@link LogItem} instances as fields. At various
* locations in the the Persistit code base, there may be a call to the
* <code>LogItem</code> instance's {@link LogItem#log(Object...)} method to
* record pertinent information in the log. Depending on the configured state of
* the logging system, the LogItem's <code>log</code> method may either do
* nothing, or may emit a log message. The HotSpot JIT compiler dynamically
* removes calls to methods that do nothing, so for disabled
*/
public class PersistitLogMessage {
/**
* Interface for a loggable item. Each location in the Persistit code base
* that potentially emits a log message uses an instance of LogItem. LogItem
* implementations may do (a) nothing, or (b) call a logging framework to
* emit the log message. This decision is made by the
* {@link #configure(PersistitLogger, PersistitLevel, String)} method which
* is called each time the logging framework is set up or changed.
*
* @author peter
*
*/
public interface LogItem {
/**
* @return whether this log item is currently enabled for logging.
*/
boolean isEnabled();
/**
* Construct a log message consisting of this LogItem's template
* combined with values in the supplied argument list.
*
* @param args
* array of argument values that will be substituted into the
* log message
* @return the message as a string
*/
String logMessage(Object... args);
/**
* Emit a log message, or do nothing if this <code>LogItem</code> is
* disabled.
*
* @param args
* array of argument values that will be substituted into the
* log message
*/
void log(Object... args);
/**
* Emit a log message for a recurring event, or do nothing if this
* <code>LogItem</code> is disabled.
*
* @param count
* number of occurrences
* @param duration
* period of time in duration array of argument values that
* will be substituted into the log message
*/
void logRecurring(int count, long duration, Object... args);
/**
* @return This LogItem's log level, one of TRACE, DEBUG, INFO, WARN or
* ERROR
*/
PersistitLevel getLevel();
/**
* Modify this <code>LogItem</code> to use the supplied
* {@link PersistitLogger}. If the <code>logger</code> enables logging
* for messages having the supplied <code>level</code> then configure
* this <code>LogItem</code> to emit log messages; else configure it to
* do nothing, causing the JIT to compile away all instructions for
* {@link LogItem#log(Object...)} method.
*
* @param logger
* The <code>PersistitLogger</code> that emits log messages
* @param level
* The <code>PersistitLevel</code> of this item
* @param message
* A message template which forms the basis of the log
* message
*/
void configure(PersistitLogger logger, PersistitLevel level, String message);
}
/**
* @return <code>LogItem</code> which may, depending on its configuration,
* emit log messages when its {@link LogItem#log(Object...)} method
* is called.
*/
public static LogItem empty() {
return new LogDispatchHandler();
}
/**
* A {@link LogItem} which may or may not emit log messages, depending on
* whether it is configured to do so by the
* {@link #configure(PersistitLogger, PersistitLevel, String)} method.
*
*/
static class LogDispatchHandler implements LogItem {
private PersistitLogger _logger;
private PersistitLevel _level;
private LogItem _dispatch = new Disabled();
@Override
public void configure(final PersistitLogger logger, final PersistitLevel level, final String message) {
_level = level;
_logger = logger;
if (_logger.isLoggable(_level)) {
_dispatch = new Enabled(logger, level, message);
} else {
_dispatch = new Disabled();
}
}
@Override
public boolean isEnabled() {
return _dispatch.isEnabled();
}
@Override
public String logMessage(final Object... args) {
return _dispatch.logMessage(args);
}
@Override
public void log(final Object... args) {
_dispatch.log(args);
}
@Override
public void logRecurring(final int count, final long duration, final Object... args) {
_dispatch.logRecurring(count, duration, args);
}
public void disable() {
_dispatch = new Disabled();
}
public PersistitLogger getLogger() {
return _logger;
}
@Override
public PersistitLevel getLevel() {
return _level;
}
}
/**
* Implementation of PersistitLogMessage returns <code>false</code> from its
* {@link #isEnabled()} method and does nothing in its
* {@link #log(Object...)} method. HotSpot eliminates calls to the log
* method.
*/
static class Disabled implements LogItem {
@Override
public boolean isEnabled() {
return false;
}
@Override
public String logMessage(final Object... args) {
return null;
}
@Override
public void log(final Object... args) {
}
@Override
public void logRecurring(final int count, final long duration, final Object... args) {
}
@Override
public PersistitLevel getLevel() {
return PersistitLevel.NONE;
}
@Override
public void configure(final PersistitLogger logger, final PersistitLevel level, final String message) {
}
}
/**
* Implementation of LogMessage that returns <code>true</code> from
* {@link #isEnabled()} and emits a log message from the
* {@link #log(Object...)} message.
*
* @author peter
*
*/
static class Enabled implements LogItem {
private final PersistitLogger _logger;
private final PersistitLevel _level;
private final String _message;
Enabled(final PersistitLogger logger, final PersistitLevel level, final String message) {
_logger = logger;
_level = level;
_message = message;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public String logMessage(final Object... args) {
final StringBuilder sb = new StringBuilder(String.format("[%s] %s ", Thread.currentThread().getName(),
_level));
try {
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof RuntimeException) {
args[i] = throwableFormatter((RuntimeException) args[i]);
}
}
sb.append(String.format(_message, args));
} catch (final Exception e) {
sb.append("Bad log message ");
sb.append(_message);
sb.append(" [");
for (int i = 0; i < args.length; i++) {
if (i != 0) {
sb.append(',');
}
sb.append(args[i]);
}
sb.append("]");
}
return sb.toString();
}
@Override
public void log(final Object... args) {
_logger.log(_level, logMessage(args));
}
@Override
public void logRecurring(final int count, final long duration, final Object... args) {
if (count == 1) {
log(args);
} else {
_logger.log(_level, LogBase.recurring(logMessage(args), count, duration));
}
}
public PersistitLogger getLogger() {
return _logger;
}
@Override
public PersistitLevel getLevel() {
return _level;
}
public String getMessage() {
return _message;
}
@Override
public void configure(final PersistitLogger logger, final PersistitLevel level, final String message) {
}
}
static String throwableFormatter(final Throwable t) {
final StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
final StringBuffer sb = sw.getBuffer();
for (int p = -1; (p = sb.indexOf("\n", p + 1)) >= 0;) {
sb.insert(p + 1, " ");
}
return sb.toString();
}
}