From cd084b88728cb473c413688318789439486b5e48 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Thu, 26 Aug 2021 17:39:58 -0700 Subject: [PATCH 01/10] 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. --- .github/dependabot.yml | 10 + .gitignore | 1 + CHANGELOG.md | 2 + bridge/opencensus/go.mod | 2 + bridge/opencensus/test/go.mod | 2 + bridge/opentracing/go.mod | 2 + example/fib/app.go | 106 ++++ example/fib/fib.go | 35 ++ example/fib/go.mod | 72 +++ example/fib/go.sum | 17 + example/fib/main.go | 101 ++++ example/jaeger/go.mod | 2 + example/namedtracer/go.mod | 2 + example/opencensus/go.mod | 2 + example/otel-collector/go.mod | 2 + example/passthrough/go.mod | 2 + example/prometheus/go.mod | 2 + example/zipkin/go.mod | 2 + exporters/jaeger/go.mod | 2 + exporters/otlp/otlpmetric/go.mod | 2 + .../otlp/otlpmetric/otlpmetricgrpc/go.mod | 2 + .../otlp/otlpmetric/otlpmetrichttp/go.mod | 2 + exporters/otlp/otlptrace/go.mod | 2 + exporters/otlp/otlptrace/otlptracegrpc/go.mod | 2 + exporters/otlp/otlptrace/otlptracehttp/go.mod | 2 + exporters/prometheus/go.mod | 2 + exporters/stdout/stdoutmetric/go.mod | 2 + exporters/stdout/stdouttrace/go.mod | 2 + exporters/zipkin/go.mod | 2 + go.mod | 2 + internal/metric/go.mod | 2 + internal/tools/go.mod | 2 + metric/go.mod | 2 + oteltest/go.mod | 2 + sdk/export/metric/go.mod | 2 + sdk/go.mod | 2 + sdk/metric/go.mod | 2 + trace/go.mod | 2 + website_docs/getting-started.md | 542 ++++++++++++++---- 39 files changed, 820 insertions(+), 126 deletions(-) create mode 100644 example/fib/app.go create mode 100644 example/fib/fib.go create mode 100644 example/fib/go.mod create mode 100644 example/fib/go.sum create mode 100644 example/fib/main.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1b2d8c0ce82..78144b73945 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -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 diff --git a/.gitignore b/.gitignore index f798ce8fbf7..f08c826802a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ coverage.* gen/ +/example/fib/main /example/jaeger/jaeger /example/namedtracer/namedtracer /example/opencensus/opencensus diff --git a/CHANGELOG.md b/CHANGELOG.md index a8b5d5d0d71..1009b836109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/bridge/opencensus/go.mod b/bridge/opencensus/go.mod index 46983755edd..99c91c9fe87 100644 --- a/bridge/opencensus/go.mod +++ b/bridge/opencensus/go.mod @@ -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 main => ../../example/fib diff --git a/bridge/opencensus/test/go.mod b/bridge/opencensus/test/go.mod index 24f96a36f03..942a88b47ec 100644 --- a/bridge/opencensus/test/go.mod +++ b/bridge/opencensus/test/go.mod @@ -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 main => ../../../example/fib diff --git a/bridge/opentracing/go.mod b/bridge/opentracing/go.mod index e623cf8c29d..d3aa482262c 100644 --- a/bridge/opentracing/go.mod +++ b/bridge/opentracing/go.mod @@ -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 main => ../../example/fib diff --git a/example/fib/app.go b/example/fib/app.go new file mode 100644 index 00000000000..ff70561b4ce --- /dev/null +++ b/example/fib/app.go @@ -0,0 +1,106 @@ +// 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()) + } + + // Store n as a string to not overflow an int64. + nStr := strconv.FormatUint(uint64(n), 10) + // This is going to be a high cardinality attribute because the user can + // pass an unbounded number of different values. This type of attribute + // should be avoided in instrumentation for high performance systems. + span.SetAttributes(attribute.String("request.n", nStr)) + + return n, err +} + +// 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) + } +} diff --git a/example/fib/fib.go b/example/fib/fib.go new file mode 100644 index 00000000000..817cc63b104 --- /dev/null +++ b/example/fib/fib.go @@ -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 +} diff --git a/example/fib/go.mod b/example/fib/go.mod new file mode 100644 index 00000000000..a7e293b5b2b --- /dev/null +++ b/example/fib/go.mod @@ -0,0 +1,72 @@ +module main + +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 main => ./ + +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 diff --git a/example/fib/go.sum b/example/fib/go.sum new file mode 100644 index 00000000000..2ae2b35b289 --- /dev/null +++ b/example/fib/go.sum @@ -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= diff --git a/example/fib/main.go b/example/fib/main.go new file mode 100644 index 00000000000..93b70322317 --- /dev/null +++ b/example/fib/main.go @@ -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) + } + } +} diff --git a/example/jaeger/go.mod b/example/jaeger/go.mod index a2646dcaae7..aacea7863aa 100644 --- a/example/jaeger/go.mod +++ b/example/jaeger/go.mod @@ -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 main => ../fib diff --git a/example/namedtracer/go.mod b/example/namedtracer/go.mod index a8cfd6783f1..f412df7bd85 100644 --- a/example/namedtracer/go.mod +++ b/example/namedtracer/go.mod @@ -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 main => ../fib diff --git a/example/opencensus/go.mod b/example/opencensus/go.mod index 2934f5753c0..edab95c138c 100644 --- a/example/opencensus/go.mod +++ b/example/opencensus/go.mod @@ -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 main => ../fib diff --git a/example/otel-collector/go.mod b/example/otel-collector/go.mod index a1de1cde85a..c6b6849b337 100644 --- a/example/otel-collector/go.mod +++ b/example/otel-collector/go.mod @@ -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 main => ../fib diff --git a/example/passthrough/go.mod b/example/passthrough/go.mod index 59071d08ae3..640905daab2 100644 --- a/example/passthrough/go.mod +++ b/example/passthrough/go.mod @@ -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 main => ../fib diff --git a/example/prometheus/go.mod b/example/prometheus/go.mod index 05bfb72e3c7..b56a81f5517 100644 --- a/example/prometheus/go.mod +++ b/example/prometheus/go.mod @@ -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 main => ../fib diff --git a/example/zipkin/go.mod b/example/zipkin/go.mod index 0ca2cea1e54..81f39530e82 100644 --- a/example/zipkin/go.mod +++ b/example/zipkin/go.mod @@ -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 main => ../fib diff --git a/exporters/jaeger/go.mod b/exporters/jaeger/go.mod index b0c78c8352f..5db2aa34657 100644 --- a/exporters/jaeger/go.mod +++ b/exporters/jaeger/go.mod @@ -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 main => ../../example/fib diff --git a/exporters/otlp/otlpmetric/go.mod b/exporters/otlp/otlpmetric/go.mod index e495750f2f6..9272f2acac9 100644 --- a/exporters/otlp/otlpmetric/go.mod +++ b/exporters/otlp/otlpmetric/go.mod @@ -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 main => ../../../example/fib diff --git a/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod b/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod index 889b4309487..ea886a9f51d 100644 --- a/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod +++ b/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod @@ -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 main => ../../../../example/fib diff --git a/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod b/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod index 22d8a406032..5cc7a690a1d 100644 --- a/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod +++ b/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod @@ -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 main => ../../../../example/fib diff --git a/exporters/otlp/otlptrace/go.mod b/exporters/otlp/otlptrace/go.mod index 02084f3669b..8004039fe57 100644 --- a/exporters/otlp/otlptrace/go.mod +++ b/exporters/otlp/otlptrace/go.mod @@ -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 main => ../../../example/fib diff --git a/exporters/otlp/otlptrace/otlptracegrpc/go.mod b/exporters/otlp/otlptrace/otlptracegrpc/go.mod index 7b85bf70812..d6f7643b9ab 100644 --- a/exporters/otlp/otlptrace/otlptracegrpc/go.mod +++ b/exporters/otlp/otlptrace/otlptracegrpc/go.mod @@ -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 main => ../../../../example/fib diff --git a/exporters/otlp/otlptrace/otlptracehttp/go.mod b/exporters/otlp/otlptrace/otlptracehttp/go.mod index ec13eb8d084..8c43ab1aec6 100644 --- a/exporters/otlp/otlptrace/otlptracehttp/go.mod +++ b/exporters/otlp/otlptrace/otlptracehttp/go.mod @@ -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 main => ../../../../example/fib diff --git a/exporters/prometheus/go.mod b/exporters/prometheus/go.mod index 9fc8b739312..fb29c56af27 100644 --- a/exporters/prometheus/go.mod +++ b/exporters/prometheus/go.mod @@ -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 main => ../../example/fib diff --git a/exporters/stdout/stdoutmetric/go.mod b/exporters/stdout/stdoutmetric/go.mod index 49c47ae9115..ffc4b73b7b4 100644 --- a/exporters/stdout/stdoutmetric/go.mod +++ b/exporters/stdout/stdoutmetric/go.mod @@ -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 main => ../../../example/fib diff --git a/exporters/stdout/stdouttrace/go.mod b/exporters/stdout/stdouttrace/go.mod index 07a5626a71c..ac485e3b200 100644 --- a/exporters/stdout/stdouttrace/go.mod +++ b/exporters/stdout/stdouttrace/go.mod @@ -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 main => ../../../example/fib diff --git a/exporters/zipkin/go.mod b/exporters/zipkin/go.mod index 45d5f064862..b7a0b52f1af 100644 --- a/exporters/zipkin/go.mod +++ b/exporters/zipkin/go.mod @@ -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 main => ../../example/fib diff --git a/go.mod b/go.mod index 0a9e1a9eaf0..64b621d3f75 100644 --- a/go.mod +++ b/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 main => ./example/fib diff --git a/internal/metric/go.mod b/internal/metric/go.mod index 45a121a7181..5491e2c1775 100644 --- a/internal/metric/go.mod +++ b/internal/metric/go.mod @@ -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 main => ../../example/fib diff --git a/internal/tools/go.mod b/internal/tools/go.mod index ce630f48185..cee7dbccef8 100644 --- a/internal/tools/go.mod +++ b/internal/tools/go.mod @@ -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 main => ../../example/fib diff --git a/metric/go.mod b/metric/go.mod index f8fcf1e7833..b11a6bde579 100644 --- a/metric/go.mod +++ b/metric/go.mod @@ -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 main => ../example/fib diff --git a/oteltest/go.mod b/oteltest/go.mod index 29dc60a1bad..8597c290f56 100644 --- a/oteltest/go.mod +++ b/oteltest/go.mod @@ -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 main => ../example/fib diff --git a/sdk/export/metric/go.mod b/sdk/export/metric/go.mod index 21b5b47bf38..02fb2296672 100644 --- a/sdk/export/metric/go.mod +++ b/sdk/export/metric/go.mod @@ -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 main => ../../../example/fib diff --git a/sdk/go.mod b/sdk/go.mod index bca9816cc52..598e3d2d6b2 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -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 main => ../example/fib diff --git a/sdk/metric/go.mod b/sdk/metric/go.mod index c085602e07c..ef8743fd2c8 100644 --- a/sdk/metric/go.mod +++ b/sdk/metric/go.mod @@ -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 main => ../../example/fib diff --git a/trace/go.mod b/trace/go.mod index 08a79eeeecf..8ee2d75093c 100644 --- a/trace/go.mod +++ b/trace/go.mod @@ -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 main => ../example/fib diff --git a/website_docs/getting-started.md b/website_docs/getting-started.md index 02275f68f28..190274f09eb 100644 --- a/website_docs/getting-started.md +++ b/website_docs/getting-started.md @@ -3,25 +3,84 @@ 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 +This guide will walk you the common situation where you already have an application that uses a library and want to add observability. The application will use asks users what Fibonacci number they would like generated and returns to them the computed value. To start, make a new directory named `fib` and add the following to a new file named `fibonacci.go` in that directory. -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. +```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 +} +``` + +Now that you have your core logic added, you can build your application around it. Add a new `app.go` file with the following application logic. -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 +import ( + "context" + "fmt" + "io" + "log" +) -To install the necessary prerequisites for OpenTelemetry, you'll want to run the following command in the directory with your `go.mod`: +// App is an Fibonacci computation application. +type App struct { + r io.Reader + l *log.Logger +} -`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` +// NewApp returns a new App. +func NewApp(r io.Reader, l *log.Logger) *App { + return &App{r: r, l: l} +} -If you wish to include the experimental metrics support you will need to include a few additional modules: +// 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 + } -`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` + a.Write(n) + } +} -In your `main.go` file, you'll need to import several packages: +// 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 +88,421 @@ package main import ( "context" "log" - "time" + "os" +) - "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" +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) + } + } +} +``` - // 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" -) +With the code complete it is time to run the application. Before you can do that you need to initialize this directory as a Go module. In 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! + +```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 ``` -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. +# Trace Instrumentation -## Creating a Console Exporter +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 you will use the OpenTelemetry Trace API from the `go.opentelemetry.io/otel/trace` package. + +First, to install the necessary prerequisites for the Trace API, install the appropriate packages. Run the following command in your working directory. -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. +```sh +go get go.opentelemetry.io/otel@v1.0.0-RC1 \ + go.opentelemetry.io/otel/trace@v1.0.0-RC1 +``` -To initialize the console exporter, add the following code to the file your `main.go` file: +Now you can instrument the application! First add the needed imports to your `app.go` file. ```go -func main() { - traceExporter, err := stdouttrace.New( - stdouttrace.WithPrettyPrint(), - ) - if err != nil { - log.Fatalf("failed to initialize stdouttrace export pipeline: %v", err) - } +import ( + "context" + "fmt" + "io" + "log" + "strconv" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) ``` -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. +With the imports handled you are almost ready to add tracing instrumentation to the application! First you need to consider how you will identify the telemetry you create as coming from the instrumentation library you will build. OpenTelemetry does this by naming `Tracer`s with the instrumentation library name. The first thing to add to `app.go` is a constant with the package name. + +```go +// name is the Tracer name used to identify this instrumentation library. +const name = "fib" +``` -## Creating a Tracer Provider +Now that you have that out of the way, you can create traces from the appropriately named `Tracer` in the application. But first, what is a trace? And, how exactly should you build them for you application? -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. +To back up a bit, 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. +Each part of the work that a service performs is represented in the trace with a span. Those spans are not just an unordered collection, but are defined in a hierarchical relationship with each other. If that doesn't make sense now, don't worry. You will have a better understanding after we instrument the code, so let's get started. -To create a trace provider, add the following code to your `main.go` file: +Start by instrumenting the `Run` method. ```go - ctx := context.Background() - bsp := sdktrace.NewBatchSpanProcessor(traceExporter) - tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(bsp)) +// 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 + } - // Handle this error in a sensible manner where possible - defer func() { _ = tp.Shutdown(ctx) }() + a.Write(ctx, n) + span.End() + } +} ``` -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. +The above code creates a trace every iteration of the for loop 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`. + +Next, instrument the `Poll` method. -## Creating a Meter Provider +```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) -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". + // Store n as a string to not overflow an int64. + nStr := strconv.FormatUint(uint64(n), 10) + // This is going to be a high cardinality attribute because the user can + // pass an unbounded number of different values. This type of attribute + // should be avoided in instrumentation for high performance systems. + span.SetAttributes(attribute.String("request.n", nStr)) -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. + return n, err +} +``` -To create a meter provider, add the following code to your `main.go` file: +Similar to the `Run` method instrumentation, this adds a span to the method to track the computation done there. 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 - metricExporter, err := stdoutmetric.New( - stdoutmetric.WithPrettyPrint(), - ) +// 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 { - log.Fatalf("failed to initialize stdoutmetric export pipeline: %v", err) + a.l.Printf("Fibonacci(%d): %v\n", n, err) + } else { + a.l.Printf("Fibonacci(%d) = %d\n", n, f) } +} +``` - pusher := controller.New( - processor.New( - simple.NewWithExactDistribution(), - metricExporter, - ), - controller.WithExporter(metricExporter), - controller.WithCollectPeriod(5*time.Second), - ) +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. - err = pusher.Start(ctx) - if err != nil { - log.Fatalf("failed to initialize metric controller: %v", err) - } +Now that you have instrumented code it should be clearer how spans are related with a hierarchy. In OpenTelemetry Go the span hierarchy is defined explicitly with a `context.Context`. These contexts can contain references to spans. When a span is created a context is passed and reference to the created span is stored in a new context also returned with the span. If that returned context is used when creating another span, the original span will become that span's parent. This hierarchy gives traces structure and can help identify how a system works. Based on what you instrumented above and this understanding of span hierarchy you should expect a trace for each execution of the run loop to look like this. - // Handle this error in a sensible manner where possible - defer func() { _ = pusher.Stop(ctx) }() ``` +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. -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. +Now how do you actually see the produced spans? To do this you will need to configure and install an SDK. -## Setting Global Options +# SDK Installation -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). +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 things. Run the following in the `fib` directory to install the trace STDOUT exporter and the SDK. -Setting up global options uses the `otel` package - add these options to your `main.go` file as shown: +```sh +$ go get go.opentelemetry.io/otel/sdk@v1.0.0-RC1 \ + go.opentelemetry.io/otel/exporters/stdout/stdouttrace@v1.0.0-RC1 +``` + +Now add the needed imports to `main.go`. ```go - otel.SetTracerProvider(tp) - global.SetMeterProvider(pusher.MeterProvider()) - propagator := propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{}) - otel.SetTextMapPropagator(propagator) +import ( + "context" + "io" + "log" + "os" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" + "go.opentelemetry.io/otel/sdk/trace" +) ``` -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 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: -## Creating metric instruments +```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(), + ) +} +``` -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. +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. -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. +## Creating a Resource -To set up some metric instruments, add the following code to your `main.go` file - +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 - lemonsKey := attribute.Key("ex.com/lemons") - anotherKey := attribute.Key("ex.com/another") +// newResource returns a resource describing this application. +func newResource() *resource.Resource { + return resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String("fib"), + semconv.ServiceVersionKey.String("v0.1.0"), + attribute.String("environment", "demo"), + ) +} +``` + +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! - commonAttributes := []attribute.KeyValue{lemonsKey.Int(10), attribute.String("A", "1"), attribute.String("B", "2"), attribute.String("C", "3")} +## Installing a Tracer Provider - meter := global.Meter("ex.com/basic") +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 configures these delegated `Tracer`s where and how to send the data they produce. - observerCallback := func(_ context.Context, result metric.Float64ObserverResult) { - result.Observe(1, commonAttributes...) +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 configure one that sends to data to your exporter in batches. 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) } - _ = metric.Must(meter).NewFloat64ValueObserver("ex.com.one", observerCallback, - metric.WithDescription("A ValueObserver set to 1.0"), + 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 an console exporter that will export to a file. With the exporter ready, it can be registered in 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`. - valueRecorder := metric.Must(meter).NewFloat64ValueRecorder("ex.com.two") +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. - boundRecorder := valueRecorder.Bind(commonAttributes...) - defer boundRecorder.Unbind() +# Putting It All Together + +You should 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 ``` -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. +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 -# Quick Start +At this point you have a working application that is producing tracing telemetry data. Unfortunately, it was discovered 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 +# … +``` -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: +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 - tracer := otel.Tracer("ex.com/basic") +// 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 + } - // 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) + if n > 93 { + return 0, fmt.Errorf("unsupported fibonacci number %d: too large", n) + } - func(ctx context.Context) { - var span trace.Span - ctx, span = tracer.Start(ctx, "operation") + 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) + /* … */ +} +``` - span.AddEvent("Nice operation!", trace.WithAttributes(attribute.Int("bogons", 100))) - span.SetAttributes(anotherKey.String("yes")) +The `Poll` method can be updated as well with a similar fix. If the user gave bad data before the application would fail but the telemetry data did not reflect this failure. Now you can add the following updates to the `Poll` method and this error will be captured in the data. - meter.RecordBatch( - ctx, - commonAttributes, - valueRecorder.Measurement(2.0), - ) +```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() - func(ctx context.Context) { - var span trace.Span - ctx, span = tracer.Start(ctx, "Sub operation...") - defer span.End() + a.l.Print("What Fibonacci number would you like to know: ") - span.SetAttributes(lemonsKey.String("five")) - span.AddEvent("Sub span event") - boundRecorder.Record(ctx, 1.3) - }(ctx) - }(ctx) + var n uint + _, err := fmt.Fscanf(a.r, "%d", &n) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + } + /* … */ } ``` -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. +All that is left is updating imports for the `app.go` file to include the `go.opentelemetry.io/otel/codes` package. -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. +```go +import ( + "context" + "fmt" + "io" + "log" + "strconv" -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. + "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" + } + } + ], + ... + } +] +``` From 27de5ef76cc0e89e7e7ea51db3e4e1053ed52097 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 27 Aug 2021 09:04:18 -0700 Subject: [PATCH 02/10] Revise docs english --- website_docs/getting-started.md | 64 ++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/website_docs/getting-started.md b/website_docs/getting-started.md index 190274f09eb..f361e638dd2 100644 --- a/website_docs/getting-started.md +++ b/website_docs/getting-started.md @@ -5,7 +5,9 @@ weight: 2 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. -This guide will walk you the common situation where you already have an application that uses a library and want to add observability. The application will use asks users what Fibonacci number they would like generated and returns to them the computed value. To start, make a new directory named `fib` and add the following to a new file named `fibonacci.go` in that directory. +Having observability into an application is often critical to successfully identifying failures and issues, and hopefully improving the application with this information. This guide shows how this is possible to do using the OpenTelemetry Go project. You will start with an application that computes Fibonacci numbers for users, and from there you will add instrumentation to produce tracing telemetry for the application with OpenTelemetry Go. + +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 @@ -25,7 +27,7 @@ func Fibonacci(n uint) (uint64, error) { } ``` -Now that you have your core logic added, you can build your application around it. Add a new `app.go` file with the following application logic. +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 @@ -115,7 +117,7 @@ func main() { } ``` -With the code complete it is time to run the application. Before you can do that you need to initialize this directory as a Go module. In 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! +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! ```sh $ go run . @@ -127,18 +129,20 @@ What Fibonacci number would you like to know: goodbye ``` +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 you will use the OpenTelemetry Trace API from the `go.opentelemetry.io/otel/trace` package. +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, to install the necessary prerequisites for the Trace API, install the appropriate packages. Run the following command in your working directory. +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-RC1 \ go.opentelemetry.io/otel/trace@v1.0.0-RC1 ``` -Now you can instrument the application! First add the needed imports to your `app.go` file. +Now that the packages installed you can start updating your application with imports you will use in the `app.go` file. ```go import ( @@ -154,18 +158,22 @@ import ( ) ``` -With the imports handled you are almost ready to add tracing instrumentation to the application! First you need to consider how you will identify the telemetry you create as coming from the instrumentation library you will build. OpenTelemetry does this by naming `Tracer`s with the instrumentation library name. The first thing to add to `app.go` is a constant with the package name. +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" ``` -Now that you have that out of the way, you can create traces from the appropriately named `Tracer` in the application. But first, what is a trace? And, how exactly should you build them for you application? +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. 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. -Each part of the work that a service performs is represented in the trace with a span. Those spans are not just an unordered collection, but are defined in a hierarchical relationship with each other. If that doesn't make sense now, don't worry. You will have a better understanding after we instrument the code, so let's get started. +Each part of the work that a service performs is represented in the trace with 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. If that 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. @@ -188,7 +196,7 @@ func (a *App) Run(ctx context.Context) error { } ``` -The above code creates a trace every iteration of the for loop 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`. +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. @@ -214,7 +222,7 @@ func (a *App) Poll(ctx context.Context) (uint, error) { } ``` -Similar to the `Run` method instrumentation, this adds a span to the method to track the computation done there. 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. +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. @@ -238,9 +246,9 @@ func (a *App) Write(ctx context.Context, n uint) { } ``` -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. +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? -Now that you have instrumented code it should be clearer how spans are related with a hierarchy. In OpenTelemetry Go the span hierarchy is defined explicitly with a `context.Context`. These contexts can contain references to spans. When a span is created a context is passed and reference to the created span is stored in a new context also returned with the span. If that returned context is used when creating another span, the original span will become that span's parent. This hierarchy gives traces structure and can help identify how a system works. Based on what you instrumented above and this understanding of span hierarchy you should expect a trace for each execution of the run loop to look like this. +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 @@ -255,7 +263,7 @@ Now how do you actually see the produced spans? To do this you will need to conf # 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 things. Run the following in the `fib` directory to install the trace STDOUT exporter and the SDK. +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-RC1 \ @@ -305,12 +313,16 @@ Telemetry data can be crucial to solving issues with a service. The catch is, yo ```go // newResource returns a resource describing this application. func newResource() *resource.Resource { - return resource.NewWithAttributes( - semconv.SchemaURL, - semconv.ServiceNameKey.String("fib"), - semconv.ServiceVersionKey.String("v0.1.0"), - attribute.String("environment", "demo"), + 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 } ``` @@ -318,9 +330,9 @@ Any information you would like to associate with all telemetry data the SDK hand ## 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 configures these delegated `Tracer`s where and how to send the data they produce. +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 configure one that sends to data to your exporter in batches. Update your `main` function in `main.go` with the following. +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() { @@ -353,13 +365,13 @@ func main() { } ``` -There's a fair amount going on here. First you are creating an console exporter that will export to a file. With the exporter ready, it can be registered in 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`. +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 have a working application that produces trace telemetry data! Give it a try. +You should now have a working application that produces trace telemetry data! Give it a try. ```sh $ go run . @@ -375,7 +387,7 @@ A new file named `traces.txt` should be created in your working directory. All t # (Bonus) Errors -At this point you have a working application that is producing tracing telemetry data. Unfortunately, it was discovered there is an error in the core functionality of the `fib` module. +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 . @@ -431,7 +443,9 @@ func (a *App) Write(ctx context.Context, n uint) { } ``` -The `Poll` method can be updated as well with a similar fix. If the user gave bad data before the application would fail but the telemetry data did not reflect this failure. Now you can add the following updates to the `Poll` method and this error will be captured in the data. +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. From 72940cc2d1bff375c2f8a5c80009fd797fbce860 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 27 Aug 2021 09:06:30 -0700 Subject: [PATCH 03/10] Update example/fib/go.mod Co-authored-by: Anthony Mirabella --- .gitignore | 2 +- bridge/opencensus/go.mod | 2 +- bridge/opencensus/test/go.mod | 2 +- bridge/opentracing/go.mod | 2 +- example/fib/go.mod | 6 +++--- example/jaeger/go.mod | 2 +- example/namedtracer/go.mod | 2 +- example/opencensus/go.mod | 2 +- example/otel-collector/go.mod | 2 +- example/passthrough/go.mod | 2 +- example/prometheus/go.mod | 2 +- example/zipkin/go.mod | 2 +- exporters/jaeger/go.mod | 2 +- exporters/otlp/otlpmetric/go.mod | 2 +- exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod | 2 +- exporters/otlp/otlpmetric/otlpmetrichttp/go.mod | 2 +- exporters/otlp/otlptrace/go.mod | 2 +- exporters/otlp/otlptrace/otlptracegrpc/go.mod | 2 +- exporters/otlp/otlptrace/otlptracehttp/go.mod | 2 +- exporters/prometheus/go.mod | 2 +- exporters/stdout/stdoutmetric/go.mod | 2 +- exporters/stdout/stdouttrace/go.mod | 2 +- exporters/zipkin/go.mod | 2 +- go.mod | 2 +- internal/metric/go.mod | 2 +- internal/tools/go.mod | 2 +- metric/go.mod | 2 +- oteltest/go.mod | 2 +- sdk/export/metric/go.mod | 2 +- sdk/go.mod | 2 +- sdk/metric/go.mod | 2 +- trace/go.mod | 2 +- 32 files changed, 34 insertions(+), 34 deletions(-) diff --git a/.gitignore b/.gitignore index f08c826802a..759cf53e002 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,7 @@ coverage.* gen/ -/example/fib/main +/example/fib/fib /example/jaeger/jaeger /example/namedtracer/namedtracer /example/opencensus/opencensus diff --git a/bridge/opencensus/go.mod b/bridge/opencensus/go.mod index 99c91c9fe87..cf6e848744f 100644 --- a/bridge/opencensus/go.mod +++ b/bridge/opencensus/go.mod @@ -73,4 +73,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ./test -replace main => ../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../example/fib diff --git a/bridge/opencensus/test/go.mod b/bridge/opencensus/test/go.mod index 942a88b47ec..202529abb15 100644 --- a/bridge/opencensus/test/go.mod +++ b/bridge/opencensus/test/go.mod @@ -70,4 +70,4 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../../sdk/metric replace go.opentelemetry.io/otel/trace => ../../../trace -replace main => ../../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../../example/fib diff --git a/bridge/opentracing/go.mod b/bridge/opentracing/go.mod index d3aa482262c..6276f3c45e0 100644 --- a/bridge/opentracing/go.mod +++ b/bridge/opentracing/go.mod @@ -70,4 +70,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../opencensus/test -replace main => ../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../example/fib diff --git a/example/fib/go.mod b/example/fib/go.mod index a7e293b5b2b..e7d89d995e5 100644 --- a/example/fib/go.mod +++ b/example/fib/go.mod @@ -1,4 +1,4 @@ -module main +module go.opentelemetry.io/otel/example/fib go 1.15 @@ -17,8 +17,6 @@ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencens replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing -replace main => ./ - replace go.opentelemetry.io/otel/example/jaeger => ../jaeger replace go.opentelemetry.io/otel/example/namedtracer => ../namedtracer @@ -70,3 +68,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 => ./ diff --git a/example/jaeger/go.mod b/example/jaeger/go.mod index aacea7863aa..fce32b7e211 100644 --- a/example/jaeger/go.mod +++ b/example/jaeger/go.mod @@ -70,4 +70,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test -replace main => ../fib +replace go.opentelemetry.io/otel/example/fib => ../fib diff --git a/example/namedtracer/go.mod b/example/namedtracer/go.mod index f412df7bd85..8df22292771 100644 --- a/example/namedtracer/go.mod +++ b/example/namedtracer/go.mod @@ -72,4 +72,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test -replace main => ../fib +replace go.opentelemetry.io/otel/example/fib => ../fib diff --git a/example/opencensus/go.mod b/example/opencensus/go.mod index edab95c138c..5d5f16f8d02 100644 --- a/example/opencensus/go.mod +++ b/example/opencensus/go.mod @@ -74,4 +74,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test -replace main => ../fib +replace go.opentelemetry.io/otel/example/fib => ../fib diff --git a/example/otel-collector/go.mod b/example/otel-collector/go.mod index c6b6849b337..b618993de64 100644 --- a/example/otel-collector/go.mod +++ b/example/otel-collector/go.mod @@ -73,4 +73,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test -replace main => ../fib +replace go.opentelemetry.io/otel/example/fib => ../fib diff --git a/example/passthrough/go.mod b/example/passthrough/go.mod index 640905daab2..740c95f24ab 100644 --- a/example/passthrough/go.mod +++ b/example/passthrough/go.mod @@ -73,4 +73,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test -replace main => ../fib +replace go.opentelemetry.io/otel/example/fib => ../fib diff --git a/example/prometheus/go.mod b/example/prometheus/go.mod index b56a81f5517..7fe5524fabc 100644 --- a/example/prometheus/go.mod +++ b/example/prometheus/go.mod @@ -72,4 +72,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test -replace main => ../fib +replace go.opentelemetry.io/otel/example/fib => ../fib diff --git a/example/zipkin/go.mod b/example/zipkin/go.mod index 81f39530e82..94895419875 100644 --- a/example/zipkin/go.mod +++ b/example/zipkin/go.mod @@ -71,4 +71,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test -replace main => ../fib +replace go.opentelemetry.io/otel/example/fib => ../fib diff --git a/exporters/jaeger/go.mod b/exporters/jaeger/go.mod index 5db2aa34657..8e02be74b5a 100644 --- a/exporters/jaeger/go.mod +++ b/exporters/jaeger/go.mod @@ -74,4 +74,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test -replace main => ../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../example/fib diff --git a/exporters/otlp/otlpmetric/go.mod b/exporters/otlp/otlpmetric/go.mod index 9272f2acac9..664cb4e4efa 100644 --- a/exporters/otlp/otlpmetric/go.mod +++ b/exporters/otlp/otlpmetric/go.mod @@ -81,4 +81,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ./o replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test -replace main => ../../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../../example/fib diff --git a/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod b/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod index ea886a9f51d..f20b195dc0a 100644 --- a/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod +++ b/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod @@ -79,4 +79,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/opencensus/test -replace main => ../../../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib diff --git a/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod b/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod index 5cc7a690a1d..14c9c25cc62 100644 --- a/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod +++ b/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod @@ -81,4 +81,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/opencensus/test -replace main => ../../../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib diff --git a/exporters/otlp/otlptrace/go.mod b/exporters/otlp/otlptrace/go.mod index 8004039fe57..f3118e815d6 100644 --- a/exporters/otlp/otlptrace/go.mod +++ b/exporters/otlp/otlptrace/go.mod @@ -77,4 +77,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test -replace main => ../../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../../example/fib diff --git a/exporters/otlp/otlptrace/otlptracegrpc/go.mod b/exporters/otlp/otlptrace/otlptracegrpc/go.mod index d6f7643b9ab..ad28506a6f1 100644 --- a/exporters/otlp/otlptrace/otlptracegrpc/go.mod +++ b/exporters/otlp/otlptrace/otlptracegrpc/go.mod @@ -73,4 +73,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/opencensus/test -replace main => ../../../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib diff --git a/exporters/otlp/otlptrace/otlptracehttp/go.mod b/exporters/otlp/otlptrace/otlptracehttp/go.mod index 8c43ab1aec6..34f91025248 100644 --- a/exporters/otlp/otlptrace/otlptracehttp/go.mod +++ b/exporters/otlp/otlptrace/otlptracehttp/go.mod @@ -71,4 +71,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../../bridge/opencensus/test -replace main => ../../../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../../../example/fib diff --git a/exporters/prometheus/go.mod b/exporters/prometheus/go.mod index fb29c56af27..4c6faa65b69 100644 --- a/exporters/prometheus/go.mod +++ b/exporters/prometheus/go.mod @@ -76,4 +76,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test -replace main => ../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../example/fib diff --git a/exporters/stdout/stdoutmetric/go.mod b/exporters/stdout/stdoutmetric/go.mod index ffc4b73b7b4..2544a2ed04f 100644 --- a/exporters/stdout/stdoutmetric/go.mod +++ b/exporters/stdout/stdoutmetric/go.mod @@ -74,4 +74,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test -replace main => ../../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../../example/fib diff --git a/exporters/stdout/stdouttrace/go.mod b/exporters/stdout/stdouttrace/go.mod index ac485e3b200..9f8d37f09a4 100644 --- a/exporters/stdout/stdouttrace/go.mod +++ b/exporters/stdout/stdouttrace/go.mod @@ -72,4 +72,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test -replace main => ../../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../../example/fib diff --git a/exporters/zipkin/go.mod b/exporters/zipkin/go.mod index b7a0b52f1af..9a0717a8630 100644 --- a/exporters/zipkin/go.mod +++ b/exporters/zipkin/go.mod @@ -75,4 +75,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test -replace main => ../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../example/fib diff --git a/go.mod b/go.mod index 64b621d3f75..f3b1a33b67c 100644 --- a/go.mod +++ b/go.mod @@ -70,4 +70,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ./e replace go.opentelemetry.io/otel/bridge/opencensus/test => ./bridge/opencensus/test -replace main => ./example/fib +replace go.opentelemetry.io/otel/example/fib => ./example/fib diff --git a/internal/metric/go.mod b/internal/metric/go.mod index 5491e2c1775..1d571477303 100644 --- a/internal/metric/go.mod +++ b/internal/metric/go.mod @@ -70,4 +70,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test -replace main => ../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../example/fib diff --git a/internal/tools/go.mod b/internal/tools/go.mod index cee7dbccef8..d53ab01972c 100644 --- a/internal/tools/go.mod +++ b/internal/tools/go.mod @@ -74,4 +74,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test -replace main => ../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../example/fib diff --git a/metric/go.mod b/metric/go.mod index b11a6bde579..6c20097aabf 100644 --- a/metric/go.mod +++ b/metric/go.mod @@ -71,4 +71,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/test -replace main => ../example/fib +replace go.opentelemetry.io/otel/example/fib => ../example/fib diff --git a/oteltest/go.mod b/oteltest/go.mod index 8597c290f56..c3b262038d6 100644 --- a/oteltest/go.mod +++ b/oteltest/go.mod @@ -70,4 +70,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/test -replace main => ../example/fib +replace go.opentelemetry.io/otel/example/fib => ../example/fib diff --git a/sdk/export/metric/go.mod b/sdk/export/metric/go.mod index 02fb2296672..0cf6bb34d8d 100644 --- a/sdk/export/metric/go.mod +++ b/sdk/export/metric/go.mod @@ -71,4 +71,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../../bridge/opencensus/test -replace main => ../../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../../example/fib diff --git a/sdk/go.mod b/sdk/go.mod index 598e3d2d6b2..3767d045fa5 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -72,4 +72,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/test -replace main => ../example/fib +replace go.opentelemetry.io/otel/example/fib => ../example/fib diff --git a/sdk/metric/go.mod b/sdk/metric/go.mod index ef8743fd2c8..97ca6e4a97a 100644 --- a/sdk/metric/go.mod +++ b/sdk/metric/go.mod @@ -74,4 +74,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test -replace main => ../../example/fib +replace go.opentelemetry.io/otel/example/fib => ../../example/fib diff --git a/trace/go.mod b/trace/go.mod index 8ee2d75093c..7dbed4187c9 100644 --- a/trace/go.mod +++ b/trace/go.mod @@ -70,4 +70,4 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../ replace go.opentelemetry.io/otel/bridge/opencensus/test => ../bridge/opencensus/test -replace main => ../example/fib +replace go.opentelemetry.io/otel/example/fib => ../example/fib From 1a71cc594ca28c5bcd11a97700cce9f875abed09 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 27 Aug 2021 09:30:18 -0700 Subject: [PATCH 04/10] Add a What's Next section --- website_docs/getting-started.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/website_docs/getting-started.md b/website_docs/getting-started.md index f361e638dd2..ba6eb947082 100644 --- a/website_docs/getting-started.md +++ b/website_docs/getting-started.md @@ -520,3 +520,9 @@ Excellent! The application no longer returns wrong values, and looking at the te } ] ``` + +# 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. From e9bb9a87dd58763fec4efcf267eb7265541d7c3d Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 27 Aug 2021 10:22:36 -0700 Subject: [PATCH 05/10] Clean up intro --- website_docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website_docs/getting-started.md b/website_docs/getting-started.md index ba6eb947082..79471c322c6 100644 --- a/website_docs/getting-started.md +++ b/website_docs/getting-started.md @@ -5,7 +5,7 @@ weight: 2 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. -Having observability into an application is often critical to successfully identifying failures and issues, and hopefully improving the application with this information. This guide shows how this is possible to do using the OpenTelemetry Go project. You will start with an application that computes Fibonacci numbers for users, and from there you will add instrumentation to produce tracing telemetry for the application with OpenTelemetry Go. +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. 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. From 6e23ed9007837c4c9e54759a767f492d5cde5cae Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 27 Aug 2021 11:07:10 -0700 Subject: [PATCH 06/10] Apply suggestions from code review Co-authored-by: Anthony Mirabella --- website_docs/getting-started.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/website_docs/getting-started.md b/website_docs/getting-started.md index 79471c322c6..a2d98eabe97 100644 --- a/website_docs/getting-started.md +++ b/website_docs/getting-started.md @@ -138,8 +138,8 @@ OpenTelemetry is split into two parts: an API to instrument code with, and SDKs 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-RC1 \ - go.opentelemetry.io/otel/trace@v1.0.0-RC1 +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. @@ -266,8 +266,8 @@ Now how do you actually see the produced spans? To do this you will need to conf 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-RC1 \ - go.opentelemetry.io/otel/exporters/stdout/stdouttrace@v1.0.0-RC1 +$ 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`. From db9b6311c569c9956c08de4063434256ab3b6256 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Fri, 27 Aug 2021 11:16:05 -0700 Subject: [PATCH 07/10] Apply feedback --- example/fib/app.go | 5 +---- website_docs/getting-started.md | 7 ++----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/example/fib/app.go b/example/fib/app.go index ff70561b4ce..011c5405520 100644 --- a/example/fib/app.go +++ b/example/fib/app.go @@ -74,9 +74,6 @@ func (a *App) Poll(ctx context.Context) (uint, error) { // Store n as a string to not overflow an int64. nStr := strconv.FormatUint(uint64(n), 10) - // This is going to be a high cardinality attribute because the user can - // pass an unbounded number of different values. This type of attribute - // should be avoided in instrumentation for high performance systems. span.SetAttributes(attribute.String("request.n", nStr)) return n, err @@ -89,7 +86,7 @@ func (a *App) Write(ctx context.Context, n uint) { defer span.End() f, err := func(ctx context.Context) (uint64, error) { - _, span = otel.Tracer(name).Start(ctx, "Fibonacci") + _, span := otel.Tracer(name).Start(ctx, "Fibonacci") defer span.End() f, err := Fibonacci(n) if err != nil { diff --git a/website_docs/getting-started.md b/website_docs/getting-started.md index a2d98eabe97..ccae5d5bbdc 100644 --- a/website_docs/getting-started.md +++ b/website_docs/getting-started.md @@ -213,9 +213,6 @@ func (a *App) Poll(ctx context.Context) (uint, error) { // Store n as a string to not overflow an int64. nStr := strconv.FormatUint(uint64(n), 10) - // This is going to be a high cardinality attribute because the user can - // pass an unbounded number of different values. This type of attribute - // should be avoided in instrumentation for high performance systems. span.SetAttributes(attribute.String("request.n", nStr)) return n, err @@ -234,7 +231,7 @@ func (a *App) Write(ctx context.Context, n uint) { defer span.End() f, err := func(ctx context.Context) (uint64, error) { - _, span = otel.Tracer(name).Start(ctx, "Fibonacci") + _, span := otel.Tracer(name).Start(ctx, "Fibonacci") defer span.End() return Fibonacci(n) }(ctx) @@ -430,7 +427,7 @@ func (a *App) Write(ctx context.Context, n uint) { defer span.End() f, err := func(ctx context.Context) (uint64, error) { - _, span = otel.Tracer(name).Start(ctx, "Fibonacci") + _, span := otel.Tracer(name).Start(ctx, "Fibonacci") defer span.End() f, err := Fibonacci(n) if err != nil { From 56d57d29436ed3eb88722574cc1393e965aff742 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Wed, 1 Sep 2021 14:18:37 -0700 Subject: [PATCH 08/10] Return from Poll on error --- example/fib/app.go | 3 ++- website_docs/getting-started.md | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/example/fib/app.go b/example/fib/app.go index 011c5405520..5085a852dba 100644 --- a/example/fib/app.go +++ b/example/fib/app.go @@ -70,13 +70,14 @@ func (a *App) Poll(ctx context.Context) (uint, error) { 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, err + return n, nil } // Write writes the n-th Fibonacci number back to the user. diff --git a/website_docs/getting-started.md b/website_docs/getting-started.md index ccae5d5bbdc..b48d7d6aadf 100644 --- a/website_docs/getting-started.md +++ b/website_docs/getting-started.md @@ -457,8 +457,14 @@ func (a *App) Poll(ctx context.Context) (uint, error) { 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 } ``` From 993c00633ca87bcc3fa7d8d7a03a6dcd4c050327 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Wed, 1 Sep 2021 14:22:29 -0700 Subject: [PATCH 09/10] Update website_docs/getting-started.md Co-authored-by: Joshua MacDonald --- website_docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website_docs/getting-started.md b/website_docs/getting-started.md index b48d7d6aadf..74f6954b7c7 100644 --- a/website_docs/getting-started.md +++ b/website_docs/getting-started.md @@ -171,7 +171,7 @@ Using the full-qualified package name, something that should be unique for Go pa 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. 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. +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 with 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. If that 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. From ff820f557dd06ffb956a893f5fdb3a1c1aca4c96 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Wed, 1 Sep 2021 14:29:12 -0700 Subject: [PATCH 10/10] Add root and parent relationship info --- website_docs/getting-started.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/website_docs/getting-started.md b/website_docs/getting-started.md index 74f6954b7c7..bc000cc3d6d 100644 --- a/website_docs/getting-started.md +++ b/website_docs/getting-started.md @@ -173,7 +173,9 @@ Everything should be in place now to start tracing your application. But first, 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 with 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. If that 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. +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. @@ -243,7 +245,7 @@ func (a *App) Write(ctx context.Context, n uint) { } ``` -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? +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.