From f07e7401de7c553a0fb7092735a1e678e9976cc3 Mon Sep 17 00:00:00 2001 From: Colin Decker Date: Thu, 19 Sep 2024 08:17:01 -0700 Subject: [PATCH] Remove the log4j(1) backend. See reasoning in https://github.com/google/flogger/issues/306. Anyone who was still using this should be able to use the log4j2 backend with a log4j2 -> log4j1 bridge. Fixes https://github.com/google/flogger/issues/306. RELNOTES=Removed the log4j(1) backend. PiperOrigin-RevId: 676420796 --- log4j/BUILD | 59 ------ .../backend/log4j/Log4jBackendFactory.java | 61 ------ .../backend/log4j/Log4jLoggerBackend.java | 66 ------- .../backend/log4j/Log4jLoggingEventUtil.java | 184 ------------------ ...mmon.flogger.backend.system.BackendFactory | 1 - .../backend/log4j/AssertingLogger.java | 127 ------------ .../log4j/Log4jBackendFactoryTest.java | 39 ---- .../backend/log4j/Log4jBackendLoggerTest.java | 158 --------------- .../log4j/Log4jLoggingEventUtilTest.java | 172 ---------------- 9 files changed, 867 deletions(-) delete mode 100644 log4j/BUILD delete mode 100644 log4j/src/main/java/com/google/common/flogger/backend/log4j/Log4jBackendFactory.java delete mode 100644 log4j/src/main/java/com/google/common/flogger/backend/log4j/Log4jLoggerBackend.java delete mode 100644 log4j/src/main/java/com/google/common/flogger/backend/log4j/Log4jLoggingEventUtil.java delete mode 100644 log4j/src/main/resources/META-INF/services/com.google.common.flogger.backend.system.BackendFactory delete mode 100644 log4j/src/test/java/com/google/common/flogger/backend/log4j/AssertingLogger.java delete mode 100644 log4j/src/test/java/com/google/common/flogger/backend/log4j/Log4jBackendFactoryTest.java delete mode 100644 log4j/src/test/java/com/google/common/flogger/backend/log4j/Log4jBackendLoggerTest.java delete mode 100644 log4j/src/test/java/com/google/common/flogger/backend/log4j/Log4jLoggingEventUtilTest.java diff --git a/log4j/BUILD b/log4j/BUILD deleted file mode 100644 index 94f7077d..00000000 --- a/log4j/BUILD +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2018 Google Inc. All Rights Reserved. -# Author: ekempin@google.com (Edwin Kempin) -# -# Description: -# Flogger log4j backend (https://google.github.io/flogger). - -load("@google_bazel_common//testing:test_defs.bzl", "gen_java_tests") -load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library") -load("@rules_java//java:defs.bzl", "java_library") -load("//tools:maven.bzl", "pom_file") - -LOG4J_BACKEND_SRCS = glob(["src/main/java/**/*.java"]) - -LOG4J_BACKEND_RESOURCES = glob(["src/main/resources/**"]) - -java_library( - name = "log4j_backend", - srcs = LOG4J_BACKEND_SRCS, - javacopts = ["-source 8 -target 8"], - resources = LOG4J_BACKEND_RESOURCES, - tags = ["maven_coordinates=com.google.flogger:flogger-log4j-backend:${project.version}"], - deps = [ - "//api", - "//api:system_backend", - "@google_bazel_common//third_party/java/log4j", - ], -) - -pom_file( - name = "pom", - artifact_id = "flogger-log4j-backend", - artifact_name = "Flogger Log4j Backend", - targets = [":log4j_backend"], -) - -javadoc_library( - name = "javadoc", - srcs = LOG4J_BACKEND_SRCS, - root_packages = ["com.google.common.flogger.backend.log4j"], - deps = [":log4j_backend"], -) - -# ---- Unit Tests ---- - -gen_java_tests( - name = "log4j_tests", - srcs = glob(["src/test/java/**/*.java"]), - deps = [ - ":log4j_backend", - "//api", - "//api:system_backend", - "//api:testing", - "@google_bazel_common//third_party/java/guava", - "@google_bazel_common//third_party/java/jspecify_annotations", - "@google_bazel_common//third_party/java/junit", - "@google_bazel_common//third_party/java/log4j", - "@google_bazel_common//third_party/java/truth", - ], -) diff --git a/log4j/src/main/java/com/google/common/flogger/backend/log4j/Log4jBackendFactory.java b/log4j/src/main/java/com/google/common/flogger/backend/log4j/Log4jBackendFactory.java deleted file mode 100644 index 624a8cb3..00000000 --- a/log4j/src/main/java/com/google/common/flogger/backend/log4j/Log4jBackendFactory.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2018 The Flogger Authors. - * - * 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.google.common.flogger.backend.log4j; - -import com.google.common.flogger.backend.LoggerBackend; -import com.google.common.flogger.backend.system.BackendFactory; -import org.apache.log4j.Logger; - -/** - * BackendFactory for log4j. - * - *

