com.facebook.airlift.log.Logging Maven / Gradle / Ivy
/*
* 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.facebook.airlift.log;
import com.google.common.collect.ImmutableSortedMap;
import javax.annotation.concurrent.GuardedBy;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import static com.google.common.collect.Maps.fromProperties;
/**
* Initializes the logging subsystem.
*
* java.util.Logging, System.out and System.err are tunneled through the logging system.
*
* System.out and System.err are assigned to loggers named "stdout" and "stderr", respectively.
*/
public class Logging
{
private static final Logger log = Logger.get(Logging.class);
private static final String ROOT_LOGGER_NAME = "";
private static final java.util.logging.Logger ROOT = java.util.logging.Logger.getLogger("");
private static Logging instance;
// hard reference to loggers for which we set the level
private final Map loggers = new ConcurrentHashMap<>();
@GuardedBy("this")
private OutputStreamHandler consoleHandler;
/**
* Sets up default logging:
*
* - INFO level
* - Log entries are written to stderr
*
* @return the logging system singleton
*/
public static synchronized Logging initialize()
{
if (instance == null) {
instance = new Logging();
}
return instance;
}
private Logging()
{
ROOT.setLevel(Level.INFO.toJulLevel());
for (Handler handler : ROOT.getHandlers()) {
ROOT.removeHandler(handler);
}
rewireStdStreams();
}
private void rewireStdStreams()
{
logConsole(new NonCloseableOutputStream(System.err));
log.info("Logging to stderr");
redirectStdStreams();
}
private static void redirectStdStreams()
{
System.setOut(new PrintStream(new LoggingOutputStream(Logger.get("stdout")), true));
System.setErr(new PrintStream(new LoggingOutputStream(Logger.get("stderr")), true));
}
private synchronized void logConsole(OutputStream stream)
{
consoleHandler = new OutputStreamHandler(stream);
ROOT.addHandler(consoleHandler);
}
public synchronized void disableConsole()
{
log.info("Disabling stderr output");
ROOT.removeHandler(consoleHandler);
consoleHandler = null;
}
public void logToFile(String logPath, int maxHistory, long maxSizeInBytes)
{
log.info("Logging to %s", logPath);
RollingFileHandler rollingFileHandler = new RollingFileHandler(logPath, maxHistory, maxSizeInBytes);
ROOT.addHandler(rollingFileHandler);
}
public Level getRootLevel()
{
return getLevel(ROOT_LOGGER_NAME);
}
public void setRootLevel(Level newLevel)
{
setLevel(ROOT_LOGGER_NAME, newLevel);
}
public void setLevels(File file)
throws IOException
{
Properties properties = new Properties();
try (InputStream inputStream = new FileInputStream(file)) {
properties.load(inputStream);
}
fromProperties(properties).forEach((loggerName, value) ->
setLevel(loggerName, Level.valueOf(value.toUpperCase(Locale.US))));
}
public Level getLevel(String loggerName)
{
return getEffectiveLevel(java.util.logging.Logger.getLogger(loggerName));
}
private static Level getEffectiveLevel(java.util.logging.Logger logger)
{
java.util.logging.Level level = logger.getLevel();
if (level == null) {
java.util.logging.Logger parent = logger.getParent();
if (parent != null) {
return getEffectiveLevel(parent);
}
}
if (level == null) {
return Level.OFF;
}
return Level.fromJulLevel(level);
}
public void setLevel(String loggerName, Level level)
{
loggers.computeIfAbsent(loggerName, java.util.logging.Logger::getLogger)
.setLevel(level.toJulLevel());
}
public Map getAllLevels()
{
ImmutableSortedMap.Builder levels = ImmutableSortedMap.naturalOrder();
for (String loggerName : Collections.list(LogManager.getLogManager().getLoggerNames())) {
java.util.logging.Level level = java.util.logging.Logger.getLogger(loggerName).getLevel();
if (level != null) {
levels.put(loggerName, Level.fromJulLevel(level));
}
}
return levels.build();
}
public void configure(LoggingConfiguration config)
{
if (config.getLogPath() != null) {
logToFile(config.getLogPath(), config.getMaxHistory(), config.getMaxSize().toBytes());
}
if (!config.isConsoleEnabled()) {
disableConsole();
}
if (config.getLevelsFile() != null) {
try {
setLevels(new File(config.getLevelsFile()));
}
catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
}