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

Getting Started with OpenTelemetry Tracing in JJHub OBSERVE

Onboard OpenTelemetry tracing with the Java Agent in minutes: dependency and exporter configuration, verifying traces are flowing, correlating logs via trace_id, and choosing a sampling strategy that controls cost without losing problem traces.

Getting Started with OpenTelemetry Tracing in JJHub OBSERVE

Before You Start

Check two things first: the OBSERVE server is deployed and you have the OTLP endpoint (default http://<host>:4318), and your application can reach that endpoint. This guide uses a Java app as the example; the flow is the same for other languages, only the SDK dependency differs. In the OBSERVE console, open the "Onboarding" wizard on the left, pick your language, and it will generate the configuration and endpoint details for you to copy.

One decision to make up front is the OTLP protocol. OBSERVE accepts both http/protobuf (port 4318) and grpc (port 4317). If your network has a firewall or proxy that handles HTTP more easily, prefer http/protobuf; it's the default in most SDKs and simpler to debug with curl.

Add the Dependency and Configure

Java apps integrate via the OpenTelemetry Java Agent, so you don't need to change business code. Download opentelemetry-javaagent.jar, then add it to the startup command:

java -javaagent:path/to/opentelemetry-javaagent.jar   -Dotel.service.name=order-service   -Dotel.traces.exporter=otlp   -Dotel.metrics.exporter=otlp   -Dotel.logs.exporter=otlp   -Dotel.exporter.otlp.endpoint=http://<host>:4318   -Dotel.exporter.otlp.protocol=http/protobuf   -jar app.jar

otel.service.name is the service's name in the trace graph—make it globally unique or your call graph will get tangled. In production, put these parameters into the startup script or container environment variables instead of typing them by hand. Note the three exporter flags in the command: traces, metrics, and logs can be toggled independently. If you only need tracing for now, disable the other two to cut overhead—you can turn them back on later without changing code, just flip the flag and restart.

For Go apps, add the OTLP exporters and set up a TracerProvider:

import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
    "go.opentelemetry.io/otel/sdk/resource"
    semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

exp, _ := otlptracehttp.New(ctx, otlptracehttp.WithEndpoint("<host>:4318"),
    otlptracehttp.WithInsecure())
tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exp),
    sdktrace.WithResource(resource.NewWithAttributes(
        semconv.ServiceName("order-service"))))
otel.SetTracerProvider(tp)

Python apps can use opentelemetry-instrument for auto-instrumentation, pointing the same OTLP endpoint via environment variables. Whatever the language, the service.name resource attribute is what OBSERVE keys the service map on, so set it deliberately.

Verify Traces Are Flowing

After starting, send a few requests, then check the "Tracing" page in OBSERVE. Three quick self-checks:

  • order-service shows up in the service list.
  • Opening any trace shows the full call chain and per-hop latency.
  • In the Logs page, entries carrying trace_id can jump to their trace with one click.

If you see metrics but no traces, the exporter config likely didn't take effect or the port is unreachable—check connectivity to 4318 first with curl -v http://<host>:4318/v1/traces, then look for OTLP errors in the agent startup log.

Correlate Logs and Business Fields

Once traces are flowing, tie logs to them. The agent automatically injects trace_id and span_id into your logs; you just need to print them in your logging framework:

<pattern>%d %-5level [%X{trace_id:-}] [%X{span_id:-}] %msg%n</pattern>

Also pass business fields like user_id and order_id as span attributes. That way you can filter traces by "which user, which order" during an incident instead of reading entries one by one. For cross-service calls, make sure the upstream forwards the traceparent header downstream, or the trace will break at the service boundary.

Sampling Strategy

Don't run 100% sampling under high traffic. OBSERVE supports ratio-based and error-based sampling: sample 10% of normal traffic but keep 100% of failed requests. This caps storage cost while making sure every problem request has a full trace to inspect. Sampling is configured centrally on the server, so clients don't need to change repeatedly. If a trace is sampled out, its logs are still ingested—only the span data is dropped—so you can always fall back to log search for the unsampled traffic.

Troubleshooting Common Issues

Three issues account for most onboarding tickets:

  • Traces stop at one service: the upstream didn't forward traceparent. Check that your HTTP client and message queues propagate the context across the boundary.
  • High-cardinality spans: dynamic span names or unbounded attributes blow up storage. Use static span names and a fixed attribute allowlist.
  • Duplicate service names: two deployments sharing otel.service.name merge into one node. Namespace the name by environment (e.g. order-service-prod).

The service map on the Tracing page is the fastest way to spot these: a missing edge usually means a propagation problem, and an unexpected edge usually means a naming collision.