gRPC og højtydende API'er · Lektion

Tracing med OpenTelemetry

Integrér OpenTelemetry for at opnå distribueret tracing og visualisere anmodningsforløb fra ende til anden på tværs af services.

Lektion 2 af 412 trin

Tracing med OpenTelemetry er en gratis gRPC og højtydende API'er-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i gRPC og højtydende API'er, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. gRPC og højtydende API'er-kurset indeholder 4 lektioner i alt.

Mikrotjenester og behovet for tracing

Moderne applikationer består ofte af mange små, indbyrdes forbundne tjenester, der kaldes mikrotjenester. Denne arkitektur giver fleksibilitet, men kan gøre fejlfinding af komplekse interaktioner til en stor udfordring.

Når en brugerforespørgsel bevæger sig gennem flere tjenester, kan det føles som at finde en nål i en høstak at fastslå, hvor en forsinkelse eller fejl opstod.

Følg forespørgslens vej

Distribueret tracing hjælper dig med at visualisere hele en forespørgsels forløb, mens den bevæger sig gennem forskellige tjenester i dit system.

  • Det forbinder alle handlinger, der hører til den samme forespørgsel.
  • Det viser, hvor lang tid hver del af forespørgslen tog.
  • Det hjælper med at identificere flaskehalse i ydeevnen og fejl på tværs af tjenestegrænser.

OpenTelemetry: Standarden for observability

OpenTelemetry (OTel) er et open source-projekt, der giver en standardiseret måde at indsamle telemetridata fra dine applikationer på. Det omfatter traces, metrikker og logge.

I stedet for leverandørspecifikke SDK'er tilbyder OTel ét samlet sæt af API'er, SDK'er og værktøjer til at instrumentere dine tjenester, så dine observability-data kan flyttes mellem forskellige løsninger.

Traces opbygget af spans

I OpenTelemetry repræsenterer en trace hele udførelsesforløbet for en forespørgsel gennem dit system. Det er som hele historien om forespørgslen.

En trace består af en eller flere spans. Hver span repræsenterer én handling eller arbejdsenhed i denne trace, som et trin i historien. Spans har et starttidspunkt, et sluttidspunkt og metadata.

Videregivelse af trace-kontekst

For at spans kan danne en komplet trace på tværs af forskellige tjenester, skal de forbindes. Det sker gennem kontekstvideregivelse.

Når en tjeneste kalder en anden, sender den en "trace-kontekst" med (ofte i HTTP/2-headere for gRPC). Denne kontekst fortæller den modtagende tjeneste, hvilken trace og overordnede span dens nye handlinger hører til, så der oprettes en forælder-barn-relation mellem spans.

Det vigtigste om OTel-tracing

For at implementere tracing arbejder du med disse centrale OpenTelemetry-komponenter:

  • TracerProvider: Administrerer Tracer-instanser og konfigurerer span-processorer og eksportører.
  • Tracer: Opretter Span-objekter.
  • SpanProcessor: Behandler spans, før de eksporteres (f.eks. ved batching).
  • Exporter: Sender indsamlede spans til en backend (f.eks. en konsol, Jaeger eller en OTLP-collector).

Opsætning af OTel: TracerProvider og Exporter

Lad os opsætte en grundlæggende OpenTelemetry-TracerProvider i Go. Vi bruger en enkel stdouttrace-eksportør til at udskrive tracedata i konsollen, så du kan se de spans, der bliver genereret.

Dette kodestykke initialiserer den globale TracerProvider og sikrer, at alle spans tømmes, når applikationen afsluttes.

Kør denne kode for at se den grundlæggende opsætning:

package main

import (
	"context"
	"log"
	"os"
	"time"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
	"go.opentelemetry.io/otel/sdk/resource"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
	semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
	"go.opentelemetry.io/otel/trace"
)

// initTracerProvider initializes an OpenTelemetry TracerProvider
// with a stdout exporter for demonstration.
func initTracerProvider() *sdktrace.TracerProvider {
	// Create stdout exporter to print traces to the console
	exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
	if err != nil {
		log.Fatalf("failed to create stdout exporter: %v", err)
	}

	// Create a new TracerProvider with the exporter and a service name
	tp := sdktrace.NewTracerProvider(
		sdktrace.WithBatcher(exporter), // Batch spans for efficiency
		sdktrace.WithResource(resource.NewWithAttributes(
			semconv.SchemaURL,
			semconv.ServiceName("my-simple-service"),
			attribute.String("environment", "development"),
		)),
	)
	otel.SetTracerProvider(tp) // Set the global TracerProvider

	return tp
}

