Last active
March 24, 2025 02:57
-
-
Save wreulicke/41b82cf4b1fbb5d8a0aefc9d7baf6491 to your computer and use it in GitHub Desktop.
JUnitでotelのセットアップするやつ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// (deprecated) テストでjavaagentを入れるサンプル | |
configurations { | |
otel {} | |
} | |
tasks.named('test') { | |
jvmArgs += [ | |
"-XX:+EnableDynamicAgentLoading", | |
"-javaagent:${configurations.otel.singleFile}", | |
"-Dotel.resource.attributes=service.name=SERVICE_NAME", | |
"-Dotel.instrumentation.enabled=true", | |
"-Dotel.javaagent.debug=false", | |
"-Dotel.traces.exporter=console", | |
"-Dotel.metrics.exporter=none", | |
"-Dotel.logs.exporter=none", | |
] | |
} | |
dependencies { | |
otel 'io.opentelemetry.javaagent:opentelemetry-javaagent:2.14.0' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.wreulicke.otel; | |
import io.opentelemetry.api.GlobalOpenTelemetry; | |
import io.opentelemetry.api.trace.Span; | |
import io.opentelemetry.api.trace.Tracer; | |
import io.opentelemetry.context.Scope; | |
import io.opentelemetry.sdk.OpenTelemetrySdk; | |
import io.opentelemetry.sdk.common.CompletableResultCode; | |
import io.opentelemetry.sdk.trace.SdkTracerProvider; | |
import io.opentelemetry.sdk.trace.data.SpanData; | |
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; | |
import io.opentelemetry.sdk.trace.export.SpanExporter; | |
import net.logstash.logback.argument.StructuredArguments; | |
import org.junit.platform.engine.TestExecutionResult; | |
import org.junit.platform.engine.UniqueId; | |
import org.junit.platform.launcher.LauncherSession; | |
import org.junit.platform.launcher.LauncherSessionListener; | |
import org.junit.platform.launcher.TestExecutionListener; | |
import org.junit.platform.launcher.TestIdentifier; | |
import org.junit.platform.launcher.TestPlan; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import javax.annotation.Nullable; | |
import java.util.Collection; | |
import java.util.Map; | |
import java.util.concurrent.ConcurrentHashMap; | |
// 使う時は、META-INF/services/org.junit.platform.launcher.LauncherSessionListener に OtelLauncherSessinListener fqcn を書く必要がある | |
// @AutoService(LauncherSessionListener.class) | |
public class OtelLauncherSessinListener implements LauncherSessionListener { | |
@Nullable | |
private volatile OtelFixture fixture; | |
@Override | |
public void launcherSessionOpened(LauncherSession session) { | |
session.getLauncher().registerTestExecutionListeners(new TestExecutionListener() { | |
record TestScope(Span span, Scope scope) { | |
} | |
private final Map<UniqueId, TestScope> testScopes = new ConcurrentHashMap<>(); | |
@Override | |
public void testPlanExecutionStarted(TestPlan testPlan) { | |
if (fixture == null) { | |
fixture = new OtelFixture(); | |
fixture.setUp(); | |
} | |
} | |
@Override | |
@SuppressWarnings("MustBeClosedChecker") | |
public void executionStarted(TestIdentifier testIdentifier) { | |
if (fixture != null) { | |
Span span = fixture.getTracer() | |
.spanBuilder(testIdentifier.getLegacyReportingName()) | |
.startSpan(); | |
Scope scope = span.makeCurrent(); | |
testScopes.put(testIdentifier.getUniqueIdObject(), new TestScope(span, scope)); | |
} | |
} | |
@Override | |
public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { | |
TestScope testScope = testScopes.remove(testIdentifier.getUniqueIdObject()); | |
if (testScope != null) { | |
testScope.span().end(); | |
testScope.scope().close(); | |
} | |
} | |
}); | |
} | |
@Override | |
public void launcherSessionClosed(LauncherSession session) { | |
if (fixture != null) { | |
fixture.close(); | |
fixture = null; | |
} | |
} | |
static class OtelFixture { | |
@Nullable | |
private OpenTelemetrySdk sdk; | |
@Nullable | |
private Tracer tracer; | |
void setUp() { | |
this.sdk = OpenTelemetrySdk.builder() | |
.setTracerProvider( | |
SdkTracerProvider.builder() | |
.addSpanProcessor( | |
SimpleSpanProcessor.create(new Slf4jSpanExporter()) | |
) | |
.build() | |
) | |
.build(); | |
GlobalOpenTelemetry.set(sdk); | |
} | |
Tracer getTracer() { | |
if (sdk == null) { | |
throw new IllegalStateException("setUp() not called"); | |
} | |
if (tracer == null) { | |
tracer = sdk.getTracer(OtelLauncherSessinListener.class.getName()); | |
} | |
return tracer; | |
} | |
void close() { | |
if (sdk != null) { | |
sdk.close(); | |
GlobalOpenTelemetry.resetForTest(); | |
} | |
} | |
} | |
static class Slf4jSpanExporter implements SpanExporter { | |
private static final Logger log = LoggerFactory.getLogger(Slf4jSpanExporter.class); | |
@Override | |
@SuppressWarnings("Slf4jPlaceholderMismatch") | |
public CompletableResultCode export(Collection<SpanData> collection) { | |
collection.forEach(spanData -> log.info("span", StructuredArguments.kv("span", spanData))); | |
return CompletableResultCode.ofSuccess(); | |
} | |
@Override | |
public CompletableResultCode flush() { | |
return CompletableResultCode.ofSuccess(); | |
} | |
@Override | |
public CompletableResultCode shutdown() { | |
return CompletableResultCode.ofSuccess(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.wreulicke.otel; | |
import io.opentelemetry.api.GlobalOpenTelemetry; | |
import io.opentelemetry.api.trace.Span; | |
import io.opentelemetry.api.trace.Tracer; | |
import io.opentelemetry.sdk.OpenTelemetrySdk; | |
import io.opentelemetry.sdk.common.CompletableResultCode; | |
import io.opentelemetry.sdk.trace.SdkTracerProvider; | |
import io.opentelemetry.sdk.trace.data.SpanData; | |
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; | |
import io.opentelemetry.sdk.trace.export.SpanExporter; | |
import net.logstash.logback.argument.StructuredArguments; | |
import org.junit.jupiter.api.extension.AfterEachCallback; | |
import org.junit.jupiter.api.extension.BeforeAllCallback; | |
import org.junit.jupiter.api.extension.BeforeEachCallback; | |
import org.junit.jupiter.api.extension.ExtensionContext; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import java.util.Collection; | |
// @ExtendWith(TestSpanProvider.class)でExtension設定しないといけないので面倒なので | |
// こっちはCloseableResourceを使ったサンプルとして置いておく。 | |
public class TestSpanProvider implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback { | |
static class OtelSdkResource implements ExtensionContext.Store.CloseableResource { | |
public final OpenTelemetrySdk sdk; | |
public OtelSdkResource(OpenTelemetrySdk sdk) { | |
this.sdk = sdk; | |
} | |
public void init() { | |
GlobalOpenTelemetry.set(sdk); | |
} | |
@Override | |
public void close() throws Throwable { | |
sdk.close(); | |
GlobalOpenTelemetry.resetForTest(); | |
} | |
} | |
static class Slf4jSpanExporter implements SpanExporter { | |
private static final Logger log = LoggerFactory.getLogger(Slf4jSpanExporter.class); | |
@Override | |
@SuppressWarnings("Slf4jPlaceholderMismatch") | |
public CompletableResultCode export(Collection<SpanData> collection) { | |
collection.forEach(spanData -> log.info("span", StructuredArguments.kv("span", spanData))); | |
return CompletableResultCode.ofSuccess(); | |
} | |
@Override | |
public CompletableResultCode flush() { | |
return CompletableResultCode.ofSuccess(); | |
} | |
@Override | |
public CompletableResultCode shutdown() { | |
return CompletableResultCode.ofSuccess(); | |
} | |
} | |
@Override | |
public void beforeAll(ExtensionContext context) { | |
context.getStore(ExtensionContext.Namespace.GLOBAL) | |
.getOrComputeIfAbsent(TestSpanProvider.class.getName(), s -> { | |
OpenTelemetrySdk sdk = OpenTelemetrySdk.builder() | |
.setTracerProvider( | |
SdkTracerProvider.builder() | |
.addSpanProcessor( | |
SimpleSpanProcessor.create(new Slf4jSpanExporter()) | |
) | |
.build() | |
) | |
.build(); | |
OtelSdkResource resource = new OtelSdkResource(sdk); | |
resource.init(); | |
return resource; | |
}, OtelSdkResource.class); | |
} | |
@Override | |
public void beforeEach(ExtensionContext context) { | |
OtelSdkResource resource = context.getStore(ExtensionContext.Namespace.GLOBAL) | |
.get(TestSpanProvider.class.getName(), OtelSdkResource.class); | |
Tracer tracer = resource.sdk.getTracer(TestSpanProvider.class.getName()); | |
Span span = tracer.spanBuilder(context.getDisplayName()).startSpan(); | |
context.getStore(ExtensionContext.Namespace.create(context.getUniqueId())) | |
.put(Span.class, span); | |
} | |
@Override | |
public void afterEach(ExtensionContext context) throws Exception { | |
Span span = context.getStore(ExtensionContext.Namespace.create(context.getUniqueId())) | |
.get(Span.class, Span.class); | |
span.end(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment