← Back to blog
Guide 4 min read 炬鲸团队

Onboarding Tracing with OpenTelemetry: Three Steps to a Complete Call Graph

A step-by-step guide to sending traces to OBSERVE via OpenTelemetry and OTLP, using a Java service as an example, plus three common pitfalls — sampling, resource attributes, and context propagation.

Why OpenTelemetry

OpenTelemetry (OTel) is a CNCF-hosted standard for observability data. A single SDK produces traces, metrics, and logs, but the bigger win is standardization: the data format and collection method are fixed by the spec, so switching backends later means changing an exporter config, not your business code. OBSERVE speaks the OTLP protocol natively, so onboarding is largely a matter of pointing an endpoint at us.

Three steps to onboard

Using a Java service as the example.

Step one: add the SDK and the auto-instrumentation agent. Put opentelemetry-api and opentelemetry-sdk in pom.xml, then attach the Java agent at startup:

java -javaagent:opentelemetry-javaagent.jar \
  -Dotel.exporter.otlp.endpoint=https://ob.jjhub.cn/otlp \
  -Dotel.service.name=order-service \
  -Dotel.traces.exporter=otlp \
  -jar app.jar

Step two: configure the exporter. Point the endpoint at OBSERVE's OTLP receiver and set service.name. This value is the unique identifier for a service in a trace, so agree on a naming convention first — something like {department}-{system}-{module} — instead of letting each engineer invent their own.

Step three: verify. Fire one real request, then search for it in OBSERVE by service name. If you see the full span hierarchy, you're connected. If not, check the agent log for otel errors first; nine times out of ten it's an endpoint or auth misconfiguration.

Three pitfalls that bite

  • Sampling rate: default sampling can drop low-frequency errors. In production, set otel.traces.sampler=parentbased_traceidratio and tune the ratio to your traffic — don't capture everything, and don't let the default silently discard errors. For critical endpoints, override with always_on.
  • Resource attributes: inject deployment.environment and service.version at startup so you can filter by environment and version during rollbacks. These are painful to backfill once you're live.
  • Lost propagation: forward the traceparent header at every ingress, and pass context manually through async threads and message queues — otherwise a trace breaks into disconnected fragments that look like missing data.

Rollout advice

Start with one core service, link traces to your existing logs through trace_id, then expand gradually. Put trace links inside your alerts so one click jumps from an alert to the exact span; that alone often cuts time-to-resolution from half an hour to minutes. On instrumentation strategy, lean on auto-instrumentation to cover framework calls and hand-write spans only on business-critical paths — don't aim for full manual instrumentation on day one.