Elido
12 Min. LesezeitEngineering
Eckpfeiler

Sentry/GlitchTip über 12 Go-Services verdrahten, ohne den Hot Path zu brechen

Wie Elido ein gemeinsames sentryinit-Paket ausgeliefert hat, das jedem Go-Service dasselbe Panic- + 5xx-Auto-Capture gibt - und beim p95-15ms-Budget von edge-redirect zero-alloc bleibt.

Marius Voß
DevRel · edge infra
Diagram of 12 Go service tiles each emitting events into a central GlitchTip ingest, with the edge-redirect tile labelled zero-alloc on the happy path

Wenn Sie einen Go-Service haben, ist Error-Tracking eine halbstündige Aufgabe: sentry-go einfügen, aus SENTRY_DSN initialisieren, sentry.CaptureException an den wenigen Stellen aufrufen, die wichtig sind, ausliefern. Wenn Sie zwölf Go-Services haben, wird dieselbe halbstündige Entscheidung zu einer Steuer, die sich potenziert - jeder Service wächst seinen eigenen leicht unterschiedlichen Init-Code, seine eigene leicht unterschiedliche Middleware, seine eigene Meinung darüber, was „Release Tag" bedeutet. Bis ein Produktions-Panic passiert, entdecken Sie, dass drei Services das SDK überhaupt nicht initialisieren, weil jemand die Env-Variable im Deployment-Manifest vergessen hat.

Wir haben gerade diese Verdrahtung bei Elido fertiggestellt - zwölf Go-Services plus ein Audit-Chain-Backfill-CLI plus drei Next.js-Apps plus zwei Node-Services, alle speisen ein selbst-gehostetes GlitchTip unter sentry.elido.app. Die interessanten Teile waren nicht die SDK-Aufrufe. Es war die Form des gemeinsamen Pakets, die die SDK-Aufrufe in einer Zeile pro Service verschwinden lässt, und die Einschränkungen, die sich daraus ergeben, die Middleware auf edge-redirects Hot Path zu brauchen, ohne das p95-15ms-Budget zu verbrennen.

Dieser Beitrag ist ein vollständiger Bericht darüber, wie die Verdrahtung funktioniert, was wir richtig gemacht haben und die beiden Kompromisse, die wir bewusst eingegangen sind.

TL;DR#

  • Ein gemeinsames Paket, pkg/sentryinit, ersetzt zwölf func main-Kopien. Einen neuen Service hinzuzufügen ist ein einziges defer sentryinit.Init(logger, "service-name")() plus eine Middleware-Zeile.
  • ChiMiddleware() erfasst automatisch Panics und nicht-panicende 5xx-Antworten auf Warm-Path-Services. FastHTTPMiddleware() macht dasselbe für edge-redirect und ist zero-alloc auf dem Happy Path - verifiziert durch einen Benchmark, der mit dem Paket ausgeliefert wird.
  • Wir haben GlitchTip (Sentry-kompatibel, selbst-gehostet) gegenüber Sentry SaaS für EU-Residenz gewählt. Das SDK ist unverändert.
  • Der Hot Path ruft explizit NICHT sentry.CaptureException aus Handler-Code auf. Alle Erfassung passiert an der Middleware-Grenze, wo die Kosten nur materialisieren, wenn es etwas zu berichten gibt.

Warum ein gemeinsames Paket, nicht zwölf Kopien#

Das gemeinsame sentryinit-Paket, das Init, ChiMiddleware und FastHTTPMiddleware bereitstellt, und die chi-Warm-Path-Services sowie den fasthttp-edge-redirect-Service jeweils mit einer Zeile pro main.go verdrahtet

Die minimale brauchbare Sentry-Verdrahtung in Go sind sechs Zeilen:

sentry.Init(sentry.ClientOptions{
    Dsn:              os.Getenv("SENTRY_DSN"),
    Environment:      os.Getenv("ENV"),
    Release:          os.Getenv("ELIDO_VERSION"),
    ServerName:       "api-core",
    AttachStacktrace: true,
})
defer sentry.Flush(2 * time.Second)

Sechs Zeilen, zwölf Services. Zweiundsiebzig Zeilen, die mit der Zeit divergieren. Das Problem ist nicht die Anzahl - es ist die Drift. Ein Service vergisst Release. Ein anderer setzt Environment aus einer leicht anders benannten Env-Variable. Ein dritter hat einen Ein-Sekunden-Flush und verliert Ereignisse bei einem schnellen SIGTERM. Das Verhalten von Error-Tracking über die Flotte hört auf, eine Eigenschaft der Plattform zu sein, und beginnt, eine Eigenschaft des Engineers zu sein, der die main.go dieses Services geschrieben hat.

