OpenTelemetry Integration

Introduction

Goal

Use OpenTelemetry to collect distributed traces from your Bloomreach Experience Manager CMS application and send them to an observability backend such as Honeycomb, Jaeger, Grafana, or Dynatrace. Only traces are supported; metrics and logs export are not part of this integration.

What is OpenTelemetry

OpenTelemetry is an open-source observability framework maintained by the CNCF (Cloud Native Computing Foundation). It provides a standard way to collect and export telemetry data — traces, metrics, and logs — from applications.

In Bloomreach Experience Manager, OpenTelemetry is supported through the standard Java auto-instrumentation agent. The agent attaches to the JVM at startup and automatically creates trace spans for servlets, JDBC calls, JAX-RS endpoints, and HTTP clients — without any code changes.

On top of this auto-instrumentation, Bloomreach Experience Manager enriches traces with CMS-specific context: the Wicket UI action that triggered the request, the logged-in CMS user, and — for slow requests — a full breakdown of the internal diagnostic task tree. This enrichment bridges the existing CMS Diagnostics feature with OpenTelemetry, so the same data that appears in the diagnostic console log is also available in your observability backend.

The agent's presence is the switch: attach it to get traces, remove it to return to zero overhead. No code changes, no configuration flags.

Default Configuration

With the default configuration, trace data is collected via auto-instrumentation for supported Java libraries and frameworks, along with brXM-specific enrichment.

The default configuration is deliberately conservative. It disables JDBC instrumentation, metrics, and logs export out of the box to minimize overhead and reduce trace data volume. These settings are applied automatically by the otel-auto-instrumentation Maven profile for local development. For production deployments, configure these properties manually as described in the Production Deployment section.

Setting Default
otel.instrumentation.jdbc.enabled false
otel.metrics.exporter none
otel.instrumentation.log4j-appender.enabled false
otel.resource.disabled.keys process.command_args
otel.instrumentation.common.experimental.controller-telemetry.enabled true

What Traces Include

Traces contain three layers of data:

1. OTel Auto-Instrumentation

The Java agent automatically creates spans for servlets, JAX-RS endpoints, HTTP clients, and other standard frameworks. This is what you get out of the box by attaching the agent — no code changes required.

2. CMS-Specific Enrichment

Each CMS request is enriched with the logged-in CMS user, the project version, and — for Ajax requests — the Wicket UI action that triggered it (e.g., which button or link the user clicked). The UI action appears as the hdc.brxm_ui_trace span attribute, connecting backend traces to specific user interactions.

3. CMS Diagnostics (HDC Task Tree)

The internal CMS diagnostic task tree is automatically exported as child spans when the OTel agent is attached. This is independent from the CMS Diagnostics console logging feature — you do not need to enable CMS Diagnostics to get OTel traces. The thresholdMillisec property (default: 3000 ms) controls which requests produce the detailed subtask breakdown.

Running Locally with an Archetype Project

Step 1: Configure Your Backend

Create or edit conf/platform-dev.properties in your project root:

otel.service.name=my-project-cms
otel.exporter.otlp.endpoint=https://api.eu1.honeycomb.io
otel.exporter.otlp.headers=x-honeycomb-team=YOUR_API_KEY
otel.resource.attributes=deployment.environment=local-dev

Replace my-project-cms with a service name of your choice. These four properties are the minimum required:

Refer to your observability backend's documentation for the correct endpoint, headers, and any additional configuration.

Step 2: Run with the OTel Maven Profile

mvn clean verify && mvn -P cargo.run,otel-auto-instrumentation

The otel-auto-instrumentation profile downloads the OTel Java agent JAR, reads properties from conf/platform-dev.properties, passes them as JVM system properties, and attaches the agent via -javaagent:.

Properties defined in conf/platform-dev.properties can be overridden with -D on the Maven command line. To pass additional properties not defined in the profile, use cargo.jvm.args:

mvn -P cargo.run,otel-auto-instrumentation -Dcargo.jvm.args="-Dmy.custom.property=value"

For full control over all settings, copy the otel-auto-instrumentation profile from the parent POM into your project's POM and modify it directly.

Step 3: Verify Agent Attachment

On startup, check cms.log for the following message:

WARN  OpenTelemetry: Java agent detected. service.name=my-project-cms,
  endpoint=https://api.eu1.honeycomb.io,
  resource.attributes=[service.version=17.0.0,deployment.environment=local],
  sampler=parentbased_always_on (default), jdbc=false

If this message does not appear, verify that you included the otel-auto-instrumentation profile in your Maven command.

Step 4: Generate and View Traces

Open the CMS at http://localhost:8080/cms/, perform some actions (browse documents, edit, publish), then check your observability backend for traces with the service name you configured.

Customizing Your Codebase

Using Traceable Wicket Components

When building custom Wicket components with Ajax interactions, extend the traceable base classes to automatically include UI action names in traces. The action name appears as the hdc.brxm_ui_trace span attribute, making it easy to identify which user action triggered a request.

Instead of Use
AjaxLink TraceableAjaxLink
AjaxButton TraceableAjaxButton
AjaxEventBehavior TraceableAjaxEventBehavior

The default implementation auto-generates an action name from the component's label, model, or class name. Override getActionName() if you need a more descriptive name:

new TraceableAjaxLink<Void>("myAction") {
    @Override
    public void onClick(AjaxRequestTarget target) {
        // handle click
    }

    @Override
    protected String getActionName() {
        return "custom:my-special-action";
    }
};

Adding Custom OTel Spans

Use the standard OpenTelemetry API to add custom spans.

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;

Tracer tracer = GlobalOpenTelemetry.get().getTracer("my-project");
Span span = tracer.spanBuilder("processOrder")
    .setAttribute("order.id", orderId)
    .startSpan();
try (Scope scope = span.makeCurrent()) {
    // your business logic
} finally {
    span.end();
}

Custom spans automatically nest under the agent's server span and appear in your trace backend alongside auto-instrumented spans. When no agent is attached, all API calls are no-ops with zero overhead.

Production Deployment

brXM's OpenTelemetry integration uses the standard OpenTelemetry Java auto-instrumentation agent, widely adopted across the Java ecosystem for production observability. The agent attaches to the JVM at startup and collects trace data with minimal overhead.

The default configuration is conservative. This is a conscious design choice to minimize overhead, reduce trace data volume, and avoid noise from internal framework activity.

Before enabling this feature in production, first enable OpenTelemetry in a non-production environment to verify compatibility and observe resource usage in your specific deployment.

To enable OpenTelemetry in a production environment:

  • Attach the OpenTelemetry Java agent JAR to the JVM via -javaagent.

  • Configure the OTel properties as JVM system properties or environment variables. The recommended values below are based on the default configuration; adjust them according to your specific needs.

    Property Required Example
    otel.service.name Yes brxm-cms
    otel.exporter.otlp.endpoint Yes https://your-backend:4318
    otel.exporter.otlp.headers Backend-specific Authorization=Bearer TOKEN
    otel.resource.attributes Recommended deployment.environment=production
    otel.instrumentation.jdbc.enabled Recommended false
    otel.metrics.exporter Recommended none
    otel.instrumentation.log4j-appender.enabled Recommended false
    otel.resource.disabled.keys Recommended process.command_args
  • Configure a sampling strategy appropriate for your environment. Head-based sampling (configured via otel.traces.sampler) is simpler to set up, while tail-based sampling (using an OTel Collector) allows decisions based on trace outcome such as errors or latency. Refer to the OpenTelemetry documentation for guidance.

Did you find this page helpful?
How could this documentation serve you better?
On this page
    Did you find this page helpful?
    How could this documentation serve you better?