Setup Base
Configurare Clue e OpenTelemetry
I sistemi distribuiti moderni sono complessi. Quando qualcosa va storto, il tradizionale logging da solo non è sufficiente per capire cosa è successo. Hai bisogno di vedere come le richieste fluiscono attraverso il tuo sistema, misurare le prestazioni e monitorare la salute del sistema. È qui che entra in gioco l’osservabilità.
L’osservabilità è la tua capacità di comprendere cosa sta succedendo all’interno del tuo sistema osservando i suoi output. In Goa, raggiungiamo questo attraverso tre pilastri principali:
Clue è il pacchetto di osservabilità raccomandato da Goa. È costruito su OpenTelemetry, lo standard industriale per l’osservabilità, e fornisce una stretta integrazione con il codice generato da Goa.
Ecco un semplice esempio di come appare l’osservabilità nella pratica:
import (
"go.opentelemetry.io/otel" // OpenTelemetry standard
"go.opentelemetry.io/otel/attribute" // OpenTelemetry standard
"goa.design/clue/log" // Pacchetto di logging di Clue
)
func (s *Service) CreateOrder(ctx context.Context, order *Order) error {
// Uso dell'API standard di OpenTelemetry
ctx, span := otel.Tracer("service").Start(ctx, "create_order")
defer span.End()
// Attributi standard di OpenTelemetry
span.SetAttributes(
attribute.String("order.id", order.ID),
attribute.Float64("order.amount", order.Amount))
// Metriche standard di OpenTelemetry
s.orderCounter.Add(ctx, 1,
attribute.String("type", order.Type))
// Logging strutturato di Clue (opzionale)
log.Info(ctx, "elaborazione ordine",
log.KV{"order_id", order.ID})
if err := s.processOrder(ctx, order); err != nil {
// Registrazione standard degli errori di OpenTelemetry
span.RecordError(err)
return err
}
return nil
}
Nota che la maggior parte del codice usa pacchetti standard di OpenTelemetry
(go.opentelemetry.io/otel/*
). Solo il logging usa codice specifico di Clue, e
anche quello potrebbe essere sostituito con la tua soluzione di logging preferita. Questo significa che puoi:
Clue segue un approccio OpenTelemetry-first. Questo significa:
Tracce sono il tuo principale strumento di debug. Ti mostrano:
Metriche ti aiutano a monitorare la salute del sistema:
Log sono usati con parsimonia, principalmente per:
Questo approccio scala meglio del logging tradizionale perché:
Per aggiungere l’osservabilità al tuo servizio Goa, dovrai:
Le seguenti guide ti guideranno attraverso ogni passo:
Ecco come appare nella pratica un servizio Goa completamente osservabile:
func main() {
// 1. Crea logger con formattazione appropriata
format := log.FormatJSON
if log.IsTerminal() {
format = log.FormatTerminal
}
ctx := log.Context(context.Background(),
log.WithFormat(format),
log.WithFunc(log.Span))
// 2. Configura OpenTelemetry con esportatori OTLP
spanExporter, err := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint(*coladdr),
otlptracegrpc.WithTLSCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf(ctx, err, "inizializzazione tracciamento fallita")
}
metricExporter, err := otlpmetricgrpc.New(ctx,
otlpmetricgrpc.WithEndpoint(*coladdr),
otlpmetricgrpc.WithTLSCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf(ctx, err, "inizializzazione metriche fallita")
}
// 3. Inizializza Clue con OpenTelemetry
cfg, err := clue.NewConfig(ctx,
genservice.ServiceName,
genservice.APIVersion,
metricExporter,
spanExporter)
clue.ConfigureOpenTelemetry(ctx, cfg)
// 4. Crea servizio con middleware
svc := front.New(fc, lc)
endpoints := genservice.NewEndpoints(svc)
endpoints.Use(debug.LogPayloads()) // Logging di debug
endpoints.Use(log.Endpoint) // Logging delle richieste
endpoints.Use(middleware.ErrorReporter())
// 5. Configura handler HTTP con osservabilità
mux := goahttp.NewMuxer()
debug.MountDebugLogEnabler(debug.Adapt(mux)) // Controllo dinamico livello log
debug.MountPprofHandlers(debug.Adapt(mux)) // Endpoint profiling Go
// Aggiungi middleware nell'ordine corretto:
handler := otelhttp.NewHandler(mux, serviceName) // 3. OpenTelemetry
handler = debug.HTTP()(handler) // 2. Endpoint debug
handler = log.HTTP(ctx)(handler) // 1. Logging richieste
// 6. Monta controlli di salute su porta separata
check := health.Handler(health.NewChecker(
health.NewPinger("locator", *locatorHealthAddr),
health.NewPinger("forecaster", *forecasterHealthAddr)))
http.Handle("/healthz", log.HTTP(ctx)(check))
// 7. Avvia server con spegnimento graceful
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
log.Printf(ctx, "Server HTTP in ascolto su %s", *httpAddr)
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Errorf(ctx, err, "errore server")
}
}()
// Gestisci spegnimento
<-ctx.Done()
if err := server.Shutdown(context.Background()); err != nil {
log.Errorf(ctx, err, "errore spegnimento")
}
wg.Wait()
}
Questo servizio mostra diverse importanti funzionalità di osservabilità che aiutano a monitorare e debuggare l’applicazione in produzione. Implementa logging strutturato che propaga il contesto attraverso il servizio, permettendo alle richieste di essere tracciate attraverso i componenti. Il servizio integra OpenTelemetry per il tracciamento distribuito e la raccolta di metriche, fornendo insight sulle prestazioni e sul comportamento. Gli endpoint di controllo della salute monitorano lo stato delle dipendenze come i servizi locator e forecaster. Gli endpoint di debug abilitano il profiling del servizio in esecuzione per identificare colli di bottiglia nelle prestazioni. Il servizio supporta anche il controllo dinamico del livello di log per regolare la verbosità a runtime senza riavvii. Infine, implementa la gestione dello spegnimento graceful per pulire correttamente le risorse e completare le richieste in volo quando si ferma il servizio.