Enable OpenTelemetry tracing in CAP, propagate trace context, export to SAP Cloud ALM, and read span trees.
@sap/xb-msg-amqp-v100 tracing plugin in CAPWhen a user reports "the purchase order page is slow", structured logs tell you that something took 3 seconds but not which call in the chain caused it. Distributed tracing generates a trace — a tree of spans showing every service call, DB query, and downstream request with its duration. You can see immediately whether the slowdown is in CAP handler code, an HANA query, or an S/4HANA OData call.
| Term | Meaning |
|---|---|
| Trace | A complete request flow across all services |
| Span | One unit of work (a function call, a DB query, an HTTP request) |
| Trace ID | Unique ID shared by all spans in one trace |
| Span ID | Unique ID for one span |
| Parent Span | The span that initiated the current work unit |
| W3C Trace Context | Standard HTTP headers: traceparent, tracestate |
CAP supports OpenTelemetry (OTel) tracing through @sap/cds-tracing:
npm install @sap/cds-tracing @opentelemetry/sdk-node
// srv/server.js — enable before anything else
const tracing = require('@sap/cds-tracing');
tracing.enable(); // auto-instruments DB calls, service calls, HTTP handlers
CAP will create spans automatically for:
- Every incoming OData request (root span)
- Every this.on() / this.before() / this.after() handler
- Every CQL DB operation
- Every cds.connect.to() remote service call
When CAP calls a remote service (S/4HANA, Event Mesh), pass the W3C traceparent header automatically:
// CAP does this for you when using cds.connect.to()
// For manual fetch calls, propagate explicitly:
const { context } = require('@opentelemetry/api');
const { propagation } = require('@opentelemetry/api');
async function callDownstream(url, req) {
const headers = {};
propagation.inject(context.active(), headers); // injects traceparent
return fetch(url, { headers });
}
Configure the OTel exporter to send to Cloud ALM:
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { NodeSDK } = require('@opentelemetry/sdk-node');
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
headers: {
Authorization: `Bearer ${process.env.OTEL_EXPORTER_TOKEN}`
}
})
});
sdk.start();
Set OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_TOKEN from the Cloud ALM service key in your CF manifest:
env:
OTEL_EXPORTER_OTLP_ENDPOINT: https://cloudalm.cfapps.eu10.hana.ondemand.com/api/v1/traces
OTEL_EXPORTER_TOKEN: ((cloud-alm-token))
Include the trace ID in your structured log entries so you can jump from a log line to its trace:
const { trace } = require('@opentelemetry/api');
logger.info('Starting approval workflow', {
traceId: trace.getActiveSpan()?.spanContext().traceId,
userId: req.user.id,
poAmount: req.data.totalAmount
});
In Kibana, click the traceId value → opens the trace in Cloud ALM (if linked).
| Mistake | Fix |
|---|---|
| Enabling tracing after other middleware | Enable @sap/cds-tracing before all other requires |
Not propagating traceparent in manual fetch calls |
Use the OTel propagation API |
| Sampling rate at 100% in production | Set OTEL_TRACES_SAMPLER_ARG=0.1 (10%) to control volume |
| No trace ID in log entries | Correlate logs and traces via the trace ID field |
| Ignoring span attributes | Span attributes (user, SQL, entity) are what make traces useful |
You can enable OpenTelemetry tracing in CAP with @sap/cds-tracing, propagate trace context in downstream calls, export traces to SAP Cloud ALM, read the span tree to find the slow step, and correlate traces with structured log entries via trace ID.