84 lines
2.0 KiB
Go
84 lines
2.0 KiB
Go
// 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
|
|
}
|