Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Distributed Tracing with OpenTelemetry and OSGi

TBD

Spring XML-Configuration

TBD

Accessing OpenTelemetry APIs

The Apache CXF  abstracts as much of the tracer-specific APIs behind TracerContext as possible. However, sometimes there is a need to get access to OpenTelemetry APIs in order to leverages the rich set of available instrumentations. To make it possible, TracerContext has a dedicated unwrap method which returns underlying Tracer instance. The snippet below shows off how to use this API and use OpenTelemetry instrumentation for OpenFeign client through OpenTracing shim (as of today, OpenFeign client does not provide native support for OpenTelemetry).

Most of the distributed tracers compatible with OpenTelemetry API could be deployed into OSGi container and as such, the integration is fully available for Apache CXF services running inside the container. For a complete example please take a look on jax_rs_tracing_opentelemetry_osgi sample project, but here is the typical OSGi  Blueprint snippet in case logging span exporter.

Code Block
xml
xml
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cxf="http://cxf.apache.org/blueprint/core"
       xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs"

       xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
                           http://cxf.apache.org/blueprint/core http://cxf.apache.org/schemas/blueprint/core.xsd
      
Code Block
javajava
final SdkTracerProvider sdkTracerProvider = SdkTracerProvider
    .builder()
    ...
    .build();

final OpenTelemetry openTelemetry = OpenTelemetrySdk
    .builder()
    .setTracerProvider(sdkTracerProvider)
    ...
    .buildAndRegisterGlobal();

// Use OpenTelemetru OpenTracing shim for Feign OpenTracing integration
GlobalTracer.registerIfAbsent(OpenTracingShim.createTracerShim(openTelemetry));
Code Block
javajava
@GET
@Path("/search")
@Produces(MediaType.APPLICATION_JSON)
public JsonObject search(@QueryParam("q") final String query, @Context final TracerContext tracing) throws Exception {
    final GoogleBooksApi api = Feign.builder()
        .client(new TracingClient(new ApacheHttpClient(), tracing.unwrap(Tracer.class)))
        .target(GoogleBooksApi.class, "https://www.googleapis.com");
 
    final Response response = api.search(query);
    try (final Reader reader = response.body().asReader()) {http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsd">

    <bean id="tracingFeature" class="org.apache.cxf.tracing.opentelemetry.jaxrs.OpenTelemetryFeature">
        return Json.createReader(reader).readObject();
    }
}

The usage of tracer-specific APIs is not generally advisable (because of portability reasons) but in case there are no other options available, it is available.

Using non-JAX-RS clients

The  Apache CXF  uses native OpenTelemetry capabilities so the existing instrumentations for different HTTP clients work as expected. The usage of only JAX-RS client is not required. For example, the following snippet demonstrates the usage of traceble OkHttp client  to call JAX-RS resources, backed by Apache CXF and OpenTelemetry OkHttp3 instrumentation.

...

<argument index="0">
            <bean factory-ref="withPropagators" factory-method="build"/>
        </argument>
        <argument index="1" value="tracer" />
    </bean>

    <bean id="openTelemetrySdk" class="io.opentelemetry.sdk.OpenTelemetrySdk" factory-method="builder" />
    <bean id="sdkTracerProvider" class="io.opentelemetry.sdk.trace.SdkTracerProvider" factory-method="builder" />

    <bean id="withSampler" factory-ref="sdkTracerProvider" factory-method="setSampler">
        <argument index="0">
            <bean class="io.opentelemetry.sdk.trace.samplers.Sampler" factory-method="alwaysOn" />
        </argument>
    </bean>

    <bean id="processor" class="io.opentelemetry.sdk.trace.export.BatchSpanProcessor" factory-method="builder">
        <argument index="0">
            <bean class="io.opentelemetry.exporter.logging.LoggingSpanExporter" factory-method="create" />
        </argument>
    </bean>

    <bean id="withSpanProcessor" factory-ref="sdkTracerProvider" factory-method="addSpanProcessor">
        <argument index="0">
            <bean factory-ref="processor" factory-method="build"/>
        </argument>
    </bean>

    <bean id="withTracerProvider" factory-ref="openTelemetrySdk" factory-method="setTracerProvider">
        <argument index="0">
            <bean factory-ref="withSpanProcessor" factory-method="build"/>
        </argument>
    </bean>

    <bean id="withPropagators" factory-ref="withTracerProvider" factory-method="setPropagators">
        <argument index="0">
            <bean class="io.opentelemetry.context.propagation.ContextPropagators" factory-method="create">
                <argument index="0">
                    <bean class="io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator" factory-method="getInstance" /> 
                </argument>
            </bean>
        </argument>
    </bean>

    <cxf:bus>
        <cxf:features>
            <cxf:logging />
        </cxf:features>
    </cxf:bus>

    <jaxrs:server id="catalogServer" address="/">
        <jaxrs:serviceBeans>
            ...
        </jaxrs:serviceBeans>
        <jaxrs:providers>
            <ref component-id="tracingFeature" />
        </jaxrs:providers>
    </jaxrs:server>

