136 lines
5.1 KiB
Java
136 lines
5.1 KiB
Java
/*
|
|
* Copyright The OpenTelemetry Authors
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package com.example.javaagent;
|
|
|
|
import com.google.auto.service.AutoService;
|
|
import io.opentelemetry.api.common.AttributeKey;
|
|
import io.opentelemetry.api.common.Attributes;
|
|
import io.opentelemetry.api.common.AttributesBuilder;
|
|
import io.opentelemetry.api.metrics.LongCounter;
|
|
import io.opentelemetry.api.metrics.Meter;
|
|
import io.opentelemetry.context.Context;
|
|
import io.opentelemetry.context.ContextKey;
|
|
import io.opentelemetry.instrumentation.api.incubator.instrumenter.InstrumenterCustomizer;
|
|
import io.opentelemetry.instrumentation.api.incubator.instrumenter.InstrumenterCustomizerProvider;
|
|
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
|
|
import io.opentelemetry.instrumentation.api.instrumenter.ContextCustomizer;
|
|
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
|
|
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
|
|
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
|
|
/**
|
|
* This example demonstrates how to use the InstrumenterCustomizerProvider SPI to customize
|
|
* instrumentation behavior without modifying the core instrumentation code.
|
|
*
|
|
* <p>This customizer adds:
|
|
*
|
|
* <ul>
|
|
* <li>Custom attributes to HTTP server spans
|
|
* <li>Custom metrics for HTTP operations
|
|
* <li>Request correlation IDs via context customization
|
|
* <li>Custom span name transformation
|
|
* </ul>
|
|
*
|
|
* <p>The customizer will be automatically applied to instrumenters that match the specified
|
|
* instrumentation name and span kind.
|
|
*
|
|
* @see InstrumenterCustomizerProvider
|
|
* @see InstrumenterCustomizer
|
|
*/
|
|
@AutoService(InstrumenterCustomizerProvider.class)
|
|
public class DemoInstrumenterCustomizerProvider implements InstrumenterCustomizerProvider {
|
|
|
|
@Override
|
|
public void customize(InstrumenterCustomizer customizer) {
|
|
String instrumentationName = customizer.getInstrumentationName();
|
|
if (isHttpServerInstrumentation(instrumentationName)) {
|
|
customizeHttpServer(customizer);
|
|
}
|
|
}
|
|
|
|
private boolean isHttpServerInstrumentation(String instrumentationName) {
|
|
return instrumentationName.contains("servlet")
|
|
|| instrumentationName.contains("jetty")
|
|
|| instrumentationName.contains("tomcat")
|
|
|| instrumentationName.contains("undertow")
|
|
|| instrumentationName.contains("spring-webmvc");
|
|
}
|
|
|
|
private void customizeHttpServer(InstrumenterCustomizer customizer) {
|
|
customizer.addAttributesExtractor(new DemoAttributesExtractor());
|
|
customizer.addOperationMetrics(new DemoMetrics());
|
|
customizer.addContextCustomizer(new DemoContextCustomizer());
|
|
customizer.setSpanNameExtractor(
|
|
unused -> (SpanNameExtractor<Object>) object -> "CustomHTTP/" + object.toString());
|
|
}
|
|
|
|
/** Custom attributes extractor that adds demo-specific attributes. */
|
|
private static class DemoAttributesExtractor implements AttributesExtractor<Object, Object> {
|
|
private static final AttributeKey<String> CUSTOM_ATTR = AttributeKey.stringKey("demo.custom");
|
|
private static final AttributeKey<String> ERROR_ATTR = AttributeKey.stringKey("demo.error");
|
|
|
|
@Override
|
|
public void onStart(AttributesBuilder attributes, Context context, Object request) {
|
|
attributes.put(CUSTOM_ATTR, "demo-extension");
|
|
}
|
|
|
|
@Override
|
|
public void onEnd(
|
|
AttributesBuilder attributes,
|
|
Context context,
|
|
Object request,
|
|
Object response,
|
|
Throwable error) {
|
|
if (error != null) {
|
|
attributes.put(ERROR_ATTR, error.getClass().getSimpleName());
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Custom metrics that track request counts. */
|
|
private static class DemoMetrics implements OperationMetrics {
|
|
@Override
|
|
public OperationListener create(Meter meter) {
|
|
LongCounter requestCounter =
|
|
meter
|
|
.counterBuilder("demo.requests")
|
|
.setDescription("Number of requests")
|
|
.setUnit("requests")
|
|
.build();
|
|
|
|
return new OperationListener() {
|
|
@Override
|
|
public Context onStart(Context context, Attributes attributes, long startNanos) {
|
|
requestCounter.add(1, attributes);
|
|
return context;
|
|
}
|
|
|
|
@Override
|
|
public void onEnd(Context context, Attributes attributes, long endNanos) {
|
|
// Could add duration metrics here if needed
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
/** Context customizer that adds request correlation IDs and custom context data. */
|
|
private static class DemoContextCustomizer implements ContextCustomizer<Object> {
|
|
private static final AtomicLong requestIdCounter = new AtomicLong(1);
|
|
private static final ContextKey<String> REQUEST_ID_KEY = ContextKey.named("demo.request.id");
|
|
|
|
@Override
|
|
public Context onStart(Context context, Object request, Attributes startAttributes) {
|
|
// Generate a unique request ID for correlation
|
|
String requestId = "req-" + requestIdCounter.getAndIncrement();
|
|
|
|
// Add custom context data that can be accessed throughout the request lifecycle
|
|
context = context.with(REQUEST_ID_KEY, requestId);
|
|
return context;
|
|
}
|
|
}
|
|
}
|