pkg/sentryinit ist der un-clevere Fix. Es lebt im Go-Workspace, jeder Service required es über eine lokale replace-Direktive, und die Aufrufstelle ist eine Zeile:

defer sentryinit.Init(logger, "api-core")()

Das Paket selbst ist klein. Die gesamte Laufzeitoberfläche besteht aus einer Init-Funktion, zwei HTTP-Middlewares (chi und net/http), einer fasthttp-Middleware und einem Debug-Endpoint zum Nachweis der Verdrahtung Ende-zu-Ende in der Produktion. Die relevanten Teile der Implementierung:

func Init(logger *zap.Logger, serverName string) func() {
    dsn := os.Getenv("SENTRY_DSN")
    if dsn == "" {
        return func() {}
    }
    env := os.Getenv("ENV")
    if env == "" {
        env = "production"
    }
    release := os.Getenv("ELIDO_VERSION")
    if err := sentry.Init(sentry.ClientOptions{
        Dsn:              dsn,
        Environment:      env,
        Release:          release,
        ServerName:       serverName,
        AttachStacktrace: true,
        EnableTracing:    false,
        SampleRate:       1.0,
        IgnoreErrors: []string{
            "context canceled",
            "http: Server closed",
        },
    }); err != nil {
        if logger != nil {
            logger.Warn("sentry init failed", zap.Error(err), zap.String("service", serverName))
        }
        return func() {}
    }
    sentry.ConfigureScope(func(scope *sentry.Scope) {
        scope.SetTag("service", serverName)
    })
    return func() { sentry.Flush(flushTimeout) }
}

Drei Dinge in diesem Snippet, die ihre Zeilen verdienen.

Erstens, der Early Return bei leerem DSN. Lokale Entwicklung hat keinen DSN. CI-Tests haben auch keinen. Ohne den Early Return würde jede Dev-Box versuchen, ein SDK zu initialisieren, das nirgendwohin zeigt, und jedes Mal eine „invalid DSN"-Warnung ausgeben, wenn go run startet. Der Early Return bedeutet, dass die Aufrufstelle nie verzweigen muss - defer sentryinit.Init(logger, "api-core")() ist in jeder Umgebung korrekt.

Zweitens, das service-Tag, das auf dem globalen Scope gepinnt ist. GlitchTip segmentiert Ereignisse bereits nach Projekt (ein Projekt pro Service), aber das Tag erlaubt projektübergreifende Suchen und Dashboards, nach Service-Slug zu filtern, ohne die Projekt-ID des DSN parsen zu müssen. Wenn dieselbe Panic-Klasse innerhalb einer Stunde in drei Services erscheint, macht das Tag dieses Muster in einer Abfrage auffindbar.

Drittens, IgnoreErrors. context canceled ist das, was jeder gRPC-Client zurückgibt, wenn eine nachgelagerte Anfrage durch ein vorgelagertes Timeout abgebrochen wird - ein normales Kontrollfluss-Ereignis in einem verketteten Microservice-Graphen, kein Bug. http: Server closed ist das, was der stdlib-HTTP-Server während des Graceful Shutdowns zurückgibt. Beide produzieren Rauschen, das das Signal übertönt. Die Deny-List filtert sie heraus, bevor sie die Warteschlange erreichen.

Einen neuen Service zu verdrahten ist, ihn an go.work anzuhängen, eine einzeilige require + replace in der go.mod des Services abzulegen und die defer-Zeile in main.go hinzuzufügen. Das ist der Vertrag. Alles andere - Flush-Timeout, Sample-Rate, ignorierte Fehlermuster - ist zentralisiert.

Die chi-Middleware#

Auf Warm-Path-Services - api-core, analytics-api, billing, domain-manager, search, url-scanner, qr-generator, metadata-fetcher, webhook-dispatcher - ist die Auto-Capture-Oberfläche HTTP. Ein Handler kann panicen oder kann ein 5xx ohne Panicen zurückgeben, und wir wollen beides sichtbar haben.

Der naive Ansatz ist, sentry-go/https eingebaute Handle-Middleware zu verwenden. Das haben wir nicht, aus zwei Gründen. Erstens startet diese Middleware immer eine Transaktion, selbst wenn EnableTracing false ist - verschwendete Allokation bei jeder Anfrage. Zweitens erfasst sie Panics, aber keine nicht-panicenden 5xx-Antworten, was bedeutet, dass ein Handler, der 503 zurückgibt, weil Postgres die Verbindung verloren hat, unsichtbar bleibt.

