A step-by-step walkthrough for sending traces, metrics, and logs to OBSERVE through the OpenTelemetry Collector, including collector config, Java auto-instrumentation flags, sampling setup, and verification—full traces with no code changes.
You need an OBSERVE tenant, its gateway address, and an access token. This guide assumes Linux + Docker and Java / Go microservices. Onboarding has three steps: deploy a Collector as the single ingestion point, send data from your services, then verify the trace.
Run one OpenTelemetry Collector that receives traces / metrics / logs from every service and forwards them to OBSERVE. Start it with docker-compose:
otel-collector:
image: otel/opentelemetry-collector-contrib:0.102.0
volumes:
- ./otel-collector-config.yaml:/etc/otelcol/config.yaml
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
- "8889:8889" # Prometheus scrape
The config's key parts are the exporter and the pipelines:
receivers:
otlp:
protocols: { grpc: {}, http: {} }
prometheus:
config:
scrape_configs:
- job_name: 'otel-collector'
scrape_interval: 15s
static_configs: [{ targets: ['localhost:8888'] }]
exporters:
otlp/jjhub:
endpoint: "obs-gateway.jjhub.cn:4317"
headers: { authorization: "Bearer <TOKEN>" }
service:
pipelines:
traces: { receivers: [otlp], exporters: [otlp/jjhub] }
metrics: { receivers: [otlp, prometheus], exporters: [otlp/jjhub] }
logs: { receivers: [otlp], exporters: [otlp/jjhub] }
The fastest path with no code changes is auto-instrumentation. For Java, add the javaagent at startup:
java -javaagent: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://localhost:4318 -jar app.jar
For Go, instrument manually with otelhttp and otelgrpc, or use eBPF agentless collection on legacy systems you can't instrument (see the v2.6.0 release notes). In all cases set service.name to a globally unique value—otherwise traces and metrics from different services get mixed together.
Collecting 100% of everything isn't realistic in production: under load, both the Collector and storage get saturated. By default, add a tail_sampling or probabilistic_sampling processor in the Collector and keep only slow and failed requests:
processors:
probabilistic_sampler:
sampling_percentage: 10
tail_sampling:
policies:
- name: slow
type: latency
latency: { threshold_ms: 500 }
- name: errors
type: status_code
status_code: { status_codes: [ERROR] }
Slow requests and errors are kept at 100%, normal requests are sampled at 10%—you keep the full traces you need for troubleshooting while controlling storage cost. Tune the sampling policy gradually: run full collection for a day to see the volume, then tighten.
The Collector is also your single place to attach batching and retries, so a brief network blip between your services and OBSERVE doesn't silently drop data. Add a batch processor and a retry-on-failure queue on the exporter:
processors:
batch:
send_batch_size: 512
timeout: 1s
exporters:
otlp/jjhub:
retry_on_failure:
enabled: true
initial_interval: 2s
max_elapsed_time: 60s
For a Go service, instrument an HTTP handler in a few lines using the otelhttp wrapper:
import (
"net/http"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
http.Handle("/api/order", otelhttp.NewHandler(orderHandler, "place-order"))
The wrapper starts a span per request, records status and latency, and propagates the trace context to downstream calls automatically—no manual span plumbing in your handlers.
service.name and you should see a complete trace with per-span latency, status codes, and error details.otel..curl -v http://localhost:4318 to check the port, then read the collector logs for export errors—usually a wrong token or endpoint.Finally, pin the javaagent and Collector image versions instead of using latest. Auto-instrumentation and Collector releases move fast, and a silent upgrade can change sampling behavior or drop a field you depend on. Version-pinning is cheap insurance for an observability pipeline that has to be boringly reliable. After your first trace shows up, export it and walk it span by span against the code—if any hop is missing, that's almost always a missing propagator or an un-instrumented HTTP client, both easy to fix now and painful to find later.