Step-by-Step Guide: Integrating OpenTelemetry Tracing in Java and Go Projects
This tutorial walks through setting up OpenTelemetry tracing from scratch for both Java and Go microservices, covering collector and Jaeger deployment, required dependencies, configuration parameters, code examples for automatic and manual instrumentation, and how to add custom span attributes and spans.
Background
This guide demonstrates a hands‑on integration of OpenTelemetry tracing using two languages: Java (automatic instrumentation via the OpenTelemetry Java agent) and Go (manual instrumentation via the SDK). The example consists of a Spring Boot gRPC client, a Go gRPC server, an OpenTelemetry Collector, and a Jaeger UI for trace visualization.
Project Structure
java-demo – Spring Boot client that sends gRPC requests (OpenTelemetry Java agent 2.4.0, Spring Boot 2.7.14).
k8s-combat – Go gRPC server (OpenTelemetry Collector 0.98.0, Go 1.22).
Jaeger – All‑in‑one Jaeger container for trace storage and UI (jaegertracing/all-in-one:1.56).
opentelemetry-collector-contrib – Collector service that receives OTLP data and forwards it to Jaeger.
Deploy Collector and Jaeger
docker run --rm -d --name jaeger \
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
-p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 \
-p 16686:16686 -p 4317:4317 -p 4318:4318 \
-p 14250:14250 -p 14268:14268 -p 14269:14269 \
-p 9411:9411 \
jaegertracing/all-in-one:1.56
docker run --rm -d -v $(pwd)/coll-config.yaml:/etc/otelcol-contrib/config.yaml \
--name coll -p 5318:4318 -p 5317:4317 \
otel/opentelemetry-collector-contrib:0.98.0The collector configuration ( coll-config.yaml) defines an OTLP receiver, a batch processor, a debug exporter, and a pipeline that sends traces to the Jaeger endpoint ( 127.0.0.1:4317).
Java Application
The Java side uses grpc-spring-boot-starter and a simple helloworld.proto that defines a SayHello RPC. The gRPC client configuration (in application.yml) is:
grpc:
server:
port: 9192
client:
greeter:
address: 'static://127.0.0.1:50051'
enableKeepAlive: true
keepAliveWithoutCalls: true
negotiationType: plaintextTracing is enabled by launching the Java process with the OpenTelemetry Java agent and a set of system properties:
java -javaagent:opentelemetry-javaagent-2.4.0.jar \
-Dotel.traces.exporter=otlp \
-Dotel.metrics.exporter=otlp \
-Dotel.logs.exporter=none \
-Dotel.service.name=demo \
-Dotel.exporter.otlp.protocol=grpc \
-Dotel.propagators=tracecontext,baggage \
-Dotel.exporter.otlp.endpoint=http://127.0.0.1:5317 \
-jar target/demo-0.0.1-SNAPSHOT.jarKey parameters: otel.traces.exporter – destination for trace data (OTLP to the collector). otel.metrics.exporter – destination for metric data (used later). otel.service.name – logical service name appearing in traces. otel.exporter.otlp.protocol – transport protocol (grpc or http/protobuf). otel.propagators – context propagation formats (tracecontext, baggage, etc.).
After starting the collector, Jaeger, and the Java client, a request such as curl http://127.0.0.1:9191/request?name=demo generates a trace visible in the Jaeger UI ( http://localhost:16686).
Go Application
The Go side pulls the OpenTelemetry SDK packages and registers gRPC instrumentation:
go get "go.opentelemetry.io/otel" \
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" \
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" \
"go.opentelemetry.io/otel/propagation" \
"go.opentelemetry.io/otel/sdk/metric" \
"go.opentelemetry.io/otel/sdk/resource" \
"go.opentelemetry.io/otel/sdk/trace" \
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"Tracer provider initialization with an OTLP gRPC exporter and a resource that captures OS, process, and container information:
func initTracerProvider() *sdktrace.TracerProvider {
ctx := context.Background()
exporter, err := otlptracegrpc.New(ctx)
if err != nil {
log.Printf("new otlp trace grpc exporter failed: %v", err)
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(initResource()),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{}, propagation.Baggage{}))
return tp
}
func initResource() *sdkresource.Resource {
initResourcesOnce.Do(func(){
extra, _ := sdkresource.New(context.Background(),
sdkresource.WithOS(),
sdkresource.WithProcess(),
sdkresource.WithContainer(),
sdkresource.WithHost())
resource, _ = sdkresource.Merge(sdkresource.Default(), extra)
})
return resource
}Start the gRPC server with the OpenTelemetry stats handler so that each incoming RPC automatically creates a span:
s := grpc.NewServer(
grpc.StatsHandler(otelgrpc.NewServerHandler()),
)
pb.RegisterGreeterServer(s, &server{})Manual spans can be created when needed:
ctx, span := tracer.Start(ctx, "hello-span")
defer span.End()
log.Printf("create span")Custom Span Attributes and Events
Additional data can be attached to the current span using SetAttributes (Go) or the equivalent API in Java:
span := trace.SpanFromContext(ctx)
span.SetAttributes(attribute.String("request.name", in.Name))Both languages also support adding events and links to a span via AddEvent and AddLink methods.
Creating New Spans
In Go, a new span is started explicitly with tracer.Start. In Java, the @WithSpan annotation (from opentelemetry-instrumentation-annotations) automatically creates a span around the annotated method:
@WithSpan("span")
public void span(@SpanAttribute("request.name") String name) throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
log.info("span:{}", name);
}Summary
OpenTelemetry provides automatic instrumentation for languages that support agents (e.g., Java) and manual SDK‑based instrumentation for languages like Go. The end‑to‑end example shows how to deploy a collector and Jaeger, configure a Java Spring Boot client with the Java agent, and set up a Go gRPC server with explicit tracer and resource initialization. It also demonstrates adding custom attributes, events, and manually created spans, preparing the reader for the next topic—metrics.
Source code for the Go demo: https://github.com/crossoverJie/k8s-combat. Reference for Java configuration: https://opentelemetry.io/docs/languages/java/configuration/.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
