Skip to content

Commit 1b6d3d1

Browse files
committed
Sketch out slf4j bridge
1 parent 090f148 commit 1b6d3d1

File tree

24 files changed

+522
-5
lines changed

24 files changed

+522
-5
lines changed

dependencyManagement/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ plugins {
55
data class DependencySet(val group: String, val version: String, val modules: List<String>)
66

77
// this line is managed by .github/scripts/update-sdk-version.sh
8-
val otelSdkVersion = "1.57.0"
8+
val otelSdkVersion = "1.58.0-SNAPSHOT"
99
val otelContribVersion = "1.52.0-alpha"
1010
val otelSdkAlphaVersion = otelSdkVersion.replaceFirst("(-SNAPSHOT)?$".toRegex(), "-alpha$1")
1111

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
plugins {
2+
id("otel.javaagent-bootstrap")
3+
}
4+
5+
dependencies {
6+
compileOnly(project(":javaagent-bootstrap"))
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.bootstrap.logging;
7+
8+
import io.opentelemetry.javaagent.Slf4jLogRecorder;
9+
import io.opentelemetry.javaagent.bootstrap.Slf4jBridgeLogRecorderHolder;
10+
11+
public class Slf4jBridgeInstaller {
12+
private Slf4jBridgeInstaller() {}
13+
14+
public static void installSlf4jLogger(Slf4jLogRecorder slf4JLogRecorder) {
15+
Slf4jBridgeLogRecorderHolder.initialize(slf4JLogRecorder);
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.bootstrap.logging;
7+
8+
import java.util.concurrent.atomic.AtomicBoolean;
9+
10+
public final class Slf4jBridgeInstallerFlags {
11+
12+
private Slf4jBridgeInstallerFlags() {}
13+
14+
public static final AtomicBoolean IS_INSTALLED = new AtomicBoolean(false);
15+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
plugins {
2+
id("otel.javaagent-instrumentation")
3+
}
4+
5+
muzzle {
6+
pass {
7+
group.set("org.slf4j")
8+
module.set("slf4j-api")
9+
versions.set("[2.0.0,)")
10+
assertInverse.set(true)
11+
}
12+
}
13+
14+
val latestDepTest = findProperty("testLatestDeps") as Boolean
15+
dependencies {
16+
bootstrap(project(":instrumentation:internal:internal-slf4j-bridge:bootstrap"))
17+
18+
compileOnly(project(":javaagent-bootstrap"))
19+
20+
compileOnly("org.slf4j:slf4j-api") {
21+
version {
22+
// 2.0.0 introduced fluent API
23+
strictly("2.0.0")
24+
}
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.internal.slf4jbridge;
7+
8+
import static net.bytebuddy.matcher.ElementMatchers.named;
9+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
10+
11+
import io.opentelemetry.javaagent.bootstrap.logging.Slf4jBridgeInstallerFlags;
12+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
13+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
14+
import net.bytebuddy.asm.Advice;
15+
import net.bytebuddy.description.type.TypeDescription;
16+
import net.bytebuddy.matcher.ElementMatcher;
17+
18+
public class LoggerFactoryInstrumentation implements TypeInstrumentation {
19+
20+
@Override
21+
public ElementMatcher<TypeDescription> typeMatcher() {
22+
return named("org.slf4j.LoggerFactory");
23+
}
24+
25+
@Override
26+
public void transform(TypeTransformer transformer) {
27+
// once a call to getILoggerFactory() exits we can be certain that slf4j is properly initialized
28+
transformer.applyAdviceToMethod(
29+
named("getILoggerFactory").and(takesArguments(0)),
30+
this.getClass().getName() + "$GetLoggerFactoryAdvice");
31+
}
32+
33+
@SuppressWarnings("unused")
34+
public static class GetLoggerFactoryAdvice {
35+
36+
@Advice.OnMethodExit(suppress = Throwable.class)
37+
public static void onExit() {
38+
if (Slf4jBridgeInstallerFlags.IS_INSTALLED.compareAndSet(false, true)) {
39+
Slf4jLogRecorderImpl.install();
40+
}
41+
}
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.internal.slf4jbridge;
7+
8+
import com.google.auto.service.AutoService;
9+
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder;
10+
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer;
11+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
12+
13+
@AutoService(IgnoredTypesConfigurer.class)
14+
public final class Slf4jBridgeIgnoredTypesConfigurer implements IgnoredTypesConfigurer {
15+
16+
@Override
17+
public void configure(IgnoredTypesBuilder builder, ConfigProperties config) {
18+
builder.allowClass("org.slf4j.LoggerFactory");
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.internal.slf4jbridge;
7+
8+
import static java.util.Collections.singletonList;
9+
10+
import com.google.auto.service.AutoService;
11+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
12+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
13+
import java.util.List;
14+
15+
@AutoService(InstrumentationModule.class)
16+
public class Slf4jBridgeInstrumentationModule extends InstrumentationModule {
17+
18+
public Slf4jBridgeInstrumentationModule() {
19+
super("internal-slf4j-bridge");
20+
}
21+
22+
@Override
23+
public List<TypeInstrumentation> typeInstrumentations() {
24+
return singletonList(new LoggerFactoryInstrumentation());
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.internal.slf4jbridge;
7+
8+
import io.opentelemetry.api.common.Attributes;
9+
import io.opentelemetry.api.common.Value;
10+
import io.opentelemetry.api.logs.LoggerProvider;
11+
import io.opentelemetry.api.logs.Severity;
12+
import io.opentelemetry.context.Context;
13+
import io.opentelemetry.javaagent.Slf4jLogRecorder;
14+
import io.opentelemetry.javaagent.bootstrap.CallDepth;
15+
import javax.annotation.Nullable;
16+
import io.opentelemetry.javaagent.bootstrap.logging.Slf4jBridgeInstaller;
17+
import org.slf4j.Logger;
18+
import org.slf4j.LoggerFactory;
19+
import org.slf4j.event.Level;
20+
import org.slf4j.spi.LoggingEventBuilder;
21+
22+
public final class Slf4jLogRecorderImpl implements Slf4jLogRecorder {
23+
24+
private Slf4jLogRecorderImpl() {}
25+
26+
public static void install() {
27+
Slf4jBridgeInstaller.installSlf4jLogger(new Slf4jLogRecorderImpl());
28+
}
29+
30+
@Override
31+
public void record(
32+
Context context,
33+
String scopeName,
34+
@Nullable String eventName,
35+
@Nullable Value<?> bodyValue,
36+
Attributes attributes,
37+
Severity severity) {
38+
CallDepth callDepth = CallDepth.forClass(LoggerProvider.class);
39+
try {
40+
if (callDepth.getAndIncrement() > 0) {
41+
return;
42+
}
43+
recordToSlf4j(scopeName, eventName, bodyValue, attributes, severity);
44+
} finally {
45+
callDepth.decrementAndGet();
46+
}
47+
}
48+
49+
@SuppressWarnings("CheckReturnValue")
50+
public static void recordToSlf4j(
51+
String scopeName,
52+
@Nullable String eventName,
53+
@Nullable Value<?> bodyValue,
54+
Attributes attributes,
55+
Severity severity) {
56+
Logger logger = LoggerFactory.getLogger(scopeName);
57+
Level level = toSlf4jLevel(severity);
58+
if (!logger.isEnabledForLevel(level)) {
59+
return;
60+
}
61+
LoggingEventBuilder builder = logger.atLevel(level);
62+
if (bodyValue != null) {
63+
builder.setMessage(bodyValue.asString());
64+
}
65+
attributes.forEach((key, value) -> builder.addKeyValue(key.getKey(), value));
66+
67+
// append event_name last to take priority over attributes
68+
if (eventName != null) {
69+
builder.addKeyValue("event_name", eventName);
70+
}
71+
builder.log();
72+
}
73+
74+
private static Level toSlf4jLevel(Severity severity) {
75+
switch (severity) {
76+
case TRACE:
77+
case TRACE2:
78+
case TRACE3:
79+
case TRACE4:
80+
return Level.TRACE;
81+
case DEBUG:
82+
case DEBUG2:
83+
case DEBUG3:
84+
case DEBUG4:
85+
return Level.DEBUG;
86+
case INFO:
87+
case INFO2:
88+
case INFO3:
89+
case INFO4:
90+
return Level.INFO;
91+
case WARN:
92+
case WARN2:
93+
case WARN3:
94+
case WARN4:
95+
return Level.WARN;
96+
case ERROR:
97+
case ERROR2:
98+
case ERROR3:
99+
case ERROR4:
100+
case FATAL:
101+
case FATAL2:
102+
case FATAL3:
103+
case FATAL4:
104+
return Level.ERROR;
105+
case UNDEFINED_SEVERITY_NUMBER:
106+
return Level.INFO;
107+
}
108+
throw new IllegalArgumentException("Unknown severity: " + severity);
109+
}
110+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
classification: internal

0 commit comments

Comments
 (0)