1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-01-18 03:22:12 +02:00

Drop website_docs since it has moved to OTel website (#4219)

This commit is contained in:
Patrice Chalin 2023-06-20 08:38:22 -04:00 committed by GitHub
parent be7539e30d
commit fc88a6a25b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 3 additions and 1287 deletions

View File

@ -120,7 +120,9 @@ Once verified be sure to [make a release for the `contrib` repository](https://g
### Website Documentation
Update [the documentation](./website_docs) for [the OpenTelemetry website](https://opentelemetry.io/docs/go/).
Update the [Go instrumentation documentation] in the OpenTelemetry website under [content/en/docs/instrumentation/go].
Importantly, bump any package versions referenced to be the latest one you just released and ensure all code examples still compile and are accurate.
[OpenTelemetry Specification]: https://github.com/open-telemetry/opentelemetry-specification
[Go instrumentation documentation]: https://opentelemetry.io/docs/instrumentation/go/
[content/en/docs/instrumentation/go]: https://github.com/open-telemetry/opentelemetry.io/tree/main/content/en/docs/instrumentation/go

View File

@ -1,20 +0,0 @@
---
title: Go
description: >-
<img width="35" class="img-initial" src="/img/logos/32x32/Golang_SDK.svg"
alt="Go"></img> A language-specific implementation of OpenTelemetry in Go.
aliases: [/golang, /golang/metrics, /golang/tracing]
cascade:
github_repo: &repo https://github.com/open-telemetry/opentelemetry-go
github_subdir: website_docs
path_base_for_github_subdir: content/en/docs/instrumentation/go/
github_project_repo: *repo
spelling: cSpell:ignore godoc
weight: 16
---
{{% lang_instrumentation_index_head "go" /%}}
## More
- [Contrib repository](https://github.com/open-telemetry/opentelemetry-go-contrib)

View File

@ -1,8 +0,0 @@
---
title: API reference
linkTitle: API
redirect: https://pkg.go.dev/go.opentelemetry.io/otel
manualLinkTarget: _blank
_build: { render: link }
weight: 210
---

View File

@ -1,7 +0,0 @@
---
title: Examples
redirect: https://github.com/open-telemetry/opentelemetry-go/tree/main/example
manualLinkTarget: _blank
_build: { render: link }
weight: 220
---

View File

@ -1,57 +0,0 @@
---
title: Exporters
aliases: [/docs/instrumentation/go/exporting_data]
weight: 50
---
In order to visualize and analyze your [traces](/docs/concepts/signals/traces/)
and metrics, you will need to export them to a backend.
## OTLP endpoint
To send trace data to an OTLP endpoint (like the [collector](/docs/collector) or
Jaeger >= v1.35.0) you'll want to configure an OTLP exporter that sends to your
endpoint.
### Using HTTP
```go
import (
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
)
func installExportPipeline(ctx context.Context) (func(context.Context) error, error) {
client := otlptracehttp.NewClient()
exporter, err := otlptrace.New(ctx, client)
if err != nil {
return nil, fmt.Errorf("creating OTLP trace exporter: %w", err)
}
/* … */
}
```
To learn more on how to use the OTLP HTTP exporter, try out the
[otel-collector](https://github.com/open-telemetry/opentelemetry-go/tree/main/example/otel-collector)
### Jaeger
To try out the OTLP exporter, since v1.35.0 you can run
[Jaeger](https://www.jaegertracing.io/) as an OTLP endpoint and for trace
visualization in a docker container:
```shell
docker run -d --name jaeger \
-e COLLECTOR_OTLP_ENABLED=true \
-p 16686:16686 \
-p 4318:4318 \
jaegertracing/all-in-one:latest
```
## Prometheus
Prometheus export is available in the
`go.opentelemetry.io/otel/exporters/prometheus` package.
Please find more documentation on
[GitHub](https://github.com/open-telemetry/opentelemetry-go/tree/main/exporters/prometheus)

View File

@ -1,693 +0,0 @@
---
title: Getting Started
weight: 10
---
Welcome to the OpenTelemetry for Go getting started guide! This guide will walk
you through the basic steps in installing, instrumenting with, configuring, and
exporting data from OpenTelemetry. Before you get started, be sure to have Go
1.16 or newer installed.
Understanding how a system is functioning when it is failing or having issues is
critical to resolving those issues. One strategy to understand this is with
tracing. This guide shows how the OpenTelemetry Go project can be used to trace
an example application. You will start with an application that computes
Fibonacci numbers for users, and from there you will add instrumentation to
produce tracing telemetry with OpenTelemetry Go.
For reference, a complete example of the code you will build can be found
[here](https://github.com/open-telemetry/opentelemetry-go/tree/main/example/fib).
To start building the application, make a new directory named `fib` to house our
Fibonacci project. Next, add the following to a new file named `fib.go` in that
directory.
```go
package main
// Fibonacci returns the n-th fibonacci number.
func Fibonacci(n uint) (uint64, error) {
if n <= 1 {
return uint64(n), nil
}
var n2, n1 uint64 = 0, 1
for i := uint(2); i < n; i++ {
n2, n1 = n1, n1+n2
}
return n2 + n1, nil
}
```
With your core logic added, you can now build your application around it. Add a
new `app.go` file with the following application logic.
```go
package main
import (
"context"
"fmt"
"io"
"log"
)
// App is a Fibonacci computation application.
type App struct {
r io.Reader
l *log.Logger
}
// NewApp returns a new App.
func NewApp(r io.Reader, l *log.Logger) *App {
return &App{r: r, l: l}
}
// Run starts polling users for Fibonacci number requests and writes results.
func (a *App) Run(ctx context.Context) error {
for {
n, err := a.Poll(ctx)
if err != nil {
return err
}
a.Write(ctx, n)
}
}
// Poll asks a user for input and returns the request.
func (a *App) Poll(ctx context.Context) (uint, error) {
a.l.Print("What Fibonacci number would you like to know: ")
var n uint
_, err := fmt.Fscanf(a.r, "%d\n", &n)
return n, err
}
// Write writes the n-th Fibonacci number back to the user.
func (a *App) Write(ctx context.Context, n uint) {
f, err := Fibonacci(n)
if err != nil {
a.l.Printf("Fibonacci(%d): %v\n", n, err)
} else {
a.l.Printf("Fibonacci(%d) = %d\n", n, f)
}
}
```
With your application fully composed, you need a `main()` function to actually
run the application. In a new `main.go` file add the following run logic.
```go
package main
import (
"context"
"log"
"os"
"os/signal"
)
func main() {
l := log.New(os.Stdout, "", 0)
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt)
errCh := make(chan error)
app := NewApp(os.Stdin, l)
go func() {
errCh <- app.Run(context.Background())
}()
select {
case <-sigCh:
l.Println("\ngoodbye")
return
case err := <-errCh:
if err != nil {
l.Fatal(err)
}
}
}
```
With the code complete it is almost time to run the application. Before you can
do that you need to initialize this directory as a Go module. From your
terminal, run the command `go mod init fib` in the `fib` directory. This will
create a `go.mod` file, which is used by Go to manage imports. Now you should be
able to run the application!
```console
$ go run .
What Fibonacci number would you like to know:
42
Fibonacci(42) = 267914296
What Fibonacci number would you like to know:
^C
goodbye
```
The application can be exited with <kbd>Ctrl+C</kbd>. You should see a similar
output as above, if not make sure to go back and fix any errors.
## Trace Instrumentation
OpenTelemetry is split into two parts: an API to instrument code with, and SDKs
that implement the API. To start integrating OpenTelemetry into any project, the
API is used to define how telemetry is generated. To generate tracing telemetry
in your application you will use the OpenTelemetry Trace API from the
[`go.opentelemetry.io/otel/trace`] package.
First, you need to install the necessary packages for the Trace API. Run the
following command in your working directory.
```sh
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/trace
```
Now that the packages installed you can start updating your application with
imports you will use in the `app.go` file.
```go
import (
"context"
"fmt"
"io"
"log"
"strconv"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
```
With the imports added, you can start instrumenting.
The OpenTelemetry Tracing API provides a [`Tracer`] to create traces. These
[`Tracer`]s are designed to be associated with one instrumentation library. That
way telemetry they produce can be understood to come from that part of a code
base. To uniquely identify your application to the [`Tracer`] you will create a
constant with the package name in `app.go`.
```go
// name is the Tracer name used to identify this instrumentation library.
const name = "fib"
```
Using the full-qualified package name, something that should be unique for Go
packages, is the standard way to identify a [`Tracer`]. If your example package
name differs, be sure to update the name you use here to match.
Everything should be in place now to start tracing your application. But first,
what is a trace? And, how exactly should you build them for your application?
To back up a bit, a trace is a type of telemetry that represents work being done
by a service. A trace is a record of the connection(s) between participants
processing a transaction, often through client/server requests processing and
other forms of communication.
Each part of the work that a service performs is represented in the trace by a
span. Those spans are not just an unordered collection. Like the call stack of
our application, those spans are defined with relationships to one another. The
"root" span is the only span without a parent, it represents how a service
request is started. All other spans have a parent relationship to another span
in the same trace.
If this last part about span relationships doesn't make complete sense now,
don't worry. The most important takeaway is that each part of your code, which
does some work, should be represented as a span. You will have a better
understanding of these span relationships after you instrument your code, so
let's get started.
Start by instrumenting the `Run` method.
```go
// Run starts polling users for Fibonacci number requests and writes results.
func (a *App) Run(ctx context.Context) error {
for {
// Each execution of the run loop, we should get a new "root" span and context.
newCtx, span := otel.Tracer(name).Start(ctx, "Run")
n, err := a.Poll(newCtx)
if err != nil {
span.End()
return err
}
a.Write(newCtx, n)
span.End()
}
}
```
The above code creates a span for every iteration of the for loop. The span is
created using a [`Tracer`] from the
[global `TracerProvider`](https://pkg.go.dev/go.opentelemetry.io/otel#GetTracerProvider).
You will learn more about [`TracerProvider`]s and handle the other side of
setting up a global [`TracerProvider`] when you install an SDK in a later
section. For now, as an instrumentation author, all you need to worry about is
that you are using an appropriately named [`Tracer`] from a [`TracerProvider`]
when you write `otel.Tracer(name)`.
Next, instrument the `Poll` method.
```go
// Poll asks a user for input and returns the request.
func (a *App) Poll(ctx context.Context) (uint, error) {
_, span := otel.Tracer(name).Start(ctx, "Poll")
defer span.End()
a.l.Print("What Fibonacci number would you like to know: ")
var n uint
_, err := fmt.Fscanf(a.r, "%d\n", &n)
// Store n as a string to not overflow an int64.
nStr := strconv.FormatUint(uint64(n), 10)
span.SetAttributes(attribute.String("request.n", nStr))
return n, err
}
```
Similar to the `Run` method instrumentation, this adds a span to the method to
track the computation performed. However, it also adds an attribute to annotate
the span. This annotation is something you can add when you think a user of your
application will want to see the state or details about the run environment when
looking at telemetry.
Finally, instrument the `Write` method.
```go
// Write writes the n-th Fibonacci number back to the user.
func (a *App) Write(ctx context.Context, n uint) {
var span trace.Span
ctx, span = otel.Tracer(name).Start(ctx, "Write")
defer span.End()
f, err := func(ctx context.Context) (uint64, error) {
_, span := otel.Tracer(name).Start(ctx, "Fibonacci")
defer span.End()
return Fibonacci(n)
}(ctx)
if err != nil {
a.l.Printf("Fibonacci(%d): %v\n", n, err)
} else {
a.l.Printf("Fibonacci(%d) = %d\n", n, f)
}
}
```
This method is instrumented with two spans. One to track the `Write` method
itself, and another to track the call to the core logic with the `Fibonacci`
function. Do you see how context is passed through the spans? Do you see how
this also defines the relationship between spans?
In OpenTelemetry Go the span relationships are defined explicitly with a
`context.Context`. When a span is created a context is returned alongside the
span. That context will contain a reference to the created span. If that context
is used when creating another span the two spans will be related. The original
span will become the new span's parent, and as a corollary, the new span is said
to be a child of the original. This hierarchy gives traces structure, structure
that helps show a computation path through a system. Based on what you
instrumented above and this understanding of span relationships you should
expect a trace for each execution of the run loop to look like this.
```
Run
├── Poll
└── Write
└── Fibonacci
```
A `Run` span will be a parent to both a `Poll` and `Write` span, and the `Write`
span will be a parent to a `Fibonacci` span.
Now how do you actually see the produced spans? To do this you will need to
configure and install an SDK.
## SDK Installation
OpenTelemetry is designed to be modular in its implementation of the
OpenTelemetry API. The OpenTelemetry Go project offers an SDK package,
[`go.opentelemetry.io/otel/sdk`], that implements this API and adheres to the
OpenTelemetry specification. To start using this SDK you will first need to
create an exporter, but before anything can happen we need to install some
packages. Run the following in the `fib` directory to install the trace STDOUT
exporter and the SDK.
```sh
go get go.opentelemetry.io/otel/sdk \
go.opentelemetry.io/otel/exporters/stdout/stdouttrace
```
Now add the needed imports to `main.go`.
```go
import (
"context"
"io"
"log"
"os"
"os/signal"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
)
```
### Creating a Console Exporter
The SDK connects telemetry from the OpenTelemetry API to exporters. Exporters
are packages that allow telemetry data to be emitted somewhere - either to the
console (which is what we're doing here), or to a remote system or collector for
further analysis and/or enrichment. OpenTelemetry supports a variety of
exporters through its ecosystem including popular open source tools like
[Jaeger](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger),
[Zipkin](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/zipkin), and
[Prometheus](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/prometheus).
To initialize the console exporter, add the following function to the `main.go`
file:
```go
// newExporter returns a console exporter.
func newExporter(w io.Writer) (trace.SpanExporter, error) {
return stdouttrace.New(
stdouttrace.WithWriter(w),
// Use human-readable output.
stdouttrace.WithPrettyPrint(),
// Do not print timestamps for the demo.
stdouttrace.WithoutTimestamps(),
)
}
```
This creates a new console exporter with basic options. You will use this
function later when you configure the SDK to send telemetry data to it, but
first you need to make sure that data is identifiable.
### Creating a Resource
Telemetry data can be crucial to solving issues with a service. The catch is,
you need a way to identify what service, or even what service instance, that
data is coming from. OpenTelemetry uses a [`Resource`] to represent the entity
producing telemetry. Add the following function to the `main.go` file to create
an appropriate [`Resource`] for the application.
```go
// newResource returns a resource describing this application.
func newResource() *resource.Resource {
r, _ := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName("fib"),
semconv.ServiceVersion("v0.1.0"),
attribute.String("environment", "demo"),
),
)
return r
}
```
Any information you would like to associate with all telemetry data the SDK
handles can be added to the returned [`Resource`]. This is done by registering
the [`Resource`] with the [`TracerProvider`]. Something you can now create!
### Installing a Tracer Provider
You have your application instrumented to produce telemetry data and you have an
exporter to send that data to the console, but how are they connected? This is
where the [`TracerProvider`] is used. It is a centralized point where
instrumentation will get a [`Tracer`] from and funnels the telemetry data from
these [`Tracer`]s to export pipelines.
The pipelines that receive and ultimately transmit data to exporters are called
[`SpanProcessor`]s. A [`TracerProvider`] can be configured to have multiple span
processors, but for this example you will only need to configure only one.
Update your `main` function in `main.go` with the following.
```go
func main() {
l := log.New(os.Stdout, "", 0)
// Write telemetry data to a file.
f, err := os.Create("traces.txt")
if err != nil {
l.Fatal(err)
}
defer f.Close()
exp, err := newExporter(f)
if err != nil {
l.Fatal(err)
}
tp := trace.NewTracerProvider(
trace.WithBatcher(exp),
trace.WithResource(newResource()),
)
defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
l.Fatal(err)
}
}()
otel.SetTracerProvider(tp)
/* … */
}
```
There's a fair amount going on here. First you are creating a console exporter
that will export to a file. You are then registering the exporter with a new
[`TracerProvider`]. This is done with a [`BatchSpanProcessor`] when it is passed
to the [`trace.WithBatcher`] option. Batching data is a good practice and will
help not overload systems downstream. Finally, with the [`TracerProvider`]
created, you are deferring a function to flush and stop it, and registering it
as the global OpenTelemetry [`TracerProvider`].
Do you remember in the previous instrumentation section when we used the global
[`TracerProvider`] to get a [`Tracer`]? This last step, registering the
[`TracerProvider`] globally, is what will connect that instrumentation's
[`Tracer`] with this [`TracerProvider`]. This pattern, using a global
[`TracerProvider`], is convenient, but not always appropriate.
[`TracerProvider`]s can be explicitly passed to instrumentation or inferred from
a context that contains a span. For this simple example using a global provider
makes sense, but for more complex or distributed codebases these other ways of
passing [`TracerProvider`]s may make more sense.
## Putting It All Together
You should now have a working application that produces trace telemetry data!
Give it a try.
```console
$ go run .
What Fibonacci number would you like to know:
42
Fibonacci(42) = 267914296
What Fibonacci number would you like to know:
^C
goodbye
```
A new file named `traces.txt` should be created in your working directory. All
the traces created from running your application should be in there!
## (Bonus) Errors
At this point you have a working application and it is producing tracing
telemetry data. Unfortunately, it was discovered that there is an error in the
core functionality of the `fib` module.
```console
$ go run .
What Fibonacci number would you like to know:
100
Fibonacci(100) = 3736710778780434371
# …
```
But the 100-th Fibonacci number is `354224848179261915075`, not
`3736710778780434371`! This application is only meant as a demo, but it
shouldn't return wrong values. Update the `Fibonacci` function to return an
error instead of computing incorrect values.
```go
// Fibonacci returns the n-th fibonacci number. An error is returned if the
// fibonacci number cannot be represented as a uint64.
func Fibonacci(n uint) (uint64, error) {
if n <= 1 {
return uint64(n), nil
}
if n > 93 {
return 0, fmt.Errorf("unsupported fibonacci number %d: too large", n)
}
var n2, n1 uint64 = 0, 1
for i := uint(2); i < n; i++ {
n2, n1 = n1, n1+n2
}
return n2 + n1, nil
}
```
Great, you have fixed the code, but it would be ideal to include errors returned
to a user in the telemetry data. Luckily, spans can be configured to communicate
this information. Update the `Write` method in `app.go` with the following code.
```go
// Write writes the n-th Fibonacci number back to the user.
func (a *App) Write(ctx context.Context, n uint) {
var span trace.Span
ctx, span = otel.Tracer(name).Start(ctx, "Write")
defer span.End()
f, err := func(ctx context.Context) (uint64, error) {
_, span := otel.Tracer(name).Start(ctx, "Fibonacci")
defer span.End()
f, err := Fibonacci(n)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
}
return f, err
}(ctx)
/* … */
}
```
With this change any error returned from the `Fibonacci` function will mark that
span as an error and record an event describing the error.
This is a great start, but it is not the only error returned in from the
application. If a user makes a request for a non unsigned integer value the
application will fail. Update the `Poll` method with a similar fix to capture
this error in the telemetry data.
```go
// Poll asks a user for input and returns the request.
func (a *App) Poll(ctx context.Context) (uint, error) {
_, span := otel.Tracer(name).Start(ctx, "Poll")
defer span.End()
a.l.Print("What Fibonacci number would you like to know: ")
var n uint
_, err := fmt.Fscanf(a.r, "%d\n", &n)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return 0, err
}
// Store n as a string to not overflow an int64.
nStr := strconv.FormatUint(uint64(n), 10)
span.SetAttributes(attribute.String("request.n", nStr))
return n, nil
}
```
All that is left is updating imports for the `app.go` file to include the
[`go.opentelemetry.io/otel/codes`] package.
```go
import (
"context"
"fmt"
"io"
"log"
"strconv"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)
```
With these fixes in place and the instrumentation updated, re-trigger the bug.
```console
$ go run .
What Fibonacci number would you like to know:
100
Fibonacci(100): unsupported fibonacci number 100: too large
What Fibonacci number would you like to know:
^C
goodbye
```
Excellent! The application no longer returns wrong values, and looking at the
telemetry data in the `traces.txt` file you should see the error captured as an
event.
```json
"Events": [
{
"Name": "exception",
"Attributes": [
{
"Key": "exception.type",
"Value": {
"Type": "STRING",
"Value": "*errors.errorString"
}
},
{
"Key": "exception.message",
"Value": {
"Type": "STRING",
"Value": "unsupported fibonacci number 100: too large"
}
}
],
...
}
]
```
## What's Next
This guide has walked you through adding tracing instrumentation to an
application and using a console exporter to send telemetry data to a file. There
are many other topics to cover in OpenTelemetry, but you should be ready to
start adding OpenTelemetry Go to your projects at this point. Go instrument your
code!
For more information about instrumenting your code and things you can do with
spans, refer to the [manual instrumentation](/docs/instrumentation/go/manual/)
documentation.
You'll also want to configure an appropriate exporter to
[export your telemetry data](/docs/instrumentation/go/exporters/) to one or more
telemetry backends.
[`go.opentelemetry.io/otel/trace`]:
https://pkg.go.dev/go.opentelemetry.io/otel/trace
[`go.opentelemetry.io/otel/sdk`]:
https://pkg.go.dev/go.opentelemetry.io/otel/sdk
[`go.opentelemetry.io/otel/codes`]:
https://pkg.go.dev/go.opentelemetry.io/otel/codes
[`tracer`]: https://pkg.go.dev/go.opentelemetry.io/otel/trace#Tracer
[`tracerprovider`]:
https://pkg.go.dev/go.opentelemetry.io/otel/trace#TracerProvider
[`resource`]: https://pkg.go.dev/go.opentelemetry.io/otel/sdk/resource#Resource
[`spanprocessor`]:
https://pkg.go.dev/go.opentelemetry.io/otel/sdk/trace#SpanProcessor
[`batchspanprocessor`]:
https://pkg.go.dev/go.opentelemetry.io/otel/sdk/trace#NewBatchSpanProcessor
[`trace.withbatcher`]:
https://pkg.go.dev/go.opentelemetry.io/otel/sdk/trace#WithBatcher

View File

@ -1,114 +0,0 @@
---
title: Using instrumentation libraries
linkTitle: Libraries
aliases:
- /docs/instrumentation/go/using_instrumentation_libraries
- /docs/instrumentation/go/automatic_instrumentation
weight: 40
---
Go does not support truly automatic instrumentation like other languages today.
Instead, you'll need to depend on
[instrumentation libraries](/docs/specs/otel/glossary/#instrumentation-library)
that generate telemetry data for a particular instrumented library. For example,
the instrumentation library for `net/http` will automatically create spans that
track inbound and outbound requests once you configure it in your code.
## Setup
Each instrumentation library is a package. In general, this means you need to
`go get` the appropriate package:
```sh
go get go.opentelemetry.io/contrib/instrumentation/{import-path}/otel{package-name}
```
And then configure it in your code based on what the library requires to be
activated.
## Example with `net/http`
As an example, here's how you can set up automatic instrumentation for inbound
HTTP requests for `net/http`:
First, get the `net/http` instrumentation library:
```sh
go get go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
```
Next, use the library to wrap an HTTP handler in your code:
```go
package main
import (
"context"
"fmt"
"log"
"net/http"
"time"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
)
// Package-level tracer.
// This should be configured in your code setup instead of here.
var tracer = otel.Tracer("github.com/full/path/to/mypkg")
// sleepy mocks work that your application does.
func sleepy(ctx context.Context) {
_, span := tracer.Start(ctx, "sleep")
defer span.End()
sleepTime := 1 * time.Second
time.Sleep(sleepTime)
span.SetAttributes(attribute.Int("sleep.duration", int(sleepTime)))
}
// httpHandler is an HTTP handler function that is going to be instrumented.
func httpHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World! I am instrumented automatically!")
ctx := r.Context()
sleepy(ctx)
}
func main() {
// Wrap your httpHandler function.
handler := http.HandlerFunc(httpHandler)
wrappedHandler := otelhttp.NewHandler(handler, "hello-instrumented")
http.Handle("/hello-instrumented", wrappedHandler)
// And start the HTTP serve.
log.Fatal(http.ListenAndServe(":3030", nil))
}
```
Assuming that you have a `Tracer` and [exporter](../exporters/) configured, this
code will:
- Start an HTTP server on port `3030`
- Automatically generate a span for each inbound HTTP request to
`/hello-instrumented`
- Create a child span of the automatically-generated one that tracks the work
done in `sleepy`
Connecting manual instrumentation you write in your app with instrumentation
generated from a library is essential to get good observability into your apps
and services.
## Available packages
A full list of instrumentation libraries available can be found in the
[OpenTelemetry registry](/ecosystem/registry/?language=go&component=instrumentation).
## Next steps
Instrumentation libraries can do things like generate telemetry data for inbound
and outbound HTTP requests, but they don't instrument your actual application.
To get richer telemetry data, use [manual instrumentation](../manual/) to enrich
your telemetry data from instrumentation libraries with instrumentation from
your running application.

View File

@ -1,302 +0,0 @@
---
title: Manual Instrumentation
linkTitle: Manual
aliases:
- /docs/instrumentation/go/instrumentation
- /docs/instrumentation/go/manual_instrumentation
weight: 30
---
Instrumentation is the process of adding observability code to your application.
There are two general types of instrumentation - automatic, and manual - and you
should be familiar with both in order to effectively instrument your software.
## Getting a Tracer
To create spans, you'll need to acquire or initialize a tracer first.
### Initializing a new tracer
Ensure you have the right packages installed:
```sh
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/trace \
go.opentelemetry.io/otel/sdk \
```
Then initialize an exporter, resources, tracer provider, and finally a tracer.
```go
package app
import (
"context"
"fmt"
"log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
"go.opentelemetry.io/otel/trace"
)
var tracer trace.Tracer
func newExporter(ctx context.Context) /* (someExporter.Exporter, error) */ {
// Your preferred exporter: console, jaeger, zipkin, OTLP, etc.
}
func newTraceProvider(exp sdktrace.SpanExporter) *sdktrace.TracerProvider {
// Ensure default SDK resources and the required service name are set.
r, err := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName("ExampleService"),
),
)
if err != nil {
panic(err)
}
return sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exp),
sdktrace.WithResource(r),
)
}
func main() {
ctx := context.Background()
exp, err := newExporter(ctx)
if err != nil {
log.Fatalf("failed to initialize exporter: %v", err)
}
// Create a new tracer provider with a batch span processor and the given exporter.
tp := newTraceProvider(exp)
// Handle shutdown properly so nothing leaks.
defer func() { _ = tp.Shutdown(ctx) }()
otel.SetTracerProvider(tp)
// Finally, set the tracer that can be used for this package.
tracer = tp.Tracer("ExampleService")
}
```
You can now access `tracer` to manually instrument your code.
## Creating Spans
Spans are created by tracers. If you don't have one initialized, you'll need to
do that.
To create a span with a tracer, you'll also need a handle on a `context.Context`
instance. These will typically come from things like a request object and may
already contain a parent span from an [instrumentation library][].
```go
func httpHandler(w http.ResponseWriter, r *http.Request) {
ctx, span := tracer.Start(r.Context(), "hello-span")
defer span.End()
// do some work to track with hello-span
}
```
In Go, the `context` package is used to store the active span. When you start a
span, you'll get a handle on not only the span that's created, but the modified
context that contains it.
Once a span has completed, it is immutable and can no longer be modified.
### Get the current span
To get the current span, you'll need to pull it out of a `context.Context` you
have a handle on:
```go
// This context needs contain the active span you plan to extract.
ctx := context.TODO()
span := trace.SpanFromContext(ctx)
// Do something with the current span, optionally calling `span.End()` if you want it to end
```
This can be helpful if you'd like to add information to the current span at a
point in time.
### Create nested spans
You can create a nested span to track work in a nested operation.
If the current `context.Context` you have a handle on already contains a span
inside of it, creating a new span makes it a nested span. For example:
```go
func parentFunction(ctx context.Context) {
ctx, parentSpan := tracer.Start(ctx, "parent")
defer parentSpan.End()
// call the child function and start a nested span in there
childFunction(ctx)
// do more work - when this function ends, parentSpan will complete.
}
func childFunction(ctx context.Context) {
// Create a span to track `childFunction()` - this is a nested span whose parent is `parentSpan`
ctx, childSpan := tracer.Start(ctx, "child")
defer childSpan.End()
// do work here, when this function returns, childSpan will complete.
}
```
Once a span has completed, it is immutable and can no longer be modified.
### Span Attributes
Attributes are keys and values that are applied as metadata to your spans and
are useful for aggregating, filtering, and grouping traces. Attributes can be
added at span creation, or at any other time during the lifecycle of a span
before it has completed.
```go
// setting attributes at creation...
ctx, span = tracer.Start(ctx, "attributesAtCreation", trace.WithAttributes(attribute.String("hello", "world")))
// ... and after creation
span.SetAttributes(attribute.Bool("isTrue", true), attribute.String("stringAttr", "hi!"))
```
Attribute keys can be precomputed, as well:
```go
var myKey = attribute.Key("myCoolAttribute")
span.SetAttributes(myKey.String("a value"))
```
#### Semantic Attributes
Semantic Attributes are attributes that are defined by the [OpenTelemetry
Specification][] in order to provide a shared set of attribute keys across
multiple languages, frameworks, and runtimes for common concepts like HTTP
methods, status codes, user agents, and more. These attributes are available in
the `go.opentelemetry.io/otel/semconv/v1.12.0` package.
For details, see [Trace semantic conventions][].
### Events
An event is a human-readable message on a span that represents "something
happening" during it's lifetime. For example, imagine a function that requires
exclusive access to a resource that is under a mutex. An event could be created
at two points - once, when we try to gain access to the resource, and another
when we acquire the mutex.
```go
span.AddEvent("Acquiring lock")
mutex.Lock()
span.AddEvent("Got lock, doing work...")
// do stuff
span.AddEvent("Unlocking")
mutex.Unlock()
```
A useful characteristic of events is that their timestamps are displayed as
offsets from the beginning of the span, allowing you to easily see how much time
elapsed between them.
Events can also have attributes of their own -
```go
span.AddEvent("Cancelled wait due to external signal", trace.WithAttributes(attribute.Int("pid", 4328), attribute.String("signal", "SIGHUP")))
```
### Set span status
A status can be set on a span, typically used to specify that there was an error
in the operation a span is tracking - .`Error`.
```go
import (
// ...
"go.opentelemetry.io/otel/codes"
// ...
)
// ...
result, err := operationThatCouldFail()
if err != nil {
span.SetStatus(codes.Error, "operationThatCouldFail failed")
}
```
By default, the status for all spans is `Unset`. In rare cases, you may also
wish to set the status to `Ok`. This should generally not be necessary, though.
### Record errors
If you have an operation that failed and you wish to capture the error it
produced, you can record that error.
```go
import (
// ...
"go.opentelemetry.io/otel/codes"
// ...
)
// ...
result, err := operationThatCouldFail()
if err != nil {
span.SetStatus(codes.Error, "operationThatCouldFail failed")
span.RecordError(err)
}
```
It is highly recommended that you also set a span's status to `Error` when using
`RecordError`, unless you do not wish to consider the span tracking a failed
operation as an error span. The `RecordError` function does **not**
automatically set a span status when called.
## Creating Metrics
The metrics API is currently unstable, documentation TBA.
## Propagators and Context
Traces can extend beyond a single process. This requires _context propagation_,
a mechanism where identifiers for a trace are sent to remote processes.
In order to propagate trace context over the wire, a propagator must be
registered with the OpenTelemetry API.
```go
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
)
...
otel.SetTextMapPropagator(propagation.TraceContext{})
```
> OpenTelemetry also supports the B3 header format, for compatibility with
> existing tracing systems (`go.opentelemetry.io/contrib/propagators/b3`) that
> do not support the W3C TraceContext standard.
After configuring context propagation, you'll most likely want to use automatic
instrumentation to handle the behind-the-scenes work of actually managing
serializing the context.
[opentelemetry specification]: /docs/specs/otel/
[trace semantic conventions]: /docs/specs/otel/trace/semantic_conventions/
[instrumentation library]: ../libraries/

View File

@ -1,50 +0,0 @@
---
title: Resources
weight: 70
---
Resources are a special type of attribute that apply to all spans generated by a
process. These should be used to represent underlying metadata about a process
that's non-ephemeral &mdash; for example, the hostname of a process, or its
instance ID.
Resources should be assigned to a tracer provider at its initialization, and are
created much like attributes:
```go
resources := resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("myService"),
semconv.ServiceVersionKey.String("1.0.0"),
semconv.ServiceInstanceIDKey.String("abcdef12345"),
)
provider := sdktrace.NewTracerProvider(
...
sdktrace.WithResource(resources),
)
```
Note the use of the `semconv` package to provide
[conventional names](/docs/concepts/semantic-conventions/) for resource
attributes. This helps ensure that consumers of telemetry produced with these
semantic conventions can easily discover relevant attributes and understand
their meaning.
Resources can also be detected automatically through `resource.Detector`
implementations. These `Detector`s may discover information about the currently
running process, the operating system it is running on, the cloud provider
hosting that operating system instance, or any number of other resource
attributes.
```go
resources := resource.New(context.Background(),
resource.WithFromEnv(), // pull attributes from OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME environment variables
resource.WithProcess(), // This option configures a set of Detectors that discover process information
resource.WithOS(), // This option configures a set of Detectors that discover OS information
resource.WithContainer(), // This option configures a set of Detectors that discover container information
resource.WithHost(), // This option configures a set of Detectors that discover host information
resource.WithDetectors(thirdparty.Detector{}), // Bring your own external Detector implementation
resource.WithAttributes(attribute.String("foo", "bar")), // Or specify resource attributes directly
)
```

View File

@ -1,35 +0,0 @@
---
title: Sampling
weight: 80
---
Sampling is a process that restricts the amount of traces that are generated by
a system. The exact sampler you should use depends on your specific needs, but
in general you should make a decision at the start of a trace, and allow the
sampling decision to propagate to other services.
A sampler needs to be set on the tracer provider when its configured, as
follows:
```go
provider := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
)
```
`AlwaysSample` and `NeverSample` are fairly self-explanatory. Always means that
every trace will be sampled, the converse holds as true for Never. When you're
getting started, or in a development environment, you'll almost always want to
use `AlwaysSample`.
Other samplers include:
- `TraceIDRatioBased`, which will sample a fraction of traces, based on the
fraction given to the sampler. Thus, if you set this to .5, half of traces
will be sampled.
- `ParentBased`, which behaves differently based on the incoming sampling
decision. In general, this will sample spans that have parents that were
sampled, and will not sample spans whose parents were _not_ sampled.
When you're in production, you should consider using the `TraceIDRatioBased`
sampler with the `ParentBased` sampler.