func main() {
	tp := initTracerProvider()
	defer func() {
		if err := tp.Shutdown(context.Background()); err != nil {
			log.Fatalf("Error shutting down tracer provider: %v", err)
		}
	}()

	// Get a tracer
	tracer := otel.Tracer("my-app-tracer")

	// Create a root span
	ctx, span := tracer.Start(context.Background(), "main-operation")
	defer span.End()

	log.Println("Hello from main-operation with tracing enabled!")
	time.Sleep(100 * time.Millisecond) // Simulate some work

	// Create a child span
	_, childSpan := tracer.Start(ctx, "sub-operation")
	defer childSpan.End()
	childSpan.SetAttributes(attribute.String("task", "processing"))
	log.Println("Doing some sub-operation...")
	time.Sleep(50 * time.Millisecond) // Simulate more work

	log.Println("Trace data will be printed on shutdown.")
}

Automatisk tracing af gRPC-kald

Hvis gRPC-forespørgsler skal traces automatisk, tilbyder OpenTelemetry interceptors. Det er middleware-funktioner, der omslutter gRPC-kald på både klient- og serversiden.

  • Serverinterceptor: Starter en ny span for indgående forespørgsler og forbinder den med trace-konteksten fra klienten.
  • Klientinterceptor: Indlejrer den aktuelle trace-kontekst i udgående forespørgsler, så den kan videregives til serveren.

Sådan anvender du dem på din gRPC-server og -klient:

package main

import (
	"context"
	"log"
	"net"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	// OpenTelemetry imports
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
	"go.opentelemetry.io/otel/sdk/resource"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
	semconv "go.opentelemetry.io/otel/semconv/v1.24.0"

	// gRPC OpenTelemetry instrumentation
	"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"

	// For this example, we'll define a simple service in-line
	pb "google.golang.org/grpc/examples/helloworld/helloworld" // Using a standard example proto
)

// initTracerProvider initializes an OpenTelemetry TracerProvider
// with a stdout exporter for demonstration.
func initTracerProvider() *sdktrace.TracerProvider {
	exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
	if err != nil {
		log.Fatalf("failed to create stdout exporter: %v", err)
	}

	tp := sdktrace.NewTracerProvider(
		sdktrace.WithBatcher(exporter),
		sdktrace.WithResource(resource.NewWithAttributes(
			semconv.SchemaURL,
			semconv.ServiceName("grpc-otel-demo"),
			attribute.String("environment", "development"),
		)),
	)
	otel.SetTracerProvider(tp)
	return tp
}

// helloServer is a simple gRPC server for demonstration
type helloServer struct {
	pb.UnimplementedGreeterServer
}

func (s *helloServer) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	log.Printf("Received: %v", in.GetName())
	// Simulate some work
	time.Sleep(50 * time.Millisecond)
	return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

func main() {
	// 1. Initialize OpenTelemetry TracerProvider
	tp := initTracerProvider()
	defer func() {
		if err := tp.Shutdown(context.Background()); err != nil {
			log.Fatalf("Error shutting down tracer provider: %v", err)
		}
	}()

	// 2. Start gRPC Server with OpenTelemetry Interceptor
	lis, err := net.Listen("tcp", ":50051")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer(
		grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()), // Apply OTel server interceptor
	)
	pb.RegisterGreeterServer(s, &helloServer{})
	log.Println("Server listening on :50051")
	go func() {
		if err := s.Serve(lis); err != nil {
			log.Fatalf("failed to serve: %v", err)
		}
	}()
	defer s.Stop()

	// Allow server to start
	time.Sleep(1 * time.Second)

	// 3. Create gRPC Client with OpenTelemetry Interceptor
	conn, err := grpc.Dial(
		"localhost:50051",
		grpc.WithInsecure(), // For simplicity, use insecure
		grpc.WithBlock(),
		grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()), // Apply OTel client interceptor
	)
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)

	// 4. Make a gRPC call
	log.Println("Making gRPC call...")
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "CoddyKit User"})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("Greeting: %s", r.GetMessage())

	// Give some time for spans to be processed and exported
	time.Sleep(1 * time.Second)
	log.Println("Trace data should now be visible in console.")
}

Læs historien i en trace

Når du har kørt det forrige eksempel, kan du se et detaljeret JSON-output i konsollen. Dette output repræsenterer tracedataene.

  • Se efter "TraceID": Dette unikke ID forbinder alle spans, der hører til den samme forespørgsel.
  • Hver post er en "Span": Den har et "SpanID", et "ParentSpanID" (hvis den ikke er roden), navn, start- og sluttidspunkter samt attributter.
  • Hierarkiet af spans (forælder-barn) viser udførelsens forløb.

