Establishing Link
SDK Official Repository Address:
https://github.com/open-telemetry/opentelemetry-go
Design an execution flow like this:
Run
executes Run1
and Run2
in sequence:
a.Run1(newCtx)
a.Run2(newCtx)
Inside Run1
, another function Run1-1
is also executed.
An example code snippet using the OpenTelemetry SDK is as follows:
package main
import (
"context"
"go.opentelemetry.io/otel"
"io"
"log"
)
// The name of the current Trace.
const name = "App"
type App struct {
r io.Reader
l *log.Logger
}
func NewApp(r io.Reader, l *log.Logger) *App {
return &App{r: r, l: l}
}
func (a *App) Run(ctx context.Context) {
newCtx, span := otel.Tracer(name).Start(ctx, "Run")
defer span.End()
a.Run1(newCtx)
a.Run2(newCtx)
}
func (a *App) Run1(ctx context.Context) {
newCtx, span := otel.Tracer(name).Start(ctx, "Run1")
defer span.End()
a.Run1_1(newCtx)
}
func (a *App) Run1_1(ctx context.Context) {
_, span := otel.Tracer(name).Start(ctx, "Run1_1")
defer span.End()
}
func (a *App) Run2(ctx context.Context) {
_, span := otel.Tracer(name).Start(ctx, "Run2")
defer span.End()
}
There will be multiple Tracers in the process, and each Tracer needs to have a name for information localization.
When creating a new Context using otel.Tracer(name).Start(ctx, "Run")
, the Trace information will be attached to the Context.
newCtx, span := otel.Tracer(name).Start(ctx, "Run")
If a function needs to call other functions, the link should be passed:
newCtx, span := otel.Tracer(name).Start(ctx, "Run")
defer span.End()
a.Run1(newCtx)
a.Run2(newCtx)
Thus, the Context is passed to the next function.
To retrieve the Span from the current Context, you can do it like this:
span := trace.SpanFromContext(ctx)
Exporting Link
In the example above, during the function calls, Trace and Span information was generated.
However, this information still needs to be exported to be displayed.
The official SDK or middleware supports the following observability data:
| Exporter | Metrics | Traces |
| ---------- | ------- | ------ |
| Jaeger | | ✓ |
| OTLP | ✓ | ✓ |
| Prometheus | ✓ | |
| stdout | ✓ | ✓ |
| Zipkin | | ✓ |
In the following example, the link information will be exported to a file using stdout
.
package main
import (
"context"
"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.10.0"
"io"
"log"
"os"
)
// Create 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(),
)
}
// Create a Resource object representing the current process
func newResource() *resource.Resource {
r, _ := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("example1"),
semconv.ServiceVersionKey.String("v0.1.0"),
attribute.String("environment", "demo"), // Add attribute
),
)
return r
}
func main() {
l := log.New(os.Stdout, "", 0)
// Export trace to a file
f, err := os.Create("traces.txt")
if err != nil {
l.Fatal(err)
}
defer f.Close()
// Create an Exporter
exp, err := newExporter(f)
if err != nil {
l.Fatal(err)
}
// Create TracerProvider
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)
app := NewApp(os.Stdin, l)
app.Run(context.Background())
}
First, to identify the current program, a Resource needs to be created.
// Create a Resource object representing the current process
func newResource() *resource.Resource {
r, _ := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("example1"),
semconv.ServiceVersionKey.String("v0.1.0"),
attribute.String("environment", "demo"), // Add attribute
),
)
return r
}
An Exporter is created, and file I/O is used here.
// Create 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(),
)
}
func newExporter(ctx context.Context) /* (someExporter.Exporter, error) */ { // Your preferred exporter: console, jaeger, zipkin, OTLP, etc. }
Create TracerProvider:
// Create trace
tp := trace.NewTracerProvider(
trace.WithBatcher(exp), // exporter
trace.WithResource(newResource()),
)
Set global TracerProvider:
otel.SetTracerProvider(tp)
After startup, the generated Trace:
{
"Name": "Run1_1",
"SpanContext": {
"TraceID": "50cf661b85718ec74a4d859f4c1aeef8",
"SpanID": "7f51afb5a078006e",
"TraceFlags": "01",
"TraceState": "",
"Remote": false
},
"Parent": {
"TraceID": "50cf661b85718ec74a4d859f4c1aeef8",
"SpanID": "11b82f4e7829ed50",
"TraceFlags": "01",
"TraceState": "",
"Remote": false
},
"SpanKind": 1,
"StartTime": "0001-01-01T00:00:00Z",
"EndTime": "0001-01-01T00:00:00Z",
"Attributes": null,
"Events": null,
"Links": null,
"Status": {
"Code": "Unset",
"Description": ""
},
"DroppedAttributes": 0,
"DroppedEvents": 0,
"DroppedLinks": 0,
"ChildSpanCount": 0,
"Resource": null,
"InstrumentationLibrary": {
"Name": "App",
"Version": "",
"SchemaURL": ""
}
}
{
"Name": "Run1",
"SpanContext": {
"TraceID": "50cf661b85718ec74a4d859f4c1aeef8",
"SpanID": "11b82f4e7829ed50",
"TraceFlags": "01",
"TraceState": "",
"Remote": false
},
"Parent": {
"TraceID": "50cf661b85718ec74a4d859f4c1aeef8",
"SpanID": "bd024e7b63a2de62",
"TraceFlags": "01",
"TraceState": "",
"Remote": false
},
"SpanKind": 1,
"StartTime": "0001-01-01T00:00:00Z",
"EndTime": "0001-01-01T00:00:00Z",
"Attributes": null,
"Events": null,
"Links": null,
"Status": {
"Code": "Unset",
"Description": ""
},
"DroppedAttributes": 0,
"DroppedEvents": 0,
"DroppedLinks": 0,
"ChildSpanCount": 1,
"Resource": null,
"InstrumentationLibrary": {
"Name": "App",
"Version": "",
"SchemaURL": ""
}
}
{
"Name": "Run2",
"SpanContext": {
"TraceID": "50cf661b85718ec74a4d859f4c1aeef8",
"SpanID": "bc9c29add7a9b100",
"TraceFlags": "01",
"TraceState": "",
"Remote": false
},
"Parent": {
"TraceID": "50cf661b85718ec74a4d859f4c1aeef8",
"SpanID": "bd024e7b63a2de62",
"TraceFlags": "01",
"TraceState": "",
"Remote": false
},
"SpanKind": 1,
"StartTime": "0001-01-01T00:00:00Z",
"EndTime": "0001-01-01T00:00:00Z",
"Attributes": null,
"Events": null,
"Links": null,
"Status": {
"Code": "Unset",
"Description": ""
},
"DroppedAttributes": 0,
"DroppedEvents": 0,
"DroppedLinks": 0,
"ChildSpanCount": 0,
"Resource": null,
"InstrumentationLibrary": {
"Name": "App",
"Version": "",
"SchemaURL": ""
}
}
{
"Name": "Run",
"SpanContext": {
"TraceID": "50cf661b85718ec74a4d859f4c1aeef8",
"SpanID": "bd024e7b63a2de62",
"TraceFlags": "01",
"TraceState": "",
"Remote": false
},
"Parent": {
"TraceID": "00000000000000000000000000000000",
"SpanID": "0000000000000000",
"TraceFlags": "00",
"TraceState": "",
"Remote": false
},
"SpanKind": 1,
"StartTime": "0001-01-01T00:00:00Z",
"EndTime": "0001-01-01T00:00:00Z",
"Attributes": null,
"Events": null,
"Links": null,
"Status": {
"Code": "Unset",
"Description": ""
},
"DroppedAttributes": 0,
"DroppedEvents": 0,
"DroppedLinks": 0,
"ChildSpanCount": 2,
"Resource": null,
"InstrumentationLibrary": {
"Name": "App",
"Version": "",
"SchemaURL": ""
}
}
Other Common Methods
Adding Attributes to Span
You can add k/v
information to the Span:
var myKey = attribute.Key("myCoolAttribute")
span.SetAttributes(myKey.String("a value"))
func (a *App) Run(ctx context.Context) {
newCtx, span := otel.Tracer(name).Start(ctx, "Run")
defer span.End()
var myKey = attribute.Key("myCoolAttribute")
span.SetAttributes(myKey.String("a value"))
a.Run1(newCtx)
a.Run2(newCtx)
}
Adding Events
Add simple event information that indicates what operation was performed during the execution.
span.AddEvent("Acquiring lock")
mutex.Lock()
span.AddEvent("Got lock, doing work...")
// do stuff
span.AddEvent("Unlocking")
mutex.Unlock()
Setting Status in Span
If there is an issue executing the current function, you can set the status in the Span and record the error information.
result, err := operationThatCouldFail()
if err != nil {
span.SetStatus(codes.Error, "operationThatCouldFail failed")
span.RecordError(err)
}
文章评论