blob: 17830dbf8aeecb87d80e487c8514e35e6dd9a406 [file] [log] [blame]
// Copyright (C) 2013 The Android Open Source Project
//
// 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.googlesource.gerrit.plugins.its.base.testutil.log;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
import java.util.Collection;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;
/** Utility functions for dealing with various Loggers */
public class LogUtil {
private static final ImmutableMap<org.apache.log4j.Level, Level> log4jToJul =
new ImmutableMap.Builder<org.apache.log4j.Level, Level>()
.put(org.apache.log4j.Level.OFF, Level.OFF)
.put(org.apache.log4j.Level.FATAL, Level.SEVERE)
.put(org.apache.log4j.Level.ERROR, Level.SEVERE)
.put(org.apache.log4j.Level.WARN, Level.WARNING)
.put(org.apache.log4j.Level.INFO, Level.INFO)
.put(org.apache.log4j.Level.DEBUG, Level.FINE)
.put(org.apache.log4j.Level.TRACE, Level.FINEST)
.put(org.apache.log4j.Level.ALL, Level.FINEST)
.build();
/**
* Makes sure that Loggers for a given name add their events to a collection
*
* <p>This is useful for capturing logged events during testing and being able to run assertions
* on them.
*
* <p>We will never be able to cover all possible backends of the Logging flavour of the day. For
* now we cover Log4j and JUL. If you use a different default logging backend, please send in
* patches.
*
* @param logName The name of the loggers to make log to the given collection.
* @param collection The collection to add log events to.
* @param level The level the logger should be set to.
*/
public static CollectionAppender logToCollection(
String logName, Collection<LogRecord> collection, org.apache.log4j.Level level) {
CollectionAppender appender = new CollectionAppender(collection);
logToCollectionLog4j(logName, appender, level);
logToCollectionJul(logName, appender, log4jToJul.get(level));
return appender;
}
/**
* Make a Log4j logger log to a given appender at a certain level
*
* @param logName The logger that should log to the appender.
* @param appender The appender to log to.
* @param level The level to user for the logger.
*/
private static void logToCollectionLog4j(
String logName, CollectionAppender appender, org.apache.log4j.Level level) {
Logger log = LogManager.getLogger(logName);
log.setLevel(level);
log.removeAllAppenders();
log.addAppender(appender);
}
/**
* Make a java.util.logging logger log to a given appender at a certain level
*
* @param logName The logger that should log to the appender.
* @param appender The appender to log to.
* @param level The level to user for the logger.
*/
private static void logToCollectionJul(String logName, CollectionAppender appender, Level level) {
// We'd love to simply get the logger of name `logName` and directly
// configure that. While this works for running the tests in bazel, it
// fails when running tests from within Eclipse. In Eclipse getting the
// logger of the same name here and from the class-under-test will get two
// different loggers, due to backend calling from a different class. So we
// instead resort to configuring the root logger and filtering the logName
// in the appender.
// The description above works for the 2nd, 3rd, ... test of a test case in
// Eclipse, but not for the first. To cover the first test as well, we
// beforehand tell Flogger to set things up by getting any random logger
// /before/ we configure the root logger.
@SuppressWarnings("unused")
FluentLogger unused = FluentLogger.forEnclosingClass();
java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger("");
julLogger.setLevel(level);
julLogger.addHandler(
new Handler() {
@Override
public void publish(LogRecord record) {
if (record.getLoggerName().equals(logName)) {
appender.append(record);
}
}
@Override
public void flush() {}
@Override
public void close() throws SecurityException {}
});
}
/**
* Converts a Log4j Logging Event to a JUL LogRecord
*
* <p>This is not a full conversion, but covers only the fields we care about. That is the logged
* message and eventual thrown Throwables.
*
* @param event The Log4j LoggingEvent to convert
* @return The corresponding JUL LogRecord
*/
public static LogRecord logRecordFromLog4jLoggingEvent(LoggingEvent event) {
LogRecord logRecord = new LogRecord(Level.ALL, event.getRenderedMessage());
ThrowableInformation tInfo = event.getThrowableInformation();
if (tInfo != null) {
logRecord.setThrown(tInfo.getThrowable());
}
return logRecord;
}
/**
* Check if a JUL and a Log4j Level correspond
*
* @param left The JUL Level to check.
* @param right The Log4j Level to check.
* @return True, if and only if the levels correspond.
*/
public static boolean equalLevels(Level left, org.apache.log4j.Level right) {
Level julRight = log4jToJul.get(right);
return (left == null && right == null) || (left != null && left.equals(julRight));
}
}