Remove logging instrumentation (#803)
This commit is contained in:
parent
91f8e8c199
commit
6508ffc889
|
@ -171,7 +171,6 @@ provide the path to a JAR file including an SPI implementation using the system
|
|||
| [Hibernate](https://github.com/hibernate/hibernate-orm) | 3.3+ |
|
||||
| [HttpURLConnection](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/HttpURLConnection.html) | Java 7+ |
|
||||
| [Hystrix](https://github.com/Netflix/Hystrix) | 1.4+ |
|
||||
| [java.util.logging](https://docs.oracle.com/en/java/javase/11/docs/api/java.logging/java/util/logging/package-summary.html) | Java 7+ |
|
||||
| [JAX-RS](https://javaee.github.io/javaee-spec/javadocs/javax/ws/rs/package-summary.html) | 0.5+ |
|
||||
| [JAX-RS Client](https://javaee.github.io/javaee-spec/javadocs/javax/ws/rs/client/package-summary.html) | 2.0+ |
|
||||
| [JDBC](https://docs.oracle.com/en/java/javase/11/docs/api/java.sql/java/sql/package-summary.html) | Java 7+ |
|
||||
|
@ -183,8 +182,6 @@ provide the path to a JAR file including an SPI implementation using the system
|
|||
| [khttp](https://khttp.readthedocs.io) | 0.1+ |
|
||||
| [Kubernetes Client](https://github.com/kubernetes-client/java) | 7.0+ |
|
||||
| [Lettuce](https://github.com/lettuce-io/lettuce-core) | 4.0+ |
|
||||
| [Log4j](https://logging.apache.org/log4j/2.x/) | 1.1+ |
|
||||
| [Logback](https://github.com/qos-ch/logback) | 1.0+ |
|
||||
| [MongoDB Drivers](https://mongodb.github.io/mongo-java-driver/) | 3.3+ |
|
||||
| [Netty](https://github.com/netty/netty) | 3.8+ |
|
||||
| [OkHttp](https://github.com/square/okhttp/) | 3.0+ |
|
||||
|
|
|
@ -46,34 +46,8 @@ configurations {
|
|||
implementation.exclude group: 'io.opentelemetry', module: 'opentelemetry-api'
|
||||
}
|
||||
|
||||
// need to perform shading in two steps in order to avoid shading java.util.logging.Logger
|
||||
// in the java-util-logging instrumentation since that instrumentation needs to
|
||||
// reference unshaded java.util.logging.Logger
|
||||
// (java.util.logging.Logger shading is not needed in any of the instrumentation modules,
|
||||
// but it is needed for the dependencies, e.g. guava, which use java.util.logging.Logger)
|
||||
task shadowJarStep1(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
|
||||
|
||||
archiveClassifier = 'step1'
|
||||
destinationDirectory = file("${project.buildDir}/step1")
|
||||
|
||||
configurations = [project.configurations.runtimeClasspath]
|
||||
|
||||
dependencies {
|
||||
exclude(project(':instrumentation:java-util-logging'))
|
||||
}
|
||||
|
||||
// rewrite dependencies calling Logger.getLogger
|
||||
relocate 'java.util.logging.Logger', 'io.opentelemetry.auto.bootstrap.PatchLogger'
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
|
||||
dependsOn shadowJarStep1
|
||||
|
||||
from {
|
||||
zipTree(shadowJarStep1.archiveFile)
|
||||
}
|
||||
|
||||
mergeServiceFiles()
|
||||
|
||||
exclude '**/module-info.class'
|
||||
|
@ -87,6 +61,9 @@ shadowJar {
|
|||
exclude(project(':auto-bootstrap'))
|
||||
}
|
||||
|
||||
// rewrite dependencies calling Logger.getLogger
|
||||
relocate 'java.util.logging.Logger', 'io.opentelemetry.auto.bootstrap.PatchLogger'
|
||||
|
||||
// relocate OpenTelemetry API usage
|
||||
relocate "io.opentelemetry.OpenTelemetry", "io.opentelemetry.auto.shaded.io.opentelemetry.OpenTelemetry"
|
||||
relocate "io.opentelemetry.common", "io.opentelemetry.auto.shaded.io.opentelemetry.common"
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
apply from: "$rootDir/gradle/instrumentation.gradle"
|
||||
apply plugin: 'org.unbroken-dome.test-sets'
|
||||
|
||||
muzzle {
|
||||
pass {
|
||||
group = 'org.jboss.logmanager'
|
||||
module = 'jboss-logmanager'
|
||||
versions = '(,)'
|
||||
}
|
||||
}
|
||||
|
||||
testSets {
|
||||
latestDepTest {
|
||||
dirName = 'test'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation 'org.jboss.logmanager:jboss-logmanager:1.0.0.GA'
|
||||
|
||||
latestDepTestImplementation 'org.jboss.logmanager:jboss-logmanager:+'
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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 io.opentelemetry.auto.instrumentation.jul;
|
||||
|
||||
import io.opentelemetry.OpenTelemetry;
|
||||
import io.opentelemetry.auto.config.Config;
|
||||
import io.opentelemetry.trace.Span;
|
||||
import io.opentelemetry.trace.Tracer;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class JavaUtilLoggingSpans {
|
||||
|
||||
private static final org.slf4j.Logger log = LoggerFactory.getLogger(JavaUtilLoggingSpans.class);
|
||||
|
||||
private static final Tracer TRACER =
|
||||
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.java-util-logging");
|
||||
|
||||
private static final Formatter FORMATTER = new AccessibleFormatter();
|
||||
|
||||
public static void capture(final Logger logger, final LogRecord logRecord) {
|
||||
|
||||
Level level = logRecord.getLevel();
|
||||
if (!logger.isLoggable(level)) {
|
||||
// this is already checked in most cases, except if Logger.log(LogRecord) was called directly
|
||||
return;
|
||||
}
|
||||
if (level.intValue() < getThreshold().intValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Throwable t = logRecord.getThrown();
|
||||
Span span =
|
||||
TRACER
|
||||
.spanBuilder("log.message")
|
||||
.setAttribute("message", FORMATTER.formatMessage(logRecord))
|
||||
.setAttribute("level", level.getName())
|
||||
.setAttribute("loggerName", logger.getName())
|
||||
.startSpan();
|
||||
if (t != null) {
|
||||
span.setAttribute("error.stack", toString(t));
|
||||
}
|
||||
span.end();
|
||||
}
|
||||
|
||||
private static String toString(final Throwable t) {
|
||||
StringWriter out = new StringWriter();
|
||||
t.printStackTrace(new PrintWriter(out));
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private static Level getThreshold() {
|
||||
String level = Config.get().getExperimentalLogCaptureThreshold();
|
||||
if (level == null) {
|
||||
return Level.OFF;
|
||||
}
|
||||
switch (level) {
|
||||
case "OFF":
|
||||
return Level.OFF;
|
||||
case "FATAL":
|
||||
case "ERROR":
|
||||
case "SEVERE":
|
||||
return Level.SEVERE;
|
||||
case "WARN":
|
||||
case "WARNING":
|
||||
return Level.WARNING;
|
||||
case "INFO":
|
||||
return Level.INFO;
|
||||
case "CONFIG":
|
||||
return Level.CONFIG;
|
||||
case "DEBUG":
|
||||
case "FINE":
|
||||
return Level.FINE;
|
||||
case "FINER":
|
||||
return Level.FINER;
|
||||
case "TRACE":
|
||||
case "FINEST":
|
||||
return Level.FINEST;
|
||||
case "ALL":
|
||||
return Level.ALL;
|
||||
default:
|
||||
log.error("unexpected value for {}: {}", Config.EXPERIMENTAL_LOG_CAPTURE_THRESHOLD, level);
|
||||
return Level.OFF;
|
||||
}
|
||||
}
|
||||
|
||||
// this is just needed for calling formatMessage in abstract super class
|
||||
public static class AccessibleFormatter extends Formatter {
|
||||
|
||||
@Override
|
||||
public String format(final LogRecord record) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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 io.opentelemetry.auto.instrumentation.jul;
|
||||
|
||||
import static io.opentelemetry.auto.tooling.bytebuddy.matcher.AgentElementMatchers.extendsClass;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import io.opentelemetry.auto.bootstrap.CallDepthThreadLocalMap;
|
||||
import io.opentelemetry.auto.bootstrap.instrumentation.logging.LoggerDepth;
|
||||
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public class JavaUtilLoggingSpansInstrumentation extends Instrumenter.Default {
|
||||
public JavaUtilLoggingSpansInstrumentation() {
|
||||
super("java-util-logging");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return extendsClass(named("java.util.logging.Logger"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
packageName + ".JavaUtilLoggingSpans",
|
||||
packageName + ".JavaUtilLoggingSpans$AccessibleFormatter"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
|
||||
transformers.put(
|
||||
isMethod()
|
||||
.and(isPublic())
|
||||
.and(named("log"))
|
||||
.and(takesArguments(1))
|
||||
.and(takesArgument(0, named("java.util.logging.LogRecord"))),
|
||||
JavaUtilLoggingSpansInstrumentation.class.getName() + "$LogAdvice");
|
||||
transformers.put(
|
||||
isMethod()
|
||||
.and(isPublic())
|
||||
.and(named("logRaw"))
|
||||
.and(takesArguments(1))
|
||||
.and(takesArgument(0, named("org.jboss.logmanager.ExtLogRecord"))),
|
||||
JavaUtilLoggingSpansInstrumentation.class.getName() + "$LogAdvice");
|
||||
return transformers;
|
||||
}
|
||||
|
||||
public static class LogAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static boolean methodEnter(
|
||||
@Advice.This final Logger logger, @Advice.Argument(0) final LogRecord logRecord) {
|
||||
// need to track call depth across all loggers in order to avoid double capture when one
|
||||
// logging framework delegates to another
|
||||
boolean topLevel = CallDepthThreadLocalMap.incrementCallDepth(LoggerDepth.class) == 0;
|
||||
if (topLevel) {
|
||||
JavaUtilLoggingSpans.capture(logger, logRecord);
|
||||
}
|
||||
return topLevel;
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void methodExit(@Advice.Enter final boolean topLevel) {
|
||||
if (topLevel) {
|
||||
CallDepthThreadLocalMap.reset(LoggerDepth.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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.
|
||||
*/
|
||||
|
||||
import io.opentelemetry.auto.test.log.events.LogEventsTestBase
|
||||
import org.jboss.logmanager.LogContext
|
||||
|
||||
class JBossJavaUtilLoggingEventTest extends LogEventsTestBase {
|
||||
|
||||
@Override
|
||||
Object createLogger(String name) {
|
||||
LogContext.create().getLogger(name)
|
||||
}
|
||||
|
||||
String warn() {
|
||||
return "warning"
|
||||
}
|
||||
|
||||
String error() {
|
||||
return "severe"
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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.
|
||||
*/
|
||||
|
||||
import io.opentelemetry.auto.test.log.events.LogEventsTestBase
|
||||
|
||||
import java.util.logging.Logger
|
||||
|
||||
class JavaUtilLoggingSpansTest extends LogEventsTestBase {
|
||||
|
||||
@Override
|
||||
Object createLogger(String name) {
|
||||
Logger.getLogger(name)
|
||||
}
|
||||
|
||||
String warn() {
|
||||
return "warning"
|
||||
}
|
||||
|
||||
String error() {
|
||||
return "severe"
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
apply from: "$rootDir/gradle/instrumentation.gradle"
|
||||
apply plugin: 'org.unbroken-dome.test-sets'
|
||||
|
||||
muzzle {
|
||||
pass {
|
||||
group = 'log4j'
|
||||
module = 'log4j'
|
||||
versions = '(,)'
|
||||
}
|
||||
}
|
||||
|
||||
testSets {
|
||||
latestDepTest {
|
||||
dirName = 'test'
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
// In order to test the real log4j library we need to remove the log4j transitive
|
||||
// dependency 'log4j-over-slf4j' brought in by :testing-common which would shadow
|
||||
// the log4j module under test using a proxy to slf4j instead.
|
||||
testImplementation.exclude group: 'org.slf4j', module: 'log4j-over-slf4j'
|
||||
|
||||
// See: https://stackoverflow.com/a/9047963/2749853
|
||||
testImplementation.exclude group: 'javax.jms', module: 'jms'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly group: 'log4j', name: 'log4j', version: '1.2.16'
|
||||
|
||||
testImplementation group: 'log4j', name: 'log4j', version: '1.2.16'
|
||||
|
||||
latestDepTestImplementation group: 'log4j', name: 'log4j', version: '+'
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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 io.opentelemetry.auto.instrumentation.log4j.v1_1;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
import io.opentelemetry.auto.config.Config;
|
||||
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
// FIXME this instrumentation relied on scope listener
|
||||
// @AutoService(Instrumenter.class)
|
||||
public class Log4jMDCInstrumentation extends Instrumenter.Default {
|
||||
public Log4jMDCInstrumentation() {
|
||||
super("log4j");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean defaultEnabled() {
|
||||
return Config.get().isLogInjectionEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return named("org.apache.log4j.MDC");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
isConstructor(), Log4jMDCInstrumentation.class.getName() + "$MDCContextAdvice");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {"io.opentelemetry.auto.tooling.log.LogContextScopeListener"};
|
||||
}
|
||||
|
||||
public static class MDCContextAdvice {
|
||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||
public static void mdcClassInitialized(@Advice.This final Object instance) {
|
||||
if (instance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Class<?> mdcClass = instance.getClass();
|
||||
Method putMethod = mdcClass.getMethod("put", String.class, Object.class);
|
||||
Method removeMethod = mdcClass.getMethod("remove", String.class);
|
||||
// FIXME this instrumentation relied on scope listener
|
||||
// GlobalTracer.get().addScopeListener(new LogContextScopeListener(putMethod,
|
||||
// removeMethod));
|
||||
} catch (final NoSuchMethodException e) {
|
||||
org.slf4j.LoggerFactory.getLogger(instance.getClass())
|
||||
.debug("Failed to add log4j ThreadContext span listener", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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 io.opentelemetry.auto.instrumentation.log4j.v1_1;
|
||||
|
||||
import io.opentelemetry.OpenTelemetry;
|
||||
import io.opentelemetry.auto.config.Config;
|
||||
import io.opentelemetry.trace.Span;
|
||||
import io.opentelemetry.trace.Tracer;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import org.apache.log4j.Category;
|
||||
import org.apache.log4j.Priority;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Log4jSpans {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Log4jSpans.class);
|
||||
|
||||
private static final Tracer TRACER =
|
||||
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.log4j-1.1");
|
||||
|
||||
// these constants are copied from org.apache.log4j.Priority and org.apache.log4j.Level because
|
||||
// Level was only introduced in 1.2, and then Level.TRACE was only introduced in 1.2.12
|
||||
private static final int OFF_INT = Integer.MAX_VALUE;
|
||||
private static final int FATAL_INT = 50000;
|
||||
private static final int ERROR_INT = 40000;
|
||||
private static final int WARN_INT = 30000;
|
||||
private static final int INFO_INT = 20000;
|
||||
private static final int DEBUG_INT = 10000;
|
||||
private static final int TRACE_INT = 5000;
|
||||
private static final int ALL_INT = Integer.MIN_VALUE;
|
||||
|
||||
public static void capture(
|
||||
final Category logger, final Priority level, final Object message, final Throwable t) {
|
||||
|
||||
if (level.toInt() < getThreshold()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Span span = TRACER.spanBuilder("log.message").startSpan();
|
||||
span.setAttribute("message", String.valueOf(message));
|
||||
span.setAttribute("level", level.toString());
|
||||
span.setAttribute("loggerName", logger.getName());
|
||||
if (t != null) {
|
||||
span.setAttribute("error.stack", toString(t));
|
||||
}
|
||||
span.end();
|
||||
}
|
||||
|
||||
private static String toString(final Throwable t) {
|
||||
StringWriter out = new StringWriter();
|
||||
t.printStackTrace(new PrintWriter(out));
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private static int getThreshold() {
|
||||
String level = Config.get().getExperimentalLogCaptureThreshold();
|
||||
if (level == null) {
|
||||
return OFF_INT;
|
||||
}
|
||||
switch (level) {
|
||||
case "OFF":
|
||||
return OFF_INT;
|
||||
case "FATAL":
|
||||
return FATAL_INT;
|
||||
case "ERROR":
|
||||
case "SEVERE":
|
||||
return ERROR_INT;
|
||||
case "WARN":
|
||||
case "WARNING":
|
||||
return WARN_INT;
|
||||
case "INFO":
|
||||
return INFO_INT;
|
||||
case "CONFIG":
|
||||
case "DEBUG":
|
||||
case "FINE":
|
||||
case "FINER":
|
||||
return DEBUG_INT;
|
||||
case "TRACE":
|
||||
case "FINEST":
|
||||
return TRACE_INT;
|
||||
case "ALL":
|
||||
return ALL_INT;
|
||||
default:
|
||||
log.error("unexpected value for {}: {}", Config.EXPERIMENTAL_LOG_CAPTURE_THRESHOLD, level);
|
||||
return OFF_INT;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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 io.opentelemetry.auto.instrumentation.log4j.v1_1;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isProtected;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import io.opentelemetry.auto.bootstrap.CallDepthThreadLocalMap;
|
||||
import io.opentelemetry.auto.bootstrap.instrumentation.logging.LoggerDepth;
|
||||
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
import org.apache.log4j.Category;
|
||||
import org.apache.log4j.Priority;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public class Log4jSpansInstrumentation extends Instrumenter.Default {
|
||||
public Log4jSpansInstrumentation() {
|
||||
super("log4j");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return named("org.apache.log4j.Category");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {packageName + ".Log4jSpans"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
|
||||
transformers.put(
|
||||
isMethod()
|
||||
.and(isProtected())
|
||||
.and(named("forcedLog"))
|
||||
.and(takesArguments(4))
|
||||
.and(takesArgument(0, named("java.lang.String")))
|
||||
.and(takesArgument(1, named("org.apache.log4j.Priority")))
|
||||
.and(takesArgument(2, named("java.lang.Object")))
|
||||
.and(takesArgument(3, named("java.lang.Throwable"))),
|
||||
Log4jSpansInstrumentation.class.getName() + "$ForcedLogAdvice");
|
||||
return transformers;
|
||||
}
|
||||
|
||||
public static class ForcedLogAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static boolean methodEnter(
|
||||
@Advice.This final Category logger,
|
||||
@Advice.Argument(1) final Priority level,
|
||||
@Advice.Argument(2) final Object message,
|
||||
@Advice.Argument(3) final Throwable t) {
|
||||
// need to track call depth across all loggers to avoid double capture when one logging
|
||||
// framework delegates to another
|
||||
boolean topLevel = CallDepthThreadLocalMap.incrementCallDepth(LoggerDepth.class) == 0;
|
||||
if (topLevel) {
|
||||
Log4jSpans.capture(logger, level, message, t);
|
||||
}
|
||||
return topLevel;
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void methodExit(@Advice.Enter final boolean topLevel) {
|
||||
if (topLevel) {
|
||||
CallDepthThreadLocalMap.reset(LoggerDepth.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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.
|
||||
*/
|
||||
|
||||
import io.opentelemetry.auto.test.log.injection.LogContextInjectionTestBase
|
||||
import org.apache.log4j.MDC
|
||||
import org.junit.Ignore
|
||||
import spock.lang.Requires
|
||||
|
||||
/**
|
||||
It looks like log4j1 is broken for any java version that doesn't have '.' in version number
|
||||
- it thinks it runs on ancient version. For example this happens for java13.
|
||||
See {@link org.apache.log4j.helpers.Loader}.
|
||||
*/
|
||||
// FIXME this instrumentation relied on scope listener
|
||||
@Ignore
|
||||
@Requires({ System.getProperty("java.version").contains(".") })
|
||||
class Log4jMDCTest extends LogContextInjectionTestBase {
|
||||
|
||||
@Override
|
||||
def put(String key, Object value) {
|
||||
return MDC.put(key, value)
|
||||
}
|
||||
|
||||
@Override
|
||||
def get(String key) {
|
||||
return MDC.get(key)
|
||||
}
|
||||
|
||||
@Override
|
||||
def remove(String key) {
|
||||
MDC.context
|
||||
return MDC.remove(key)
|
||||
}
|
||||
|
||||
@Override
|
||||
def clear() {
|
||||
return MDC.clear()
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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.
|
||||
*/
|
||||
|
||||
import io.opentelemetry.auto.test.log.events.LogEventsTestBase
|
||||
import org.apache.log4j.Logger
|
||||
|
||||
class Log4jSpansTest extends LogEventsTestBase {
|
||||
|
||||
@Override
|
||||
Object createLogger(String name) {
|
||||
Logger.getLogger(name)
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
log4j.rootLogger=WARN, CONSOLE
|
||||
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.CONSOLE.layout=org.apache.log4j.SimpleLayout
|
|
@ -1,40 +0,0 @@
|
|||
apply from: "$rootDir/gradle/instrumentation.gradle"
|
||||
apply plugin: 'org.unbroken-dome.test-sets'
|
||||
|
||||
muzzle {
|
||||
pass {
|
||||
group = 'org.apache.logging.log4j'
|
||||
module = 'log4j-core'
|
||||
versions = '(,)'
|
||||
}
|
||||
|
||||
pass {
|
||||
group = 'org.apache.logging.log4j'
|
||||
module = 'log4j-api'
|
||||
versions = '(,)'
|
||||
}
|
||||
}
|
||||
|
||||
testSets {
|
||||
latestDepTest {
|
||||
dirName = 'test'
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
// In order to test the real log4j library we need to remove the log4j transitive
|
||||
// dependency 'log4j-over-slf4j' brought in by :testing-common which would shadow
|
||||
// the log4j module under test using a proxy to slf4j instead.
|
||||
testImplementation.exclude group: 'org.slf4j', module: 'log4j-over-slf4j'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.0'
|
||||
compileOnly group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.0'
|
||||
|
||||
testImplementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.0'
|
||||
testImplementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.0'
|
||||
|
||||
latestDepTestImplementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '+'
|
||||
latestDepTestImplementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '+'
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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 io.opentelemetry.auto.instrumentation.log4j.v2_0;
|
||||
|
||||
import io.opentelemetry.OpenTelemetry;
|
||||
import io.opentelemetry.auto.config.Config;
|
||||
import io.opentelemetry.trace.Span;
|
||||
import io.opentelemetry.trace.Tracer;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.message.Message;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Log4jSpans {
|
||||
|
||||
private static final org.slf4j.Logger log = LoggerFactory.getLogger(Log4jSpans.class);
|
||||
|
||||
private static final Tracer TRACER =
|
||||
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.log4j-2.0");
|
||||
|
||||
public static void capture(
|
||||
final Logger logger, final Level level, final Message message, final Throwable t) {
|
||||
|
||||
if (level.intLevel() > getThreshold().intLevel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Span span =
|
||||
TRACER
|
||||
.spanBuilder("log.message")
|
||||
.setAttribute("message", message.getFormattedMessage())
|
||||
.setAttribute("level", level.toString())
|
||||
.setAttribute("loggerName", logger.getName())
|
||||
.startSpan();
|
||||
if (t != null) {
|
||||
span.setAttribute("error.stack", toString(t));
|
||||
}
|
||||
span.end();
|
||||
}
|
||||
|
||||
private static String toString(final Throwable t) {
|
||||
StringWriter out = new StringWriter();
|
||||
t.printStackTrace(new PrintWriter(out));
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private static Level getThreshold() {
|
||||
String level = Config.get().getExperimentalLogCaptureThreshold();
|
||||
if (level == null) {
|
||||
return Level.OFF;
|
||||
}
|
||||
switch (level) {
|
||||
case "OFF":
|
||||
return Level.OFF;
|
||||
case "FATAL":
|
||||
return Level.FATAL;
|
||||
case "ERROR":
|
||||
case "SEVERE":
|
||||
return Level.ERROR;
|
||||
case "WARN":
|
||||
case "WARNING":
|
||||
return Level.WARN;
|
||||
case "INFO":
|
||||
return Level.INFO;
|
||||
case "CONFIG":
|
||||
case "DEBUG":
|
||||
case "FINE":
|
||||
case "FINER":
|
||||
return Level.DEBUG;
|
||||
case "TRACE":
|
||||
case "FINEST":
|
||||
return Level.TRACE;
|
||||
case "ALL":
|
||||
return Level.ALL;
|
||||
default:
|
||||
log.error("unexpected value for {}: {}", Config.EXPERIMENTAL_LOG_CAPTURE_THRESHOLD, level);
|
||||
return Level.OFF;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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 io.opentelemetry.auto.instrumentation.log4j.v2_0;
|
||||
|
||||
import static io.opentelemetry.auto.tooling.bytebuddy.matcher.AgentElementMatchers.extendsClass;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isProtected;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import io.opentelemetry.auto.bootstrap.CallDepthThreadLocalMap;
|
||||
import io.opentelemetry.auto.bootstrap.instrumentation.logging.LoggerDepth;
|
||||
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.message.Message;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public class Log4jSpansInstrumentation extends Instrumenter.Default {
|
||||
public Log4jSpansInstrumentation() {
|
||||
super("log4j");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return extendsClass(named("org.apache.logging.log4j.spi.AbstractLogger"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {packageName + ".Log4jSpans"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
|
||||
transformers.put(
|
||||
isMethod()
|
||||
.and(isPublic())
|
||||
.and(named("logMessage"))
|
||||
.and(takesArguments(5))
|
||||
.and(takesArgument(0, named("java.lang.String")))
|
||||
.and(takesArgument(1, named("org.apache.logging.log4j.Level")))
|
||||
.and(takesArgument(2, named("org.apache.logging.log4j.Marker")))
|
||||
.and(takesArgument(3, named("org.apache.logging.log4j.message.Message")))
|
||||
.and(takesArgument(4, named("java.lang.Throwable"))),
|
||||
Log4jSpansInstrumentation.class.getName() + "$LogMessageAdvice");
|
||||
// log4j 2.12.1 introduced and started using this new log() method
|
||||
transformers.put(
|
||||
isMethod()
|
||||
.and(isProtected())
|
||||
.and(named("log"))
|
||||
.and(takesArguments(6))
|
||||
.and(takesArgument(0, named("org.apache.logging.log4j.Level")))
|
||||
.and(takesArgument(1, named("org.apache.logging.log4j.Marker")))
|
||||
.and(takesArgument(2, named("java.lang.String")))
|
||||
.and(takesArgument(3, named("java.lang.StackTraceElement")))
|
||||
.and(takesArgument(4, named("org.apache.logging.log4j.message.Message")))
|
||||
.and(takesArgument(5, named("java.lang.Throwable"))),
|
||||
Log4jSpansInstrumentation.class.getName() + "$LogAdvice");
|
||||
return transformers;
|
||||
}
|
||||
|
||||
public static class LogMessageAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static boolean methodEnter(
|
||||
@Advice.This final Logger logger,
|
||||
@Advice.Argument(1) final Level level,
|
||||
@Advice.Argument(3) final Message message,
|
||||
@Advice.Argument(4) final Throwable t) {
|
||||
// need to track call depth across all loggers in order to avoid double capture when one
|
||||
// logging framework delegates to another
|
||||
boolean topLevel = CallDepthThreadLocalMap.incrementCallDepth(LoggerDepth.class) == 0;
|
||||
if (topLevel) {
|
||||
Log4jSpans.capture(logger, level, message, t);
|
||||
}
|
||||
return topLevel;
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void methodExit(@Advice.Enter final boolean topLevel) {
|
||||
if (topLevel) {
|
||||
CallDepthThreadLocalMap.reset(LoggerDepth.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class LogAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static boolean methodEnter(
|
||||
@Advice.This final Logger logger,
|
||||
@Advice.Argument(0) final Level level,
|
||||
@Advice.Argument(4) final Message message,
|
||||
@Advice.Argument(5) final Throwable t) {
|
||||
// need to track call depth across all loggers in order to avoid double capture when one
|
||||
// logging framework delegates to another
|
||||
boolean topLevel = CallDepthThreadLocalMap.incrementCallDepth(LoggerDepth.class) == 0;
|
||||
if (topLevel) {
|
||||
Log4jSpans.capture(logger, level, message, t);
|
||||
}
|
||||
return topLevel;
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void methodExit(@Advice.Enter final boolean topLevel) {
|
||||
if (topLevel) {
|
||||
CallDepthThreadLocalMap.reset(LoggerDepth.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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 io.opentelemetry.auto.instrumentation.log4j.v2_0;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
import io.opentelemetry.auto.config.Config;
|
||||
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||
import io.opentelemetry.auto.tooling.log.LogContextScopeListener;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
// FIXME this instrumentation relied on scope listener
|
||||
// @AutoService(Instrumenter.class)
|
||||
public class ThreadContextInstrumentation extends Instrumenter.Default {
|
||||
public static final String MDC_INSTRUMENTATION_NAME = "log4j";
|
||||
|
||||
public ThreadContextInstrumentation() {
|
||||
super(MDC_INSTRUMENTATION_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean defaultEnabled() {
|
||||
return Config.get().isLogInjectionEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return named("org.apache.logging.log4j.ThreadContext");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
isTypeInitializer(), ThreadContextInstrumentation.class.getName() + "$ThreadContextAdvice");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {LogContextScopeListener.class.getName()};
|
||||
}
|
||||
|
||||
public static class ThreadContextAdvice {
|
||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||
public static void mdcClassInitialized(@Advice.Origin final Class threadClass) {
|
||||
try {
|
||||
Method putMethod = threadClass.getMethod("put", String.class, String.class);
|
||||
Method removeMethod = threadClass.getMethod("remove", String.class);
|
||||
// FIXME this instrumentation relied on scope listener
|
||||
// GlobalTracer.get().addScopeListener(new LogContextScopeListener(putMethod,
|
||||
// removeMethod));
|
||||
} catch (final NoSuchMethodException e) {
|
||||
org.slf4j.LoggerFactory.getLogger(threadClass)
|
||||
.debug("Failed to add log4j ThreadContext span listener", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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.
|
||||
*/
|
||||
|
||||
import io.opentelemetry.auto.test.log.events.LogEventsTestBase
|
||||
import org.apache.logging.log4j.LogManager
|
||||
|
||||
class Log4jSpansTest extends LogEventsTestBase {
|
||||
|
||||
static {
|
||||
// need to initialize logger before tests to flush out init warning message:
|
||||
// "Unable to instantiate org.fusesource.jansi.WindowsAnsiOutputStream"
|
||||
LogManager.getLogger(Log4jSpansTest)
|
||||
}
|
||||
|
||||
@Override
|
||||
Object createLogger(String name) {
|
||||
LogManager.getLogger(name)
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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.
|
||||
*/
|
||||
|
||||
import io.opentelemetry.auto.test.log.injection.LogContextInjectionTestBase
|
||||
import org.apache.logging.log4j.ThreadContext
|
||||
import org.junit.Ignore
|
||||
|
||||
// FIXME this instrumentation relied on scope listener
|
||||
@Ignore
|
||||
class Log4jThreadContextTest extends LogContextInjectionTestBase {
|
||||
|
||||
@Override
|
||||
def put(String key, Object value) {
|
||||
return ThreadContext.put(key, value as String)
|
||||
}
|
||||
|
||||
@Override
|
||||
def get(String key) {
|
||||
return ThreadContext.get(key)
|
||||
}
|
||||
|
||||
@Override
|
||||
def remove(String key) {
|
||||
return ThreadContext.remove(key)
|
||||
}
|
||||
|
||||
@Override
|
||||
def clear() {
|
||||
return ThreadContext.clearAll()
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Configuration>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="warn">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
|
@ -1,7 +0,0 @@
|
|||
apply from: "$rootDir/gradle/instrumentation.gradle"
|
||||
|
||||
dependencies {
|
||||
compileOnly group: 'ch.qos.logback', name: 'logback-classic', version: '0.9.16'
|
||||
|
||||
testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: '0.9.16'
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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 io.opentelemetry.auto.instrumentation.logback;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.classic.spi.ThrowableProxy;
|
||||
import io.opentelemetry.OpenTelemetry;
|
||||
import io.opentelemetry.auto.config.Config;
|
||||
import io.opentelemetry.trace.Span;
|
||||
import io.opentelemetry.trace.Tracer;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LogbackSpans {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LogbackSpans.class);
|
||||
|
||||
private static final Tracer TRACER =
|
||||
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.logback-1.0");
|
||||
|
||||
public static void capture(final ILoggingEvent event) {
|
||||
|
||||
Level level = event.getLevel();
|
||||
if (level.toInt() < getThreshold().toInt()) {
|
||||
// this needs to be configurable
|
||||
return;
|
||||
}
|
||||
|
||||
Object throwableProxy = event.getThrowableProxy();
|
||||
Throwable t = null;
|
||||
if (throwableProxy instanceof ThrowableProxy) {
|
||||
// there is only one other subclass of ch.qos.logback.classic.spi.IThrowableProxy
|
||||
// and it is only used for logging exceptions over the wire
|
||||
t = ((ThrowableProxy) throwableProxy).getThrowable();
|
||||
}
|
||||
|
||||
Span span =
|
||||
TRACER
|
||||
.spanBuilder("log.message")
|
||||
.setAttribute("message", event.getFormattedMessage())
|
||||
.setAttribute("level", level.toString())
|
||||
.setAttribute("loggerName", event.getLoggerName())
|
||||
.startSpan();
|
||||
if (t != null) {
|
||||
span.setAttribute("error.stack", toString(t));
|
||||
}
|
||||
span.end();
|
||||
}
|
||||
|
||||
private static String toString(final Throwable t) {
|
||||
StringWriter out = new StringWriter();
|
||||
t.printStackTrace(new PrintWriter(out));
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private static Level getThreshold() {
|
||||
String level = Config.get().getExperimentalLogCaptureThreshold();
|
||||
if (level == null) {
|
||||
return Level.OFF;
|
||||
}
|
||||
switch (level) {
|
||||
case "OFF":
|
||||
return Level.OFF;
|
||||
case "FATAL":
|
||||
case "ERROR":
|
||||
case "SEVERE":
|
||||
return Level.ERROR;
|
||||
case "WARN":
|
||||
case "WARNING":
|
||||
return Level.WARN;
|
||||
case "INFO":
|
||||
return Level.INFO;
|
||||
case "CONFIG":
|
||||
case "DEBUG":
|
||||
case "FINE":
|
||||
case "FINER":
|
||||
return Level.DEBUG;
|
||||
case "TRACE":
|
||||
case "FINEST":
|
||||
return Level.TRACE;
|
||||
case "ALL":
|
||||
return Level.ALL;
|
||||
default:
|
||||
log.error("unexpected value for {}: {}", Config.EXPERIMENTAL_LOG_CAPTURE_THRESHOLD, level);
|
||||
return Level.OFF;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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 io.opentelemetry.auto.instrumentation.logback;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import com.google.auto.service.AutoService;
|
||||
import io.opentelemetry.auto.bootstrap.CallDepthThreadLocalMap;
|
||||
import io.opentelemetry.auto.bootstrap.instrumentation.logging.LoggerDepth;
|
||||
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(Instrumenter.class)
|
||||
public class LogbackSpansInstrumentation extends Instrumenter.Default {
|
||||
public LogbackSpansInstrumentation() {
|
||||
super("logback");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return named("ch.qos.logback.classic.Logger");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {packageName + ".LogbackSpans"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
|
||||
transformers.put(
|
||||
isMethod()
|
||||
.and(isPublic())
|
||||
.and(named("callAppenders"))
|
||||
.and(takesArguments(1))
|
||||
.and(takesArgument(0, named("ch.qos.logback.classic.spi.ILoggingEvent"))),
|
||||
LogbackSpansInstrumentation.class.getName() + "$CallAppendersAdvice");
|
||||
return transformers;
|
||||
}
|
||||
|
||||
public static class CallAppendersAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static boolean methodEnter(@Advice.Argument(0) final ILoggingEvent event) {
|
||||
// need to track call depth across all loggers in order to avoid double capture when one
|
||||
// logging framework delegates to another
|
||||
boolean topLevel = CallDepthThreadLocalMap.incrementCallDepth(LoggerDepth.class) == 0;
|
||||
if (topLevel) {
|
||||
LogbackSpans.capture(event);
|
||||
}
|
||||
return topLevel;
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||
public static void methodExit(@Advice.Enter final boolean topLevel) {
|
||||
if (topLevel) {
|
||||
CallDepthThreadLocalMap.reset(LoggerDepth.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry 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.
|
||||
*/
|
||||
|
||||
import ch.qos.logback.classic.LoggerContext
|
||||
import io.opentelemetry.auto.test.log.events.LogEventsTestBase
|
||||
|
||||
class LogbackSpansTest extends LogEventsTestBase {
|
||||
|
||||
@Override
|
||||
Object createLogger(String name) {
|
||||
new LoggerContext().getLogger(name)
|
||||
}
|
||||
}
|
|
@ -22,7 +22,6 @@ rootProject.name = 'opentelemetry-java-instrumentation'
|
|||
include ':opentelemetry-javaagent'
|
||||
include ':opentelemetry-sdk-shaded-for-testing'
|
||||
include ':opentelemetry-api-beta-shaded-for-instrumenting'
|
||||
include ':logback-shaded-for-instrumenting'
|
||||
include ':agent-bootstrap'
|
||||
include ':agent-tooling'
|
||||
include ':load-generator'
|
||||
|
@ -90,7 +89,6 @@ include ':instrumentation:java-concurrent:kotlin-testing'
|
|||
include ':instrumentation:java-concurrent:scala-testing'
|
||||
include ':instrumentation:java-concurrent:akka-testing'
|
||||
include ':instrumentation:java-concurrent:akka-2.5-testing'
|
||||
include ':instrumentation:java-util-logging'
|
||||
include ':instrumentation:jaxrs:jaxrs-1.0'
|
||||
include ':instrumentation:jaxrs:jaxrs-2.0'
|
||||
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-jersey-2.0'
|
||||
|
@ -113,10 +111,7 @@ include ':instrumentation:kubernetes-client-7.0'
|
|||
include ':instrumentation:lettuce:lettuce-4.0'
|
||||
include ':instrumentation:lettuce:lettuce-5.0'
|
||||
include ':instrumentation:lettuce:lettuce-5.1'
|
||||
include ':instrumentation:log4j:log4j-1.1'
|
||||
include ':instrumentation:log4j:log4j-2.0'
|
||||
include ':instrumentation:log4j:log4j-2.13.2:library'
|
||||
include ':instrumentation:logback-1.0'
|
||||
include ':instrumentation:mongo:mongo-3.1'
|
||||
include ':instrumentation:mongo:mongo-3.7'
|
||||
include ':instrumentation:mongo:mongo-async-3.3'
|
||||
|
|
Loading…
Reference in New Issue