Переглянути джерело

Encapsulate logging (#6543)

* Encapsulate actual logging implementation better

* Apply suggestions from code review

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>

* code review comments

* revert to the old slf4j package name

* spotless

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
Mateusz Rzeszutek 2 роки тому
батько
коміт
8b2b3281fe
21 змінених файлів з 584 додано та 539 видалено
  1. 0 2
      conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-shadowing.gradle.kts
  2. 0 2
      examples/distro/gradle/shadow.gradle
  3. 0 2
      gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts
  4. 0 4
      javaagent-bootstrap/build.gradle.kts
  5. 2 3
      javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentClassLoader.java
  6. 50 0
      javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/InternalLogger.java
  7. 42 0
      javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/NoopLoggerFactory.java
  8. 61 155
      javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/PatchLogger.java
  9. 287 336
      javaagent-bootstrap/src/test/java/io/opentelemetry/javaagent/bootstrap/PatchLoggerTest.java
  10. 38 0
      javaagent-internal-logging-simple/build.gradle.kts
  11. 55 0
      javaagent-internal-logging-simple/src/main/java/io/opentelemetry/javaagent/logging/simple/Slf4jSimpleLogger.java
  12. 16 22
      javaagent-internal-logging-simple/src/main/java/io/opentelemetry/javaagent/logging/simple/Slf4jSimpleLoggingCustomizer.java
  13. 1 1
      javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentStarterImpl.java
  14. 1 3
      javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/Constants.java
  15. 2 3
      javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java
  16. 24 0
      javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/NoopLoggingCustomizer.java
  17. 1 2
      javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlConnection.java
  18. 0 1
      javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/GlobalIgnoredTypesConfigurer.java
  19. 2 1
      javaagent/build.gradle.kts
  20. 1 2
      muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/HelperClassPredicate.java
  21. 1 0
      settings.gradle.kts

+ 0 - 2
conventions/src/main/kotlin/io.opentelemetry.instrumentation.javaagent-shadowing.gradle.kts

@@ -14,8 +14,6 @@ tasks.withType<ShadowJar>().configureEach {
 
   exclude("**/module-info.class")
 
-  // Prevents conflict with other SLF4J instances. Important for premain.
-  relocate("org.slf4j", "io.opentelemetry.javaagent.slf4j")
   // rewrite dependencies calling Logger.getLogger
   relocate("java.util.logging.Logger", "io.opentelemetry.javaagent.bootstrap.PatchLogger")
 

+ 0 - 2
examples/distro/gradle/shadow.gradle

@@ -1,6 +1,4 @@
 ext.relocatePackages = { shadowJar ->
-  // Prevents conflict with other SLF4J instances. Important for premain.
-  shadowJar.relocate 'org.slf4j', 'io.opentelemetry.javaagent.slf4j'
   // rewrite dependencies calling Logger.getLogger
   shadowJar.relocate 'java.util.logging.Logger', 'io.opentelemetry.javaagent.bootstrap.PatchLogger'
 

+ 0 - 2
gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts

@@ -82,8 +82,6 @@ tasks.withType<ShadowJar>().configureEach {
 
   exclude("**/module-info.class")
 
-  // Prevents conflict with other SLF4J instances. Important for premain.
-  relocate("org.slf4j", "io.opentelemetry.javaagent.slf4j")
   // rewrite dependencies calling Logger.getLogger
   relocate("java.util.logging.Logger", "io.opentelemetry.javaagent.bootstrap.PatchLogger")
 

+ 0 - 4
javaagent-bootstrap/build.gradle.kts

@@ -5,13 +5,9 @@ plugins {
 
 group = "io.opentelemetry.javaagent"
 
-val agentSlf4jVersion = "2.0.0"
-
 dependencies {
   implementation(project(":instrumentation-api"))
   implementation(project(":instrumentation-appender-api-internal"))
-  implementation("org.slf4j:slf4j-api:$agentSlf4jVersion")
-  implementation("org.slf4j:slf4j-simple:$agentSlf4jVersion")
 
   testImplementation(project(":testing-common"))
 }

+ 2 - 3
javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentClassLoader.java

@@ -33,9 +33,8 @@ import javax.annotation.Nullable;
  */
 public class AgentClassLoader extends URLClassLoader {
 
-  // NOTE it's important not to use slf4j in this class, because this class is used before slf4j is
-  // configured, and so using slf4j here would initialize slf4j-simple before we have a chance to
-  // configure the logging levels
+  // NOTE it's important not to use logging in this class, because this class is used before logging
+  // is initialized
 
   static {
     ClassLoader.registerAsParallelCapable();

+ 50 - 0
javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/InternalLogger.java

@@ -0,0 +1,50 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.bootstrap;
+
+import java.util.concurrent.atomic.AtomicReference;
+import javax.annotation.Nullable;
+
+public abstract class InternalLogger {
+
+  private static final AtomicReference<Factory> loggerFactory =
+      new AtomicReference<>(NoopLoggerFactory.INSTANCE);
+
+  public static void initialize(Factory factory) {
+    if (!loggerFactory.compareAndSet(NoopLoggerFactory.INSTANCE, factory)) {
+      factory
+          .create(InternalLogger.class.getName())
+          .log(
+              Level.WARN,
+              "Developer error: logging system has already been initialized once",
+              null);
+    }
+  }
+
+  static InternalLogger getLogger(String name) {
+    return loggerFactory.get().create(name);
+  }
+
+  protected abstract boolean isLoggable(Level level);
+
+  protected abstract void log(Level level, String message, @Nullable Throwable error);
+
+  protected abstract String name();
+
+  public enum Level {
+    ERROR,
+    WARN,
+    INFO,
+    DEBUG,
+    TRACE
+  }
+
+  @FunctionalInterface
+  public interface Factory {
+
+    InternalLogger create(String name);
+  }
+}

+ 42 - 0
javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/NoopLoggerFactory.java

@@ -0,0 +1,42 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.bootstrap;
+
+import javax.annotation.Nullable;
+
+final class NoopLoggerFactory implements InternalLogger.Factory {
+
+  static final InternalLogger.Factory INSTANCE = new NoopLoggerFactory();
+
+  private NoopLoggerFactory() {}
+
+  @Override
+  public InternalLogger create(String name) {
+    return new NoopLogger(name);
+  }
+
+  private static final class NoopLogger extends InternalLogger {
+
+    private final String name;
+
+    private NoopLogger(String name) {
+      this.name = name;
+    }
+
+    @Override
+    public boolean isLoggable(Level level) {
+      return false;
+    }
+
+    @Override
+    public void log(Level level, String message, @Nullable Throwable error) {}
+
+    @Override
+    protected String name() {
+      return name;
+    }
+  }
+}

+ 61 - 155
javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/PatchLogger.java

@@ -11,8 +11,6 @@ import java.util.function.Supplier;
 import java.util.logging.Handler;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Dependencies of the agent sometimes call java.util.logging.Logger.getLogger(). This can have the
@@ -20,7 +18,8 @@ import org.slf4j.LoggerFactory;
  *
  * <p>Shadow rewrites will redirect those calls to this class, which will return a safe PatchLogger.
  *
- * <p>This also has the desired outcome of redirecting all logging to a single destination (SLF4J).
+ * <p>This also has the desired outcome of redirecting all logging to a single destination, as
+ * configured by the {@code LoggingCustomizer} implementation.
  */
 public class PatchLogger {
 
@@ -28,7 +27,7 @@ public class PatchLogger {
 
   public static final PatchLogger global = new PatchLogger(GLOBAL_LOGGER_NAME);
 
-  private final Logger slf4jLogger;
+  private final InternalLogger internalLogger;
 
   private ResourceBundle resourceBundle;
 
@@ -41,55 +40,50 @@ public class PatchLogger {
   }
 
   private PatchLogger(String name) {
-    this(LoggerFactory.getLogger(name));
+    this(InternalLogger.getLogger(name));
   }
 
   // visible for testing
-  PatchLogger(Logger slf4jLogger) {
-    this.slf4jLogger = slf4jLogger;
-  }
-
-  // visible for testing
-  Logger getSlf4jLogger() {
-    return slf4jLogger;
+  PatchLogger(InternalLogger internalLogger) {
+    this.internalLogger = internalLogger;
   }
 
   public String getName() {
-    return slf4jLogger.getName();
+    return internalLogger.name();
   }
 
   public void severe(String msg) {
-    slf4jLogger.error(msg);
+    internalLogger.log(InternalLogger.Level.ERROR, msg, null);
   }
 
   public void severe(Supplier<String> msgSupplier) {
-    if (slf4jLogger.isErrorEnabled()) {
-      slf4jLogger.error(msgSupplier.get());
+    if (internalLogger.isLoggable(InternalLogger.Level.ERROR)) {
+      internalLogger.log(InternalLogger.Level.ERROR, msgSupplier.get(), null);
     }
   }
 
   public void warning(String msg) {
-    slf4jLogger.warn(msg);
+    internalLogger.log(InternalLogger.Level.WARN, msg, null);
   }
 
   public void warning(Supplier<String> msgSupplier) {
-    if (slf4jLogger.isWarnEnabled()) {
-      slf4jLogger.warn(msgSupplier.get());
+    if (internalLogger.isLoggable(InternalLogger.Level.WARN)) {
+      internalLogger.log(InternalLogger.Level.WARN, msgSupplier.get(), null);
     }
   }
 
   public void info(String msg) {
-    slf4jLogger.info(msg);
+    internalLogger.log(InternalLogger.Level.INFO, msg, null);
   }
 
   public void info(Supplier<String> msgSupplier) {
-    if (slf4jLogger.isInfoEnabled()) {
-      slf4jLogger.info(msgSupplier.get());
+    if (internalLogger.isLoggable(InternalLogger.Level.INFO)) {
+      internalLogger.log(InternalLogger.Level.INFO, msgSupplier.get(), null);
     }
   }
 
   public void config(String msg) {
-    slf4jLogger.info(msg);
+    info(msg);
   }
 
   public void config(Supplier<String> msgSupplier) {
@@ -97,27 +91,27 @@ public class PatchLogger {
   }
 
   public void fine(String msg) {
-    slf4jLogger.debug(msg);
+    internalLogger.log(InternalLogger.Level.DEBUG, msg, null);
   }
 
   public void fine(Supplier<String> msgSupplier) {
-    if (slf4jLogger.isDebugEnabled()) {
-      slf4jLogger.debug(msgSupplier.get());
+    if (internalLogger.isLoggable(InternalLogger.Level.DEBUG)) {
+      internalLogger.log(InternalLogger.Level.DEBUG, msgSupplier.get(), null);
     }
   }
 
   public void finer(String msg) {
-    slf4jLogger.trace(msg);
+    internalLogger.log(InternalLogger.Level.TRACE, msg, null);
   }
 
   public void finer(Supplier<String> msgSupplier) {
-    if (slf4jLogger.isTraceEnabled()) {
-      slf4jLogger.trace(msgSupplier.get());
+    if (internalLogger.isLoggable(InternalLogger.Level.TRACE)) {
+      internalLogger.log(InternalLogger.Level.TRACE, msgSupplier.get(), null);
     }
   }
 
   public void finest(String msg) {
-    slf4jLogger.trace(msg);
+    finer(msg);
   }
 
   public void finest(Supplier<String> msgSupplier) {
@@ -125,170 +119,82 @@ public class PatchLogger {
   }
 
   public void log(LogRecord record) {
-    Level level = record.getLevel();
-    if (level.intValue() >= Level.SEVERE.intValue()) {
-      if (slf4jLogger.isErrorEnabled()) {
-        slf4jLogger.error(getMessage(record), record.getThrown());
-      }
-    } else if (level.intValue() >= Level.WARNING.intValue()) {
-      if (slf4jLogger.isWarnEnabled()) {
-        slf4jLogger.warn(getMessage(record), record.getThrown());
-      }
-    } else if (level.intValue() >= Level.CONFIG.intValue()) {
-      if (slf4jLogger.isInfoEnabled()) {
-        slf4jLogger.info(getMessage(record), record.getThrown());
-      }
-    } else if (level.intValue() >= Level.FINE.intValue()) {
-      if (slf4jLogger.isDebugEnabled()) {
-        slf4jLogger.debug(getMessage(record), record.getThrown());
-      }
-    } else {
-      if (slf4jLogger.isTraceEnabled()) {
-        slf4jLogger.trace(getMessage(record), record.getThrown());
-      }
+    InternalLogger.Level internalLevel = toInternalLevel(record.getLevel());
+    if (internalLogger.isLoggable(internalLevel)) {
+      internalLogger.log(internalLevel, getMessage(record), record.getThrown());
     }
   }
 
   public void log(Level level, String msg) {
-    if (level.intValue() >= Level.SEVERE.intValue()) {
-      slf4jLogger.error(msg);
-    } else if (level.intValue() >= Level.WARNING.intValue()) {
-      slf4jLogger.warn(msg);
-    } else if (level.intValue() >= Level.CONFIG.intValue()) {
-      slf4jLogger.info(msg);
-    } else if (level.intValue() >= Level.FINE.intValue()) {
-      slf4jLogger.debug(msg);
-    } else {
-      slf4jLogger.trace(msg);
-    }
+    internalLogger.log(toInternalLevel(level), msg, null);
   }
 
   public void log(Level level, String msg, Object param1) {
-    if (level.intValue() >= Level.SEVERE.intValue()) {
-      if (slf4jLogger.isErrorEnabled()) {
-        slf4jLogger.error(MessageFormat.format(msg, param1));
-      }
-    } else if (level.intValue() >= Level.WARNING.intValue()) {
-      if (slf4jLogger.isWarnEnabled()) {
-        slf4jLogger.warn(MessageFormat.format(msg, param1));
-      }
-    } else if (level.intValue() >= Level.CONFIG.intValue()) {
-      if (slf4jLogger.isInfoEnabled()) {
-        slf4jLogger.info(MessageFormat.format(msg, param1));
-      }
-    } else if (level.intValue() >= Level.FINE.intValue()) {
-      if (slf4jLogger.isDebugEnabled()) {
-        slf4jLogger.debug(MessageFormat.format(msg, param1));
-      }
-    } else {
-      if (slf4jLogger.isTraceEnabled()) {
-        slf4jLogger.trace(MessageFormat.format(msg, param1));
-      }
+    InternalLogger.Level internalLevel = toInternalLevel(level);
+    if (internalLogger.isLoggable(internalLevel)) {
+      internalLogger.log(internalLevel, MessageFormat.format(msg, param1), null);
     }
   }
 
   public void log(Level level, String msg, Object[] params) {
-    if (level.intValue() >= Level.SEVERE.intValue()) {
-      if (slf4jLogger.isErrorEnabled()) {
-        slf4jLogger.error(MessageFormat.format(msg, params));
-      }
-    } else if (level.intValue() >= Level.WARNING.intValue()) {
-      if (slf4jLogger.isWarnEnabled()) {
-        slf4jLogger.warn(MessageFormat.format(msg, params));
-      }
-    } else if (level.intValue() >= Level.CONFIG.intValue()) {
-      if (slf4jLogger.isInfoEnabled()) {
-        slf4jLogger.info(MessageFormat.format(msg, params));
-      }
-    } else if (level.intValue() >= Level.FINE.intValue()) {
-      if (slf4jLogger.isDebugEnabled()) {
-        slf4jLogger.debug(MessageFormat.format(msg, params));
-      }
-    } else {
-      if (slf4jLogger.isTraceEnabled()) {
-        slf4jLogger.trace(MessageFormat.format(msg, params));
-      }
+    InternalLogger.Level internalLevel = toInternalLevel(level);
+    if (internalLogger.isLoggable(internalLevel)) {
+      internalLogger.log(internalLevel, MessageFormat.format(msg, params), null);
     }
   }
 
   public void log(Level level, String msg, Throwable thrown) {
-    if (level.intValue() >= Level.SEVERE.intValue()) {
-      slf4jLogger.error(msg, thrown);
-    } else if (level.intValue() >= Level.WARNING.intValue()) {
-      slf4jLogger.warn(msg, thrown);
-    } else if (level.intValue() >= Level.CONFIG.intValue()) {
-      slf4jLogger.info(msg, thrown);
-    } else if (level.intValue() >= Level.FINE.intValue()) {
-      slf4jLogger.debug(msg, thrown);
-    } else {
-      slf4jLogger.trace(msg, thrown);
-    }
+    internalLogger.log(toInternalLevel(level), msg, thrown);
   }
 
   public void log(Level level, Supplier<String> msgSupplier) {
-    if (!isLoggable(level)) {
-      return;
-    }
-    if (level.intValue() >= Level.SEVERE.intValue()) {
-      slf4jLogger.error(msgSupplier.get());
-    } else if (level.intValue() >= Level.WARNING.intValue()) {
-      slf4jLogger.warn(msgSupplier.get());
-    } else if (level.intValue() >= Level.CONFIG.intValue()) {
-      slf4jLogger.info(msgSupplier.get());
-    } else if (level.intValue() >= Level.FINE.intValue()) {
-      slf4jLogger.debug(msgSupplier.get());
-    } else {
-      slf4jLogger.trace(msgSupplier.get());
+    InternalLogger.Level internalLevel = toInternalLevel(level);
+    if (internalLogger.isLoggable(internalLevel)) {
+      internalLogger.log(internalLevel, msgSupplier.get(), null);
     }
   }
 
   public void log(Level level, Throwable thrown, Supplier<String> msgSupplier) {
-    if (!isLoggable(level)) {
-      return;
-    }
-    if (level.intValue() >= Level.SEVERE.intValue()) {
-      slf4jLogger.error(msgSupplier.get(), thrown);
-    } else if (level.intValue() >= Level.WARNING.intValue()) {
-      slf4jLogger.warn(msgSupplier.get(), thrown);
-    } else if (level.intValue() >= Level.CONFIG.intValue()) {
-      slf4jLogger.info(msgSupplier.get(), thrown);
-    } else if (level.intValue() >= Level.FINE.intValue()) {
-      slf4jLogger.debug(msgSupplier.get(), thrown);
-    } else {
-      slf4jLogger.trace(msgSupplier.get(), thrown);
+    InternalLogger.Level internalLevel = toInternalLevel(level);
+    if (internalLogger.isLoggable(internalLevel)) {
+      internalLogger.log(internalLevel, msgSupplier.get(), thrown);
     }
   }
 
   public boolean isLoggable(Level level) {
-    if (level.intValue() >= Level.SEVERE.intValue()) {
-      return slf4jLogger.isErrorEnabled();
-    } else if (level.intValue() >= Level.WARNING.intValue()) {
-      return slf4jLogger.isWarnEnabled();
-    } else if (level.intValue() >= Level.CONFIG.intValue()) {
-      return slf4jLogger.isInfoEnabled();
-    } else if (level.intValue() >= Level.FINE.intValue()) {
-      return slf4jLogger.isDebugEnabled();
-    } else {
-      return slf4jLogger.isTraceEnabled();
-    }
+    return internalLogger.isLoggable(toInternalLevel(level));
   }
 
   public Level getLevel() {
-    if (slf4jLogger.isErrorEnabled()) {
+    if (internalLogger.isLoggable(InternalLogger.Level.ERROR)) {
       return Level.SEVERE;
-    } else if (slf4jLogger.isWarnEnabled()) {
+    } else if (internalLogger.isLoggable(InternalLogger.Level.WARN)) {
       return Level.WARNING;
-    } else if (slf4jLogger.isInfoEnabled()) {
+    } else if (internalLogger.isLoggable(InternalLogger.Level.INFO)) {
       return Level.CONFIG;
-    } else if (slf4jLogger.isDebugEnabled()) {
+    } else if (internalLogger.isLoggable(InternalLogger.Level.DEBUG)) {
       return Level.FINE;
-    } else if (slf4jLogger.isTraceEnabled()) {
+    } else if (internalLogger.isLoggable(InternalLogger.Level.TRACE)) {
       return Level.FINEST;
     } else {
       return Level.OFF;
     }
   }
 
+  private static InternalLogger.Level toInternalLevel(Level level) {
+    if (level.intValue() >= Level.SEVERE.intValue()) {
+      return InternalLogger.Level.ERROR;
+    } else if (level.intValue() >= Level.WARNING.intValue()) {
+      return InternalLogger.Level.WARN;
+    } else if (level.intValue() >= Level.CONFIG.intValue()) {
+      return InternalLogger.Level.INFO;
+    } else if (level.intValue() >= Level.FINE.intValue()) {
+      return InternalLogger.Level.DEBUG;
+    } else {
+      return InternalLogger.Level.TRACE;
+    }
+  }
+
   public void logp(Level level, String sourceClass, String sourceMethod, String msg) {
     log(level, msg);
   }

+ 287 - 336
javaagent-bootstrap/src/test/java/io/opentelemetry/javaagent/bootstrap/PatchLoggerTest.java

@@ -6,6 +6,7 @@
 package io.opentelemetry.javaagent.bootstrap;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -63,19 +64,13 @@ class PatchLoggerTest {
     assertThat(patchLoggerMethods).containsAll(julLoggerMethods);
   }
 
-  @Test
-  void testGetLogger() {
-    PatchLogger logger = PatchLogger.getLogger("abc");
-    assertThat(logger.getSlf4jLogger().getName()).isEqualTo("abc");
-  }
-
   @Test
   void testGetName() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.getName()).thenReturn("xyz");
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.name()).thenReturn("xyz");
     // when
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    PatchLogger logger = new PatchLogger(internalLogger);
     // then
     assertThat(logger.getName()).isEqualTo("xyz");
   }
@@ -83,8 +78,8 @@ class PatchLoggerTest {
   @Test
   void testNormalMethods() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // when
     logger.severe("ereves");
@@ -96,22 +91,22 @@ class PatchLoggerTest {
     logger.finest("tsenif");
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).error("ereves");
-    inOrder.verify(slf4jLogger).warn("gninraw");
-    inOrder.verify(slf4jLogger).info("ofni");
-    inOrder.verify(slf4jLogger).info("gifnoc");
-    inOrder.verify(slf4jLogger).debug("enif");
-    inOrder.verify(slf4jLogger).trace("renif");
-    inOrder.verify(slf4jLogger).trace("tsenif");
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", null);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testParameterizedLevelMethodsWithNoParams() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // when
     logger.log(Level.SEVERE, "ereves");
@@ -123,27 +118,23 @@ class PatchLoggerTest {
     logger.log(Level.FINEST, "tsenif");
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).error("ereves");
-    inOrder.verify(slf4jLogger).warn("gninraw");
-    inOrder.verify(slf4jLogger).info("ofni");
-    inOrder.verify(slf4jLogger).info("gifnoc");
-    inOrder.verify(slf4jLogger).debug("enif");
-    inOrder.verify(slf4jLogger).trace("renif");
-    inOrder.verify(slf4jLogger).trace("tsenif");
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", null);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testParameterizedLevelMethodsWithSingleParam() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isTraceEnabled()).thenReturn(true);
-    when(slf4jLogger.isDebugEnabled()).thenReturn(true);
-    when(slf4jLogger.isInfoEnabled()).thenReturn(true);
-    when(slf4jLogger.isWarnEnabled()).thenReturn(true);
-    when(slf4jLogger.isErrorEnabled()).thenReturn(true);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(any())).thenReturn(true);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // when
     logger.log(Level.SEVERE, "ereves: {0}", "a");
@@ -155,34 +146,30 @@ class PatchLoggerTest {
     logger.log(Level.FINEST, "tsenif: {0}", "g");
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).isErrorEnabled();
-    inOrder.verify(slf4jLogger).error("ereves: a");
-    inOrder.verify(slf4jLogger).isWarnEnabled();
-    inOrder.verify(slf4jLogger).warn("gninraw: b");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("ofni: c");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("gifnoc: d");
-    inOrder.verify(slf4jLogger).isDebugEnabled();
-    inOrder.verify(slf4jLogger).debug("enif: e");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("renif: f");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("tsenif: g");
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g", null);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testParameterizedLevelMethodsWithArrayOfParams() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isTraceEnabled()).thenReturn(true);
-    when(slf4jLogger.isDebugEnabled()).thenReturn(true);
-    when(slf4jLogger.isInfoEnabled()).thenReturn(true);
-    when(slf4jLogger.isWarnEnabled()).thenReturn(true);
-    when(slf4jLogger.isErrorEnabled()).thenReturn(true);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(any())).thenReturn(true);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // when
     logger.log(Level.SEVERE, "ereves: {0},{1}", new Object[] {"a", "b"});
@@ -194,29 +181,29 @@ class PatchLoggerTest {
     logger.log(Level.FINEST, "tsenif: {0},{1}", new Object[] {"g", "h"});
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).isErrorEnabled();
-    inOrder.verify(slf4jLogger).error("ereves: a,b");
-    inOrder.verify(slf4jLogger).isWarnEnabled();
-    inOrder.verify(slf4jLogger).warn("gninraw: b,c");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("ofni: c,d");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("gifnoc: d,e");
-    inOrder.verify(slf4jLogger).isDebugEnabled();
-    inOrder.verify(slf4jLogger).debug("enif: e,f");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("renif: f,g");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("tsenif: g,h");
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a,b", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b,c", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c,d", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d,e", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e,f", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f,g", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g,h", null);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testParameterizedLevelMethodsWithThrowable() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    PatchLogger logger = new PatchLogger(internalLogger);
     Throwable a = new Throwable();
     Throwable b = new Throwable();
     Throwable c = new Throwable();
@@ -235,29 +222,25 @@ class PatchLoggerTest {
     logger.log(Level.FINEST, "tsenif", g);
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).error("ereves", a);
-    inOrder.verify(slf4jLogger).warn("gninraw", b);
-    inOrder.verify(slf4jLogger).info("ofni", c);
-    inOrder.verify(slf4jLogger).info("gifnoc", d);
-    inOrder.verify(slf4jLogger).debug("enif", e);
-    inOrder.verify(slf4jLogger).trace("renif", f);
-    inOrder.verify(slf4jLogger).trace("tsenif", g);
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", a);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", b);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", c);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", d);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", e);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", f);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", g);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testIsLoggableAll() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isTraceEnabled()).thenReturn(true);
-    when(slf4jLogger.isDebugEnabled()).thenReturn(true);
-    when(slf4jLogger.isInfoEnabled()).thenReturn(true);
-    when(slf4jLogger.isWarnEnabled()).thenReturn(true);
-    when(slf4jLogger.isErrorEnabled()).thenReturn(true);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(any())).thenReturn(true);
 
     // when
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // then
     assertThat(logger.isLoggable(Level.SEVERE)).isTrue();
@@ -272,15 +255,12 @@ class PatchLoggerTest {
   @Test
   void testIsLoggableSome() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isTraceEnabled()).thenReturn(false);
-    when(slf4jLogger.isDebugEnabled()).thenReturn(false);
-    when(slf4jLogger.isInfoEnabled()).thenReturn(false);
-    when(slf4jLogger.isWarnEnabled()).thenReturn(true);
-    when(slf4jLogger.isErrorEnabled()).thenReturn(true);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(InternalLogger.Level.ERROR)).thenReturn(true);
+    when(internalLogger.isLoggable(InternalLogger.Level.WARN)).thenReturn(true);
 
     // when
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // then
     assertThat(logger.isLoggable(Level.SEVERE)).isTrue();
@@ -295,15 +275,10 @@ class PatchLoggerTest {
   @Test
   void testIsLoggableNone() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isTraceEnabled()).thenReturn(false);
-    when(slf4jLogger.isDebugEnabled()).thenReturn(false);
-    when(slf4jLogger.isInfoEnabled()).thenReturn(false);
-    when(slf4jLogger.isWarnEnabled()).thenReturn(false);
-    when(slf4jLogger.isErrorEnabled()).thenReturn(false);
+    InternalLogger internalLogger = mock(InternalLogger.class);
 
     // when
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // then
     assertThat(logger.isLoggable(Level.SEVERE)).isFalse();
@@ -318,10 +293,10 @@ class PatchLoggerTest {
   @Test
   void testGetLevelSevere() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isErrorEnabled()).thenReturn(true);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(InternalLogger.Level.ERROR)).thenReturn(true);
     // when
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    PatchLogger logger = new PatchLogger(internalLogger);
     // then
     assertThat(logger.getLevel()).isEqualTo(Level.SEVERE);
   }
@@ -329,10 +304,10 @@ class PatchLoggerTest {
   @Test
   void testGetLevelWarning() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isWarnEnabled()).thenReturn(true);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(InternalLogger.Level.WARN)).thenReturn(true);
     // when
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    PatchLogger logger = new PatchLogger(internalLogger);
     // then
     assertThat(logger.getLevel()).isEqualTo(Level.WARNING);
   }
@@ -340,10 +315,10 @@ class PatchLoggerTest {
   @Test
   void testGetLevelConfig() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isInfoEnabled()).thenReturn(true);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(InternalLogger.Level.INFO)).thenReturn(true);
     // when
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    PatchLogger logger = new PatchLogger(internalLogger);
     // then
     assertThat(logger.getLevel()).isEqualTo(Level.CONFIG);
   }
@@ -351,10 +326,10 @@ class PatchLoggerTest {
   @Test
   void testGetLevelFine() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isDebugEnabled()).thenReturn(true);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(InternalLogger.Level.DEBUG)).thenReturn(true);
     // when
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    PatchLogger logger = new PatchLogger(internalLogger);
     // then
     assertThat(logger.getLevel()).isEqualTo(Level.FINE);
   }
@@ -362,10 +337,10 @@ class PatchLoggerTest {
   @Test
   void testGetLevelFinest() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isTraceEnabled()).thenReturn(true);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(InternalLogger.Level.TRACE)).thenReturn(true);
     // when
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    PatchLogger logger = new PatchLogger(internalLogger);
     // then
     assertThat(logger.getLevel()).isEqualTo(Level.FINEST);
   }
@@ -373,9 +348,9 @@ class PatchLoggerTest {
   @Test
   void testGetLevelOff() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
+    InternalLogger internalLogger = mock(InternalLogger.class);
     // when
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    PatchLogger logger = new PatchLogger(internalLogger);
     // then
     assertThat(logger.getLevel()).isEqualTo(Level.OFF);
   }
@@ -383,8 +358,8 @@ class PatchLoggerTest {
   @Test
   void testLogpParameterizedLevelMethodsWithNoParams() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // when
     logger.logp(Level.SEVERE, null, null, "ereves");
@@ -396,27 +371,23 @@ class PatchLoggerTest {
     logger.logp(Level.FINEST, null, null, "tsenif");
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).error("ereves");
-    inOrder.verify(slf4jLogger).warn("gninraw");
-    inOrder.verify(slf4jLogger).info("ofni");
-    inOrder.verify(slf4jLogger).info("gifnoc");
-    inOrder.verify(slf4jLogger).debug("enif");
-    inOrder.verify(slf4jLogger).trace("renif");
-    inOrder.verify(slf4jLogger).trace("tsenif");
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", null);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testLogpParameterizedLevelMethodsWithSingleParam() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isTraceEnabled()).thenReturn(true);
-    when(slf4jLogger.isDebugEnabled()).thenReturn(true);
-    when(slf4jLogger.isInfoEnabled()).thenReturn(true);
-    when(slf4jLogger.isWarnEnabled()).thenReturn(true);
-    when(slf4jLogger.isErrorEnabled()).thenReturn(true);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(any())).thenReturn(true);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // when
     logger.logp(Level.SEVERE, null, null, "ereves: {0}", "a");
@@ -428,34 +399,30 @@ class PatchLoggerTest {
     logger.logp(Level.FINEST, null, null, "tsenif: {0}", "g");
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).isErrorEnabled();
-    inOrder.verify(slf4jLogger).error("ereves: a");
-    inOrder.verify(slf4jLogger).isWarnEnabled();
-    inOrder.verify(slf4jLogger).warn("gninraw: b");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("ofni: c");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("gifnoc: d");
-    inOrder.verify(slf4jLogger).isDebugEnabled();
-    inOrder.verify(slf4jLogger).debug("enif: e");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("renif: f");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("tsenif: g");
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g", null);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testLogpParameterizedLevelMethodsWithArrayOfParams() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isTraceEnabled()).thenReturn(true);
-    when(slf4jLogger.isDebugEnabled()).thenReturn(true);
-    when(slf4jLogger.isInfoEnabled()).thenReturn(true);
-    when(slf4jLogger.isWarnEnabled()).thenReturn(true);
-    when(slf4jLogger.isErrorEnabled()).thenReturn(true);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(any())).thenReturn(true);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // when
     logger.logp(Level.SEVERE, null, null, "ereves: {0},{1}", new Object[] {"a", "b"});
@@ -467,29 +434,29 @@ class PatchLoggerTest {
     logger.logp(Level.FINEST, null, null, "tsenif: {0},{1}", new Object[] {"g", "h"});
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).isErrorEnabled();
-    inOrder.verify(slf4jLogger).error("ereves: a,b");
-    inOrder.verify(slf4jLogger).isWarnEnabled();
-    inOrder.verify(slf4jLogger).warn("gninraw: b,c");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("ofni: c,d");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("gifnoc: d,e");
-    inOrder.verify(slf4jLogger).isDebugEnabled();
-    inOrder.verify(slf4jLogger).debug("enif: e,f");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("renif: f,g");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("tsenif: g,h");
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a,b", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b,c", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c,d", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d,e", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e,f", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f,g", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g,h", null);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testLogpParameterizedLevelMethodsWithThrowable() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    PatchLogger logger = new PatchLogger(internalLogger);
     Throwable a = new Throwable();
     Throwable b = new Throwable();
     Throwable c = new Throwable();
@@ -508,22 +475,22 @@ class PatchLoggerTest {
     logger.logp(Level.FINEST, null, null, "tsenif", g);
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).error("ereves", a);
-    inOrder.verify(slf4jLogger).warn("gninraw", b);
-    inOrder.verify(slf4jLogger).info("ofni", c);
-    inOrder.verify(slf4jLogger).info("gifnoc", d);
-    inOrder.verify(slf4jLogger).debug("enif", e);
-    inOrder.verify(slf4jLogger).trace("renif", f);
-    inOrder.verify(slf4jLogger).trace("tsenif", g);
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", a);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", b);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", c);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", d);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", e);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", f);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", g);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testLogrbParameterizedLevelMethodsWithNoParams() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // when
     logger.logrb(Level.SEVERE, null, null, null, "ereves");
