Production Debugging — Junior¶
At junior level, focus on this question:
How can I apply Production Debugging in one small example and prove the result?
Use the smallest realistic scenario that exposes the decision and its failure behavior.¶
Core Concepts¶
1. net/http/pprof gives you live profiling for free¶
import _ "net/http/pprof"
func main() {
go http.ListenAndServe("localhost:6060", nil) // separate port, internal only
// ... rest of your server
}
The blank import registers profiling handlers (/debug/pprof/...) on the default HTTP mux. Never expose this on a public-facing port — it reveals internal implementation detail and allows expensive profile captures to be triggered by anyone who can reach it.
2. Capturing and reading a CPU profile¶
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
(pprof) top10
(pprof) web # opens a graph visualization (requires graphviz)
top10 lists the functions consuming the most CPU time during the 30-second sample window — the starting point for "what's actually slow."
3. Capturing a heap profile¶
Shows current memory allocations grouped by the call site that allocated them — the starting point for "what's using all this memory."
4. The goroutine profile shows what every goroutine is doing right now¶
debug=2 gives full stack traces for every goroutine, grouped by identical stack — a large group sharing one stack is either a legitimate worker pool or a leak (see Goroutines and Concurrency).
5. Structured logs are queryable; free-text logs are not¶
// Hard to query at scale
log.Printf("user %s failed login after %d attempts", userID, attempts)
// Queryable: "find all failed_login events where attempts > 3"
logger.Info("failed_login", "user_id", userID, "attempts", attempts)
Go's standard log/slog package (1.21+) provides structured logging out of the box.
6. A basic systematic approach beats guessing¶
- What's the symptom, precisely? (Latency? Errors? Memory growth? Which endpoint?)
- When did it start? (Correlate with a deploy, a traffic pattern, an external event.)
- Pull the relevant profile/log/metric for that window.
- Form one hypothesis, check it against the data, before changing any code.
Code Examples¶
Example 1 — Exposing pprof on an internal-only port¶
func main() {
go func() {
log.Println(http.ListenAndServe("127.0.0.1:6060", nil))
}()
startMainServer() // public-facing, separate port
}
Example 2 — Structured logging with slog¶
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
logger.Info("request_handled", "method", r.Method, "path", r.URL.Path, "duration_ms", elapsed.Milliseconds())
Example 3 — Saving a profile to a file for later analysis¶
Best Practices¶
- Import
net/http/pprofin every service, exposed only on an internal/localhost-only port. - Adopt structured logging (
log/slogor equivalent) from day one — retrofitting is much more expensive. - Always include a request/trace ID in logs so a single request's full path is traceable.
- Form one specific hypothesis before changing code — "let's try restarting it" is not debugging.
Edge Cases & Pitfalls¶
- Exposing
/debug/pprofon a public port is an information-disclosure and DoS risk — profile captures cost CPU/memory to generate. - A CPU profile captured during an idle period tells you nothing about a problem that only manifests under load — capture during the actual symptom window.
- Free-text logs with inconsistent formats across a codebase make even basic log searches unreliable.
Common Mistakes¶
| Mistake | Fix |
|---|---|
| Exposing pprof publicly | Bind it to 127.0.0.1 or an internal-only network interface |
| Debugging by guessing and restarting | Pull a profile/log for the actual symptom window first |
| Free-text logging with no request ID | Adopt structured logging with a consistent request/trace ID field |
Apply it¶
- Choose one small, known input for Production Debugging.
- Predict the output or observable behavior.
- Run the smallest example or probe that exercises the concept.
- Change one input to trigger a failure or boundary case.
- Explain the evidence using the guide's vocabulary.
Verify your work¶
- Record the exact input, command or code path, and output.
- Repeat the probe and confirm the result is consistent.
- Show one expected success and one expected failure.
- Resolve any difference between the prediction and the evidence.
Review questions¶
- What problem does Production Debugging solve in the example?
- Which input changes the observed result, and why?
- What is the smallest useful success check?
- Which beginner mistake would your evidence catch?
In this topic
- junior
- middle
- senior
- professional