/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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 org.apache.geode.internal.logging;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.BreakIterator;
import java.text.DateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Handler;
import java.util.logging.Level;
import org.apache.geode.i18n.LogWriterI18n;
import org.apache.geode.internal.Assert;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.internal.process.StartupStatusListener;
import org.apache.geode.i18n.StringId;
/**
* Abstract implementation of {@link InternalLogWriter}. Each logger has a level and it will only
* print messages whose level is greater than or equal to the logger's level. The supported logger
* level constants, in ascending order, are:
* <ol>
* <li>{@link #ALL_LEVEL}
* <li>{@link #FINEST_LEVEL}
* <li>{@link #FINER_LEVEL}
* <li>{@link #FINE_LEVEL}
* <li>{@link #CONFIG_LEVEL}
* <li>{@link #INFO_LEVEL}
* <li>{@link #WARNING_LEVEL}
* <li>{@link #ERROR_LEVEL}
* <li>{@link #SEVERE_LEVEL}
* <li>{@link #NONE_LEVEL}
* </ol>
* <p>
* Subclasses must implement:
* <ol>
* <li>{@link #getLogWriterLevel}
* <li>{@link #put(int, String, Throwable)}
* <li>{@link #put(int, StringId, Object[], Throwable)}
* </ol>
*/
public abstract class LogWriterImpl implements InternalLogWriter {
// Constants
/**
* A bit mask to remove any potential flags added to the msgLevel. Intended to be used in
* {@link #getRealLogLevel}.
*/
private final static int LOGGING_FLAGS_MASK = 0x00FFFFFF;
/**
* A flag to indicate the {@link SecurityLogWriter#SECURITY_PREFIX} should be appended to the log
* level.
*/
protected final static int SECURITY_LOGGING_FLAG = 0x40000000;
static {
Assert.assertTrue(ALL_LEVEL == Level.ALL.intValue());
Assert.assertTrue(NONE_LEVEL == Level.OFF.intValue());
Assert.assertTrue(FINEST_LEVEL == Level.FINEST.intValue());
Assert.assertTrue(FINER_LEVEL == Level.FINER.intValue());
Assert.assertTrue(FINE_LEVEL == Level.FINE.intValue());
Assert.assertTrue(CONFIG_LEVEL == Level.CONFIG.intValue());
Assert.assertTrue(INFO_LEVEL == Level.INFO.intValue());
Assert.assertTrue(WARNING_LEVEL == Level.WARNING.intValue());
Assert.assertTrue(SEVERE_LEVEL == Level.SEVERE.intValue());
int logLevels = FINEST_LEVEL | FINER_LEVEL | FINE_LEVEL | CONFIG_LEVEL | INFO_LEVEL
| WARNING_LEVEL | SEVERE_LEVEL;
Assert.assertTrue(logLevels == (logLevels & LOGGING_FLAGS_MASK));
Assert.assertTrue(0 == (logLevels & SECURITY_LOGGING_FLAG));
}
/**
* A listener which can be registered to be informed of startup events
*/
private static volatile StartupStatusListener startupListener;
// Constructors
protected LogWriterImpl() {
this.timeFormatter = DateFormatter.createDateFormat();
}
/**
* Gets the writer's level.
*/
public abstract int getLogWriterLevel();
public boolean isSecure() {
return false;
}
public static String allowedLogLevels() {
StringBuffer b = new StringBuffer(64);
for (int i = 0; i < levelNames.length; i++) {
if (i != 0) {
b.append('|');
}
b.append(levelNames[i]);
}
return b.toString();
}
/**
* Gets the string representation for the given <code>level</code> int code.
*/
public static String levelToString(int level) {
switch (level) {
case ALL_LEVEL:
return "all";
case FINEST_LEVEL:
return "finest";
case FINER_LEVEL:
return "finer";
case FINE_LEVEL:
return "fine";
case CONFIG_LEVEL:
return "config";
case INFO_LEVEL:
return "info";
case WARNING_LEVEL:
return "warning";
case ERROR_LEVEL:
return "error";
case SEVERE_LEVEL:
return "severe";
case NONE_LEVEL:
return "none";
default:
return levelToStringSpecialCase(level);
}
}
/**
* Handles the special cases for {@link #levelToString(int)} including such cases as the
* SECURITY_LOGGING_PREFIX and an invalid log level.
*/
private static String levelToStringSpecialCase(int levelWithFlags) {
if ((levelWithFlags & SECURITY_LOGGING_FLAG) != 0) {
// We know the flag is set so XOR will zero it out.
int level = levelWithFlags ^ SECURITY_LOGGING_FLAG;
return SecurityLogWriter.SECURITY_PREFIX + levelToString(level);
} else {
// Needed to prevent infinite recursion
// This signifies an unknown log level was used
return "level-" + String.valueOf(levelWithFlags);
}
}
protected static int getRealLogLevel(int levelWithFlags) {
if (levelWithFlags == NONE_LEVEL) {
return levelWithFlags;
}
return levelWithFlags & LOGGING_FLAGS_MASK;
}
public static String join(Object[] a) {
return join(a, " ");
}
public static String join(Object[] a, String joinString) {
return join(Arrays.asList(a), joinString);
}
public static String join(List l) {
return join(l, " ");
}
public static String join(List l, String joinString) {
StringBuffer result = new StringBuffer(80);
boolean firstTime = true;
Iterator it = l.iterator();
while (it.hasNext()) {
if (firstTime) {
firstTime = false;
} else {
result.append(joinString);
}
result.append(it.next());
}
return result.toString();
}
/**
* Gets the level code for the given <code>levelName</code>.
*
* @throws IllegalArgumentException if an unknown level name is given.
*/
public static int levelNameToCode(String levelName) {
if ("all".equalsIgnoreCase(levelName)) {
return ALL_LEVEL;
}
if ("finest".equalsIgnoreCase(levelName) || "trace".equalsIgnoreCase(levelName)) {
return FINEST_LEVEL;
}
if ("finer".equalsIgnoreCase(levelName)) {
return FINER_LEVEL;
}
if ("fine".equalsIgnoreCase(levelName) || "debug".equalsIgnoreCase(levelName)) {
return FINE_LEVEL;
}
if ("config".equalsIgnoreCase(levelName)) {
return CONFIG_LEVEL;
}
if ("info".equalsIgnoreCase(levelName)) {
return INFO_LEVEL;
}
if ("warning".equalsIgnoreCase(levelName) || "warn".equalsIgnoreCase(levelName)) {
return WARNING_LEVEL;
}
if ("error".equalsIgnoreCase(levelName)) {
return ERROR_LEVEL;
}
if ("severe".equalsIgnoreCase(levelName) || "fatal".equalsIgnoreCase(levelName)) {
return SEVERE_LEVEL;
}
if ("none".equalsIgnoreCase(levelName)) {
return NONE_LEVEL;
}
try {
if (levelName.startsWith("level-")) {
String levelValue = levelName.substring("level-".length());
return Integer.parseInt(levelValue);
}
} catch (NumberFormatException ignore) {
} catch (NullPointerException ignore) {
}
throw new IllegalArgumentException(
"Unknown log-level \"" + levelName + "\". Valid levels are: " + join(levelNames) + ".");
}
/**
* Gets a String representation of the current time.
*
* @return a String representation of the current time.
*/
protected String getTimeStamp() {
return formatDate(new Date());
}
/**
* Convert a Date to a timestamp String.
*
* @param d a Date to format as a timestamp String.
* @return a String representation of the current time.
*/
protected String formatDate(Date d) {
try {
synchronized (timeFormatter) {
// Need sync: see bug 21858
return timeFormatter.format(d);
}
} catch (Exception e1) {
// Fix bug 21857
try {
return d.toString();
} catch (Exception e2) {
try {
return Long.toString(d.getTime());
} catch (Exception e3) {
return "timestampFormatFailed";
}
}
}
}
// Implementation of LogWriterI18n interface
/**
* Returns true if "severe" log messages are enabled. Returns false if "severe" log messages are
* disabled.
*/
public boolean severeEnabled() {
return getLogWriterLevel() <= SEVERE_LEVEL;
}
/**
* Writes both a message and exception to this writer. The message level is "severe".
*/
public void severe(String msg, Throwable ex) {
if (this.severeEnabled()) {
this.put(SEVERE_LEVEL, msg, ex);
}
}
/**
* Writes a message to this writer. The message level is "severe".
*/
public void severe(String msg) {
this.severe(msg, null);
}
/**
* Writes an exception to this writer. The exception level is "severe".
*/
public void severe(Throwable ex) {
this.severe(LocalizedStrings.EMPTY, ex);
}
/**
* Writes both a message and exception to this writer. The message level is "severe".
*
* @since GemFire 6.0
*/
public void severe(StringId msgID, Object[] params, Throwable ex) {
if (this.severeEnabled()) {
this.put(SEVERE_LEVEL, msgID, params, ex);
}
}
/**
* Writes both a message and exception to this writer. The message level is "severe".
*
* @since GemFire 6.0
*/
public void severe(StringId msgID, Object param, Throwable ex) {
if (this.severeEnabled()) {
this.put(SEVERE_LEVEL, msgID, new Object[] {param}, ex);
}
}
/**
* Writes both a message and exception to this writer. The message level is "severe".
*
* @since GemFire 6.0
*/
public void severe(StringId msgID, Throwable ex) {
severe(msgID, null, ex);
}
/**
* Writes both a message and exception to this writer. The message level is "severe".
*
* @since GemFire 6.0
*/
public void severe(StringId msgID, Object[] params) {
severe(msgID, params, null);
}
/**
* Writes both a message and exception to this writer. The message level is "severe".
*
* @since GemFire 6.0
*/
public void severe(StringId msgID, Object param) {
severe(msgID, param, null);
}
/**
* Writes both a message and exception to this writer. The message level is "severe".
*
* @since GemFire 6.0
*/
public void severe(StringId msgID) {
severe(msgID, null, null);
}
/**
* @return true if "error" log messages are enabled.
*/
public boolean errorEnabled() {
return getLogWriterLevel() <= ERROR_LEVEL;
}
/**
* Writes both a message and exception to this writer. The message level is "error".
*/
public void error(String msg, Throwable ex) {
if (this.errorEnabled()) {
this.put(ERROR_LEVEL, msg, ex);
}
}
/**
* Writes a message to this writer. The message level is "error".
*/
public void error(String msg) {
this.error(msg, null);
}
/**
* Writes an exception to this writer. The exception level is "error".
*/
public void error(Throwable ex) {
this.error(LocalizedStrings.EMPTY, ex);
}
/**
* Writes both a message and exception to this writer. The message level is "error".
*
* @since GemFire 6.0
*/
public void error(StringId msgID, Object[] params, Throwable ex) {
if (this.errorEnabled()) {
this.put(ERROR_LEVEL, msgID, params, ex);
}
}
/**
* Writes both a message and exception to this writer. The message level is "error".
*
* @since GemFire 6.0
*/
public void error(StringId msgID, Object param, Throwable ex) {
if (this.errorEnabled()) {
this.put(ERROR_LEVEL, msgID, new Object[] {param}, ex);
}
}
/**
* Writes both a message and exception to this writer. The message level is "error".
*
* @since GemFire 6.0
*/
public void error(StringId msgID, Throwable ex) {
error(msgID, null, ex);
}
/**
* Writes both a message and exception to this writer. The message level is "error".
*
* @since GemFire 6.0
*/
public void error(StringId msgID, Object[] params) {
error(msgID, params, null);
}
/**
* Writes both a message and exception to this writer. The message level is "error".
*
* @since GemFire 6.0
*/
public void error(StringId msgID, Object param) {
error(msgID, param, null);
}
/**
* Writes both a message and exception to this writer. The message level is "error".
*
* @since GemFire 6.0
*/
public void error(StringId msgID) {
error(msgID, null, null);
}
/**
* @return true if "warning" log messages are enabled.
*/
public boolean warningEnabled() {
return getLogWriterLevel() <= WARNING_LEVEL;
}
/**
* Writes both a message and exception to this writer. The message level is "warning".
*/
public void warning(String msg, Throwable ex) {
if (this.warningEnabled()) {
this.put(WARNING_LEVEL, msg, ex);
}
}
/**
* Writes a message to this writer. The message level is "warning".
*/
public void warning(String msg) {
this.warning(msg, null);
}
/**
* Writes an exception to this writer. The exception level is "warning".
*/
public void warning(Throwable ex) {
this.warning(LocalizedStrings.EMPTY, ex);
}
/**
* Writes both a message and exception to this writer. The message level is "warning".
*
* @since GemFire 6.0
*/
public void warning(StringId msgID, Object[] params, Throwable ex) {
if (this.warningEnabled()) {
this.put(WARNING_LEVEL, msgID, params, ex);
}
}
/**
* Writes both a message and exception to this writer. The message level is "warning".
*
* @since GemFire 6.0
*/
public void warning(StringId msgID, Object param, Throwable ex) {
if (this.warningEnabled()) {
this.put(WARNING_LEVEL, msgID, new Object[] {param}, ex);
}
}
/**
* Writes both a message and exception to this writer. The message level is "warning".
*
* @since GemFire 6.0
*/
public void warning(StringId msgID, Throwable ex) {
warning(msgID, null, ex);
}
/**
* Writes both a message and exception to this writer. The message level is "warning".
*
* @since GemFire 6.0
*/
public void warning(StringId msgID, Object[] params) {
warning(msgID, params, null);
}
/**
* Writes both a message and exception to this writer. The message level is "warning".
*
* @since GemFire 6.0
*/
public void warning(StringId msgID, Object param) {
warning(msgID, param, null);
}
/**
* Writes both a message and exception to this writer. The message level is "warning".
*
* @since GemFire 6.0
*/
public void warning(StringId msgID) {
warning(msgID, null, null);
}
/**
* @return true if "info" log messages are enabled.
*/
public boolean infoEnabled() {
return getLogWriterLevel() <= INFO_LEVEL
/* (bug 29581) && !SmHelper._memorySpaceLow() */;
}
/**
* Writes both a message and exception to this writer. The message level is "information".
*/
public void info(String msg, Throwable ex) {
if (this.infoEnabled()) {
this.put(INFO_LEVEL, msg, ex);
}
}
/**
* Writes a message to this writer. The message level is "information".
*/
public void info(String msg) {
this.info(msg, null);
}
/**
* Writes an exception to this writer. The exception level is "information".
*/
public void info(Throwable ex) {
this.info(LocalizedStrings.EMPTY, ex);
}
/**
* Writes both a message and exception to this writer. The message level is "info".
*
* @since GemFire 6.0
*/
public void info(StringId msgID, Object[] params, Throwable ex) {
if (this.infoEnabled()) {
this.put(INFO_LEVEL, msgID, params, ex);
}
}
/**
* Writes both a message and exception to this writer. The message level is "info".
*
* @since GemFire 6.0
*/
public void info(StringId msgID, Object param, Throwable ex) {
if (this.infoEnabled()) {
this.put(INFO_LEVEL, msgID, new Object[] {param}, ex);
}
}
/**
* Writes both a message and exception to this writer. The message level is "info".
*
* @since GemFire 6.0
*/
public void info(StringId msgID, Throwable ex) {
info(msgID, null, ex);
}
/**
* Writes both a message and exception to this writer. The message level is "info".
*
* @since GemFire 6.0
*/
public void info(StringId msgID, Object[] params) {
info(msgID, params, null);
}
/**
* Writes both a message and exception to this writer. The message level is "info".
*
* @since GemFire 6.0
*/
public void info(StringId msgID, Object param) {
info(msgID, param, null);
}
/**
* Writes both a message and exception to this writer. The message level is "info".
*
* @since GemFire 6.0
*/
public void info(StringId msgID) {
info(msgID, null, null);
}
/**
* @return true if "config" log messages are enabled.
*/
public boolean configEnabled() {
return getLogWriterLevel() <= CONFIG_LEVEL;
}
/**
* Writes both a message and exception to this writer. The message level is "config".
*/
public void config(String msg, Throwable ex) {
if (this.configEnabled()) {
this.put(CONFIG_LEVEL, msg, ex);
}
}
/**
* Writes a message to this writer. The message level is "config".
*/
public void config(String msg) {
this.config(msg, null);
}
/**
* Writes an exception to this writer. The exception level is "config".
*/
public void config(Throwable ex) {
this.config(LocalizedStrings.EMPTY, ex);
}
/**
* Writes both a message and exception to this writer. The message level is "config".
*
* @since GemFire 6.0
*/
public void config(StringId msgID, Object[] params, Throwable ex) {
if (this.configEnabled()) {
this.put(CONFIG_LEVEL, msgID, params, ex);
}
}
/**
* Writes both a message and exception to this writer. The message level is "config".
*
* @since GemFire 6.0
*/
public void config(StringId msgID, Object param, Throwable ex) {
if (this.configEnabled()) {
this.put(CONFIG_LEVEL, msgID, new Object[] {param}, ex);
}
}
/**
* Writes both a message and exception to this writer. The message level is "config".
*
* @since GemFire 6.0
*/
public void config(StringId msgID, Throwable ex) {
config(msgID, null, ex);
}
/**
* Writes both a message and exception to this writer. The message level is "config".
*
* @since GemFire 6.0
*/
public void config(StringId msgID, Object[] params) {
config(msgID, params, null);
}
/**
* Writes both a message and exception to this writer. The message level is "config".
*
* @since GemFire 6.0
*/
public void config(StringId msgID, Object param) {
config(msgID, param, null);
}
/**
* Writes both a message and exception to this writer. The message level is "config".
*
* @since GemFire 6.0
*/
public void config(StringId msgID) {
config(msgID, null, null);
}
/**
* @return true if "fine" log messages are enabled.
*/
public boolean fineEnabled() {
return getLogWriterLevel() <= FINE_LEVEL
/* (bug 29581) && !SmHelper._memorySpaceLow() */;
}
/**
* Writes both a message and exception to this writer. The message level is "fine".
*/
public void fine(String msg, Throwable ex) {
if (this.fineEnabled()) {
this.put(FINE_LEVEL, msg, ex);
}
}
/**
* Writes a message to this writer. The message level is "fine".
*/
public void fine(String msg) {
this.fine(msg, null);
}
/**
* Writes an exception to this writer. The exception level is "fine".
*/
public void fine(Throwable ex) {
this.fine(null, ex);
}
/**
* Returns true if "finer" log messages are enabled. Returns false if "finer" log messages are
* disabled.
*/
public boolean finerEnabled() {
return getLogWriterLevel() <= FINER_LEVEL
/* (bug 29581) && !SmHelper._memorySpaceLow() */;
}
/**
* Writes both a message and exception to this writer. The message level is "finer".
*/
public void finer(String msg, Throwable ex) {
if (this.finerEnabled()) {
this.put(FINER_LEVEL, msg, ex);
}
}
/**
* Writes a message to this writer. The message level is "finer".
*/
public void finer(String msg) {
this.finer(msg, null);
}
/**
* Writes an exception to this writer. The exception level is "finer".
*/
public void finer(Throwable ex) {
this.finer(null, ex);
}
/**
* Log a method entry.
* <p>
* The logging is done using the <code>finer</code> level. The string message will start with
* <code>"ENTRY"</code> and include the class and method names.
*
* @param sourceClass Name of class that issued the logging request.
* @param sourceMethod Name of the method that issued the logging request.
*/
public void entering(String sourceClass, String sourceMethod) {
if (this.finerEnabled()) {
this.finer("ENTRY " + sourceClass + ":" + sourceMethod);
}
}
/**
* Log a method return.
* <p>
* The logging is done using the <code>finer</code> level. The string message will start with
* <code>"RETURN"</code> and include the class and method names.
*
* @param sourceClass Name of class that issued the logging request.
* @param sourceMethod Name of the method that issued the logging request.
*/
public void exiting(String sourceClass, String sourceMethod) {
if (this.finerEnabled()) {
this.finer("RETURN " + sourceClass + ":" + sourceMethod);
}
}
/**
* Log throwing an exception.
* <p>
* Use to log that a method is terminating by throwing an exception. The logging is done using the
* <code>finer</code> level.
* <p>
* This is a convenience method that could be done instead by calling
* {@link #finer(String, Throwable)}. The string message will start with <code>"THROW"</code> and
* include the class and method names.
*
* @param sourceClass Name of class that issued the logging request.
* @param sourceMethod Name of the method that issued the logging request.
* @param thrown The Throwable that is being thrown.
*/
public void throwing(String sourceClass, String sourceMethod, Throwable thrown) {
if (this.finerEnabled()) {
this.finer("THROW " + sourceClass + ":" + sourceMethod, thrown);
}
}
/**
* Returns true if "finest" log messages are enabled. Returns false if "finest" log messages are
* disabled.
*/
public boolean finestEnabled() {
return getLogWriterLevel() <= FINEST_LEVEL
/* (bug 29581) && !SmHelper._memorySpaceLow() */;
}
/**
* Writes both a message and exception to this writer. The message level is "finest".
*/
public void finest(String msg, Throwable ex) {
if (this.finestEnabled()) {
this.put(FINEST_LEVEL, msg, ex);
}
}
/**
* Writes a message to this writer. The message level is "finest".
*/
public void finest(String msg) {
this.finest(msg, null);
}
/**
* Writes an exception to this writer. The exception level is "finest".
*/
public void finest(Throwable ex) {
this.finest(null, ex);
}
/**
* Writes both a message and exception to this writer. If a startup listener is registered, the
* message will be written to the listener as well to be reported to a user.
*
* @since GemFire 7.0
*/
public void startup(StringId msgID, Object[] params) {
String message = msgID.toLocalizedString(params);
StartupStatusListener listener = startupListener;
if (listener != null) {
listener.setStatus(message);
}
if (this.infoEnabled()) {
this.put(INFO_LEVEL, message, null);
}
}
// internal implemenation methods
/**
* Logs a message and an exception of the given level.
*
* @param msgLevel the level code for the message to log
* @param msg the actual message to log
* @param exception the actual Exception to log
*/
public abstract void put(int msgLevel, String msg, Throwable exception);
/**
* Logs a message and an exception of the given level.
*
* @param msgLevel the level code for the message to log
* @param msgId A locale agnostic form of the message
* @param params the Object arguments to plug into the message
* @param exception the actual Exception to log
*/
public abstract void put(int msgLevel, StringId msgId, Object[] params, Throwable exception);
static void formatText(PrintWriter writer, String target, int initialLength) {
BreakIterator boundary = BreakIterator.getLineInstance();
boundary.setText(target);
int start = boundary.first();
int end = boundary.next();
int lineLength = initialLength;
while (end != BreakIterator.DONE) {
// Look at the end and only accept whitespace breaks
char endChar = target.charAt(end - 1);
while (!Character.isWhitespace(endChar)) {
int lastEnd = end;
end = boundary.next();
if (end == BreakIterator.DONE) {
// give up. We are at the end of the string
end = lastEnd;
break;
}
endChar = target.charAt(end - 1);
}
int wordEnd = end;
if (endChar == '\n') {
// trim off the \n since println will do it for us
wordEnd--;
if (wordEnd > 0 && target.charAt(wordEnd - 1) == '\r') {
wordEnd--;
}
} else if (endChar == '\t') {
// figure tabs use 8 characters
lineLength += 7;
}
String word = target.substring(start, wordEnd);
lineLength += word.length();
writer.print(word);
if (endChar == '\n' || endChar == '\r') {
// force end of line
writer.println();
writer.print(" ");
lineLength = 2;
}
start = end;
end = boundary.next();
}
if (lineLength != 0) {
writer.println();
}
}
/**
* Check if a message of the given level would actually be logged by this logger. This check is
* based on the Logger effective level.
*
* @param level a message logging level
* @return true if the given message level is currently being logged.
*/
public boolean isLoggable(int level) {
return getLogWriterLevel() <= level;
}
/**
* Log a message, with associated Throwable information. If the logger is currently enabled for
* the given message level then the given message is logged.
*
* @param level One of the message level identifiers, e.g. SEVERE
* @param message The string message
* @param thrown - Throwable associated with log message.
*/
public void log(int level, String message, Throwable thrown) {
if (isLoggable(level)) {
this.put(level, message, thrown);
}
}
/**
* Log a message. If the logger is currently enabled for the given message level then the given
* message is logged.
*
* @param level One of the message level identifiers, e.g. SEVERE
* @param message The string message
*/
public void log(int level, String message) {
if (isLoggable(level)) {
this.put(level, message, null);
}
}
public Handler getHandler() {
return new GemFireHandler(this);
}
/** Utility to get a stack trace as a string from a Throwable */
public static String getStackTrace(Throwable aThrowable) {
StringWriter sw = new StringWriter();
aThrowable.printStackTrace(new PrintWriter(sw, true));
return sw.toString();
}
/**
* Utility to periodically log a stack trace for a thread. The thread should be alive when this
* method is invoked.
*
* @param toStdout whether to log to stdout or to this log writer
* @param targetThread the thread to snapshot
* @param interval millis to wait betweeen snaps
* @param done when to stop snapshotting (also ceases if targetThread dies)
*/
public void logTraces(final boolean toStdout, final Thread targetThread, final int interval,
final AtomicBoolean done) {
if (targetThread == null) {
return;
}
Thread watcherThread = new Thread("Stack Tracer for '" + targetThread.getName() + "'") {
@Override
public void run() {
while (!done.get()) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
return;
}
if (!done.get() && targetThread.isAlive()) {
StringBuffer sb = new StringBuffer(500);
if (toStdout) {
sb.append("[trace ").append(getTimeStamp()).append("] ");
}
StackTraceElement[] els = targetThread.getStackTrace();
sb.append("Stack trace for '").append(targetThread.toString()).append("'\n");
if (els.length > 0) {
for (int i = 0; i < els.length; i++) {
sb.append("\tat ").append(els[i].toString()).append("\n");
}
} else {
sb.append(" no stack\n");
}
if (toStdout) {
System.out.println(sb.toString());
} else {
info(LocalizedStrings.DEBUG, sb.toString());
}
}
}
}
};
watcherThread.start();
}
/** Utility to get a stack trace for a thread */
public static StringBuffer getStackTrace(Thread targetThread) {
StringBuffer sb = new StringBuffer(500);
StackTraceElement[] els = targetThread.getStackTrace();
sb.append("Stack trace for '").append(targetThread.toString()).append("'\n");
if (els.length > 0) {
for (int i = 0; i < els.length; i++) {
sb.append("\tat ").append(els[i].toString()).append("\n");
}
} else {
sb.append(" no stack\n");
}
return sb;
}
// instance variables
private final DateFormat timeFormatter;
/*
* @see org.apache.geode.LogWriter
*
* @since GemFire 6.0
*/
public org.apache.geode.LogWriter convertToLogWriter() {
return this;
}
/*
* @see org.apache.geode.LogWriterI18n
*
* @since GemFire 6.0
*/
public LogWriterI18n convertToLogWriterI18n() {
return this;
}
public static void setStartupListener(StartupStatusListener mainListener) {
LogWriterImpl.startupListener = mainListener;
}
public static StartupStatusListener getStartupListener() {
return LogWriterImpl.startupListener;
}
}