@@ -535,27 +502,23 @@ class PatchLoggerTest {
     logger.logrb(Level.FINEST, null, null, null, "tsenif");
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).error("ereves");
-    inOrder.verify(slf4jLogger).warn("gninraw");
-    inOrder.verify(slf4jLogger).info("ofni");
-    inOrder.verify(slf4jLogger).info("gifnoc");
-    inOrder.verify(slf4jLogger).debug("enif");
-    inOrder.verify(slf4jLogger).trace("renif");
-    inOrder.verify(slf4jLogger).trace("tsenif");
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", null);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", null);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testLogrbParameterizedLevelMethodsWithSingleParam() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isTraceEnabled()).thenReturn(true);
-    when(slf4jLogger.isDebugEnabled()).thenReturn(true);
-    when(slf4jLogger.isInfoEnabled()).thenReturn(true);
-    when(slf4jLogger.isWarnEnabled()).thenReturn(true);
-    when(slf4jLogger.isErrorEnabled()).thenReturn(true);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(any())).thenReturn(true);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // when
     logger.logrb(Level.SEVERE, null, null, null, "ereves: {0}", "a");
@@ -567,34 +530,30 @@ class PatchLoggerTest {
     logger.logrb(Level.FINEST, null, null, null, "tsenif: {0}", "g");
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).isErrorEnabled();
-    inOrder.verify(slf4jLogger).error("ereves: a");
-    inOrder.verify(slf4jLogger).isWarnEnabled();
-    inOrder.verify(slf4jLogger).warn("gninraw: b");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("ofni: c");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("gifnoc: d");
-    inOrder.verify(slf4jLogger).isDebugEnabled();
-    inOrder.verify(slf4jLogger).debug("enif: e");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("renif: f");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("tsenif: g");
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g", null);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testLogrbParameterizedLevelMethodsWithArrayOfParams() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isTraceEnabled()).thenReturn(true);
-    when(slf4jLogger.isDebugEnabled()).thenReturn(true);
-    when(slf4jLogger.isInfoEnabled()).thenReturn(true);
-    when(slf4jLogger.isWarnEnabled()).thenReturn(true);
-    when(slf4jLogger.isErrorEnabled()).thenReturn(true);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(any())).thenReturn(true);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // when
     logger.logrb(
@@ -610,34 +569,30 @@ class PatchLoggerTest {
         Level.FINEST, null, null, (String) null, "tsenif: {0},{1}", new Object[] {"g", "h"});
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).isErrorEnabled();
-    inOrder.verify(slf4jLogger).error("ereves: a,b");
-    inOrder.verify(slf4jLogger).isWarnEnabled();
-    inOrder.verify(slf4jLogger).warn("gninraw: b,c");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("ofni: c,d");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("gifnoc: d,e");
-    inOrder.verify(slf4jLogger).isDebugEnabled();
-    inOrder.verify(slf4jLogger).debug("enif: e,f");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("renif: f,g");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("tsenif: g,h");
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a,b", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b,c", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c,d", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d,e", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e,f", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f,g", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g,h", null);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testLogrbParameterizedLevelMethodsWithVarArgsOfParams() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isTraceEnabled()).thenReturn(true);
-    when(slf4jLogger.isDebugEnabled()).thenReturn(true);
-    when(slf4jLogger.isInfoEnabled()).thenReturn(true);
-    when(slf4jLogger.isWarnEnabled()).thenReturn(true);
-    when(slf4jLogger.isErrorEnabled()).thenReturn(true);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(any())).thenReturn(true);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // when
     logger.logrb(Level.SEVERE, (String) null, null, null, "ereves: {0},{1}", "a", "b");
