Simple Use of opentelemetry-go

2022年11月20日 76点热度 1人点赞 0条评论
内容目录

Establishing Link

SDK Official Repository Address:

https://github.com/open-telemetry/opentelemetry-go

Design an execution flow like this:

file

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")

file

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)
}

痴者工良

高级程序员劝退师

文章评论