mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2024-12-24 20:14:40 +02:00
Update the website getting started docs (#2203)
* Update the website getting started docs Add a new example, fib, that contains an application for the computation of Fibonacci numbers. Use this example to update the website getting started documentation. * Revise docs english * Update example/fib/go.mod Co-authored-by: Anthony Mirabella <a9@aneurysm9.com> * Add a What's Next section * Clean up intro * Apply suggestions from code review Co-authored-by: Anthony Mirabella <a9@aneurysm9.com> * Apply feedback * Return from Poll on error * Update website_docs/getting-started.md Co-authored-by: Joshua MacDonald <jmacd@users.noreply.github.com> * Add root and parent relationship info Co-authored-by: Anthony Mirabella <a9@aneurysm9.com> Co-authored-by: Joshua MacDonald <jmacd@users.noreply.github.com>
This commit is contained in:
parent
a7b9d02167
commit
04de34a2d6
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@ -56,6 +56,16 @@ updates:
|
||||
schedule:
|
||||
day: sunday
|
||||
interval: weekly
|
||||
-
|
||||
package-ecosystem: gomod
|
||||
directory: /example/fib
|
||||
labels:
|
||||
- dependencies
|
||||
- go
|
||||
- "Skip Changelog"
|
||||
schedule:
|
||||
day: sunday
|
||||
interval: weekly
|
||||
-
|
||||
package-ecosystem: gomod
|
||||
directory: /example/prom-collector
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@ coverage.*
|
||||
|
||||
gen/
|
||||
|
||||
/example/fib/fib
|
||||
/example/jaeger/jaeger
|
||||
/example/namedtracer/namedtracer
|
||||
/example/opencensus/opencensus
|
||||
|
@ -14,6 +14,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
- Added `"go.opentelemetry.io/otel/trace".WithStackTrace` option to add a stack trace when using `span.RecordError` or when panic is handled in `span.End`. (#2163)
|
||||
- Added typed slice attribute types and functionality to the `go.opentelemetry.io/otel/attribute` package to replace the existing array type and functions. (#2162)
|
||||
- `BoolSlice`, `IntSlice`, `Int64Slice`, `Float64Slice`, and `StringSlice` replace the use of the `Array` function in the package.
|
||||
- Added the `go.opentelemetry.io/otel/example/fib` example package.
|
||||
Included is an example application that computes Fibonacci numbers. (#2203)
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -72,3 +72,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ./test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/sdk/export/metric => ../../../sdk/export/metric
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric
|
||||
|
||||
replace go.opentelemetry.io/otel/trace => ../../../trace
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../example/fib
|
||||
|
@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
104
example/fib/app.go
Normal file
104
example/fib/app.go
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
// name is the Tracer name used to identify this instrumentation library.
|
||||
const name = "fib"
|
||||
|
||||
// App is an 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 {
|
||||
var span trace.Span
|
||||
ctx, span = otel.Tracer(name).Start(ctx, "Run")
|
||||
|
||||
n, err := a.Poll(ctx)
|
||||
if err != nil {
|
||||
span.End()
|
||||
return err
|
||||
}
|
||||
|
||||
a.Write(ctx, n)
|
||||
span.End()
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
a.l.Printf("Fibonacci(%d): %v\n", n, err)
|
||||
} else {
|
||||
a.l.Printf("Fibonacci(%d) = %d\n", n, f)
|
||||
}
|
||||
}
|
35
example/fib/fib.go
Normal file
35
example/fib/fib.go
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Fibonacci returns the n-th fibonacci number.
|
||||
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
|
||||
}
|
72
example/fib/go.mod
Normal file
72
example/fib/go.mod
Normal file
@ -0,0 +1,72 @@
|
||||
module go.opentelemetry.io/otel/example/fib
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
go.opentelemetry.io/otel v1.0.0-RC2
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.0.0-RC2
|
||||
go.opentelemetry.io/otel/sdk v1.0.0-RC2
|
||||
go.opentelemetry.io/otel/trace v1.0.0-RC2
|
||||
)
|
||||
|
||||
replace go.opentelemetry.io/otel => ../..
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing
|
||||
|
||||
replace go.opentelemetry.io/otel/example/jaeger => ../jaeger
|
||||
|
||||
replace go.opentelemetry.io/otel/example/namedtracer => ../namedtracer
|
||||
|
||||
replace go.opentelemetry.io/otel/example/opencensus => ../opencensus
|
||||
|
||||
replace go.opentelemetry.io/otel/example/otel-collector => ../otel-collector
|
||||
|
||||
replace go.opentelemetry.io/otel/example/passthrough => ../passthrough
|
||||
|
||||
replace go.opentelemetry.io/otel/example/prometheus => ../prometheus
|
||||
|
||||
replace go.opentelemetry.io/otel/example/zipkin => ../zipkin
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace
|
||||
|
||||
replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin
|
||||
|
||||
replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric
|
||||
|
||||
replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools
|
||||
|
||||
replace go.opentelemetry.io/otel/metric => ../../metric
|
||||
|
||||
replace go.opentelemetry.io/otel/oteltest => ../../oteltest
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk => ../../sdk
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric
|
||||
|
||||
replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
|
||||
|
||||
replace go.opentelemetry.io/otel/trace => ../../trace
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ./
|
17
example/fib/go.sum
Normal file
17
example/fib/go.sum
Normal file
@ -0,0 +1,17 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
101
example/fib/main.go
Normal file
101
example/fib/main.go
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
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.4.0"
|
||||
)
|
||||
|
||||
// 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(),
|
||||
)
|
||||
}
|
||||
|
||||
// newResource returns a resource describing this application.
|
||||
func newResource() *resource.Resource {
|
||||
r, _ := resource.Merge(
|
||||
resource.Default(),
|
||||
resource.NewWithAttributes(
|
||||
semconv.SchemaURL,
|
||||
semconv.ServiceNameKey.String("fib"),
|
||||
semconv.ServiceVersionKey.String("v0.1.0"),
|
||||
attribute.String("environment", "demo"),
|
||||
),
|
||||
)
|
||||
return r
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
@ -71,3 +71,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
@ -73,3 +73,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
@ -72,3 +72,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
@ -72,3 +72,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
@ -71,3 +71,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
@ -70,3 +70,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../fib
|
||||
|
@ -73,3 +73,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../stdout/stdou
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
@ -80,3 +80,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../stdout/st
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ./otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../example/fib
|
||||
|
@ -78,3 +78,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../stdout
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib
|
||||
|
@ -80,3 +80,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../stdout
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../otlpmetricgrpc
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib
|
||||
|
@ -76,3 +76,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../stdout/st
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../example/fib
|
||||
|
@ -72,3 +72,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../stdout
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib
|
||||
|
@ -70,3 +70,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../stdout
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib
|
||||
|
@ -75,3 +75,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../stdout/stdou
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
@ -73,3 +73,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../stdouttrace
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../example/fib
|
||||
|
@ -71,3 +71,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../stdoutmetri
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../example/fib
|
||||
|
@ -74,3 +74,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../stdout/stdou
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
2
go.mod
2
go.mod
@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ./exporters/std
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ./exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ./bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ./example/fib
|
||||
|
@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
@ -73,3 +73,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
@ -70,3 +70,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../exporters/st
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../example/fib
|
||||
|
@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../exporters/st
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../example/fib
|
||||
|
@ -70,3 +70,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../../export
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../../example/fib
|
||||
|
@ -71,3 +71,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../exporters/st
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../example/fib
|
||||
|
@ -73,3 +73,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
|
||||
|
@ -69,3 +69,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../exporters/st
|
||||
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../exporters/otlp/otlpmetric/otlpmetrichttp
|
||||
|
||||
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/test
|
||||
|
||||
replace go.opentelemetry.io/otel/example/fib => ../example/fib
|
||||
|
@ -3,25 +3,86 @@ title: "Getting Started"
|
||||
weight: 2
|
||||
---
|
||||
|
||||
Welcome to the OpenTelemetry for Go getting started guide! This guide will walk you the basic steps in installing, configuring, and exporting data from OpenTelemetry.
|
||||
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.15 or newer installed.
|
||||
|
||||
# Installation
|
||||
Understand 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.
|
||||
|
||||
OpenTelemetry packages for Go are available in the `go.opentelemetry.io/otel` namespace. You will need to add references to them in the `import` statement. We suggest using Go 1.15 or newer, for module support.
|
||||
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.
|
||||
|
||||
To get started with this guide, create a new directory and add a new file named `main.go` to it. In your terminal, run the command `go mod init main` in the same directory. This will create a `go.mod` file, which is used by Go to manage imports.
|
||||
```go
|
||||
package main
|
||||
|
||||
# Initialization and Configuration
|
||||
// Fibonacci returns the n-th fibonacci number.
|
||||
func Fibonacci(n uint) (uint64, error) {
|
||||
if n <= 1 {
|
||||
return uint64(n), nil
|
||||
}
|
||||
|
||||
To install the necessary prerequisites for OpenTelemetry, you'll want to run the following command in the directory with your `go.mod`:
|
||||
var n2, n1 uint64 = 0, 1
|
||||
for i := uint(2); i < n; i++ {
|
||||
n2, n1 = n1, n1+n2
|
||||
}
|
||||
|
||||
`go get go.opentelemetry.io/otel@v1.0.0-RC1 go.opentelemetry.io/otel/sdk@v1.0.0-RC1 go.opentelemetry.io/otel/exporters/stdout/stdouttrace@v1.0.0-RC1 go.opentelemetry.io/otel/trace@v1.0.0-RC1`
|
||||
return n2 + n1, nil
|
||||
}
|
||||
```
|
||||
|
||||
If you wish to include the experimental metrics support you will need to include a few additional modules:
|
||||
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 get go.opentelemetry.io/otel/metric@v0.21.0 go.opentelemetry.io/otel/sdk/metric@v0.21.0 go.opentelemetry.io/otel/exporters/stdout/stdoutmetric@v0.21.0`
|
||||
```go
|
||||
package main
|
||||
|
||||
In your `main.go` file, you'll need to import several packages:
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
// App is an 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()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.Write(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
|
||||
@ -29,190 +90,444 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
// For experimental metrics support, also include:
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/metric/global"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
||||
controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
|
||||
processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
|
||||
"go.opentelemetry.io/otel/sdk/metric/selector/simple"
|
||||
"os"
|
||||
)
|
||||
```
|
||||
|
||||
These packages contain the basic requirements for OpenTelemetry Go - the API itself, the metrics and tracing SDK, and context propagation. The exact libraries and packages that you'll use in an application will vary depending on what features you need - for example, if you're writing a library that will be used by others, you don't need to require the SDK packages and will rely solely on the API. In general, you should configure the SDK in your code as close to program initialization as possible in order to capture telemetry at the earliest time it's available.
|
||||
|
||||
## Creating a Console Exporter
|
||||
|
||||
The SDK requires an exporter to be created. 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, Zipkin, and Prometheus.
|
||||
|
||||
To initialize the console exporter, add the following code to the file your `main.go` file:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
traceExporter, err := stdouttrace.New(
|
||||
stdouttrace.WithPrettyPrint(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize stdouttrace export pipeline: %v", err)
|
||||
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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This creates a new console exporter with basic options - `WithPrettyPrint` formats the text nicely when its printed, so that it's easier for humans to read.
|
||||
|
||||
## Creating a Tracer Provider
|
||||
|
||||
A trace is a type of telemetry that represents work being done by a service. In a distributed system, a trace can be thought of as a 'stack trace', showing the work being done by each service as well as the upstream and downstream calls that its making to other services.
|
||||
|
||||
OpenTelemetry requires a trace provider to be initialized in order to generate traces. A trace provider can have multiple span processors, which are components that allow for span data to be modified or exported after it's created.
|
||||
|
||||
To create a trace provider, add the following code to your `main.go` file:
|
||||
|
||||
```go
|
||||
ctx := context.Background()
|
||||
bsp := sdktrace.NewBatchSpanProcessor(traceExporter)
|
||||
tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(bsp))
|
||||
|
||||
// Handle this error in a sensible manner where possible
|
||||
defer func() { _ = tp.Shutdown(ctx) }()
|
||||
```
|
||||
|
||||
This block of code will create a new batch span processor, a type of span processor that batches up multiple spans over a period of time, that writes to the exporter we created in the previous step. You can see examples of other uses for span processors in [this file](https://github.com/open-telemetry/opentelemetry-go/blob/v0.16.0/sdk/trace/span_processor_example_test.go). We also created an instance of a Go context. It will be used later to store some important data.
|
||||
|
||||
## Creating a Meter Provider
|
||||
|
||||
A metric is a captured measurement about the execution of a computer program at run time. Examples of metrics can be "count the number of requests completed", "count the number of active requests", "capture a queue length" or "capture the number of cache misses".
|
||||
|
||||
OpenTelemetry requires a meter provider to be initialized in order to create instruments that will generate metrics. The way metrics are exported depends on the used system. For example, prometheus uses a pull model, while OTLP uses a push model. In this document we use an stdout exporter which uses the latter. Thus we need to create a push controller that will periodically push the collected metrics to the exporter.
|
||||
|
||||
To create a meter provider, add the following code to your `main.go` file:
|
||||
|
||||
```go
|
||||
metricExporter, err := stdoutmetric.New(
|
||||
stdoutmetric.WithPrettyPrint(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize stdoutmetric export pipeline: %v", err)
|
||||
}
|
||||
|
||||
pusher := controller.New(
|
||||
processor.New(
|
||||
simple.NewWithExactDistribution(),
|
||||
metricExporter,
|
||||
),
|
||||
controller.WithExporter(metricExporter),
|
||||
controller.WithCollectPeriod(5*time.Second),
|
||||
)
|
||||
|
||||
err = pusher.Start(ctx)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize metric controller: %v", err)
|
||||
}
|
||||
|
||||
// Handle this error in a sensible manner where possible
|
||||
defer func() { _ = pusher.Stop(ctx) }()
|
||||
```
|
||||
|
||||
Again we create an exporter, this time using the `stdoutmetric` exporter package. Then we create a controller that uses a basic processor to aggregate and process metrics that are then sent to the exporter. The basic processor here uses a simple aggregator selector that decides what kind of an aggregator to use to aggregate measurements from a specific instrument. The processor also uses the exporter to learn how to prepare the aggregated measurements for the exporter to consume. The controller will periodically push aggregated measurements to the exporter.
|
||||
|
||||
## Setting Global Options
|
||||
|
||||
When using OpenTelemetry, it's a good practice to set a global tracer provider and a global meter provider. Doing so will make it easier for libraries and other dependencies that use the OpenTelemetry API to easily discover the SDK, and emit telemetry data. In addition, you'll want to configure context propagation options. Context propagation allows for OpenTelemetry to share values across multiple services - this includes trace identifiers, which ensure that all spans for a single request are part of the same trace, as well as baggage, which are arbitrary key/value pairs that you can use to pass observability data between services (for example, sharing a customer ID from one service to the next).
|
||||
|
||||
Setting up global options uses the `otel` package - add these options to your `main.go` file as shown:
|
||||
|
||||
```go
|
||||
otel.SetTracerProvider(tp)
|
||||
global.SetMeterProvider(pusher.MeterProvider())
|
||||
propagator := propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{})
|
||||
otel.SetTextMapPropagator(propagator)
|
||||
```
|
||||
|
||||
It's important to note that if you do not set a propagator, the default is to use the `NoOp` option, which means that context will not be shared between multiple services. To avoid that, we set up a composite propagator that consist of a baggage propagator and trace context propagator. That way, both trace information (trace IDs, span IDs, etc) and baggage will be propagated.
|
||||
|
||||
## Creating metric instruments
|
||||
|
||||
The next step is to create metric instruments that will capture measurements. There are two kinds of instruments: synchronous and asynchronous. Synchronous instruments capture measurements by explicitly calling the capture either by the application or by an instrumented library. Depending on the semantics of the measurements, we can say that synchronous instruments record or add measurements. Asynchronous instruments provide a callback that captures measurements. The callback is periodically called by meter in the background. We can say that asynchronous instrument performs observations.
|
||||
|
||||
Each measurement can be associated with attributes that can later be used by visualisation software to categorize and filter measurements. In case of synchronous instruments the attributes can be passed at the moment of capturing a measurement or can be passed when binding the instrument. Such a bound instrument can be later used to capture measurements without passing the attributes. In case of asynchronous instruments, the attributes are passed each time an observation is made explicitly in the callback.
|
||||
|
||||
To set up some metric instruments, add the following code to your `main.go` file -
|
||||
|
||||
```go
|
||||
lemonsKey := attribute.Key("ex.com/lemons")
|
||||
anotherKey := attribute.Key("ex.com/another")
|
||||
|
||||
commonAttributes := []attribute.KeyValue{lemonsKey.Int(10), attribute.String("A", "1"), attribute.String("B", "2"), attribute.String("C", "3")}
|
||||
|
||||
meter := global.Meter("ex.com/basic")
|
||||
|
||||
observerCallback := func(_ context.Context, result metric.Float64ObserverResult) {
|
||||
result.Observe(1, commonAttributes...)
|
||||
}
|
||||
_ = metric.Must(meter).NewFloat64ValueObserver("ex.com.one", observerCallback,
|
||||
metric.WithDescription("A ValueObserver set to 1.0"),
|
||||
)
|
||||
|
||||
valueRecorder := metric.Must(meter).NewFloat64ValueRecorder("ex.com.two")
|
||||
|
||||
boundRecorder := valueRecorder.Bind(commonAttributes...)
|
||||
defer boundRecorder.Unbind()
|
||||
```
|
||||
|
||||
In this block we first create some keys and attributes that we will later use when capturing the measurements. Then we ask a global meter provider to give us a named meter instance ("ex.com/basic"). This acts as a way to namespace our instruments and make them distinct from other instruments in this process or another. Then we use the meter to create two instruments - an asynchronous value observer and a synchronous value recorder.
|
||||
|
||||
# Quick Start
|
||||
|
||||
Let's put the concepts we've just covered together, and create a trace and some measurements in a single process. In our main function, after the initialization code, add the following:
|
||||
|
||||
```go
|
||||
tracer := otel.Tracer("ex.com/basic")
|
||||
|
||||
// we're ignoring errors here since we know these values are valid,
|
||||
// but do handle them appropriately if dealing with user-input
|
||||
foo, _ := baggage.NewMember("ex.com.foo", "foo1")
|
||||
bar, _ := baggage.NewMember("ex.com.bar", "bar1")
|
||||
bag, _ := baggage.New(foo, bar)
|
||||
ctx = baggage.ContextWithBaggage(ctx, bag)
|
||||
|
||||
func(ctx context.Context) {
|
||||
var span trace.Span
|
||||
ctx, span = tracer.Start(ctx, "operation")
|
||||
defer span.End()
|
||||
|
||||
span.AddEvent("Nice operation!", trace.WithAttributes(attribute.Int("bogons", 100)))
|
||||
span.SetAttributes(anotherKey.String("yes"))
|
||||
|
||||
meter.RecordBatch(
|
||||
ctx,
|
||||
commonAttributes,
|
||||
valueRecorder.Measurement(2.0),
|
||||
)
|
||||
|
||||
func(ctx context.Context) {
|
||||
var span trace.Span
|
||||
ctx, span = tracer.Start(ctx, "Sub operation...")
|
||||
defer span.End()
|
||||
|
||||
span.SetAttributes(lemonsKey.String("five"))
|
||||
span.AddEvent("Sub span event")
|
||||
boundRecorder.Record(ctx, 1.3)
|
||||
}(ctx)
|
||||
}(ctx)
|
||||
}
|
||||
```
|
||||
|
||||
In this snippet, we're doing a few things. First, we're asking the global trace provider for an instance of a tracer, which is the object that manages spans for our service. We provide a name (`"ex.com/basic"`) too, which acts in the same way as a name we gave to our meter instance. Here we can also see the use of the Go context - it contains baggage items that are propagated to other places in our code and to other processes. Which means that baggage items should be used within limits as baggage may be sent over the network. The other use of the Go context is to store a reference to a span, so it can be propagated between function calls and processes.
|
||||
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!
|
||||
|
||||
Inside our function, we're creating a new span by calling `tracer.Start` with the context we just created, and a name. Passing the context will set our span as 'active' in it, which is used in our inner function to make a new child span. The name is important - every span needs a name, and these names are the primary method of indicating what a span represents. Calling `defer span.End()` ensures that our span will complete once this function has finished its work. Spans can have attributes and events, which are metadata and log statements that help you interpret traces after-the-fact. Finally, in this code snippet we can see an example of creating a new function and propagating the span to it inside our code. When you run this program, you'll see that the 'Sub operation...' span has been created as a child of the 'operation' span.
|
||||
```sh
|
||||
$ go run .
|
||||
What Fibonacci number would you like to know:
|
||||
42
|
||||
Fibonacci(42) = 267914296
|
||||
What Fibonacci number would you like to know:
|
||||
^C
|
||||
goodbye
|
||||
```
|
||||
|
||||
We also record some measurements. Recording measurements with asynchronous instruments is controlled by SDK and the controller we use, so we do not need to do anything else after creating the instrument and passing the callback to it. For synchronous instruments there are two ways of recording measurements - either through the instrument, bounded or not (in our case it's a value recorder, so we use the `Record` function), or by making a batched measurement (with `meter.RecordBatch`). Batched measurements allow you to use multiple instruments to create measurement and record them once.
|
||||
The application can be exited with CTRL+C. 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@v1.0.0-RC2 \
|
||||
go.opentelemetry.io/otel/trace@v1.0.0-RC2
|
||||
```
|
||||
|
||||
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 use 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 you 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 to much sense now, don't worry. The important thing to take away is each part of your code that does work should to 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 {
|
||||
var span trace.Span
|
||||
ctx, span = otel.Tracer(name).Start(ctx, "Run")
|
||||
|
||||
n, err := a.Poll(ctx)
|
||||
if err != nil {
|
||||
span.End()
|
||||
return err
|
||||
}
|
||||
|
||||
a.Write(ctx, 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`. 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)
|
||||
|
||||
// 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@v1.0.0-RC2 \
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace@v1.0.0-RC2
|
||||
```
|
||||
|
||||
Now add the needed imports to `main.go`.
|
||||
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
||||
"go.opentelemetry.io/otel/sdk/trace"
|
||||
)
|
||||
```
|
||||
|
||||
## 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, Zipkin, and 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.ServiceNameKey.String("fib"),
|
||||
semconv.ServiceVersionKey.String("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.
|
||||
|
||||
```sh
|
||||
$ 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.
|
||||
|
||||
```sh
|
||||
$ 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)
|
||||
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.
|
||||
|
||||
```sh
|
||||
$ 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.
|
||||
|
||||
```
|
||||
"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 [Instrumenting](https://opentelemetry.io/docs/go/instrumentation/) documentation. Likewise, advanced topics about processing and exporting telemetry data can be found in the [Processing and Exporting Data](https://opentelemetry.io/docs/go/exporting_data/) documentation.
|
||||
|
Loading…
Reference in New Issue
Block a user