@@ -649,34 +604,30 @@ class PatchLoggerTest {
     logger.logrb(Level.FINEST, (String) null, null, null, "tsenif: {0},{1}", "g", "h");
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).isErrorEnabled();
-    inOrder.verify(slf4jLogger).error("ereves: a,b");
-    inOrder.verify(slf4jLogger).isWarnEnabled();
-    inOrder.verify(slf4jLogger).warn("gninraw: b,c");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("ofni: c,d");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("gifnoc: d,e");
-    inOrder.verify(slf4jLogger).isDebugEnabled();
-    inOrder.verify(slf4jLogger).debug("enif: e,f");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("renif: f,g");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("tsenif: g,h");
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a,b", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b,c", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c,d", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d,e", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e,f", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f,g", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g,h", null);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testLogrbParameterizedLevelMethodsWithVarArgsOfParams2() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    when(slf4jLogger.isTraceEnabled()).thenReturn(true);
-    when(slf4jLogger.isDebugEnabled()).thenReturn(true);
-    when(slf4jLogger.isInfoEnabled()).thenReturn(true);
-    when(slf4jLogger.isWarnEnabled()).thenReturn(true);
-    when(slf4jLogger.isErrorEnabled()).thenReturn(true);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    when(internalLogger.isLoggable(any())).thenReturn(true);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // when
     logger.logrb(Level.SEVERE, (ResourceBundle) null, "ereves: {0},{1}", "a", "b");
