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