OpenTelemetry Integration
Introduction
Purpose
Integrate OpenTelemetry with Bloomreach Experience Manager (XM) to collect distributed traces from your CMS application and forward them to an observability backend such as Honeycomb, Jaeger, Grafana, or Dynatrace. This integration supports trace data only; metrics and logs export are not included.
OpenTelemetry Overview
OpenTelemetry is an open-source observability framework maintained by the Cloud Native Computing Foundation (CNCF). It standardizes the collection and export of telemetry data—traces, metrics, and logs—from applications.
Bloomreach Experience Manager supports OpenTelemetry through the Java auto-instrumentation agent. The agent attaches to the JVM at startup and automatically generates trace spans for servlets, JDBC calls, JAX-RS endpoints, and HTTP clients without requiring code changes.
In addition to auto-instrumentation, Bloomreach Experience Manager enriches traces with CMS-specific context. This includes the Wicket UI action that initiated the request, the logged-in CMS user, and, for slow requests, a detailed breakdown of the internal diagnostic task tree. This enrichment extends the CMS Diagnostics feature to OpenTelemetry, making the same diagnostic data available in your observability backend.
The agent determines whether tracing is active: attach the agent to enable tracing, or remove it to eliminate overhead. No code changes or configuration flags are required.
Default Configuration
By default, trace data is collected via auto-instrumentation for supported Java libraries and frameworks and includes enrichment specific to Bloomreach Experience Manager.
The default configuration prioritizes low overhead and minimal trace data volume. JDBC instrumentation, metrics, and logs export are disabled by default. These settings are automatically applied 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 |
Trace Content
Traces include three layers of information:
1. OTel Auto-Instrumentation
The Java agent automatically creates spans for servlets, JAX-RS endpoints, HTTP clients, and other supported frameworks. No code changes are necessary; attaching the agent is sufficient.
2. CMS-Specific Enrichment
Each CMS request is enriched with details such as the logged-in CMS user, the project version, and, for Ajax requests, the Wicket UI action that triggered the request. The UI action is recorded as the hdc.brxm_ui_trace span attribute, linking backend traces to specific user interactions.
3. CMS Diagnostics (HDC Task Tree)
The internal CMS diagnostic task tree is exported as child spans when the OTel agent is attached. This export is independent of the CMS Diagnostics console logging feature; you do not need to enable CMS Diagnostics to obtain OTel traces. The thresholdMillisec property (default: 3000 ms) determines which requests include a detailed subtask breakdown.
Running Locally with an Archetype Project
Step 1: Configure the 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.name=local-dev
Replace my-project-cms with your preferred service name. These four properties are required at minimum.
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 using -javaagent:.
You can override properties defined in conf/platform-dev.properties by specifying them with -D on the Maven command line. To add 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 as needed.
Step 3: Verify Agent Attachment
On startup, check cms.log for a message similar to the following:
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.name=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
Access the CMS at http://localhost:8080/cms/, perform actions such as browsing, editing, or publishing documents, and then check your observability backend for traces using the configured service name.
Customizing Your Codebase
Using Traceable Wicket Components
When creating custom Wicket components with Ajax interactions, extend the traceable base classes to automatically include UI action names in traces. The action name is recorded as the hdc.brxm_ui_trace span attribute, allowing you to identify which user action triggered a request.
| Instead of | Use |
|---|---|
| AjaxLink | TraceableAjaxLink |
| AjaxButton | TraceableAjaxButton |
| AjaxEventBehavior | TraceableAjaxEventBehavior |
The default implementation generates an action name based on the component's label, model, or class name. To specify a custom action name, override getActionName():
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 are nested under the agent's server span and appear in your trace backend alongside auto-instrumented spans. If the agent is not attached, all API calls are no-ops and introduce no overhead.
Production Deployment
Bloomreach Experience Manager's OpenTelemetry integration uses the standard OpenTelemetry Java auto-instrumentation agent. The agent attaches to the JVM at startup and collects trace data with minimal overhead.
The default configuration is conservative to minimize overhead, reduce trace data volume, and avoid noise from internal framework activity.
Before enabling OpenTelemetry in production, first enable it in a non-production environment to verify compatibility and monitor resource usage in your deployment.
To enable OpenTelemetry in production:
-
Attach the OpenTelemetry Java agent JAR to the JVM using
-javaagent. -
Configure OTel properties as JVM system properties or environment variables. The following table lists recommended values based on the default configuration; adjust as needed for your environment.
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.name=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 -
Select a sampling strategy appropriate for your environment. Head-based sampling (configured via
otel.traces.sampler) is straightforward to set up. Tail-based sampling (using an OTel Collector) allows sampling decisions based on trace outcomes, such as errors or latency. Refer to the OpenTelemetry documentation for guidance.