@@ -688,29 +639,29 @@ class PatchLoggerTest {
     logger.logrb(Level.FINEST, (ResourceBundle) null, "tsenif: {0},{1}", "g", "h");
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).isErrorEnabled();
-    inOrder.verify(slf4jLogger).error("ereves: a,b");
-    inOrder.verify(slf4jLogger).isWarnEnabled();
-    inOrder.verify(slf4jLogger).warn("gninraw: b,c");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("ofni: c,d");
-    inOrder.verify(slf4jLogger).isInfoEnabled();
-    inOrder.verify(slf4jLogger).info("gifnoc: d,e");
-    inOrder.verify(slf4jLogger).isDebugEnabled();
-    inOrder.verify(slf4jLogger).debug("enif: e,f");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("renif: f,g");
-    inOrder.verify(slf4jLogger).isTraceEnabled();
-    inOrder.verify(slf4jLogger).trace("tsenif: g,h");
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.ERROR);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves: a,b", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.WARN);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw: b,c", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni: c,d", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.INFO);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc: d,e", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.DEBUG);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif: e,f", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif: f,g", null);
+    inOrder.verify(internalLogger).isLoggable(InternalLogger.Level.TRACE);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif: g,h", null);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testLogrbParameterizedLevelMethodsWithThrowable() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    PatchLogger logger = new PatchLogger(internalLogger);
     Throwable a = new Throwable();
     Throwable b = new Throwable();
     Throwable c = new Throwable();
