telemetry

Sentry

Env-kapcsolós hibariportolás az OTel tetején: errs-kód szerinti issue-csoportosítással és teljes trace-linkeléssel.

Egy DSN beállítása után a sentryx a Sentry hibariportolást a slog-on és az OpenTelemetryn keresztül kapcsolja be. Ez nem technikai apróság, hanem a lényeg: mivel a httperr handlerek, a chi panic-recovery és a worker már ma is slog-on, trace-hordozó contexttel logolnak minden 5xx-et és task-hibát, egyetlen slog-handler bekötése az összes belépési pontot lefedi: a te kódodban semmi Sentry-specifikus nincs.

Bekapcsolás:

SENTRY_DSN=https://<key>@<org>.ingest.sentry.io/<project>

Ennyi. SENTRY_DSN nélkül az integráció teljesen ki van kapcsolva: minden hook no-op, a viselkedés azonos egy Sentry nélküli kittel.

Konfiguráció

VáltozóDefaultJelentés
SENTRY_DSNüresprojekt DSN; üresen az integráció teljesen ki
SENTRY_ENVIRONMENTdev → development, egyébként productionenvironment tag minden eventen
SENTRY_TRACEStruea kit OTel spanjeinek küldése a Sentrybe; false, ha a collectorod továbbítja őket
SENTRY_DEBUGfalsea Sentry SDK saját debug-logja stderr-re

A release minden eventen <service>@<verzió> (az OTEL_SERVICE_NAME / OTEL_SERVICE_VERSION értékéből), a Sentry release-követése külön beállítás nélkül működik.

Az OTel tetején, nem helyette

A sentryx nem hoz saját trace-elést, saját middleware-hálót, saját mérést: az OTel marad a gerinc, a Sentry három ponton csatlakozik rá. Mindhármat a telemetry.Setup drótozza be, te sosem hívod a sentryx.Setup-ot:

  • Spanek → Sentry. A Handle.SpanExporter() egy plusz exporterként kerül az otelx tracer providerére (otelx.WithSpanExporter): ugyanazok a spanek mennek a collectorodba és a Sentrybe. A hiba így a teljes trace-waterfallhoz linkelődik: a HTTP-span, minden DB-query az idejével, a queue-taskok. A sampling OTel-vezérelt marad (OTEL_TRACES_SAMPLER), nincs külön Sentry sample rate.
  • Hibák → Sentry. A Handle.SlogHandler() a log-fanout egyik ága: minden ERROR-szintű rekord Sentry event lesz. A rekord első hiba-értékű attr-ja a captured exception; egy errs hiba a keletkezési pontján rögzített stackjét, kódját, publikus üzenetét és wrap-láncát is magával viszi (az event errs contextjében). Az alacsonyabb szintű rekordok (DEBUG/INFO/WARN) breadcrumb-ként gyűlnek, így az event alatt ott a hozzá vezető log-nyomvonal, a LOG_LEVEL-t ugyanúgy tisztelik, mint bármely sink.
  • Hiba ↔ trace linkelés. Az OTel-integráció minden eventre rábélyegzi az aktív trace/span ID-t, így a hiba a helyes spanre kerül a waterfallban.

A breadcrumb-izoláció kérésenkénti: a chassis minden belépési ponton (chi middleware, worker task-middleware) meghívja a sentryx.WithRequestHub-ot, ami a contextre egy friss, klónozott hubot tesz, így az egyik kérés logsorai nem szivárognak át a másik kérés eventjébe. Hub nélküli contextben (pl. egy saját goroutine context.Background()-dal) a breadcrumb-ok inkább eldobódnak, mint hogy a globális hubot szennyezzék.

Egy hiba = egy issue: az errs-kód mint fingerprint

A Sentry alapból a legkülső exception típusa szerint címez és csoportosít. Go-ban ez a típusnév lenne, és mivel egy errs-lánc minden szintje ugyanazt a *errs.Error típust hordozza, minden hibád egyetlen *errs.Error nevű issue-ba folyna össze. A sentryx ezért küldés előtt átírja az eventet:

  • Az issue címe a hiba errs-kódja lesz (kód híján a legkülső wrap-üzenete).
  • A kód a grouping fingerprint is: egy deklarált hiba minden előfordulása egy issue-ba csoportosul, függetlenül attól, hány különböző kódútvonalon keletkezett.
  • A másodlagos sor a Public üzenet (ha van): az issue-lista kód / kliens felé mutatott szöveg formában olvasható, a teljes belső lánc egy kattintásra, az event errs contextjében.

