A step-by-step guide to wiring your services into Jujing OBSERVE via OpenTelemetry — getting credentials, configuring the OTLP exporter over HTTP or gRPC, verifying traces, and fixing the most common onboarding failures.
The cost of onboarding often decides whether a team actually uses tracing. Jujing OBSERVE is fully OpenTelemetry-compatible — any SDK or collector that speaks OTLP can connect directly. Here's the most common path, from zero to your first trace, along with the decisions you'll actually have to make.
In the OBSERVE console, open "Data Access → OpenTelemetry" and create a data source. You'll get two key values:
https://otel.jjhub.cn (HTTP on port 4318; gRPC on port 4317)jj_xxxxx. Keep it out of public repos.Before writing any config, decide on your topology. The simplest option is to have each application export directly to the OTLP endpoint. That works for a handful of services, but as you grow you'll want an OpenTelemetry Collector in between: apps send data to the local collector, which aggregates and forwards upstream. Production prefers this — it gives you one place to handle sampling, redaction, retries, and credential rotation, so you don't have to touch every app when a token changes.
For a Java service, the OTel Java Agent instruments without touching business code — just attach it at startup:
java -javaagent:opentelemetry-javaagent.jar -Dotel.resource.attributes=service.name=pay-svc,deployment.environment=prod -Dotel.exporter.otlp.endpoint=https://otel.jjhub.cn -Dotel.exporter.otlp.protocol=http/protobuf -Dotel.exporter.otlp.headers=Authorization=Bearer jj_xxxxx -jar pay-svc.jar
For Go, initialize the SDK in main and inject config via environment variables to stay decoupled from deployment:
OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.jjhub.cn
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer%20jj_xxxxx
OTEL_SERVICE_NAME=pay-svc
Two gotchas: the space between "Bearer" and the token must be URL-encoded as %20 in an env var, and keep service names consistent or later per-service aggregation turns into a mess. Pick a convention (pay-svc, not paySvc, pay_service, or payment-service) and stick to it everywhere.
Choose gRPC or HTTP based on your environment, not on performance folklore. gRPC (port 4317) has a slight edge at very high volume, but HTTP/protobuf (4318) is easier to get through corporate proxies and firewalls, which matters more in practice. If your exporters sit behind an egress proxy, HTTP is usually the path of least resistance.
Set resource attributes deliberately — service.name, deployment.environment, and a version tag (service.version) are the three that drive most dashboards and alert routing later. Retrofitting them after you've accumulated days of traces is painful, because old traces can't be re-tagged cheaply. Decide the values at the same time you write the exporter config, and put them in a shared config snippet so every service in the team starts from the same defaults.
Back in OBSERVE, open "Tracing → Services". You should see pay-svc with live QPS, P99 latency, and error rate. Open any trace to see the full waterfall (span timeline) plus each span's duration, attributes, and correlated logs.
If the service appears but traces don't, it's almost always sampling. OTel's parent-based sampler falls back to OTEL_TRACES_SAMPLER when there's no parent span; the default samples everything, but if you've set a low traceidratio, most traces get dropped. While debugging, bump sampling to 100% to confirm the pipeline works, then dial it back.
If the service doesn't appear at all, check the exporter side first: does the app log an OTel error, and can it reach the endpoint? Then check the "Ingest Diagnostics" page on the OBSERVE side — it shows the last report timestamp and the exact rejection reason (bad token, wrong protocol, malformed payload).
Traces alone show the call graph, not the actual error. Inject trace_id into your logs so a trace links straight to its log lines. With Logback, add a pattern that carries the OTel trace_id and span_id:
<pattern>%d %-5level [%X{trace_id}/%X{span_id}] %logger{36} - %msg%n</pattern>
Then have the collector parse trace_id as a field. Now when you open a slow request in OBSERVE you see which downstreams it called, how long each took, and can jump directly to the erroring log lines. This one integration is where most of the tracing value actually comes from.
When onboarding fails, check in order: is the endpoint reachable (curl/telnet); is the token correct and the header well-formed; does the protocol match (HTTP→4318, gRPC→4317); is the service name set. Nine out of ten issues live in those four. If all look right, check sampling, then the diagnostics page.
Start with one service, get it clean end to end, then roll the same pattern out. A single well-instrumented service teaches you more than ten half-wired ones.