For up-to-date information on running OpenTelemetry in production, please visit our Getting Started Guide for OpenTelemetry and GolangGetting Started Guide for OpenTelemetry and Golang
We're excited that OpenTelemetry is moving to the alpha phase. It's a great time to get involvedto get involved and share your feedback.
You can check out the full [SIG progressSIG progress] here.
Alpha v0.1
The following examples use the Alpha v0.1 version of OpenTelemetry-GoOpenTelemetry-Go (an OpenTelemetry comptible tracer). These examples assume that the Jaeger all-in-one imageJaeger all-in-one image is running locally via Docker:
$ docker run -d -p 6831:6831/udp -p 16686:16686 jaegertracing/all-in-one:latest`
Setting up your tracer
There are three objects configured here: a new tracing SDK, a new simple span processor, and a new Jaeger exporter.
import (
"context"
"log"
"go.opentelemetry.io/api/trace"
"go.opentelemetry.io/exporter/trace/jaeger"
sdk "go.opentelemetry.io/sdk/trace"
)
func setupTracer() (trace.Tracer, *jaeger.Exporter, error) {
// Register installs a new global tracer instance.
tracer := sdk.Register()
// Construct and register an export pipeline using the Jaeger
// exporter and a span processor.
exporter, err := jaeger.NewExporter(
jaeger.Options{
AgentEndpoint: "localhost:6831",
},
)
if err != nil {
return nil, nil, err
}
// A simple span processor calls through to the exporter
// without buffering.
ssp := sdk.NewSimpleSpanProcessor(exporter)
sdk.RegisterSpanProcessor(ssp)
// Use sdk.AlwaysSample sampler to send all spans.
sdk.ApplyConfig(
sdk.Config{
DefaultSampler: sdk.AlwaysSample(),
},
)
return tracer, exporter, nil
}
The returned Tracer
is ready to use. The returned Exporter
will be useful for flushing spans before exiting the process.
Start and end a span
import "go.opentelemetry.io/api/trace"
func sayHello() {
ctx := context.Background()
tracer := trace.GlobalTracer()
ctx, trace := tracer.Start(ctx, "say-hello")
trace.End()
}
There is another way to start a span that supports recovering from panics automatically, so that spans still send in these situations.
import "go.opentelemetry.io/api/trace"
func sayHello2() {
ctx := context.Background()
tracer := trace.GlobalTracer()
err := tracer.WithSpan(ctx, "say-hello", func(ctx context.Context) error {
// This body is traced, and the span will End() despite panics.
return nil
})
if err != nil {
// ...
}
}
Creating child spans
This example main()
function sets up the tracer and sends three spans, two of them children, as part of a single trace. Note the call to exporter.Flush()
to send spans before exiting the process.
func main() {
// Setup tracing and get a Tracer instance. We'll use the
// exporter to flush before exiting.
tracer, exporter, err := setupTracer()
if err != nil {
log.Fatal("Could not initialize tracing: ", err)
}
// Tracing uses the standard context for propagation, we'll
// start with a background context.
ctx := context.Background()
_ = tracer.WithSpan(ctx, "foo",
func(ctx context.Context) error {
tracer.WithSpan(ctx, "bar",
func(ctx context.Context) error {
tracer.WithSpan(ctx, "baz",
func(ctx context.Context) error {
return nil
},
)
return nil
},
)
return nil
},
)
// The Jaeger exporter will have buffered spans at this point, send them.
exporter.Flush()
}
Trace an HTTP request
This example shows how to propagate trace context across an HTTP request. First we have to select the type of context carrier—in this example we use W3C. On the client side, a call to httptrace.W3C
configures the http.Request
object to use W3C trace context headers, and a call to httptrace.Inject
both injects the headers and applies conventional HTTP attributes to the span.
import (
"context"
"io/ioutil"
"net/http"
"go.opentelemetry.io/api/trace"
"go.opentelemetry.io/plugin/httptrace"
"google.golang.org/grpc/codes"
)
func sayHTTPHello(ctx context.Context) {
var body []byte
client := http.DefaultClient
tracer := trace.GlobalTracer()
tracer.WithSpan(ctx, "client-call",
func(ctx context.Context) error {
req, _ := http.NewRequest("GET", "http://localhost:7777/hello", nil)
ctx, req = httptrace.W3C(ctx, req)
httptrace.Inject(ctx, req)
res, err := client.Do(req)
if err != nil {
panic(err)
}
body, err = ioutil.ReadAll(res.Body)
res.Body.Close()
trace.CurrentSpan(ctx).SetStatus(codes.OK)
return err
})
}
Continuing on the server side, this is an HTTP handler that adds tracing support.
func helloHandler(w http.ResponseWriter, req *http.Request) {
tracer := trace.GlobalTracer()
// Extracts the conventional HTTP span attributes,
// distributed context tags, and a span context for
// tracing this request.
attrs, tags, spanCtx := httptrace.Extract(req.Context(), req)
// Apply the distributed context tags to the request
// context.
req = req.WithContext(tag.WithMap(req.Context(), tag.NewMap(tag.MapUpdate{
MultiKV: tags,
})))
// Start the server-side span, passing the remote
// child span context explicitly.
_, span := tracer.Start(
req.Context(),
"hello",
trace.WithAttributes(attrs...),
trace.ChildOf(spanCtx),
)
defer span.End()
_, _ = io.WriteString(w, "Hello, world!\n")
}
View your traces
If you have Jaeger all-in-one running, you can view your traces at localhost:16686
.
Interested in joining our team? See our open positions herehere.
Explore more articles

A modern guide to distributed tracing
Austin Parker | Dec 21, 2022Austin Parker reviews developments, innovations, & updates in the world of distributed tracing
Learn moreLearn more
Distributed Tracing: Why It’s Needed and How It Evolved
Austin Parker | Oct 1, 2020Distributed tracing is the “call stack” for a distributed system, a way to represent a single request as it flows from one computer to another.
Learn moreLearn more
How we built & scaled log search and pattern extraction
Karthik Kumar, Katia Bazzi | Jul 31, 2020We recently added the ability to search and aggregate trace logs in Lightstep! This article will go through how we built & scaled log search and pattern extraction.
Learn moreLearn moreLightstep sounds like a lovely idea
Monitoring and observability for the world’s most reliable systems