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.