blob: 38b24bf3a38f2eaa8eadc0f2204f308818cd1e2f [file] [log] [blame]
/*
* 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.management.internal.cli;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.BreakIterator;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import org.apache.geode.annotations.internal.MakeNotStatic;
import org.apache.geode.cache.Cache;
import org.apache.geode.management.internal.cli.shell.GfshConfig;
/**
* NOTE: Should be used only in 1. gfsh process 2. on a Manager "if" log is required to be sent back
* to gfsh too. For logging only on manager use, cache.getLogger()
*
* @since GemFire 7.0
*/
public class LogWrapper {
private static final Object INSTANCE_LOCK = new Object();
@MakeNotStatic
private static volatile LogWrapper INSTANCE = null;
private Logger logger;
private LogWrapper(Cache cache) {
logger = Logger.getLogger(getClass().getCanonicalName());
if (cache != null && !cache.isClosed()) {
logger.addHandler(cache.getLogger().getHandler());
}
logger.setUseParentHandlers(false);
}
/**
* Used in the manager when logging is required to be sent back to gfsh
*/
public static LogWrapper getInstance(Cache cache) {
if (INSTANCE == null) {
synchronized (INSTANCE_LOCK) {
if (INSTANCE == null) {
INSTANCE = new LogWrapper(cache);
}
}
}
return INSTANCE;
}
/**
* used in the gfsh process
*/
public static LogWrapper getInstance() {
return getInstance(null);
}
public void configure(GfshConfig config) {
if (config.isLoggingEnabled()) {
try {
FileHandler fileHandler = new FileHandler(config.getLogFilePath(),
config.getLogFileSizeLimit(), config.getLogFileCount(), true);
fileHandler.setFormatter(new GemFireFormatter());
fileHandler.setLevel(config.getLogLevel());
logger.addHandler(fileHandler);
logger.setLevel(config.getLogLevel());
} catch (SecurityException | IOException e) {
addDefaultConsoleHandler(logger, e.getMessage(), config.getLogFilePath());
}
}
}
private static void addDefaultConsoleHandler(Logger logger, String errorMessage,
String logFilePath) {
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(new GemFireFormatter());
logger.addHandler(consoleHandler);
System.err
.println("ERROR: Could not log to file: " + logFilePath + ". Reason: " + errorMessage);
System.err.println("Logs will be written on Console.");
try {
Thread.sleep(3000); // sleep for 3 secs for the message to appear
} catch (InterruptedException ignore) {
}
}
/**
* Closes the current LogWrapper.
*/
public static void close() {
synchronized (INSTANCE_LOCK) {
if (INSTANCE != null) {
Logger innerLogger = INSTANCE.logger;
// remove any existing handlers
cleanupLogger(innerLogger);
}
// make singleton null
INSTANCE = null;
}
}
/**
* Removed all the handlers of the given {@link Logger} instance.
*
* @param logger {@link Logger} to be cleaned up.
*/
private static void cleanupLogger(Logger logger) {
if (logger != null) {
Handler[] handlers = logger.getHandlers();
for (Handler handler : handlers) {
handler.close();
logger.removeHandler(handler);
}
}
}
/**
* Make logger null when the singleton (which was referred by INSTANCE) gets garbage collected.
* Makes an attempt at removing associated {@link Handler}s of the {@link Logger}.
*/
@Override
protected void finalize() throws Throwable {
cleanupLogger(logger);
logger = null;
}
public void setParentFor(Logger otherLogger) {
if (otherLogger.getParent() != logger) {
otherLogger.setParent(logger);
}
}
public void setLogLevel(Level newLevel) {
if (logger.getLevel() != newLevel) {
logger.setLevel(newLevel);
}
}
public Level getLogLevel() {
return logger.getLevel();
}
Logger getLogger() {
return logger;
}
public boolean severeEnabled() {
return logger.isLoggable(Level.SEVERE);
}
public void severe(String message) {
if (severeEnabled()) {
logger.severe(message);
}
}
public void severe(String message, Throwable t) {
if (severeEnabled()) {
logger.log(Level.SEVERE, message, t);
}
}
public boolean warningEnabled() {
return logger.isLoggable(Level.WARNING);
}
public void warning(String message) {
if (warningEnabled()) {
logger.warning(message);
}
}
public void warning(String message, Throwable t) {
if (warningEnabled()) {
logger.log(Level.WARNING, message, t);
}
}
public boolean infoEnabled() {
return logger.isLoggable(Level.INFO);
}
public void info(String message) {
if (infoEnabled()) {
logger.info(message);
}
}
public void info(String message, Throwable t) {
if (infoEnabled()) {
logger.log(Level.INFO, message, t);
}
}
public boolean configEnabled() {
return logger.isLoggable(Level.CONFIG);
}
public void config(String message) {
if (configEnabled()) {
logger.config(message);
}
}
public void config(String message, Throwable t) {
if (configEnabled()) {
logger.log(Level.CONFIG, message, t);
}
}
public boolean fineEnabled() {
return logger.isLoggable(Level.FINE);
}
public void fine(String message) {
if (fineEnabled()) {
logger.fine(message);
}
}
public void fine(String message, Throwable t) {
if (fineEnabled()) {
logger.log(Level.FINE, message, t);
}
}
public boolean finerEnabled() {
return logger.isLoggable(Level.FINER);
}
public void finer(String message) {
if (finerEnabled()) {
logger.finer(message);
}
}
public void finer(String message, Throwable t) {
if (finerEnabled()) {
logger.log(Level.FINER, message, t);
}
}
public boolean finestEnabled() {
return logger.isLoggable(Level.FINEST);
}
public void finest(String message) {
if (finestEnabled()) {
logger.finest(message);
}
}
public void finest(String message, Throwable t) {
if (finestEnabled()) {
logger.log(Level.FINEST, message, t);
}
}
/**
*
* @since GemFire 7.0
*/
static class GemFireFormatter extends Formatter {
private static final String FORMAT = "yyyy/MM/dd HH:mm:ss.SSS z";
private final SimpleDateFormat sdf = new SimpleDateFormat(FORMAT);
@Override
public synchronized String format(LogRecord record) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.println();
pw.print('[');
pw.print(record.getLevel().getName().toLowerCase());
pw.print(' ');
pw.print(formatDate(new Date(record.getMillis())));
String threadName = Thread.currentThread().getName();
if (threadName != null) {
pw.print(' ');
pw.print(threadName);
}
pw.print(" tid=0x");
pw.print(Long.toHexString(Thread.currentThread().getId()));
pw.print("] ");
pw.print("(msgTID=");
pw.print(record.getThreadID());
pw.print(" msgSN=");
pw.print(record.getSequenceNumber());
pw.print(") ");
String msg = record.getMessage();
if (msg != null) {
try {
formatText(pw, msg, 40);
} catch (RuntimeException e) {
pw.println(msg);
pw.println("Ignoring the following exception:");
e.printStackTrace(pw);
}
} else {
pw.println();
}
if (record.getThrown() != null) {
record.getThrown().printStackTrace(pw);
}
pw.close();
try {
sw.close();
} catch (IOException ignore) {
}
return sw.toString();
}
private 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();
}
}
private String formatDate(Date date) {
return sdf.format(date);
}
}
}