Add basic-tracing example: nested spans and graceful shutdown
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
83
examples/basic-tracing/main.go
Normal file
83
examples/basic-tracing/main.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// OpenTelemetry tracing with nested spans and graceful shutdown.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand/v2"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"git.codelab.vc/pkg/obsx"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer stop()
|
||||
|
||||
shutdown, err := obsx.SetupTracer(ctx, obsx.TracerConfig{
|
||||
ServiceName: "basic-tracing",
|
||||
ServiceVersion: "0.1.0",
|
||||
Endpoint: "localhost:4317",
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
if err := shutdown(context.Background()); err != nil {
|
||||
log.Printf("tracer shutdown: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := obsx.StartSpan(r.Context(), "HandleGetUser")
|
||||
defer span.End()
|
||||
|
||||
userID := r.PathValue("id")
|
||||
span.SetAttributes(attribute.String("user.id", userID))
|
||||
|
||||
user, err := fetchUser(ctx, userID)
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
span.SetStatus(codes.Ok, "")
|
||||
fmt.Fprintln(w, user)
|
||||
})
|
||||
|
||||
srv := &http.Server{Addr: ":8080", Handler: mux}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
_ = srv.Shutdown(context.Background())
|
||||
}()
|
||||
|
||||
log.Println("listening on :8080 (try GET /users/42)")
|
||||
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// fetchUser simulates a database query with its own child span.
|
||||
func fetchUser(ctx context.Context, id string) (string, error) {
|
||||
ctx, span := obsx.StartSpan(ctx, "db.QueryUser")
|
||||
defer span.End()
|
||||
|
||||
span.SetAttributes(attribute.String("db.statement", "SELECT * FROM users WHERE id = $1"))
|
||||
|
||||
// Simulate DB latency.
|
||||
time.Sleep(time.Duration(rand.IntN(50)) * time.Millisecond)
|
||||
|
||||
_ = ctx // context carries the span for further nesting if needed
|
||||
return fmt.Sprintf(`{"id": %q, "name": "Alice"}`, id), nil
|
||||
}
|
||||
Reference in New Issue
Block a user