Eine Anfrage tritt in den Router und die Middleware ein; bei einer normalen 2xx- oder 3xx-Antwort wird nichts erfasst, waehrend ein Panic oder eine nicht-panicende 5xx-Antwort wiederhergestellt, geskopiert und als Ereignis zu GlitchTip transportiert wird

Der Ersatz ist klein:

func ChiMiddleware() func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            hub := sentry.GetHubFromContext(r.Context())
            if hub == nil {
                hub = sentry.CurrentHub().Clone()
                r = r.WithContext(sentry.SetHubOnContext(r.Context(), hub))
            }
            hub.Scope().SetRequest(r)

            ww := chimw.NewWrapResponseWriter(w, r.ProtoMajor)
            defer func() {
                if rvr := recover(); rvr != nil {
                    if rvr == http.ErrAbortHandler {
                        panic(rvr)
                    }
                    hub.RecoverWithContext(r.Context(), rvr)
                    if ww.Status() == 0 {
                        ww.WriteHeader(http.StatusInternalServerError)
                    }
                    return
                }
                if status := ww.Status(); status >= 500 && status < 600 {
                    hub.WithScope(func(scope *sentry.Scope) {
                        scope.SetLevel(sentry.LevelError)
                        scope.SetTag("status_code", strconv.Itoa(status))
                        hub.CaptureMessage(fmt.Sprintf("HTTP %d %s %s", status, r.Method, r.URL.Path))
                    })
                }
            }()

            next.ServeHTTP(ww, r)
        })
    }
}

Der Hub wird pro Anfrage geklont und auf dem Kontext gespeichert. Das erlaubt Handlern, domänenspezifische Breadcrumbs anzuhängen (sentry.GetHubFromContext(r.Context()).AddBreadcrumb(...)), ohne in andere In-Flight-Anfragen zu lecken. Der chi-interne WrapResponseWriter bewahrt die Interfaces http.Flusher / http.Hijacker / http.Pusher - einige chi-Middleware nachgelagert peeken auf diese, und ein handgerollter Wrapper verliert sie. Für Services, die chi nicht verwenden (click-ingester und analytics-export mounten plain http.ServeMux), liefert das Paket einen stdlib-only Zwilling namens HTTPMiddleware() aus.

Ein subtiles Verhalten: http.ErrAbortHandler wird re-paniced statt erfasst. Das ist die stdlib-Konvention für „der Client hat die Verbindung getrennt, die Goroutine sauber unterdrücken". Ihn als Exception zu erfassen würde die Warteschlange mit Nicht-Bugs überfluten.

Die Verdrahtung ist über die Warm-Path-Services hinweg identisch:

r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(sentryinit.ChiMiddleware())
r.Use(oteltrace.ChiMiddleware("api-core"))
// ... rest of the middleware stack

sentryinit.ChiMiddleware kommt vor oteltrace.ChiMiddleware, damit Panics in der Tracing-Schicht immer noch erfasst werden.

Der schwierige Teil: fasthttp auf dem Redirect Hot Path#

edge-redirect ist ein anderes Tier. Sein Budget ist p50 5ms / p95 15ms bei einem Cache-Hit, gemessen über drei Produktions-POPs. Alles, was pro Anfrage allokiert, taucht im GC-Profil auf und schließlich im p99-Tail. Die obige chi-Middleware ist in Ordnung für Warm-Path-Services, die frei allokieren; am Edge wäre sie ein Problem.

sentry-go/fasthttp.Handle war aus demselben Grund ein Nicht-Starter wie sentry-go/http.Handle: Es baut bei jeder Anfrage einen http.Request-Snapshot, einschließlich des Happy Paths, selbst wenn es nichts zu berichten gibt. Für einen Service, der tausende Anfragen pro Sekunde pro POP bedient, sind das tausende unnötige http.Request-Structs pro Sekunde pro POP.

Die fasthttp-Middleware in pkg/sentryinit dreht das Kostenmodell um: Nichts allokiert, bis tatsächlich etwas zu erfassen ist.

