Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Observation otel IT #297

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ private static Severity toSeverity(final Level level) {
}

public static void install(final OpenTelemetry openTelemetry) {
Logger logger = openTelemetry.getLogsBridge().loggerBuilder(OpenTelemetryConfig.INSTRUMENTATION_NAME).build();
Logger logger = openTelemetry.getLogsBridge()
.loggerBuilder(OpenTelemetryConfig.INSTRUMENTATION_NAME)
.setInstrumentationVersion(OpenTelemetryConfig.INSTRUMENTATION_VERSION)
.build();
LogManager.getLogManager().getLogger("").addHandler(new OpenTelemetryHandler(logger));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class HistogramTest {
void histogramTest() {
manualHistogramBean.recordHistogram();

MetricData testSummary = exporter.getFinishedHistogramItem("testSummary", 4);
MetricData testSummary = exporter.getLastFinishedHistogramItem("testSummary", 4);
assertNotNull(testSummary);
assertThat(testSummary)
.hasDescription("This is a test distribution summary")
Expand Down
8 changes: 0 additions & 8 deletions implementation/observation-otel-bridge/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,6 @@
<artifactId>smallrye-opentelemetry-observation-otel-bridge</artifactId>
<name>SmallRye OpenTelemetry: Observation to OpenTelemetry bridge</name>

<properties>
<micrometer-docs-generator.version>1.0.2</micrometer-docs-generator.version>
<micrometer-docs-generator.inputPath>${project.build.sourceDirectory}</micrometer-docs-generator.inputPath>
<micrometer-docs-generator.inclusionPattern>.*</micrometer-docs-generator.inclusionPattern>
<micrometer-docs-generator.outputPath>${project.build.directory}/observation-docs/
</micrometer-docs-generator.outputPath>
</properties>

<dependencies>
<dependency>
<groupId>io.smallrye.opentelemetry</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@
import jakarta.enterprise.inject.spi.Extension;
import jakarta.enterprise.util.Nonbinding;

import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.annotation.Observed;

public class ObservationExtension implements Extension {
public void beforeBeanDiscovery(@Observes BeforeBeanDiscovery beforeBeanDiscovery, BeanManager beanManager) {
beforeBeanDiscovery.addInterceptorBinding(
new ObservedAnnotatedType(beanManager.createAnnotatedType(Observed.class)));

beforeBeanDiscovery.addAnnotatedType(ObservationRegistry.class, ObservationRegistry.class.getName());
beforeBeanDiscovery.addAnnotatedType(ObservationRegistryProducer.class, ObservationRegistryProducer.class.getName());
}

public void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.smallrye.opentelemetry.instrumentation.observation;
package io.smallrye.opentelemetry.instrumentation.observation.cdi;

import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;
Expand All @@ -13,6 +13,7 @@
import io.smallrye.opentelemetry.instrumentation.observation.handler.OpenTelemetryObservationHandler;
import io.smallrye.opentelemetry.instrumentation.observation.handler.PropagatingReceiverTracingObservationHandler;
import io.smallrye.opentelemetry.instrumentation.observation.handler.PropagatingSenderTracingObservationHandler;
import io.smallrye.opentelemetry.instrumentation.observation.handler.TracingAwareMeterObservationHandler;

@Singleton
public class ObservationRegistryProducer {
Expand All @@ -23,15 +24,12 @@ public class ObservationRegistryProducer {
OpenTelemetry openTelemetry;

@Inject
OpenTelemetryObservationHandler openTelemetryObservationHandler;

@Inject
MeterRegistry registry;
MeterRegistry meterRegistry;

@Produces
@Singleton
public ObservationRegistry registry() {
ObservationRegistry observationRegistry = ObservationRegistry.create();
final ObservationRegistry observationRegistry = ObservationRegistry.create();

observationRegistry.observationConfig()
// .observationFilter(new CloudObservationFilter()) // Where global filters go
Expand All @@ -41,9 +39,16 @@ public ObservationRegistry registry() {
openTelemetry.getPropagators().getTextMapPropagator()),
new PropagatingReceiverTracingObservationHandler(tracer,
openTelemetry.getPropagators().getTextMapPropagator()),
// new TracingAwareMeterObservationHandler(tracer) // For exemplars... Maybe not be needed
openTelemetryObservationHandler))
.observationHandler(new DefaultMeterObservationHandler(registry));
new OpenTelemetryObservationHandler(tracer)))
// todo duplicate the tracer strategy for baggage, adding a condition to bypass when no baggage is present
// todo just implement the receiver and sender handlers
// todo. Alternatively we can split in 2 the tracing handlers, one to create spans (in the current .observationHandler(new ObservationHandler.FirstMatchingCompositeObservationHandler )
// todo. A new .observationHandler bloc to process the baggage on the receiver side.
// todo. Another to propagate the context in a new .observationHandler )
// todo. We assume on the receiver side we open and close the baggage once because it should have just 1 scope app wide and the
// todo. user will use the baggage api itself. We are just making sure we don't break the propagation to the user.
.observationHandler(
Comment on lines +42 to +49
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this ok?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It means it doesn't provide baggage propagation through the Observation API. The OTel API can be used instead...
Implementing this is not trivial and the comment explains the possible approach.

new TracingAwareMeterObservationHandler(new DefaultMeterObservationHandler(meterRegistry), tracer));
// .observationHandler(new PrintOutHandler()) // Can be implemented for debugging. Other handlers for future frameworks can also be added.
return observationRegistry;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import static io.opentelemetry.semconv.SemanticAttributes.NET_SOCK_PEER_ADDR;
import static io.opentelemetry.semconv.SemanticAttributes.NET_SOCK_PEER_PORT;
import static io.opentelemetry.semconv.SemanticAttributes.PEER_SERVICE;
import static io.smallrye.opentelemetry.instrumentation.observation.handler.HandlerUtil.HIGH_CARD_ATTRIBUTES;
import static io.smallrye.opentelemetry.instrumentation.observation.handler.HandlerUtil.LOW_CARD_ATTRIBUTES;

import java.net.URI;
import java.util.logging.Logger;
Expand Down Expand Up @@ -108,16 +110,37 @@ protected Span getParentSpan(T context) {
return null;
}

@SuppressWarnings("unchecked")
protected void tagSpan(T context, Span span) {
final Attributes highCardAttributes = context.get(HIGH_CARD_ATTRIBUTES);
setOtelAttributes(span, highCardAttributes);

final Attributes lowCardAttributes = context.get(LOW_CARD_ATTRIBUTES);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these always set, no matter what?

Copy link
Contributor Author

@brunobat brunobat Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The low cardinality ones, yes.
The high cardinality are only used in traces.
This is there to go around a limitation of the Observation API... Currently, It only supports storage of Strings. Will file a feature request there.

setOtelAttributes(span, lowCardAttributes);

for (KeyValue keyValue : context.getAllKeyValues()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it contain keys from HIGH_CARD_ATTRIBUTESand LOW_CARD_ATTRIBUTES?

Copy link
Contributor Author

@brunobat brunobat Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, but originally stored in 2 different structures.

if (!keyValue.getKey().equalsIgnoreCase("ERROR")) {
span.setAttribute(keyValue.getKey(), keyValue.getValue());

} else {
span.recordException(new RuntimeException(keyValue.getValue()));
}
}
}

private void setOtelAttributes(Span span, Attributes contextAttributes) {
if (contextAttributes != null) {
contextAttributes.forEach((key, value) -> {
// FIXME this is a bit of a hack because KeyValue only allows String values
if (key.getKey().equalsIgnoreCase("ERROR")) {
span.recordException(new RuntimeException(value.toString()));
} else {
span.setAttribute((AttributeKey<Object>) key, value);
}
});
}
}

protected SpanBuilder remoteSpanBuilder(Kind kind,
String remoteServiceName,
String remoteServiceAddress,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.smallrye.opentelemetry.instrumentation.observation.handler;

public class HandlerUtil {
public static final String LOW_CARD_ATTRIBUTES = "low_card_attributes";
public static final String HIGH_CARD_ATTRIBUTES = "high_card_attributes";
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,21 @@

import java.util.logging.Logger;

import jakarta.inject.Inject;
import jakarta.inject.Singleton;

import io.micrometer.common.util.StringUtils;
import io.micrometer.observation.Observation;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.smallrye.opentelemetry.instrumentation.observation.context.TracingObservationContext;

@Singleton
public class OpenTelemetryObservationHandler extends AbstractTracingObservationHandler<Observation.Context> {

private static final Logger logger = Logger.getLogger(OpenTelemetryObservationHandler.class.getName());
private final Tracer tracer;

@Inject
Tracer tracer;
public OpenTelemetryObservationHandler(Tracer tracer) {
this.tracer = tracer;
}

@Override
public void onStart(Observation.Context context) {
Expand Down Expand Up @@ -52,7 +50,8 @@ private Span nextSpan(Tracer tracer, Span parent) {
}

private Span nextSpan(Tracer tracer) {
return tracer.spanBuilder("").startSpan();
return tracer.spanBuilder("")
.startSpan();
}

private String getSpanName(Observation.Context context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public String get(C carrier, String key) {
return getter.get(carrier, key);
}
});
// extracted.makeCurrent(); // this actually fixes the baggage test
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this ok?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, if you uncomment, baggage propagation will be fixed, is there for reference... We can remove these comments but I will need to document these things somewhere, maybe on a README... What do you think?

return tracer.spanBuilder("receiver").setParent(extracted);// FIXME the name needs to be set
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public PropagatingSenderTracingObservationHandler(Tracer tracer, TextMapPropagat
public void onStart(T context) {
Span childSpan = createSenderSpan(context);
try (Scope scope = childSpan.makeCurrent()) {
// todo this code could also be used for the baggage:
this.propagator.inject(Context.current(), context.getCarrier(),
(carrier, key, value) -> context.getSetter().set(carrier, key, value));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.smallrye.opentelemetry.instrumentation.observation.handler;

import io.micrometer.core.instrument.observation.MeterObservationHandler;
import io.micrometer.observation.Observation;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
import io.smallrye.opentelemetry.instrumentation.observation.context.TracingObservationContext;

public class TracingAwareMeterObservationHandler<T extends Observation.Context> implements MeterObservationHandler<T> {

private final MeterObservationHandler<T> delegate;

private final Tracer tracer;

/**
* Creates a new instance of {@link TracingAwareMeterObservationHandler}.
*
* @param delegate a {@link MeterObservationHandler} delegate
* @param tracer tracer
*/
public TracingAwareMeterObservationHandler(MeterObservationHandler<T> delegate, Tracer tracer) {
this.delegate = delegate;
this.tracer = tracer;
}

@Override
public void onStart(T context) {
this.delegate.onStart(context);
}

@Override
public void onError(T context) {
this.delegate.onError(context);
}

@Override
public void onEvent(Observation.Event event, T context) {
this.delegate.onEvent(event, context);
}

@Override
public void onScopeOpened(T context) {
this.delegate.onScopeOpened(context);
}

@Override
public void onScopeClosed(T context) {
this.delegate.onScopeClosed(context);
}

@Override
public void onStop(T context) {
TracingObservationContext tracingContext = context
.getRequired(TracingObservationContext.class);
Span currentSpan = tracingContext.getSpan();
if (currentSpan != null) {
try (Scope scope = currentSpan.makeCurrent()) {
this.delegate.onStop(context);
}
} else {
this.delegate.onStop(context);
}
}

@Override
public boolean supportsContext(Observation.Context context) {
return this.delegate.supportsContext(context);
}

}
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
otel.logs.exporter=none
#otel.logs.exporter=none
otel.metric.export.interval=1000
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,13 @@
import io.smallrye.opentelemetry.implementation.cdi.OpenTelemetryExtension;
import io.smallrye.opentelemetry.implementation.config.OpenTelemetryConfigProducer;
import io.smallrye.opentelemetry.implementation.micrometer.cdi.MicrometerExtension;
import io.smallrye.opentelemetry.instrumentation.observation.ObservationRegistryProducer;
import io.smallrye.opentelemetry.instrumentation.observation.cdi.ObservationExtension;
import io.smallrye.opentelemetry.instrumentation.observation.handler.OpenTelemetryObservationHandler;
import io.smallrye.opentelemetry.test.InMemoryExporter;
import io.smallrye.opentelemetry.test.InMemoryExporterProducer;

@EnableAutoWeld
@AddExtensions({ OpenTelemetryExtension.class, ConfigExtension.class, ObservationExtension.class, MicrometerExtension.class })
@AddBeanClasses({ OpenTelemetryConfigProducer.class, ObservationRegistryProducer.class, OpenTelemetryObservationHandler.class,
@AddBeanClasses({ OpenTelemetryConfigProducer.class,
InMemoryExporter.class, InMemoryExporterProducer.class })
class ObservationOTelTest {
@Inject
Expand Down Expand Up @@ -174,7 +172,7 @@ void testObservationWithDefaults() {
attributeEntry("code.namespace",
"io.smallrye.opentelemetry.implementation.observation.ObservationOTelTest$ObservationSpan"))));

MetricData methodObserved = exporter.getFinishedHistogramItem("method.observed", 1);
MetricData methodObserved = exporter.getLastFinishedHistogramItem("method.observed", 1);
assertThat(methodObserved)
.hasUnit("ms")
.hasHistogramSatisfying(hist -> hist.isCumulative().hasPointsSatisfying(points -> points.hasCount(1)
Expand Down
45 changes: 45 additions & 0 deletions implementation/rest-observation/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.smallrye.opentelemetry</groupId>
<artifactId>smallrye-opentelemetry-parent</artifactId>
<version>2.8.2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

<artifactId>smallrye-opentelemetry-rest-observation</artifactId>
<name>SmallRye OpenTelemetry: REST Observation</name>

<dependencies>
<dependency>
<groupId>io.smallrye.opentelemetry</groupId>
<artifactId>smallrye-opentelemetry-api</artifactId>
</dependency>
<dependency>
<groupId>io.smallrye.opentelemetry</groupId>
<artifactId>smallrye-opentelemetry-micrometer-otel-bridge</artifactId>
</dependency>
<dependency>
<groupId>io.smallrye.opentelemetry</groupId>
<artifactId>smallrye-opentelemetry-observation-otel-bridge</artifactId>
</dependency>

<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>io.opentelemetry.semconv</groupId>
<artifactId>opentelemetry-semconv</artifactId>
</dependency>
</dependencies>
</project>
Loading
Loading