Denne "historie" hjælper dig med at forstå, hvor lang tid hvert trin tog, og hvilken tjeneste der var ansvarlig.

Berigelse af spans med attributter

Selvom automatisk instrumentering giver grundlæggende spans, har du ofte brug for at føje mere specifikke oplysninger til dine traces. Span-attributter (nøgle-værdi-par) gør det muligt.

Du kan registrere oplysninger som bruger-ID'er, forespørgselsparametre eller resultater af specifik forretningslogik direkte på en span. Det gør fejlfinding og analyse langt mere effektiv.

Brug span.SetAttributes() til at tilføje brugerdefinerede attributter.

package main

import (
	"context"
	"log"
	"time"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
	"go.opentelemetry.io/otel/sdk/resource"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
	semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
	"go.opentelemetry.io/otel/trace"
)

// initTracerProvider initializes an OpenTelemetry TracerProvider
func initTracerProvider() *sdktrace.TracerProvider {
	exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
	if err != nil {
		log.Fatalf("failed to create stdout exporter: %v", err)
	}
	tp := sdktrace.NewTracerProvider(
		sdktrace.WithBatcher(exporter),
		sdktrace.WithResource(resource.NewWithAttributes(
			semconv.SchemaURL,
			semconv.ServiceName("custom-attributes-demo"),
		)),
	)
	otel.SetTracerProvider(tp)
	return tp
}

func main() {
	tp := initTracerProvider()
	defer func() {
		if err := tp.Shutdown(context.Background()); err != nil {
			log.Fatalf("Error shutting down tracer provider: %v", err)
		}
	}()

	tracer := otel.Tracer("my-custom-tracer")

	// Start a root span
	ctx, span := tracer.Start(context.Background(), "process-order")
	defer span.End()

	// Add custom attributes to the root span
	span.SetAttributes(
		attribute.String("user.id", "user123"),
		attribute.Int("order.id", 45678),
		attribute.Bool("is_premium_user", true),
	)
	log.Println("Processing order with custom attributes...")
	time.Sleep(100 * time.Millisecond)

	// Create a child span
	_, childSpan := tracer.Start(ctx, "database-query")
	defer childSpan.End()
	childSpan.SetAttributes(
		attribute.String("db.system", "postgres"),
		attribute.String("db.statement", "SELECT * FROM orders WHERE id=45678"),
	)
	log.Println("Executing database query...")
	time.Sleep(50 * time.Millisecond)

	log.Println("Check the console output for custom attributes in the spans!")
	time.Sleep(500 * time.Millisecond) // Ensure spans are exported
}

Test af tracing-begreber

Du har lært om distribueret tracing, og hvordan OpenTelemetry hjælper. Lad os teste din forståelse.

Opsummering: Tracing med OpenTelemetry

I denne lektion lærte du, hvordan distribueret tracing hjælper med at navigere i mikrotjenesternes kompleksitet ved at vise en forespørgsels fulde forløb.

Vi undersøgte OpenTelemetry som standarden til indsamling af traces og gennemgik centrale begreber som traces, spans og kontekstvideregivelse. Du så, hvordan man opsætter en OTel-TracerProvider, og hvordan gRPC-interceptors gør det enkelt at instrumentere dine tjenester. Til sidst gennemgik vi, hvordan du beriger dine spans med brugerdefinerede attributter for at få dybere indsigt.

Med OpenTelemetry får du afgørende indsigt i din gRPC-applikations adfærd!

Gratis at komme i gang

Lær gRPC og højtydende API'er med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
12
Lektioner
48

Ofte stillede spørgsmål

Er lektionen “Tracing med OpenTelemetry” gratis?

Ja — hele teksten til “Tracing med OpenTelemetry” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af gRPC og højtydende API'er-kurset, skal du opgradere til CoddyKit PRO. gRPC og højtydende API'er-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Tracing med OpenTelemetry”?

Integrér OpenTelemetry for at opnå distribueret tracing og visualisere anmodningsforløb fra ende til anden på tværs af services. Du øver dig i gRPC og højtydende API'er med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på gRPC og højtydende API'er?

Der kræves ingen tidligere erfaring. gRPC og højtydende API'er på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.

Hvor lang tid tager lektionen “Tracing med OpenTelemetry”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne gRPC og højtydende API'er-lektion?

Ja. Alle gRPC og højtydende API'er-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Logning af gRPC-interaktioner
  2. Tracing med OpenTelemetry
  3. Overvågning af gRPC-metrikker
  4. Sundhedstjek og readiness-prober
← Tilbage til gRPC og højtydende API'er