func FastHTTPMiddleware() func(fasthttp.RequestHandler) fasthttp.RequestHandler {
    return func(next fasthttp.RequestHandler) fasthttp.RequestHandler {
        return func(ctx *fasthttp.RequestCtx) {
            defer func() {
                if rvr := recover(); rvr != nil {
                    if rvr == http.ErrAbortHandler {
                        panic(rvr)
                    }
                    hub := sentry.CurrentHub().Clone()
                    req := fasthttpRequestSnapshot(ctx)
                    hub.Scope().SetRequest(req)
                    hub.RecoverWithContext(
                        context.WithValue(context.Background(), sentry.RequestContextKey, req),
                        rvr,
                    )
                    ctx.Response.Reset()
                    ctx.SetStatusCode(fasthttp.StatusInternalServerError)
                    return
                }
                if status := ctx.Response.StatusCode(); status >= 500 && status < 600 {
                    hub := sentry.CurrentHub().Clone()
                    req := fasthttpRequestSnapshot(ctx)
                    hub.WithScope(func(scope *sentry.Scope) {
                        scope.SetRequest(req)
                        scope.SetLevel(sentry.LevelError)
                        scope.SetTag("status_code", strconv.Itoa(status))
                        hub.CaptureMessage("HTTP " + strconv.Itoa(status) + " " + string(ctx.Method()) + " " + string(ctx.Path()))
                    })
                }
            }()
            next(ctx)
        }
    }
}

Die Form ist dieselbe wie bei der chi-Version, aber der Hub-Clone und die Request-Snapshot-Konstruktion sind innerhalb der recover- / 5xx-Zweige geschoben. Bei einer 302-Cache-Hit-Antwort - dem überwältigend häufigen Fall - feuert der Defer-Body, recover() gibt nil zurück, die Status-Prüfung gibt false zurück, und nichts anderes läuft. Der Closure selbst ist das, was Go bei dieser Aufruf-Form in den Stack-Frame inlinet, sodass selbst die Kosten der deferred-Funktion auf nichts Detektierbares amortisiert werden.

Es gibt einen Benchmark im Paket (fasthttp_test.go), der das festnagelt:

func BenchmarkFastHTTPMiddleware_HappyPath(b *testing.B) {
    noop := func(ctx *fasthttp.RequestCtx) {
        ctx.SetStatusCode(fasthttp.StatusFound)
    }
    wrapped := FastHTTPMiddleware()(noop)

    ctx := &fasthttp.RequestCtx{}
    ctx.Init(&ctx.Request, &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234}, nil)
    ctx.Request.SetRequestURI("/abc123")
    ctx.Request.Header.SetMethod("GET")
    ctx.Request.Header.SetHost("f.elido.me")

    b.ReportAllocs()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        wrapped(ctx)
    }
}

Gepaart mit BenchmarkFastHTTPHandler_Bare (gleicher Handler, keine Middleware) liegt das Delta auf einer 2024er M3-Dev-Box im Rauschen - die wrapped-Version berichtet null zusätzliche Allokationen pro Op. Die Sentry-Middleware auf dem edge-redirect-Hot-Path kostet auf dem Happy Path nichts. Sie kostet nur etwas, wenn es einen Panic oder ein 5xx gibt, was genau dann ist, wenn es Sie nicht stört zu zahlen.

Die Verdrahtung in edge-redirects main.go ist eine Zeile:

rootHandler := sentryinit.FastHTTPMiddleware()(h.Route)

Was dies explizit NICHT tut: Es streut keine sentry.CaptureException-Aufrufe durch den Redirect-Handler selbst. Der Handler bleibt so, wie das Latenzbudget es benötigt - keine Sentry-Awareness, keine Per-Request-Allokation für Error-Tracking-Zwecke. Die Middleware-Grenze ist der einzige Ort, an dem Erfassung passiert, und die Middleware-Grenze ist auf dem Happy Path strukturell frei.

Das ist ein bewusster Kompromiss. Wenn edge-redirect einen Logik-Bug hat, der eine falsche Ziel-URL produziert, ohne abzustürzen oder 5xx zurückzugeben - etwa eine fehlkonfigurierte Regel, die EU-Traffic zum falschen Fallback routet - wird Sentry es nicht sehen. Die Bot-Dashboards und das synthetische Monitoring werden. Der Trade ist, dass wir den Redirect günstig halten; Observability für Nicht-Fehler-Korrektheit lebt außerhalb des SDK.

Warum GlitchTip, nicht Sentry SaaS#

