Skip to content

Commit

Permalink
Support OpenTelemetry End User attributes added as Span attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
michalvavrik committed May 7, 2024
1 parent fb12a4d commit b4a152e
Show file tree
Hide file tree
Showing 22 changed files with 1,245 additions and 12 deletions.
19 changes: 19 additions & 0 deletions docs/src/main/asciidoc/opentelemetry.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,25 @@ public class CustomConfiguration {
}
----

==== End User attributes

When enabled, Quarkus adds OpenTelemetry End User attributes as Span attributes.

Check warning on line 403 in docs/src/main/asciidoc/opentelemetry.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'.", "location": {"path": "docs/src/main/asciidoc/opentelemetry.adoc", "range": {"start": {"line": 403, "column": 13}}}, "severity": "INFO"}
The attributes are only added when authentication has already happened on a best-efforts basis.
Before you enable this feature, verify that Quarkus Security extension is present and configured.
More information about the Quarkus Security can be found in the xref:security-overview.adoc[Quarkus Security overview].

[source,application.properties]
----
quarkus.otel.traces.eusp.enabled=true <1>
quarkus.http.auth.proactive=true <2>
----
<1> Enable the End User Attributes feature so that the `SecurityIdentity` principal and roles are added as Span attributes.
The End User attributes are personally identifiable information, therefore make sure you want to export them before you enable this feature.

Check warning on line 414 in docs/src/main/asciidoc/opentelemetry.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsWarnings] Consider using 'verify' rather than 'make sure' unless updating existing content that uses the term. Raw Output: {"message": "[Quarkus.TermsWarnings] Consider using 'verify' rather than 'make sure' unless updating existing content that uses the term.", "location": {"path": "docs/src/main/asciidoc/opentelemetry.adoc", "range": {"start": {"line": 414, "column": 27}}}, "severity": "WARNING"}
<2> Optionally enable proactive authentication.
The best possible results are achieved when proactive authentication is enabled because the authentication happens sooner.

IMPORTANT: This feature is not supported when a custom xref:security-customization.adoc#jaxrs-security-context[Jakarta REST SecurityContexts] is used.

[[sampler]]
=== Sampler
A https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampling[sampler] decides whether a trace should be discarded or forwarded, effectively managing noise and reducing overhead by limiting the number of collected traces sent to the collector.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package io.quarkus.opentelemetry.deployment.tracing;

import static io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig.SecurityEvents.SecurityEventType.ALL;
import static io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig.SecurityEvents.SecurityEventType.AUTHENTICATION_SUCCESS;
import static io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig.SecurityEvents.SecurityEventType.AUTHORIZATION_FAILURE;
import static io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig.SecurityEvents.SecurityEventType.AUTHORIZATION_SUCCESS;

import java.net.URL;
import java.util.ArrayList;
Expand All @@ -11,6 +14,7 @@
import java.util.Set;
import java.util.function.BooleanSupplier;

import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.inject.spi.EventContext;

import org.jboss.jandex.AnnotationInstance;
Expand All @@ -21,12 +25,16 @@
import org.jboss.jandex.MethodInfo;
import org.jboss.logging.Logger;

import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.IdGenerator;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import io.quarkus.arc.Unremovable;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.arc.deployment.ObserverRegistrationPhaseBuildItem;
Expand All @@ -49,6 +57,7 @@
import io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig.SecurityEvents.SecurityEventType;
import io.quarkus.opentelemetry.runtime.tracing.TracerRecorder;
import io.quarkus.opentelemetry.runtime.tracing.cdi.TracerProducer;
import io.quarkus.opentelemetry.runtime.tracing.security.EndUserSpanProcessor;
import io.quarkus.opentelemetry.runtime.tracing.security.SecurityEventUtil;
import io.quarkus.vertx.http.deployment.spi.FrameworkEndpointsBuildItem;
import io.quarkus.vertx.http.deployment.spi.StaticResourcesBuildItem;
Expand Down Expand Up @@ -198,6 +207,70 @@ void registerSecurityEventObserver(Capabilities capabilities, OTelBuildConfig bu
}
}

