# OpenTelemetry Go Tracing

> Instrument Go services with the OpenTelemetry Tracing API, create spans, record errors, and send distributed traces to Uptrace.

![undefined](/devicon/go-original.svg)## Prerequisites

<partial path="otel-prereq-go">



</partial>

## Installation

[OpenTelemetry-Go](https://github.com/open-telemetry/opentelemetry-go) is the Go implementation of OpenTelemetry. It provides OpenTelemetry Tracing API which you can use to instrument your application with [OpenTelemetry tracing](/opentelemetry/distributed-tracing).

```shell
go get go.opentelemetry.io/otel
```

## Quickstart

**Step 1**. Let's instrument the following function:

```go
func insertUser(ctx context.Context, user *User) error {
    if _, err := db.NewInsert().Model(user).Exec(ctx); err != nil {
        return err
    }
    return nil
}
```

**Step 2**. Wrap the operation with a [span](/opentelemetry/distributed-tracing#spans):

```go
import "go.opentelemetry.io/otel"

var tracer = otel.Tracer("app_or_package_name")

func insertUser(ctx context.Context, user *User) error {
    ctx, span := tracer.Start(ctx, "insert-user")
    defer span.End()

    if _, err := db.NewInsert().Model(user).Exec(ctx); err != nil {
        return err
    }
    return nil
}
```

**Step 3**. Record errors and set [status code](/opentelemetry/distributed-tracing#status-code):

<code-group>

```go [Raw]
func insertUser(ctx context.Context, user *User) error {
    ctx, span := tracer.Start(ctx, "insert-user")
    defer span.End()

    if _, err := db.NewInsert().Model(user).Exec(ctx); err != nil {
        span.RecordError(err)
        span.SetStatus(codes.Error, err.Error())
        return err
    }
    return nil
}
```

```go [Zap]
import "github.com/uptrace/opentelemetry-go-extra/tree/main/otelzap"

func insertUser(ctx context.Context, user *User) error {
    ctx, span := tracer.Start(ctx, "insert-user")
    defer span.End()

    if _, err := db.NewInsert().Model(user).Exec(ctx); err != nil {
        // Rely on Zap to record the error and set status code.
        otelzap.L().Ctx(ctx).Error("insert failed", zap.Error(err))
        return err
    }
    return nil
}
```

```go [Logrus]
import "github.com/uptrace/opentelemetry-go-extra/tree/main/otellogrus"

func insertUser(ctx context.Context, user *User) error {
    ctx, span := tracer.Start(ctx, "insert-user")
    defer span.End()

    if _, err := db.NewInsert().Model(user).Exec(ctx); err != nil {
        // Rely on logrus to record the error and set status code.
        logrus.WithContext(ctx).WithError(err).Error("insert failed")
        return err
    }
    return nil
}
```

</code-group>

**Step 4**. Record contextual information with [attributes](/opentelemetry/distributed-tracing#attributes):

```go
func insertUser(ctx context.Context, user *User) error {
    ctx, span := tracer.Start(ctx, "insert-user")
    defer span.End()

    if _, err := db.NewInsert().Model(user).Exec(ctx); err != nil {
        span.RecordError(err)
        span.SetStatus(codes.Error, err.Error())
        return err
    }

    if span.IsRecording() {
        span.SetAttributes(
            attribute.Int64("enduser.id", user.ID),
            attribute.String("enduser.email", user.Email),
        )
    }

    return nil
}
```

And that's it! The operation is fully instrumented.

## Tracer

To start creating [spans](/opentelemetry/distributed-tracing#spans), you need a tracer. You can create a tracer by providing the name and version of the library/application doing the instrumentation:

```go
import "go.opentelemetry.io/otel"

var tracer = otel.Tracer("app_or_package_name")
```

You can have as many tracers as you want, but usually you need only one tracer for an app or a library. Later, you can use tracer names to identify the instrumentation that produces the spans.

## Creating spans

Once you have a tracer, creating [spans](/opentelemetry/distributed-tracing#spans) is easy:

```go
import "go.opentelemetry.io/otel/trace"

// Create a span with name "operation-name" and kind="server".
ctx, span := tracer.Start(ctx, "operation-name",
    trace.WithSpanKind(trace.SpanKindServer))

doSomeWork()

// End the span when the operation we are measuring is done.
span.End()
```

## Context

OpenTelemetry stores the active span in a [context.Context](https://blog.golang.org/context). You [propagate](/opentelemetry/distributed-tracing#context) the context by [passing](https://blog.golang.org/context-and-structs) it to a function as the first argument.

`tracer.Start` saves the span in the context for you, but you can also access the span manually:

```go
import "go.opentelemetry.io/otel/trace"

// Get the active span from the context.
span = trace.SpanFromContext(ctx)

// Save the active span in the context.
ctx = trace.ContextWithSpan(ctx, span)
```

For context propagation details including W3C TraceContext, manual injection/extraction, and custom propagators, see [OpenTelemetry Context Propagation in Go](/get/opentelemetry-go/propagation).

## Adding span attributes

To record contextual information, you can annotate spans with [attributes](/opentelemetry/distributed-tracing#attributes). For example, an HTTP endpoint may have such attributes as `http.method = GET` and `http.route = /projects/:id`.

```go
// To avoid expensive computations, check that span was sampled
// before setting any attributes.
if span.IsRecording() {
    span.SetAttributes(
        attribute.String("http.method", "GET"),
        attribute.String("http.route", "/projects/:id"),
    )
}
```

You can name attributes as you want, but for common operations you should use semantic attributes convention.

## Setting status code

You can set `error` [status code](/opentelemetry/distributed-tracing#status-code) to indicate that the operation contains an error:

```go
import "go.opentelemetry.io/otel/codes"

if err != nil {
    span.SetStatus(codes.Error, err.Error())
}
```

## Adding span events

You can annotate spans with [events](/opentelemetry/distributed-tracing#events), for example, you can use events to record log messages:

```go
import "go.opentelemetry.io/otel/trace"

span.AddEvent("log", trace.WithAttributes(
    attribute.String("log.severity", "error"),
    attribute.String("log.message", "User not found"),
    attribute.String("enduser.id", "123"),
))
```

## Adding span links

You can add links to other traces, for example, when a trace creates sub-traces:

```go
import (
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/trace"
)

span.AddLink(trace.Link{
    SpanContext: trace.SpanContextFromContext(ctx),
    Attributes: []attribute.KeyValue{
        attribute.String("foo", "bar"),
    },
})
```

There is also a shortcut that creates a link from a `context.Context`:

```go
import (
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/trace"
)

span.AddLink(trace.LinkFromContext(ctx), attribute.String("foo", "bar"))
```

## Errors monitoring

OpenTelemetry provides a shortcut to record errors which is usually used together with the `SetStatus` API:

```go
import "go.opentelemetry.io/otel/codes"

if err != nil {
    // Record the error.
    span.RecordError(err)

    // Also mark span as failed.
    span.SetStatus(codes.Error, err.Error())
}
```

You can also record errors stack trace like this:

```go
import "go.opentelemetry.io/otel/trace"

span.RecordError(err, trace.WithStackTrace(true))
```

## Logs monitoring

To monitor logs, use OpenTelemetry instrumentations for popular logging libraries:

- [OpenTelemetry Zap](/guides/opentelemetry-zap)
- [OpenTelemetry Logrus](/guides/opentelemetry-logrus)

If that is not possible, see [Monitoring Logs](/get/logs) for more options such as Vector or FluentBit.

## SDK logging

By default, OpenTelemetry Go SDK logs error messages to stderr. You can discard or redirect those logs by providing an error handler:

```go
import "go.opentelemetry.io/otel"

otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) {
    // ignore the error
}))
```

## What's next?

- [Get started](/get/opentelemetry-go)
- [Learn about OpenTelemetry Sampling <span>

Go

</span>

](/get/opentelemetry-go/sampling)
- [Learn about OpenTelemetry Go Metrics API](/get/opentelemetry-go/metrics)
- [OpenTelemetry Gin](/guides/opentelemetry-gin)
- [OpenTelemetry Beego](/guides/opentelemetry-beego)
- [OpenTelemetry GORM](/guides/opentelemetry-gorm)