Ein DSGVO-first-Produkt, das Kundendaten an einen US-gehosteten Error-Tracking-Service schreibt, ist ein Widerspruch, den Auditoren bemerken. Stack-Traces von api-core enthalten URL-Pfade, gelegentlich Tenant-IDs, manchmal IP-Adressen (wir redigieren sie über Sentrys BeforeSend-Hook, aber die Redaktion kann versehentlich umgangen werden). Der sauberste Weg ist, die Datenebene innerhalb unserer eigenen EU-Region zu halten.

GlitchTip ist die Wahl. Es spricht das Sentry-Wire-Protokoll, sodass das SDK byte-identisch ist - kein Fork, kein Shim, keine zweite Auth-Bibliothek. Das Dashboard ist Sentry-geformt und lebt unter sentry.elido.app hinter unserem wg-easy VPN. Der Ingestion-Endpoint unter o<projectId>.sentry.elido.app/api/<id>/store/ ist von jedem Service über das öffentliche Internet erreichbar, mit Rate-Limits auf der nginx-Ebene. Der jüngste Commit fix(ops/nginx): open Sentry ingestion endpoints; keep dashboard VPN-only erfasst genau diese Trennung.

Die Migrationskosten von Sentry SaaS zu GlitchTip sind ungefähr eine DNS-Änderung, ein DSN-Swap pro Projekt und ein Postgres- + Redis-Deployment hinter dem Dashboard-Host. Wir sind nie auf SaaS gelaufen - wir haben GlitchTip von Tag eins an verdrahtet - aber der Pfad ist in beide Richtungen offen. Das SDK weiß nicht, mit welchem Backend es spricht.

Es gibt zwei GlitchTip-spezifische Caveats, die wir beim Rollout getroffen und behoben haben. Erstens erfordert GlitchTips Anmeldungs-Flow, dass die Registrierung offen ist, damit die initiale Admin-Einladung funktioniert; wir haben sie während des Bootstraps geöffnet, die Einladungen gesendet und sie wieder geschlossen. Zweitens registriert sich GlitchTips ausgehende E-Mail über Resend, und die From-Domain muss verifiziert sein, bevor die E-Mail-Verifizierung bei der Anmeldung erfolgreich ist - wir überspringen die E-Mail-Verifizierung, bis die Resend-Domain grün ist, und aktivieren sie danach wieder. Beides ist im Runbook für jeden dokumentiert, der das wiederholt.

Der Debug-Panic-Endpoint#

End-to-End-Testen der Verdrahtung in der Produktion ohne ein frisches Deployment ist die Art von Sache, die leise nie erledigt wird - bis ein echter Panic passiert und Sie entdecken, dass die Verdrahtung vor drei Wochen kaputt war. Wir haben für genau das eine stehende diagnostische Oberfläche hinzugefügt.

func DebugPanicHandler() http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        expected := os.Getenv(debugTokenEnv)
        if expected == "" || r.URL.Query().Get("token") != expected {
            http.NotFound(w, r)
            return
        }
        panic("elido sentry-debug panic: " + r.RemoteAddr + " " + r.URL.RawQuery)
    }
}

Gemountet unter GET /debug/sentry-panic, gegated durch ELIDO_SENTRY_DEBUG_TOKEN. Mit nicht gesetzter Env-Variable gibt die Route 404 - sicher, in die Produktion zu liefern. Wenn die Variable gesetzt ist und die Anfrage ?token=<value> trägt, panict der Handler absichtlich. Die Middleware fängt es ab, das SDK transportiert es zu GlitchTip, das Ereignis landet im richtigen Projekt. Der gesamte Round-Trip kann in unter einer Minute ohne Redeployment verifiziert werden.

Es gibt einen fasthttp-Zwilling für den Edge:

func DebugPanicFastHTTPHandler() fasthttp.RequestHandler {
    return func(ctx *fasthttp.RequestCtx) {
        expected := os.Getenv(debugTokenEnv)
        if expected == "" || string(ctx.QueryArgs().Peek("token")) != expected {
            ctx.SetStatusCode(fasthttp.StatusNotFound)
            return
        }
        panic("elido sentry-debug panic: " + ctx.RemoteAddr().String() + " " + string(ctx.QueryArgs().QueryString()))
    }
}

Gleiches Token-Gate, gleiches verstecktes-wenn-unkonfiguriert-Verhalten. Das Erste, was nach einem Deployment passiert, ist, dass der On-Call den Debug-Endpoint auf dem betroffenen Service trifft. Wenn das Ereignis innerhalb von zehn Sekunden in GlitchTip landet, ist die Verdrahtung gesund. Wenn nicht, wird das Deployment zurückgerollt, bevor der nächste Ausfall die kaputte Verdrahtung auf die harte Tour entdeckt.