A shop kanonikus hibáján ez így fest:

var ErrOutOfStock = errs.Define("shop_out_of_stock",
    errs.Public("Egy vagy több tétel nincs elegendő készleten."))

// repository:
return ErrOutOfStock.New("inventory: decrement: insufficient stock")
// service:
return errs.Wrap(err, "shop: place order")

Akárhány rendelés bukik el, akárhogy alakul közben a stack, a Sentryben egyetlen issue: shop_out_of_stock, alatta a publikus üzenettel, minden eventjén a teljes stackkel, a wrap-lánccal, a request breadcrumb-jaival és a trace-linkkel. Amikor pedig eldöntitek, hogy a készlethiány nem incidens, hanem elvárt kliens-viselkedés, adjatok a definícióhoz errs.Status(http.StatusConflict)-ot: onnantól 409-ként megy ki, és se request failed logsor, se Sentry event nem születik belőle, az issue magától elhal.

A Sentry OTLP-ingestje a trace-eket fogadja, de az OTel span-eventeket eldobja: a lifecycle-nézet a valódi spanekből (HTTP, DB, queue, S3) és a breadcrumb-nyomvonalból áll össze. A forráskód-kontextust a Sentry felületén a GitHub/SCM integráció adja; a Go binárisok nem hordoznak forrást.

Dev módban is működik: collector nélkül

OTEL_DEV_MODE=true mellett az OTel nem épít exportert, de ha a SENTRY_DSN be van állítva, a kit egy Sentry-only tracer providert húz fel, így lokálisan, mindenféle collector nélkül megkapod a teljes trace-t és a hibát a Sentryben. Ez a legolcsóbb módja annak, hogy a „kövesd a rendelést" trace-t élőben lásd: dev mód + egy dev Sentry-projekt DSN-je.

Miért wrapper: a kivétel, ami erősíti a szabályt

A kit elve a centralizál, nem absztrahál, és a sentryx a három kimondott kivétel egyike: a sentry-go-t tényleg becsomagolja. Az ok az elhagyhatóság. A Sentry opcionális integráció: env-ből kapcsolható, és mivel kizárólag a telemetry facade és a chassis importálja a sentryx-et, egy nem-chassis bináris (CLI, migrátor, bármi, ami csak otelx-et vagy dbx-et használ) be sem linkeli a sentry-go-t. Ha a sentry-go típusok átfolynának a kit API-ján, ez a garancia elveszne. A napi munkában amúgy sem hívod: a hibáid a return err-rel jutnak el a Sentrybe.

Az API ettől még ott van, ha kell (a telemetry is ezen keresztül dolgozik):

func Setup(ctx context.Context, cfg Config, serviceName, serviceVersion string, dev bool) (*Handle, error)

func (h *Handle) Enabled() bool
func (h *Handle) SpanExporter() sdktrace.SpanExporter  // nil, ha ki van kapcsolva / SENTRY_TRACES=false
func (h *Handle) SlogHandler() slog.Handler            // nil, ha ki van kapcsolva
func (h *Handle) Flush(ctx context.Context) error      // event-transport drain

func WithRequestHub(ctx context.Context) context.Context // kérésenkénti hub, a chassis hívja

Flush a legvégén

A Sentry event-transportja aszinkron: a capture sosem blokkolja a kérést. A lezárást a telemetria-shutdown intézi: a Flush az OTel providerek leállása után fut, legutolsóként az életciklusban, így a leállás közben keletkező hibák is kimennek, mielőtt a processz kilép.

Használt patternek

A sentryx a kit három kimondott interfész-mögé-rejtési kivételének egyike; lásd Design patternek: Architekturális alapelvek. A WithSpanExporter-be illesztés a functional options nil-toleráns variánsának konkrét példája.

A Sentry hivatalos Go-dokumentációja: docs.sentry.io/platforms/go.

Copyright © 2026