@@ -729,22 +680,22 @@ class PatchLoggerTest {
     logger.logrb(Level.FINEST, null, null, (String) null, "tsenif", g);
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).error("ereves", a);
-    inOrder.verify(slf4jLogger).warn("gninraw", b);
-    inOrder.verify(slf4jLogger).info("ofni", c);
-    inOrder.verify(slf4jLogger).info("gifnoc", d);
-    inOrder.verify(slf4jLogger).debug("enif", e);
-    inOrder.verify(slf4jLogger).trace("renif", f);
-    inOrder.verify(slf4jLogger).trace("tsenif", g);
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", a);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", b);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", c);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", d);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", e);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", f);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", g);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testLogrbParameterizedLevelMethodsWithThrowable2() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    PatchLogger logger = new PatchLogger(internalLogger);
     Throwable a = new Throwable();
     Throwable b = new Throwable();
     Throwable c = new Throwable();
@@ -763,22 +714,22 @@ class PatchLoggerTest {
     logger.logrb(Level.FINEST, null, null, (ResourceBundle) null, "tsenif", g);
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).error("ereves", a);
-    inOrder.verify(slf4jLogger).warn("gninraw", b);
-    inOrder.verify(slf4jLogger).info("ofni", c);
-    inOrder.verify(slf4jLogger).info("gifnoc", d);
-    inOrder.verify(slf4jLogger).debug("enif", e);
-    inOrder.verify(slf4jLogger).trace("renif", f);
-    inOrder.verify(slf4jLogger).trace("tsenif", g);
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", a);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", b);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", c);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", d);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", e);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", f);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", g);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testLogrbParameterizedLevelMethodsWithResourceBundleObjectAndThrowable() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    PatchLogger logger = new PatchLogger(internalLogger);
     Throwable a = new Throwable();
     Throwable b = new Throwable();
     Throwable c = new Throwable();