/**
* Generates {@link SpanProcessor} that adds end-user attributes to new Spans.
*
* Generates:
*
* <code>
* import io.quarkus.arc.Unremovable;
* import io.quarkus.opentelemetry.runtime.tracing.security.SecurityEventUtil;
* import jakarta.enterprise.context.Dependent;
*
* import io.opentelemetry.context.Context;
* import io.opentelemetry.sdk.trace.ReadWriteSpan;
* import io.opentelemetry.sdk.trace.ReadableSpan;
* import io.opentelemetry.sdk.trace.SpanProcessor;
*
* &#064;Dependent
* &#064;Unremovable
* public class EndUserSpanProcessor implements SpanProcessor {
*
* &#064;Override
* public void onStart(Context parentContext, ReadWriteSpan span) {
* SecurityEventUtil.addEndUserAttributes(span);
* }
*
* &#064;Override
* public boolean isStartRequired() {
* return Boolean.TRUE;
* }
*
* &#064;Override
* public void onEnd(ReadableSpan span) {
* }
*
* &#064;Override
* public boolean isEndRequired() {
* return Boolean.FALSE;
* }
*
* }
* </code>
*
*/
@BuildStep(onlyIf = EndUserAttributesEnabled.class)
void addEndUserAttributesSpanProcessor(BuildProducer<AdditionalBeanBuildItem> additionalBeanProducer,
Capabilities capabilities) {
if (capabilities.isPresent(Capability.SECURITY)) {
additionalBeanProducer.produce(AdditionalBeanBuildItem.unremovableOf(EndUserSpanProcessor.class));
}
}

@BuildStep(onlyIf = EndUserAttributesEnabled.class)
void registerEndUserAttributesEventObserver(Capabilities capabilities,
ObserverRegistrationPhaseBuildItem observerRegistrationPhase,
BuildProducer<ObserverConfiguratorBuildItem> observerProducer) {
if (capabilities.isPresent(Capability.SECURITY)) {
observerProducer
.produce(createEventObserver(observerRegistrationPhase, AUTHENTICATION_SUCCESS, "addEndUserAttributes"));
observerProducer
.produce(createEventObserver(observerRegistrationPhase, AUTHORIZATION_SUCCESS, "updateEndUserAttributes"));
observerProducer
.produce(createEventObserver(observerRegistrationPhase, AUTHORIZATION_FAILURE, "updateEndUserAttributes"));
}
}

private static ObserverConfiguratorBuildItem createEventObserver(
ObserverRegistrationPhaseBuildItem observerRegistrationPhase, SecurityEventType eventType, String utilMethodName) {
return new ObserverConfiguratorBuildItem(observerRegistrationPhase.getContext()
Expand Down Expand Up @@ -232,4 +305,18 @@ public boolean getAsBoolean() {
return enabled;
}
}

static final class EndUserAttributesEnabled implements BooleanSupplier {

private final boolean enabled;

EndUserAttributesEnabled(OTelBuildConfig config) {
this.enabled = config.traces().addEndUserAttributes();
}

@Override
public boolean getAsBoolean() {
return enabled;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import io.quarkus.runtime.annotations.ConfigGroup;
import io.smallrye.config.WithDefault;
import io.smallrye.config.WithName;

/**
* Tracing build time configuration
Expand Down Expand Up @@ -51,4 +52,15 @@ public interface TracesBuildConfig {
*/
@WithDefault(SamplerType.Constants.PARENT_BASED_ALWAYS_ON)
String sampler();

/**
* If OpenTelemetry End User attributes should be added as Span attributes on a best-efforts basis.
*
* @see <a href="https://opentelemetry.io/docs/specs/semconv/attributes-registry/enduser/">OpenTelemetry End User
* attributes</a>
*/
@WithName("eusp.enabled")
@WithDefault("false")
boolean addEndUserAttributes();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.quarkus.opentelemetry.runtime.tracing.security;

import jakarta.enterprise.context.Dependent;

import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;

/**
* Main purpose of this processor is to cover adding of the End User attributes to user-created Spans.
*/
@Dependent
public class EndUserSpanProcessor implements SpanProcessor {

@Override
public void onStart(Context context, ReadWriteSpan span) {
SecurityEventUtil.addEndUserAttributes(span);
}

@Override
public boolean isStartRequired() {
return true;
}

@Override
public void onEnd(ReadableSpan readableSpan) {

}

@Override
public boolean isEndRequired() {
return false;
}
}
Loading

0 comments on commit b4a152e

Please sign in to comment.