| /* |
| * ========================================================================= |
| * Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved. |
| * This product is protected by U.S. and international copyright |
| * and intellectual property laws. Pivotal products are covered by |
| * more patents listed at http://www.pivotal.io/patents. |
| * ======================================================================== |
| */ |
| package com.gemstone.gemfire.management.internal.cli; |
| |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| 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.Filter; |
| 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 com.gemstone.gemfire.cache.Cache; |
| import com.gemstone.gemfire.internal.i18n.LocalizedStrings; |
| import com.gemstone.gemfire.management.internal.cli.remote.CommandExecutionContext; |
| import com.gemstone.gemfire.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() |
| * |
| * @author Abhishek Chaudhari |
| * @author John Blum |
| * @since 7.0 |
| */ |
| public class LogWrapper { |
| private static Object INSTANCE_LOCK = new Object(); |
| private volatile static LogWrapper INSTANCE = null; |
| |
| private Logger logger; |
| |
| private LogWrapper() { |
| logger = Logger.getLogger(this.getClass().getCanonicalName()); |
| |
| Cache cache = CliUtil.getCacheIfExists(); |
| if (cache != null && !cache.isClosed()) { |
| //TODO - Abhishek how to set different log levels for different handlers??? |
| logger.addHandler(cache.getLogger().getHandler()); |
| CommandResponseWriterHandler handler = new CommandResponseWriterHandler(); |
| handler.setFilter(new Filter() { |
| @Override |
| public boolean isLoggable(LogRecord record) { |
| return record.getLevel().intValue() >= Level.FINE.intValue(); |
| } |
| }); |
| handler.setLevel(Level.FINE); |
| logger.addHandler(handler); |
| } |
| logger.setUseParentHandlers(false); |
| } |
| |
| public static LogWrapper getInstance() { |
| if (INSTANCE == null) { |
| synchronized (INSTANCE_LOCK) { |
| if (INSTANCE == null) { |
| INSTANCE = new LogWrapper(); |
| } |
| } |
| } |
| |
| return INSTANCE; |
| } |
| |
| public void configure(GfshConfig config) { |
| if (config.isLoggingEnabled()) { |
| try { |
| FileHandler fileHandler = new FileHandler(config.getLogFilePath(), |
| config.getLogFileSizeLimit(), |
| config.getLogFileCount(), |
| true /*append*/); |
| fileHandler.setFormatter(new GemFireFormatter()); |
| fileHandler.setLevel(config.getLogLevel()); |
| logger.addHandler(fileHandler); |
| logger.setLevel(config.getLogLevel()); |
| } catch (SecurityException e) { |
| addDefaultConsoleHandler(logger, e.getMessage(), config.getLogFilePath()); |
| } catch (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}. |
| */ |
| protected void finalize() throws Throwable { |
| cleanupLogger(this.logger); |
| this.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(); |
| } |
| |
| //TODO - Abhishek - ideally shouldn't be exposed outside. |
| /*package*/ 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); |
| } |
| } |
| |
| //TODO - Abhishek - Check whether we can use GemFireLevel.ERROR |
| // public boolean errorEnabled() { |
| // return severeEnabled(); |
| // } |
| // |
| // public void error(String message) { |
| // logger.severe(message); |
| // } |
| // |
| // public void error(String message, Throwable t) { |
| // 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); |
| } |
| } |
| |
| /** |
| * |
| * @author Abhishek Chaudhari |
| * @since 7.0 |
| */ |
| // Formatter code "copied" from LogWriterImpl |
| static class GemFireFormatter extends Formatter { |
| private final static String FORMAT = "yyyy/MM/dd HH:mm:ss.SSS z"; |
| |
| private SimpleDateFormat sdf = new SimpleDateFormat(FORMAT); |
| |
| @Override |
| public synchronized String format(LogRecord record) { |
| java.io.StringWriter sw = new java.io.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(LocalizedStrings.GemFireFormatter_IGNORING_THE_FOLLOWING_EXCEPTION.toLocalizedString()); |
| e.printStackTrace(pw); |
| } |
| } else { |
| pw.println(); |
| } |
| if (record.getThrown() != null) { |
| record.getThrown().printStackTrace(pw); |
| } |
| pw.close(); |
| try { |
| sw.close(); |
| } catch (java.io.IOException ignore) {} |
| String result = sw.toString(); |
| return result; |
| } |
| |
| 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); |
| } |
| } |
| |
| /** |
| * Handler to write to CommandResponseWriter |
| * |
| * @author Abhishek Chaudhari |
| * @since 7.0 |
| */ |
| static class CommandResponseWriterHandler extends Handler { |
| |
| public CommandResponseWriterHandler() { |
| setFormatter(new GemFireFormatter()); |
| } |
| |
| @Override |
| public void publish(LogRecord record) { |
| CommandResponseWriter responseWriter = CommandExecutionContext.getAndCreateIfAbsentCommandResponseWriter(); |
| responseWriter.println(getFormatter().format(record)); |
| } |
| |
| @Override |
| public void flush() { |
| } |
| |
| @Override |
| public void close() throws SecurityException { |
| } |
| } |
| } |