@@ -797,22 +748,22 @@ class PatchLoggerTest {
     logger.logrb(Level.FINEST, null, null, (ResourceBundle) null, "tsenif", g);
 
     // then
-    InOrder inOrder = Mockito.inOrder(slf4jLogger);
-    inOrder.verify(slf4jLogger).error("ereves", a);
-    inOrder.verify(slf4jLogger).warn("gninraw", b);
-    inOrder.verify(slf4jLogger).info("ofni", c);
-    inOrder.verify(slf4jLogger).info("gifnoc", d);
-    inOrder.verify(slf4jLogger).debug("enif", e);
-    inOrder.verify(slf4jLogger).trace("renif", f);
-    inOrder.verify(slf4jLogger).trace("tsenif", g);
-    verifyNoMoreInteractions(slf4jLogger);
+    InOrder inOrder = Mockito.inOrder(internalLogger);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.ERROR, "ereves", a);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.WARN, "gninraw", b);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "ofni", c);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.INFO, "gifnoc", d);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.DEBUG, "enif", e);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "renif", f);
+    inOrder.verify(internalLogger).log(InternalLogger.Level.TRACE, "tsenif", g);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testEnteringExitingThrowingMethods() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    InternalLogger internalLogger = mock(InternalLogger.class);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // when
     logger.entering(null, null);