</blueprint>


Info

As of now, OpenTelemetry does not provide dedicated OSGi bundles and the service loader mechanism is not working very well. It is very likely that you may need to declare own span exporters.

Accessing OpenTelemetry APIs

The Apache CXF  abstracts as much of the tracer-specific APIs behind TracerContext as possible. However, sometimes there is a need to get access to OpenTelemetry APIs in order to leverages the rich set of available instrumentations. To make it possible, TracerContext has a dedicated unwrap method which returns underlying Tracer instance. The snippet below shows off how to use this API and use OpenTelemetry instrumentation for OpenFeign client through OpenTracing shim (as of today, OpenFeign client does not provide native support for OpenTelemetry).

Code Block
java
java
final SdkTracerProvider sdkTracerProvider = SdkTracerProvider
    .builder()
    ...
    .build();

final OpenTelemetry openTelemetry = OpenTelemetrySdk
    .builder()
    .setTracerProvider(sdkTracerProvider)
    ...
    .buildAndRegisterGlobal();

// Use OpenTelemetru OpenTracing shim for Feign OpenTracing integration
GlobalTracer.registerIfAbsent(OpenTracingShim.createTracerShim(openTelemetry));


Code Block
java
java
@GET
@Path("/search")
@Produces(MediaType.APPLICATION_JSON)
public JsonObject search(@QueryParam("q") final String query, @Context final TracerContext tracing) throws Exception {
    final GoogleBooksApi api = Feign.builder()
        .client(new TracingClient(new ApacheHttpClient(), tracing.unwrap(Tracer.class)))
        .target(GoogleBooksApi.class, "https://www.googleapis.com");
 
    final Response response = api.search(query);
    try (final Reader reader = response.body().asReader()) {
        return Json.createReader(reader).readObject();
    }
}

The usage of tracer-specific APIs is not generally advisable (because of portability reasons) but in case there are no other options available, it is available.

Using non-JAX-RS clients

The  Apache CXF  uses native OpenTelemetry capabilities so the existing instrumentations for different HTTP clients work as expected. The usage of only JAX-RS client is not required. For example, the following snippet demonstrates the usage of traceble OkHttp client  to call JAX-RS resources, backed by Apache CXF and OpenTelemetry OkHttp3 instrumentation.

Code Block
java
java
final SdkTracerProvider sdkTracerProvider = SdkTracerProvider
    .builder()
    ...
    .build();

final OpenTelemetry openTelemetry = OpenTelemetrySdk
    .builder()
    .setTracerProvider(sdkTracerProvider)
    ...
    .buildAndRegisterGlobal();

final OkHttpClient client = OkHttpClient.Builder().build();
final Call.Factory factory =  OkHttpTelemetry.builder(openTelemetry).build().newCallFactory(client));

final Request request = new Request.Builder()
    .url("http://localhost:9000/catalog")
    .header("Accept", "application/json")
    .build();
 
try (final Response response = factory.newCall(request).execute()) {
    // Do something with response.body()
}

Samples