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

Onboarding OpenTelemetry to JUJING OBSERVE: Tracing in 15 Minutes

A minimal path to OpenTelemetry from scratch: deploy the Collector, export OTLP to JUJING OBSERVE, instrument a Java or Go service, and verify your first full trace—plus the common errors you'll hit and how to fix them.

OpenTelemetry (OTel) has become the de facto standard for collecting observability data. It unifies traces, metrics, and logs under one reporting format, so you instrument your application once and swap backends freely. This post gets a complete trace into JUJING OBSERVE with minimal config in about fifteen minutes.

The big picture

The flow has three hops: your app emits data via the OTel SDK, that data is sent to an OpenTelemetry Collector over OTLP, and the Collector forwards it to JUJING OBSERVE's OTLP endpoint.

Why not have the app report straight to the backend? The Collector centralizes receiving, redaction, sampling, and retries. Your app only needs to know the Collector's address, so switching backends never means touching application code. That indirection is the most valuable part of the OTel architecture.

Step 1: Deploy the Collector

Spin up a Collector with Docker. Here's the config:

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:
    timeout: 5s

exporters:
  otlphttp:
    endpoint: https://ob.jjhub.cn/otel/v1/traces
    headers:
      Authorization: "Bearer <your access token>"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp]

Generate the access token from the "Data Ingestion" page in the JUJING OBSERVE console. Use separate tokens for production and staging so you can filter by source and track usage later.

docker run -d --name otel-collector   -v $PWD/otel-collector.yaml:/etc/otelcol/config.yaml   -p 4317:4317 -p 4318:4318   otel/opentelemetry-collector-contrib:latest

Step 2: Instrument your app

For Java, a single agent is all you need. It injects instrumentation via bytecode, so there's no change to your business code:

java -javaagent:opentelemetry-javaagent.jar   -Dotel.exporter.otlp.endpoint=http://localhost:4317   -Dotel.service.name=order-service   -jar app.jar

For Go, initialize a tracer in main:

import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/sdk/trace"
)

exp, _ := otlptracegrpc.New(ctx,
    otlptracegrpc.WithEndpoint("localhost:4317"),
    otlptracegrpc.WithInsecure())
tp := trace.NewTracerProvider(trace.WithBatcher(exp))
otel.SetTracerProvider(tp)

The one thing that matters is service.name: it's the unique identifier that separates services in a trace, so make it meaningful. The automatic instrumentation for HTTP frameworks (Servlet in Java, net/http in Go) generates spans for you, and trace context propagates across service calls automatically — no manual header passing.

Step 3: Verify and debug

Fire a real request, open the "Tracing" page in JUJING OBSERVE, filter by service.name, and you should see the full call graph with per-service latency and dependencies laid out.

Three mistakes people hit most often:

  1. Collector won't start: check the container logs — it's almost always a YAML indentation error or a typo in the exporter endpoint.
  2. Spans appear but aren't linked: make sure every service uses the same propagator. The default tracecontext is fine; the problem is mixing in custom headers.
  3. Only some spans show up: the batch processor buffers data in memory for a few seconds before flushing. During testing, drop timeout to something like 1s so you aren't staring at an empty screen.

Once it's working, set batch.timeout back to 5s. In production, put a probabilistic_sampler in front of the Collector to keep your ingest volume from running away under load.