@@ -823,21 +774,21 @@ class PatchLoggerTest {
     logger.throwing(null, null, null);
 
     // then
-    verifyNoMoreInteractions(slf4jLogger);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   @Test
   void testResourceBundle() {
     // given
-    org.slf4j.Logger slf4jLogger = mock(org.slf4j.Logger.class);
+    InternalLogger internalLogger = mock(InternalLogger.class);
 
     // when
-    PatchLogger logger = new PatchLogger(slf4jLogger);
+    PatchLogger logger = new PatchLogger(internalLogger);
 
     // then
     assertThat(logger.getResourceBundle()).isNull();
     assertThat(logger.getResourceBundleName()).isNull();
-    verifyNoMoreInteractions(slf4jLogger);
+    verifyNoMoreInteractions(internalLogger);
   }
 
   static class MethodSignature {

+ 38 - 0
javaagent-internal-logging-simple/build.gradle.kts

@@ -0,0 +1,38 @@
+import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
+
+plugins {
+  id("otel.java-conventions")
+  id("otel.publish-conventions")
+
+  id("com.github.johnrengelman.shadow")
+}
+
+group = "io.opentelemetry.javaagent"
+
+val agentSlf4jVersion = "2.0.0"
+
+dependencies {
+  compileOnly(project(":javaagent-bootstrap"))
+  compileOnly(project(":javaagent-tooling"))
+
+  implementation("org.slf4j:slf4j-api:$agentSlf4jVersion")
+  implementation("org.slf4j:slf4j-simple:$agentSlf4jVersion")
+
+  annotationProcessor("com.google.auto.service:auto-service")
+  compileOnly("com.google.auto.service:auto-service-annotations")
+  testCompileOnly("com.google.auto.service:auto-service-annotations")
+}
+
+tasks {
+  val shadowJar by existing(ShadowJar::class) {
+    // required for META-INF/services files relocation
+    mergeServiceFiles()
+
+    // Prevents configuration naming conflict with other SLF4J instances
+    relocate("org.slf4j", "io.opentelemetry.javaagent.slf4j")
+  }
+
+  assemble {
+    dependsOn(shadowJar)
+  }
+}

+ 55 - 0
javaagent-internal-logging-simple/src/main/java/io/opentelemetry/javaagent/logging/simple/Slf4jSimpleLogger.java

@@ -0,0 +1,55 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.logging.simple;
+
+import io.opentelemetry.javaagent.bootstrap.InternalLogger;
+import javax.annotation.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class Slf4jSimpleLogger extends InternalLogger {
+
+  static Slf4jSimpleLogger create(String name) {
+    return new Slf4jSimpleLogger(name);
+  }
+
+  private final Logger logger;
+
+  Slf4jSimpleLogger(String name) {
+    logger = LoggerFactory.getLogger(name);
+  }
+
+  @Override
+  protected boolean isLoggable(Level level) {
+    return logger.isEnabledForLevel(toSlf4jLevel(level));
+  }
+
+  @Override
+  protected void log(Level level, String message, @Nullable Throwable error) {
+    logger.makeLoggingEventBuilder(toSlf4jLevel(level)).setCause(error).log(message);
+  }
+
+  @Override
+  protected String name() {
+    return logger.getName();
+  }
+
+  private static org.slf4j.event.Level toSlf4jLevel(Level level) {
+    switch (level) {
+      case ERROR:
+        return org.slf4j.event.Level.ERROR;
+      case WARN:
+        return org.slf4j.event.Level.WARN;
+      case INFO:
+        return org.slf4j.event.Level.INFO;
+      case DEBUG:
+        return org.slf4j.event.Level.DEBUG;
+      case TRACE:
+        return org.slf4j.event.Level.TRACE;
+    }
+    throw new IllegalStateException("Missing logging level value in switch");
+  }
+}

+ 16 - 22
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/DefaultLoggingCustomizer.java → javaagent-internal-logging-simple/src/main/java/io/opentelemetry/javaagent/logging/simple/Slf4jSimpleLoggingCustomizer.java

@@ -3,23 +3,27 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package io.opentelemetry.javaagent.tooling;
+package io.opentelemetry.javaagent.logging.simple;
 
-import java.lang.reflect.InvocationTargetException;
+import com.google.auto.service.AutoService;
+import io.opentelemetry.javaagent.bootstrap.InternalLogger;
+import io.opentelemetry.javaagent.tooling.LoggingCustomizer;
 import java.util.Locale;
+import org.slf4j.LoggerFactory;
 
-final class DefaultLoggingCustomizer implements LoggingCustomizer {
+@AutoService(LoggingCustomizer.class)
+public final class Slf4jSimpleLoggingCustomizer implements LoggingCustomizer {
 
+  // org.slf4j package name in the constants will be shaded too
   private static final String SIMPLE_LOGGER_SHOW_DATE_TIME_PROPERTY =
-      "io.opentelemetry.javaagent.slf4j.simpleLogger.showDateTime";
+      "org.slf4j.simpleLogger.showDateTime";
   private static final String SIMPLE_LOGGER_DATE_TIME_FORMAT_PROPERTY =
-      "io.opentelemetry.javaagent.slf4j.simpleLogger.dateTimeFormat";
+      "org.slf4j.simpleLogger.dateTimeFormat";
   private static final String SIMPLE_LOGGER_DATE_TIME_FORMAT_DEFAULT =
       "'[otel.javaagent 'yyyy-MM-dd HH:mm:ss:SSS Z']'";
   private static final String SIMPLE_LOGGER_DEFAULT_LOG_LEVEL_PROPERTY =
-      "io.opentelemetry.javaagent.slf4j.simpleLogger.defaultLogLevel";
-  private static final String SIMPLE_LOGGER_PREFIX =
-      "io.opentelemetry.javaagent.slf4j.simpleLogger.log.";
+      "org.slf4j.simpleLogger.defaultLogLevel";
+  private static final String SIMPLE_LOGGER_PREFIX = "org.slf4j.simpleLogger.log.";
 
   @Override
   public void init() {
@@ -35,20 +39,10 @@ final class DefaultLoggingCustomizer implements LoggingCustomizer {
       setSystemPropertyDefault(SIMPLE_LOGGER_PREFIX + "muzzleMatcher", "OFF");
     }
 
-    ClassLoader previous = Thread.currentThread().getContextClassLoader();
-    try {
-      // make sure that slf4j finds the provider in the bootstrap CL
-      Thread.currentThread().setContextClassLoader(null);
-      Class<?> loggerFactory = Class.forName("org.slf4j.LoggerFactory");
-      loggerFactory.getMethod("getILoggerFactory").invoke(null);
-    } catch (ClassNotFoundException
-        | InvocationTargetException
-        | IllegalAccessException
-        | NoSuchMethodException e) {
-      throw new IllegalStateException("Failed to initialize logging", e);
-    } finally {
-      Thread.currentThread().setContextClassLoader(previous);
-    }
+    // trigger loading the provider from the agent CL
+    LoggerFactory.getILoggerFactory();
+
+    InternalLogger.initialize(Slf4jSimpleLogger::create);
   }
 
   @Override

+ 1 - 1
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentStarterImpl.java

@@ -77,7 +77,7 @@ public class AgentStarterImpl implements AgentStarter {
     if (loggingCustomizers.hasNext()) {
       loggingCustomizer = loggingCustomizers.next();
     } else {
-      loggingCustomizer = new DefaultLoggingCustomizer();
+      loggingCustomizer = NoopLoggingCustomizer.INSTANCE;
     }
 
     Throwable startupError = null;

+ 1 - 3
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/Constants.java

@@ -20,9 +20,7 @@ public final class Constants {
   public static final List<String> BOOTSTRAP_PACKAGE_PREFIXES =
       Collections.unmodifiableList(
           Arrays.asList(
-              "io.opentelemetry.javaagent.bootstrap",
-              "io.opentelemetry.javaagent.shaded",
-              "io.opentelemetry.javaagent.slf4j"));
+              "io.opentelemetry.javaagent.bootstrap", "io.opentelemetry.javaagent.shaded"));
 
   private Constants() {}
 }

+ 2 - 3
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java

@@ -37,9 +37,8 @@ import net.bytebuddy.dynamic.loading.MultipleParentClassLoader;
 public class ExtensionClassLoader extends URLClassLoader {
   public static final String EXTENSIONS_CONFIG = "otel.javaagent.extensions";
 
-  // NOTE it's important not to use slf4j in this class, because this class is used before slf4j is
-  // configured, and so using slf4j here would initialize slf4j-simple before we have a chance to
-  // configure the logging levels
+  // NOTE it's important not to use logging in this class, because this class is used before logging
+  // is initialized
 
   static {
     ClassLoader.registerAsParallelCapable();

+ 24 - 0
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/NoopLoggingCustomizer.java

@@ -0,0 +1,24 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.tooling;
+
+enum NoopLoggingCustomizer implements LoggingCustomizer {
+  INSTANCE;
+
+  @Override
+  public void init() {}
+
+  @Override
+  @SuppressWarnings("SystemOut")
+  public void onStartupFailure(Throwable throwable) {
+    // there's no logging implementation installed, just print out the exception
+    System.err.println("OpenTelemetry Javaagent failed to start");
+    throwable.printStackTrace();
+  }
+
+  @Override
+  public void onStartupSuccess() {}
+}

+ 1 - 2
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlConnection.java

@@ -38,8 +38,7 @@ public class RemappingUrlConnection extends URLConnection {
           rule(
               "#io.opentelemetry.extension.aws",
               "#io.opentelemetry.javaagent.shaded.io.opentelemetry.extension.aws"),
-          rule("#java.util.logging.Logger", "#io.opentelemetry.javaagent.bootstrap.PatchLogger"),
-          rule("#org.slf4j", "#io.opentelemetry.javaagent.slf4j"));
+          rule("#java.util.logging.Logger", "#io.opentelemetry.javaagent.bootstrap.PatchLogger"));
 
   private final JarFile delegateJarFile;
   private final JarEntry entry;

+ 0 - 1
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/GlobalIgnoredTypesConfigurer.java

@@ -61,7 +61,6 @@ public class GlobalIgnoredTypesConfigurer implements IgnoredTypesConfigurer {
     // tests under "io.opentelemetry.javaagent." will still be instrumented
     builder.ignoreClass("io.opentelemetry.javaagent.bootstrap.");
     builder.ignoreClass("io.opentelemetry.javaagent.shaded.");
-    builder.ignoreClass("io.opentelemetry.javaagent.slf4j.");
 
     builder
         .ignoreClass("java.")

+ 2 - 1
javaagent/build.gradle.kts

@@ -33,7 +33,6 @@ val javaagentLibs by configurations.creating {
 // exclude dependencies that are to be placed in bootstrap from agent libs - they won't be added to inst/
 listOf(baseJavaagentLibs, javaagentLibs).forEach {
   it.run {
-    exclude("org.slf4j")
     exclude("io.opentelemetry", "opentelemetry-api")
     exclude("io.opentelemetry", "opentelemetry-semconv")
   }
@@ -62,6 +61,7 @@ dependencies {
   baseJavaagentLibs(project(":javaagent-extension-api"))
 
   baseJavaagentLibs(project(":javaagent-tooling"))
+  baseJavaagentLibs(project(":javaagent-internal-logging-simple", configuration = "shadow"))
   baseJavaagentLibs(project(":muzzle"))
   baseJavaagentLibs(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:javaagent"))
   baseJavaagentLibs(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.4:javaagent"))
@@ -81,6 +81,7 @@ dependencies {
   //  in case there are dependencies (accidentally) pulled in by instrumentation modules
   //  but I couldn't get that to work
   licenseReportDependencies(project(":javaagent-tooling"))
+  licenseReportDependencies(project(":javaagent-internal-logging-simple"))
   licenseReportDependencies(project(":javaagent-extension-api"))
 
   testCompileOnly(project(":javaagent-bootstrap"))

+ 1 - 2
muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/HelperClassPredicate.java

@@ -49,8 +49,7 @@ public final class HelperClassPredicate {
         || className.startsWith("io.opentelemetry.javaagent.bootstrap.")
         || className.startsWith("io.opentelemetry.api.")
         || className.startsWith("io.opentelemetry.context.")
-        || className.startsWith("io.opentelemetry.semconv.")
-        || className.startsWith("org.slf4j.");
+        || className.startsWith("io.opentelemetry.semconv.");
   }
 
   private static boolean isJavaagentHelperClass(String className) {

+ 1 - 0
settings.gradle.kts

@@ -104,6 +104,7 @@ include(":javaagent-bootstrap")
 include(":javaagent-extension-api")
 include(":javaagent-tooling")
 include(":javaagent-tooling:javaagent-tooling-java9")
+include(":javaagent-internal-logging-simple")
 include(":javaagent")
 
 include(":bom")