When using Flogger's {@link com.google.common.flogger.backend.system.DefaultPlatform}, this - * factory will automatically be used if it is included on the classpath and no other implementation - * of {@code BackendFactory} (other than the default implementation) is. To specify it more - * explicitly or to work around an issue where multiple {@code BackendFactory} implementations are - * on the classpath, you can set the {@code flogger.backend_factory} system property: - * - *

- * - *

Note: This code is mostly derived from the equivalently named class in the Log4j2 backend - * implementation, and should be kept in-sync with it as far as possible. If possible, any changes - * to the functionality of this class should first be made in the log4j2 backend and then reflected - * here. If the behaviour of this class starts to deviate from that of the log4j2 backend in any - * significant way, this difference should be called out clearly in the documentation. - */ -public final class Log4jBackendFactory extends BackendFactory { - - // Must be public for ServiceLoader - public Log4jBackendFactory() {} - - @Override - public LoggerBackend create(String loggingClassName) { - // Compute the logger name exactly the same way as in SimpleBackendFactory. - // The logger name must match the name of the logging class so that we can return it from - // Log4jLoggerBackend#getName(). - // TODO(b/27920233): Strip inner/nested classes when deriving logger name. - Logger logger = Logger.getLogger(loggingClassName.replace('$', '.')); - return new Log4jLoggerBackend(logger); - } - - @Override - public String toString() { - return "Log4j backend"; - } -} diff --git a/log4j/src/main/java/com/google/common/flogger/backend/log4j/Log4jLoggerBackend.java b/log4j/src/main/java/com/google/common/flogger/backend/log4j/Log4jLoggerBackend.java deleted file mode 100644 index 32ddaa1b..00000000 --- a/log4j/src/main/java/com/google/common/flogger/backend/log4j/Log4jLoggerBackend.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2018 The Flogger Authors. - * - * 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.google.common.flogger.backend.log4j; - -import static com.google.common.flogger.backend.log4j.Log4jLoggingEventUtil.toLog4jLevel; -import static com.google.common.flogger.backend.log4j.Log4jLoggingEventUtil.toLog4jLoggingEvent; - -import com.google.common.flogger.backend.LogData; -import com.google.common.flogger.backend.LoggerBackend; -import org.apache.log4j.Logger; - -/** - * A logging backend that uses log4j to output log statements. - * - *

Note: This code is mostly derived from the equivalently named class in the Log4j2 backend - * implementation, and should be kept in-sync with it as far as possible. If possible, any changes - * to the functionality of this class should first be made in the log4j2 backend and then reflected - * here. If the behaviour of this class starts to deviate from that of the log4j2 backend in any - * significant way, this difference should be called out clearly in the documentation. - */ -final class Log4jLoggerBackend extends LoggerBackend { - private final Logger logger; - - // VisibleForTesting - Log4jLoggerBackend(Logger logger) { - this.logger = logger; - } - - @Override - public String getLoggerName() { - // Logger#getName() returns exactly the name that we used to create the Logger in - // Log4jBackendFactory. It matches the name of the logging class. - return logger.getName(); - } - - @Override - public boolean isLoggable(java.util.logging.Level level) { - return logger.isEnabledFor(toLog4jLevel(level)); - } - - @Override - public void log(LogData logData) { - // The caller is responsible to call isLoggable() before calling this method to ensure that only - // messages above the given threshold are logged. - logger.callAppenders(toLog4jLoggingEvent(logger, logData)); - } - - @Override - public void handleError(RuntimeException error, LogData badData) { - logger.callAppenders(toLog4jLoggingEvent(logger, error, badData)); - } -} diff --git a/log4j/src/main/java/com/google/common/flogger/backend/log4j/Log4jLoggingEventUtil.java b/log4j/src/main/java/com/google/common/flogger/backend/log4j/Log4jLoggingEventUtil.java deleted file mode 100644 index 30ea327a..00000000 --- a/log4j/src/main/java/com/google/common/flogger/backend/log4j/Log4jLoggingEventUtil.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2018 The Flogger Authors. - * - * 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.google.common.flogger.backend.log4j; - -import static java.util.concurrent.TimeUnit.NANOSECONDS; -import static java.util.logging.Level.WARNING; - -import com.google.common.flogger.LogContext; -import com.google.common.flogger.LogSite; -import com.google.common.flogger.backend.LogData; -import com.google.common.flogger.backend.MessageUtils; -import com.google.common.flogger.backend.Metadata; -import com.google.common.flogger.backend.MetadataProcessor; -import com.google.common.flogger.backend.Platform; -import com.google.common.flogger.backend.SimpleMessageFormatter; -import java.util.Collections; -import java.util.Map; -import java.util.logging.Level; -import org.apache.log4j.Logger; -import org.apache.log4j.spi.LocationInfo; -import org.apache.log4j.spi.LoggingEvent; -import org.apache.log4j.spi.ThrowableInformation; - -/** - * Helper to format LogData. - * - *

Note: This code is mostly derived from the equivalently named class in the Log4j2 backend - * implementation, and should be kept in-sync with it as far as possible. If possible, any changes - * to the functionality of this class should first be made in the log4j2 backend and then reflected - * here. If the behaviour of this class starts to deviate from that of the log4j2 backend in any - * significant way, this difference should be called out clearly in the documentation. - */ -final class Log4jLoggingEventUtil { - private Log4jLoggingEventUtil() {} - - static LoggingEvent toLog4jLoggingEvent(Logger logger, LogData logData) { - MetadataProcessor metadata = - MetadataProcessor.forScopeAndLogSite(Platform.getInjectedMetadata(), logData.getMetadata()); - String message = SimpleMessageFormatter.getDefaultFormatter().format(logData, metadata); - Throwable thrown = metadata.getSingleValue(LogContext.Key.LOG_CAUSE); - return toLog4jLoggingEvent(logger, logData, message, toLog4jLevel(logData.getLevel()), thrown); - } - - static LoggingEvent toLog4jLoggingEvent(Logger logger, RuntimeException error, LogData badData) { - String message = formatBadLogData(error, badData); - // Re-target this log message as a warning (or above) since it indicates a real bug. - Level level = badData.getLevel().intValue() < WARNING.intValue() ? WARNING : badData.getLevel(); - return toLog4jLoggingEvent(logger, badData, message, toLog4jLevel(level), error); - } - - private static LoggingEvent toLog4jLoggingEvent( - Logger logger, - LogData logData, - String message, - org.apache.log4j.Level level, - Throwable thrown) { - // The Nested Diagnostic Context (NDC) allows to include additional metadata into logs which - // are written from the current thread. - // - // Example: - // NDC.push("user.id=" + userId); - // // do business logic that triggers logs - // NDC.pop(); - // NDC.remove(); - // - // By using '%x' in the ConversionPattern of an appender this data can be included in the logs. - // - // We could include this data here by doing 'NDC.get()', but we don't want to encourage people - // using the log4j specific NDC. Instead this should be supported by a LoggingContext and usage - // of Flogger tags. - String nestedDiagnosticContext = ""; - - // The Mapped Diagnostic Context (MDC) allows to include additional metadata into logs which - // are written from the current thread. - // - // Example: - // MDC.put("user.id", userId); - // // do business logic that triggers logs - // MDC.clear(); - // - // By using '%X{key}' in the ConversionPattern of an appender this data can be included in the - // logs. - // - // We could include this data here by doing 'MDC.getContext()', but we don't want to encourage - // people using the log4j specific MDC. Instead this should be supported by a LoggingContext and - // usage of Flogger tags. - Map mdcProperties = Collections.emptyMap(); - - LogSite logSite = logData.getLogSite(); - LocationInfo locationInfo = - new LocationInfo( - logSite.getFileName(), - logSite.getClassName(), - logSite.getMethodName(), - Integer.toString(logSite.getLineNumber())); - - return new LoggingEvent( - logData.getLoggerName(), - logger, - NANOSECONDS.toMillis(logData.getTimestampNanos()), - level, - message, - Thread.currentThread().getName(), - thrown != null ? new ThrowableInformation(thrown) : null, - nestedDiagnosticContext, - locationInfo, - mdcProperties); - } - - /** Converts java.util.logging.Level to org.apache.log4j.Level. */ - static org.apache.log4j.Level toLog4jLevel(java.util.logging.Level level) { - if (level.intValue() >= java.util.logging.Level.SEVERE.intValue()) { - return org.apache.log4j.Level.ERROR; - } else if (level.intValue() >= java.util.logging.Level.WARNING.intValue()) { - return org.apache.log4j.Level.WARN; - } else if (level.intValue() >= java.util.logging.Level.INFO.intValue()) { - return org.apache.log4j.Level.INFO; - } else if (level.intValue() >= java.util.logging.Level.FINE.intValue()) { - return org.apache.log4j.Level.DEBUG; - } - return org.apache.log4j.Level.TRACE; - } - - /** - * Formats the log message in response to an exception during a previous logging attempt. A - * synthetic error message is generated from the original log data and the given exception is set - * as the cause. The level of this record is the maximum of WARNING or the original level. - */ - private static String formatBadLogData(RuntimeException error, LogData badLogData) { - StringBuilder errorMsg = - new StringBuilder("LOGGING ERROR: ").append(error.getMessage()).append('\n'); - int length = errorMsg.length(); - try { - appendLogData(badLogData, errorMsg); - } catch (RuntimeException e) { - // Reset partially written buffer when an error occurs. - errorMsg.setLength(length); - errorMsg.append("Cannot append LogData: ").append(e); - } - return errorMsg.toString(); - } - - /** Appends the given {@link LogData} to the given {@link StringBuilder}. */ - private static void appendLogData(LogData data, StringBuilder out) { - out.append(" original message: "); - if (data.getTemplateContext() == null) { - out.append(data.getLiteralArgument()); - } else { - // We know that there's at least one argument to display here. - out.append(data.getTemplateContext().getMessage()); - out.append("\n original arguments:"); - for (Object arg : data.getArguments()) { - out.append("\n ").append(MessageUtils.safeToString(arg)); - } - } - Metadata metadata = data.getMetadata(); - if (metadata.size() > 0) { - out.append("\n metadata:"); - for (int n = 0; n < metadata.size(); n++) { - out.append("\n "); - out.append(metadata.getKey(n).getLabel()).append(": ").append(metadata.getValue(n)); - } - } - out.append("\n level: ").append(data.getLevel()); - out.append("\n timestamp (nanos): ").append(data.getTimestampNanos()); - out.append("\n class: ").append(data.getLogSite().getClassName()); - out.append("\n method: ").append(data.getLogSite().getMethodName()); - out.append("\n line number: ").append(data.getLogSite().getLineNumber()); - } -} diff --git a/log4j/src/main/resources/META-INF/services/com.google.common.flogger.backend.system.BackendFactory b/log4j/src/main/resources/META-INF/services/com.google.common.flogger.backend.system.BackendFactory deleted file mode 100644 index 01de94f4..00000000 --- a/log4j/src/main/resources/META-INF/services/com.google.common.flogger.backend.system.BackendFactory +++ /dev/null @@ -1 +0,0 @@ -com.google.common.flogger.backend.log4j.Log4jBackendFactory \ No newline at end of file diff --git a/log4j/src/test/java/com/google/common/flogger/backend/log4j/AssertingLogger.java b/log4j/src/test/java/com/google/common/flogger/backend/log4j/AssertingLogger.java deleted file mode 100644 index 7ec0907f..00000000 --- a/log4j/src/test/java/com/google/common/flogger/backend/log4j/AssertingLogger.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2018 The Flogger Authors. - * - * 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.google.common.flogger.backend.log4j; - -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.truth.Truth.assertThat; - -import java.util.ArrayList; -import java.util.List; -import org.apache.log4j.Level; -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; -import org.apache.log4j.Priority; -import org.apache.log4j.spi.LoggingEvent; - -/** - * Helper class for testing the log4j backend. - * - *

This class implements a log4j Logger with assertion functionality. It captures the - * LoggingEvents so that they can be asserted later. - * - *

The message of the captured LoggingEvents is already formatted (it already contains all - * arguments and the metadata context was already appended). - * - *

Note that we do not just capture the LoggingEvent because we know that it's the action of - * calling some of the getters that causes work to be done (which is what any log handler would be - * expected to do). - * - *

Usage: - * - *

- *   AssertingLogger logger = AssertingLogger.createOrGet("foo");
- *   LoggerBackend backend = new Log4jLoggerBackend(logger);
- *   backend.log(FakeLogData.withPrintfStyle("Hello World"));
- *   logger.assertLogCount(1);
- *   logger.assertLogEntry(0, INFO, "Hello World");
- * 
- */ -final class AssertingLogger extends Logger { - - private static class LogEntry { - private final String message; - private final Level level; - private final Throwable thrown; - - LogEntry(String message, Level level, Throwable thrown) { - this.message = message; - this.level = level; - this.thrown = thrown; - } - } - - /** - * Returns an AssertingLogger with the given name. - * - *

If an AssertingLogger with the given name already exists, then that instance is returned. - * - *

It is safe to call this method in parallel tests, but it's up to the caller to use unique - * names if they want unique instances. - * - * @throws IllegalStateException if a logger with the given name already exists but is not an - * AssertingLogger - */ - static AssertingLogger createOrGet(String name) { - Logger logger = LogManager.getLogger(name, AssertingLogger::new); - checkState( - logger instanceof AssertingLogger, - "Logger %s already exists, but is not an AssertingLogger", - name); - return (AssertingLogger) logger; - } - - private final List entries = new ArrayList<>(); - - private AssertingLogger(String name) { - super(name); - } - - @Override - public boolean isEnabledFor(Priority level) { - return true; - } - - @Override - public void callAppenders(LoggingEvent event) { - entries.add( - new LogEntry( - event.getRenderedMessage(), - event.getLevel(), - event.getThrowableInformation() != null - ? event.getThrowableInformation().getThrowable() - : null)); - } - - String getMessage(int index) { - return entries.get(index).message; - } - - void assertLogCount(int count) { - assertThat(entries.size()).isEqualTo(count); - } - - void assertLogEntry(int index, Level level, String message) { - LogEntry entry = entries.get(index); - assertThat(entry.level).isEqualTo(level); - assertThat(entry.message).isEqualTo(message); - assertThat(entry.thrown).isNull(); - } - - void assertThrown(int index, Throwable thrown) { - assertThat(entries.get(index).thrown).isSameInstanceAs(thrown); - } -} diff --git a/log4j/src/test/java/com/google/common/flogger/backend/log4j/Log4jBackendFactoryTest.java b/log4j/src/test/java/com/google/common/flogger/backend/log4j/Log4jBackendFactoryTest.java deleted file mode 100644 index 50f331d0..00000000 --- a/log4j/src/test/java/com/google/common/flogger/backend/log4j/Log4jBackendFactoryTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2021 The Flogger Authors. - * - * 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.google.common.flogger.backend.log4j; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.collect.ImmutableList; -import com.google.common.flogger.backend.system.BackendFactory; -import java.util.ServiceLoader; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Tests specific to the {@link Log4jBackendFactory} implementation. */ -@RunWith(JUnit4.class) -public final class Log4jBackendFactoryTest { - - @Test - public void testCanLoadWithServiceLoader() { - ImmutableList factories = - ImmutableList.copyOf(ServiceLoader.load(BackendFactory.class)); - assertThat(factories).hasSize(1); - assertThat(factories.get(0)).isInstanceOf(Log4jBackendFactory.class); - } -} diff --git a/log4j/src/test/java/com/google/common/flogger/backend/log4j/Log4jBackendLoggerTest.java b/log4j/src/test/java/com/google/common/flogger/backend/log4j/Log4jBackendLoggerTest.java deleted file mode 100644 index 4342ca50..00000000 --- a/log4j/src/test/java/com/google/common/flogger/backend/log4j/Log4jBackendLoggerTest.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2018 The Flogger Authors. - * - * 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.google.common.flogger.backend.log4j; - -import static com.google.common.truth.Truth.assertThat; -import static org.apache.log4j.Level.DEBUG; -import static org.apache.log4j.Level.ERROR; -import static org.apache.log4j.Level.INFO; -import static org.apache.log4j.Level.TRACE; -import static org.apache.log4j.Level.WARN; -import static org.junit.Assert.fail; - -import com.google.common.flogger.LogContext; -import com.google.common.flogger.MetadataKey; -import com.google.common.flogger.backend.LogData; -import com.google.common.flogger.backend.LoggerBackend; -import com.google.common.flogger.parser.ParseException; -import com.google.common.flogger.testing.FakeLogData; -import org.apache.log4j.Hierarchy; -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -public class Log4jBackendLoggerTest { - private static final MetadataKey COUNT_KEY = MetadataKey.single("count", Integer.class); - private static final MetadataKey ID_KEY = MetadataKey.single("id", String.class); - - @Rule public TestName name = new TestName(); - - private static LoggerBackend newBackend(Logger logger) { - return new Log4jLoggerBackend(logger); - } - - @Before - public void setup() { - removeCachedLoggers(); - } - - @After - public void cleanup() { - removeCachedLoggers(); - } - - @Test - public void testMessage() { - AssertingLogger logger = getAssertingLogger(); - LoggerBackend backend = newBackend(logger); - - backend.log(FakeLogData.of("Hello World")); - backend.log(FakeLogData.withPrintfStyle("Hello %s %s", "Foo", "Bar")); - - logger.assertLogCount(2); - logger.assertLogEntry(0, INFO, "Hello World"); - logger.assertLogEntry(1, INFO, "Hello Foo Bar"); - } - - @Test - public void testMetadata() { - AssertingLogger logger = getAssertingLogger(); - LoggerBackend backend = newBackend(logger); - - backend.log( - FakeLogData.withPrintfStyle("Foo='%s'", "bar") - .addMetadata(COUNT_KEY, 23) - .addMetadata(ID_KEY, "test ID")); - - logger.assertLogCount(1); - logger.assertLogEntry(0, INFO, "Foo='bar' [CONTEXT count=23 id=\"test ID\" ]"); - } - - @Test - public void testLevels() { - AssertingLogger logger = getAssertingLogger(); - LoggerBackend backend = newBackend(logger); - - backend.log(FakeLogData.of("finest").setLevel(java.util.logging.Level.FINEST)); - backend.log(FakeLogData.of("finer").setLevel(java.util.logging.Level.FINER)); - backend.log(FakeLogData.of("fine").setLevel(java.util.logging.Level.FINE)); - backend.log(FakeLogData.of("config").setLevel(java.util.logging.Level.CONFIG)); - backend.log(FakeLogData.of("info").setLevel(java.util.logging.Level.INFO)); - backend.log(FakeLogData.of("warning").setLevel(java.util.logging.Level.WARNING)); - backend.log(FakeLogData.of("severe").setLevel(java.util.logging.Level.SEVERE)); - - logger.assertLogCount(7); - logger.assertLogEntry(0, TRACE, "finest"); - logger.assertLogEntry(1, TRACE, "finer"); - logger.assertLogEntry(2, DEBUG, "fine"); - logger.assertLogEntry(3, DEBUG, "config"); - logger.assertLogEntry(4, INFO, "info"); - logger.assertLogEntry(5, WARN, "warning"); - logger.assertLogEntry(6, ERROR, "severe"); - } - - @Test - public void testErrorHandling() { - AssertingLogger logger = getAssertingLogger(); - LoggerBackend backend = newBackend(logger); - - LogData data = FakeLogData.withPrintfStyle("Hello %?X World", "ignored"); - try { - backend.log(data); - fail("expected ParseException"); - } catch (ParseException expected) { - logger.assertLogCount(0); - backend.handleError(expected, data); - logger.assertLogCount(1); - assertThat(logger.getMessage(0)).contains("lo %[?]X Wo"); - } - } - - @Test - public void testWithThrown() { - AssertingLogger logger = getAssertingLogger(); - LoggerBackend backend = newBackend(logger); - - Throwable cause = new Throwable("Original Cause"); - backend.log(FakeLogData.of("Hello World").addMetadata(LogContext.Key.LOG_CAUSE, cause)); - - logger.assertLogCount(1); - logger.assertThrown(0, cause); - } - - private AssertingLogger getAssertingLogger() { - // Each test requires a unique Logger instance. To ensure this the test method name is included - // into the logger name. This assumes that JUnit never runs the same test multiple times - // concurrently. - // Using "toUpperCase()" below is not strictly correct as regards locale etc, but since tests - // should not start with an upper case character, we should never get a clash. - String testMethod = name.getMethodName(); - return AssertingLogger.createOrGet( - "LoggerFor" + testMethod.substring(0, 1).toUpperCase() + testMethod.substring(1)); - } - - private void removeCachedLoggers() { - ((Hierarchy) LogManager.getLoggerRepository()).clear(); - } -} diff --git a/log4j/src/test/java/com/google/common/flogger/backend/log4j/Log4jLoggingEventUtilTest.java b/log4j/src/test/java/com/google/common/flogger/backend/log4j/Log4jLoggingEventUtilTest.java deleted file mode 100644 index 1f12d060..00000000 --- a/log4j/src/test/java/com/google/common/flogger/backend/log4j/Log4jLoggingEventUtilTest.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2018 The Flogger Authors. - * - * 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.google.common.flogger.backend.log4j; - -import static com.google.common.truth.Truth.assertThat; -import static org.apache.log4j.Level.DEBUG; -import static org.apache.log4j.Level.ERROR; -import static org.apache.log4j.Level.INFO; -import static org.apache.log4j.Level.TRACE; -import static org.apache.log4j.Level.WARN; - -import com.google.common.flogger.LogContext; -import com.google.common.flogger.MetadataKey; -import com.google.common.flogger.backend.LogData; -import com.google.common.flogger.testing.FakeLogData; -import java.util.logging.Level; -import org.apache.log4j.Logger; -import org.apache.log4j.spi.LocationInfo; -import org.apache.log4j.spi.LoggingEvent; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -public class Log4jLoggingEventUtilTest { - private static final MetadataKey COUNT_KEY = MetadataKey.single("count", Integer.class); - private static final MetadataKey ID_KEY = MetadataKey.single("id", String.class); - - private static Logger getLogger(LogData logData) { - return Logger.getLogger(logData.getLoggerName()); - } - - private static LoggingEvent toLog4jLoggingEvent(LogData data) { - return Log4jLoggingEventUtil.toLog4jLoggingEvent(getLogger(data), data); - } - - @Test - public void testLoggerName() { - LogData data = FakeLogData.of("foo"); - assertThat(toLog4jLoggingEvent(data).getLoggerName()).isEqualTo(data.getLoggerName()); - } - - @Test - public void testLocationInfo() { - LogData data = FakeLogData.of("foo"); - LoggingEvent loggingEvent = toLog4jLoggingEvent(data); - - LocationInfo locationInfo = loggingEvent.getLocationInformation(); - assertThat(locationInfo.getClassName()).isEqualTo(data.getLogSite().getClassName()); - assertThat(locationInfo.getMethodName()).isEqualTo(data.getLogSite().getMethodName()); - assertThat(locationInfo.getFileName()).isEqualTo(data.getLogSite().getFileName()); - assertThat(locationInfo.getLineNumber()) - .isEqualTo(Integer.toString(data.getLogSite().getLineNumber())); - } - - @Test - public void testLevels() { - testLevel(Level.FINEST, TRACE); - testLevel(Level.FINER, TRACE); - testLevel(Level.FINE, DEBUG); - testLevel(Level.CONFIG, DEBUG); - testLevel(Level.INFO, INFO); - testLevel(Level.WARNING, WARN); - testLevel(Level.SEVERE, ERROR); - } - - private void testLevel(Level level, org.apache.log4j.Level expectedLevel) { - LoggingEvent loggingEvent = - toLog4jLoggingEvent(FakeLogData.of(level.getName()).setLevel(level)); - assertThat(loggingEvent.getLevel()).isEqualTo(expectedLevel); - assertThat(loggingEvent.getMessage()).isEqualTo(level.getName()); - } - - @Test - public void testMessage() { - LoggingEvent loggingEvent = toLog4jLoggingEvent(FakeLogData.of("Hello World")); - assertThat(loggingEvent.getMessage()).isEqualTo("Hello World"); - - loggingEvent = toLog4jLoggingEvent(FakeLogData.withPrintfStyle("Hello %s %s", "Foo", "Bar")); - assertThat(loggingEvent.getMessage()).isEqualTo("Hello Foo Bar"); - } - - @Test - public void testWithThrown() { - Throwable cause = new Throwable("Goodbye World"); - LogData data = - FakeLogData.withPrintfStyle("Hello World").addMetadata(LogContext.Key.LOG_CAUSE, cause); - assertThat(toLog4jLoggingEvent(data).getThrowableInformation().getThrowable()) - .isSameInstanceAs(cause); - } - - @Test - public void testTimestamp() { - LogData data = FakeLogData.withPrintfStyle("Foo='%s'", "bar").setTimestampNanos(123456000000L); - assertThat(toLog4jLoggingEvent(data).getTimeStamp()).isEqualTo(123456L); - } - - @Test - public void testMetadata() { - LogData data = - FakeLogData.withPrintfStyle("Foo='%s'", "bar") - .addMetadata(COUNT_KEY, 23) - .addMetadata(ID_KEY, "test ID"); - - assertThat(toLog4jLoggingEvent(data).getMessage()) - .isEqualTo("Foo='bar' [CONTEXT count=23 id=\"test ID\" ]"); - } - - @Test - public void testErrorHandling() { - Throwable cause = new Throwable("Original Cause"); - LogData data = - FakeLogData.withPrintfStyle("Hello World").addMetadata(LogContext.Key.LOG_CAUSE, cause); - - RuntimeException error = new RuntimeException("Runtime Error"); - LoggingEvent loggingEvent = - Log4jLoggingEventUtil.toLog4jLoggingEvent(getLogger(data), error, data); - - assertThat(loggingEvent.getLevel()).isEqualTo(WARN); - assertThat(loggingEvent.getThrowableInformation().getThrowable()).isEqualTo(error); - - String message = (String) loggingEvent.getMessage(); - assertThat(message).contains("message: Hello World"); - // This is formatted from the original log data. - assertThat(message).contains("level: INFO"); - // The original cause is in the metadata of the original log data. - assertThat(message).contains("Original Cause"); - } - - @Test - public void testBadLogDataFormatting() { - LogData data = - FakeLogData.withPrintfStyle("Foo='%s'", "bar") - .setTimestampNanos(123456789000L) - .addMetadata(COUNT_KEY, 23) - .addMetadata(ID_KEY, "test ID"); - - RuntimeException error = new RuntimeException("Runtime Error"); - String message = - (String) - Log4jLoggingEventUtil.toLog4jLoggingEvent(getLogger(data), error, data).getMessage(); - - assertThat(message).contains("level: INFO"); - assertThat(message) - .contains( - " original message: Foo='%s'\n" - + " original arguments:\n" - + " bar\n" - + " metadata:\n" - + " count: 23\n" - + " id: test ID\n" - + " level: INFO\n" - + " timestamp (nanos): 123456789000\n" - + " class: com.google.FakeClass\n" - + " method: fakeMethod\n" - + " line number: 123"); - } -}