Skip to content

feat(tracewaygin): tag CPU profiles with endpoint route label#7

Open
FrameAutomata wants to merge 5 commits into
tracewayapp:mainfrom
FrameAutomata:feat/gin-endpoint-pprof-label
Open

feat(tracewaygin): tag CPU profiles with endpoint route label#7
FrameAutomata wants to merge 5 commits into
tracewayapp:mainfrom
FrameAutomata:feat/gin-endpoint-pprof-label

Conversation

@FrameAutomata

Copy link
Copy Markdown

What

Wraps each gin request handler's execution in pprof.Do with an "endpoint" label set to the request's route, so CPU profile samples taken during a request carry the route and can be sliced per-endpoint in the dashboard.

  • Label key is exactly "endpoint" to match the backend allowlist.
  • Label value is the matched route (c.FullPath(), e.g. /users/:id), falling back to the raw path only for recorded unmatched routes (WithRecordUnmatched(true)) — the same value already used for the trace endpoint.
  • The labeling lives inside wrapAndExecute, alongside the existing c.Next() call and panic recovery, so the request-execution concern stays in one place. pprof.Do runs the handler synchronously, so panics still propagate to the deferred recover.
  • The labeled context is set back onto c.Request, so goroutines spawned by the handler inherit the endpoint tag in their own profile samples.

Why

Continuous CPU profiles are far more actionable when each sample is attributed to the route that produced it — letting the dashboard answer "which endpoint is burning CPU?" rather than just showing aggregate hot functions.

Testing

  • TestMiddleware_EndpointPprofLabel — matched route /users/:id sees pprof.Label(ctx, "endpoint") == "/users/:id" during handler execution (route pattern, not raw URL).
  • TestMiddleware_EndpointPprofLabel_Unmatched — with WithRecordUnmatched(true), a NoRoute handler sees the raw path label.
  • Full existing suite + go vet pass; manually verified with go tool pprof -tags that 100% of samples carry endpoint: /users/:id.

🤖 Generated with Claude Code

FrameAutomata and others added 5 commits June 23, 2026 22:00
Add an opt-in background profiler that, on an interval (default 60s, CPU
window ~30s capped to interval/2), captures CPU and heap pprof profiles
and POSTs each as a separate request to <server>/api/profiles/ingest,
reusing the connection-string token and host.

The ingest URL is derived from the existing report URL (scheme+host +
/api/profiles/ingest); Bearer auth, serverName and appVersion reuse the
existing report plumbing. Profiling never affects the host app: capture
and upload failures are isolated and silent unless WithDebug is set, and
a bad URL disables profiling without failing Init.

Verified against the backend contract (tracewayapp/traceway
/api/profiles/ingest): path, Bearer auth, service/serverName/appVersion
query params, and raw already-gzipped pprof body with no extra
Content-Encoding.

Core package only; HTTP middleware pass-through options are a follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Init split the connection string on "@" and unconditionally indexed
connParts[1], panicking with "index out of range" when the string had
no "@". Use SplitN with a length guard so a malformed connection string
returns an error, and so a "@" inside the URL (e.g. userinfo) no longer
silently truncates the endpoint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wrap each request handler's execution in pprof.Do with an "endpoint"
label set to the matched route (c.FullPath(), falling back to the raw
path for recorded unmatched routes). CPU profile samples taken during a
request now carry the route, so profiles can be sliced per-endpoint in
the dashboard.

The label key is exactly "endpoint" to match the backend allowlist. The
labeling lives inside wrapAndExecute alongside the existing c.Next() and
panic-recovery handling, and the labeled context is set back on the
request so handler-spawned goroutines inherit the tag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The gin middleware tags each request's CPU samples with an `endpoint`
pprof label, but tracewaygin had no way to turn continuous profiling on,
so those labels were never captured by a running CPU profile. Enabling
profiling required a separate, undocumented traceway.Init(WithProfiling)
call before adding the middleware (relying on the second Init no-opping).

Add WithProfiling(serviceName) and WithProfilingInterval(d) option
passthroughs, matching the existing wrappers, so:

    r.Use(tracewaygin.New(conn, tracewaygin.WithProfiling("shop")))

enables endpoint-labeled continuous profiling in one call.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant