Package com.proofpoint.log

Source Code of com.proofpoint.log.Logging

/*
* Copyright 2010 Proofpoint, 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.proofpoint.log;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.jul.LevelChangePropagator;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.OutputStreamAppender;
import ch.qos.logback.core.encoder.Encoder;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import ch.qos.logback.core.status.Status;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.slf4j.bridge.SLF4JBridgeHandler;

import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Handler;
import java.util.logging.LogManager;

import static com.google.common.base.Charsets.UTF_8;

/**
* Initializes the logging subsystem.
* <p/>
* java.util.Logging, System.out & System.err are tunneled through the logging system.
* <p/>
* System.out and System.err are assigned to loggers named "stdout" and "stderr", respectively.
*/
public class Logging
{
    private static final String PATTERN = "%d{yyyy-MM-dd'T'HH:mm:ss.SSSZ}\\t%5p\\t%t\\t%c\\t%X\\t%m%n";
    private final LoggerContext context;
    private final ch.qos.logback.classic.Logger root;
    private static final Logger log = Logger.get(Logging.class);
    private OutputStreamAppender<ILoggingEvent> consoleAppender;

    private static final String TEMP_FILE_EXTENSION = ".tmp";
    private static final String LOG_FILE_EXTENSION = ".log";

    private static Logging instance;

    public enum Level
    {
        DEBUG, INFO, WARN, ERROR
    }

    /**
     * Sets up default logging:
     * <p/>
     * - INFO level
     * - Log entries are written to stderr
     */
    public static synchronized Logging initialize()
    {
        if (instance == null) {
            instance = new Logging();
        }

        return instance;
    }

    private Logging()
    {
        // initialize root logger
        root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

        // assume SLF4J is bound to logback in the current environment
        context = (LoggerContext) LoggerFactory.getILoggerFactory();
        context.reset();

        LevelChangePropagator levelPropagator = new LevelChangePropagator();
        levelPropagator.setContext(context);
        context.addListener(levelPropagator);

        root.setLevel(ch.qos.logback.classic.Level.INFO);

        redirectJULToSLF4j();
        rewireStdStreams();
    }

    @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
    private void rewireStdStreams()
    {
        redirectSlf4jTo(new NonCloseableOutputStream(System.err));
        log.info("Logging to stderr");

        redirectStdStreams();
    }

    @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
    private void redirectStdStreams()
    {
        try {
            System.setOut(new PrintStream(new LoggingOutputStream(Logger.get("stdout")), true, "UTF-8"));
            System.setErr(new PrintStream(new LoggingOutputStream(Logger.get("stderr")), true, "UTF-8"));
        }
        catch (UnsupportedEncodingException ignored) {
        }
    }

    private void redirectSlf4jTo(OutputStream stream)
    {
        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setPattern(PATTERN);
        encoder.setContext(context);
        encoder.start();

        consoleAppender = new OutputStreamAppender<>();
        consoleAppender.setContext(context);
        consoleAppender.setEncoder(encoder);
        consoleAppender.setOutputStream(stream); // needs to happen after setEncoder()
        consoleAppender.start();
        root.addAppender(consoleAppender);
    }

    public void disableConsole()
    {
        log.info("Disabling stderr output");
        root.detachAppender(consoleAppender);
        consoleAppender.stop();
    }

    public void logToFile(String logPath, int maxHistory, long maxSizeInBytes)
    {
        log.info("Logging to %s", logPath);

        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setPattern(PATTERN);
        encoder.setContext(context);
        encoder.start();

        root.addAppender(createFileAppender(logPath, maxHistory, maxSizeInBytes, encoder, context));
    }