Was wir nicht verdrahtet haben#

Drei Dinge, die wie offensichtliche Ergänzungen aussehen, aber bewusst außerhalb des Scopes bleiben.

Tracing. EnableTracing: false in Init. Wir verwenden OpenTelemetry für verteiltes Tracing (das pkg/oteltrace-Paket verdrahtet es über dieselben Services). Sentry parallel Tracing machen zu lassen, würde die Per-Request-Transaktion-Allokationen verdoppeln und die Kosten der Kontextpropagation durch den Aufrufgraphen verdoppeln. Sentrys Stärke sind Fehler; OTels Stärke sind Spans. Wir verwenden jedes für das, wofür es gut ist.

Manuelles CaptureException auf dem Redirect-Pfad. Oben behandelt. Der Hot Path importiert sentryinit nicht zum Zweck, es aus Handlern aufzurufen. Die Middleware ist die einzige Capture-Grenze.

Performance Monitoring (Transaktionen). Aus demselben Grund wie Tracing. redirect_duration_seconds ist ein Prometheus-Histogramm mit region- und cache_tier-Labels. Das ist die Source of Truth für Latenz. Dieselben Daten durch Sentrys Performance Monitoring zu drücken wäre eine doppelte Pipeline mit schlechterer Aggregation.

Wie es von außen aussieht#

Zwölf Go-Services einschließlich edge-redirect, die alle in ein selbst-gehostetes GlitchTip unter sentry.elido.app einfließen, wobei jedes Ereignis einen service-Tag, Environment und Release für projektübergreifende Suche trägt

Zwölf Services, ein gemeinsames Paket, eine Zeile pro main.go, eine Zeile Middleware pro Router. Wenn ein Panic passiert - und sie passieren - erscheint er in GlitchTip unter dem richtigen Projekt mit dem richtigen service-Tag, dem richtigen Environment, dem richtigen Release und einem Stack-Trace tief genug, um die Zeile zu finden. Wenn ein nicht-panicendes 5xx entkommt - und die passieren auch, normalerweise nach einem Datenbank-Schluckauf - erscheint es genauso.

Die Kompromisse sind explizit, im paketebenen Doc-Kommentar des Pakets aufgeschrieben und mit einem Benchmark getestet. Die Verdrahtung ist am selben Ort wie die Runbooks dokumentiert, nicht in Stammeswissen. Den dreizehnten Service hinzuzufügen wird fünfzehn Minuten dauern - fünf davon den Test zu schreiben, fünf davon den DSN ins Deployment-Manifest zu verdrahten und fünf davon make build zu laufen und es mit dem Debug-Endpoint zu beweisen.

Das ist die Form, die hält. Sechs Zeilen pro Service würden immer driften. Eine Zeile plus ein gemeinsames Paket plus ein Benchmark tun das nicht.


Die Verdrahtung ist im Monorepo unter pkg/sentryinit/ offen für jeden, der eine Go-Flotte auf Sentry oder GlitchTip betreibt und eine Form zum Kopieren möchte. Das zugehörige Runbook deckt die Rotationsprozedur für DSNs, die GlitchTip-Bootstrap-Caveats und den Rollback-Pfad ab. Für Teams, die den gesamten Elido-Stack selbst hosten, deckt das k3s-Playbook ab, wo das SDK in das breitere Kubernetes-Deployment passt. Für einen Deep-Dive in das, was „zero-alloc auf dem Happy Path" unter Last tatsächlich bedeutet, ist der Redirect-p95-Beitrag das Begleitstück.


Marius Voß ist DevRel und Edge-Infra bei Elido. Er hat das sentryinit-Paket parallel zum oben beschriebenen Rollout ausgeliefert und die letzte Woche damit verbracht, das GlitchTip-Dashboard mit Ereignissen zu beobachten, die zuvor unsichtbar waren.

Elido testen

URL einfügen, kurzer Link in Sekunden

Kein Konto nötig. Link bleibt 30 Tage aktiv. Konto erstellen, um ihn dauerhaft zu behalten.

Kostenlos, keine Anmeldung erforderlich · 2 pro Tag

Elido testen

URL-Shortener mit EU-Hosting: eigene Domains, tiefe Analytik und eine offene API. Kostenloser Tarif - keine Kreditkarte nötig.

Tags
sentry go middleware
glitchtip self-hosted
observability url shortener
fasthttp panic recovery
chi middleware
go error tracking

Weiterlesen