    public static <T> Appender<T> createFileAppender(String logPath, int maxHistory, long maxSizeInBytes, Encoder<T> encoder, LoggerContext context)
    {
        recoverTempFiles(logPath);

        RollingFileAppender<T> fileAppender = new RollingFileAppender<>();
        TimeBasedRollingPolicy<T> rollingPolicy = new TimeBasedRollingPolicy<>();
        SizeAndTimeBasedFNATP<T> triggeringPolicy = new SizeAndTimeBasedFNATP<>();

        rollingPolicy.setContext(context);
        rollingPolicy.setFileNamePattern(logPath + "-%d{yyyy-MM-dd}.%i.log.gz");
        rollingPolicy.setMaxHistory(maxHistory);
        rollingPolicy.setTimeBasedFileNamingAndTriggeringPolicy(triggeringPolicy);
        rollingPolicy.setParent(fileAppender);
        rollingPolicy.start();

        triggeringPolicy.setMaxFileSize(Long.toString(maxSizeInBytes));
        triggeringPolicy.setContext(context);
        triggeringPolicy.setTimeBasedRollingPolicy(rollingPolicy);
        triggeringPolicy.start();

        fileAppender.setFile(logPath);
        fileAppender.setAppend(true);
        fileAppender.setEncoder(encoder);
        fileAppender.setRollingPolicy(rollingPolicy);
        fileAppender.setContext(context);
        fileAppender.start();
        return fileAppender;
    }

    private void redirectJULToSLF4j()
    {
        java.util.logging.Logger javaRootLogger = LogManager.getLogManager().getLogger("");
        for (Handler handler : javaRootLogger.getHandlers()) {
            javaRootLogger.removeHandler(handler);
        }
        javaRootLogger.addHandler(new SLF4JBridgeHandler());
    }

    public void setLevels(File file)
            throws IOException
    {
        Properties properties = new Properties();
        try (Reader reader = new InputStreamReader(new FileInputStream(file), UTF_8)) {
            properties.load(reader);
        }

        processLevels(properties);
    }

    public void setLevel(String loggerName, Level level)
    {
        ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(loggerName);
        logger.setLevel(ch.qos.logback.classic.Level.toLevel(level.toString()));
    }

    private void processLevels(Properties properties)
    {
        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            String name = entry.getKey().toString();
            String level = entry.getValue().toString();

            ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(name);
            logger.setLevel(ch.qos.logback.classic.Level.toLevel(level));
        }
    }

    private static void recoverTempFiles(String logPath)
    {
        // logback has a tendency to leave around temp files if it is interrupted
        // these .tmp files are log files that are about to be compressed.
        // This method recovers them so that they aren't orphaned

        File logPathFile = new File(logPath).getParentFile();
        File[] tempFiles = logPathFile.listFiles(new FilenameFilter()
        {
            @Override
            public boolean accept(File dir, String name)
            {
                return name.endsWith(TEMP_FILE_EXTENSION);
            }
        });

        if (tempFiles != null) {
            for (File tempFile : tempFiles) {
                String newName = tempFile.getName().substring(0, tempFile.getName().length() - TEMP_FILE_EXTENSION.length());
                File newFile = new File(tempFile.getParent(), newName + LOG_FILE_EXTENSION);
                if (tempFile.renameTo(newFile)) {
                    log.info("Recovered temp file: %s", tempFile);
                }
                else {
                    log.warn("Could not rename temp file [%s] to [%s]", tempFile, newFile);
                }
            }
        }
    }

    public void configure(LoggingConfiguration config)
            throws IOException
    {
        if (config.getLogPath() == null && !config.isConsoleEnabled()) {
            throw new IllegalArgumentException("No log file is configured (log.output-file) and logging to console is disabled (log.enable-console)");
        }

        if (config.getLogPath() != null) {
            logToFile(config.getLogPath(), config.getMaxHistory(), config.getMaxSegmentSize().toBytes());
        }

        // logback keeps a internal list of errors that it encounters as the logging system is being
        // initialized. Here we check if any errors have happened so far -- especially important before we turn
        // off console logging, as file logging may be broken due to invalid paths or missing config.
        // If any errors have occurred, log them (to the console, which is guaranteed to be properly set up)
        // and bail out with an exception
        boolean error = false;
        for (Status status : root.getLoggerContext().getStatusManager().getCopyOfStatusList()) {
            if (status.getLevel() == Status.ERROR) {
                log.error(status.getMessage());
                error = true;
            }
        }

        if (error) {
            throw new RuntimeException("Error initializing logger, aborting");
        }

        if (!config.isConsoleEnabled()) {
            disableConsole();
        }

        if (config.getLevelsFile() != null) {
            setLevels(new File(config.getLevelsFile()));
        }
    }

    public static void putMDC(String key, Object value)
    {
        MDC.put(key, value.toString());
    }

    public static void removeMDC(String key)
    {
        MDC.remove(key);
    }
}
TOP

Related Classes of com.proofpoint.log.Logging

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.