Package trace implements tracing of requests and long-lived objects. It exports HTTP interfaces on /debug/requests and /debug/events.
import "golang.org/x/net/trace"// AuthRequest determines whether a request is permitted to load debug pages
var AuthRequest = func(req *http.Request) (any, sensitive bool) { /* ... */ }
// DebugUseAfterFinish controls whether to debug uses of Trace values after finishing
var DebugUseAfterFinish = falseAuthRequest returns two bools: whether the page may be viewed, and whether sensitive events will be shown. The default allows access only from localhost/127.0.0.1/[::1].
// Trace represents an active request
type Trace interface {
LazyLog(x fmt.Stringer, sensitive bool)
LazyPrintf(format string, a ...interface{})
SetError()
SetRecycler(f func(interface{}))
SetTraceInfo(traceID, spanID uint64)
SetMaxEvents(m int)
Finish()
}
// EventLog provides a log of events associated with a specific object
type EventLog interface {
Printf(format string, a ...interface{})
Errorf(format string, a ...interface{})
Finish()
}// New returns a new Trace with the specified family and title
func New(family, title string) Trace
// NewEventLog returns a new EventLog with the specified family name and title
func NewEventLog(family, title string) EventLog
// NewContext returns a copy of the parent context associated with a Trace
func NewContext(ctx context.Context, tr Trace) context.Context
// FromContext returns the Trace bound to the context, if any
func FromContext(ctx context.Context) (tr Trace, ok bool)
// Traces responds with traces from the program (registered at /debug/requests)
func Traces(w http.ResponseWriter, req *http.Request)
// Events responds with a page of events collected by EventLogs (registered at /debug/events)
func Events(w http.ResponseWriter, req *http.Request)
// Render renders the HTML page typically served at /debug/requests
func Render(w io.Writer, req *http.Request, sensitive bool)
// RenderEvents renders the HTML page typically served at /debug/events
func RenderEvents(w http.ResponseWriter, req *http.Request, sensitive bool)import (
"golang.org/x/net/trace"
"net/http"
)
func fooHandler(w http.ResponseWriter, req *http.Request) {
tr := trace.New("mypkg.Foo", req.URL.Path)
defer tr.Finish()
tr.LazyPrintf("processing request from %s", req.RemoteAddr)
result, err := doSomething()
if err != nil {
tr.LazyPrintf("doSomething failed: %v", err)
tr.SetError()
http.Error(w, "Internal error", 500)
return
}
tr.LazyPrintf("completed successfully: %v", result)
w.Write([]byte("OK"))
}// Fetcher fetches URL paths for a single domain
type Fetcher struct {
domain string
events trace.EventLog
}
func NewFetcher(domain string) *Fetcher {
return &Fetcher{
domain: domain,
events: trace.NewEventLog("mypkg.Fetcher", domain),
}
}
func (f *Fetcher) Fetch(path string) (string, error) {
url := "http://" + f.domain + "/" + path
resp, err := http.Get(url)
if err != nil {
f.events.Errorf("Get(%q) = %v", path, err)
return "", err
}
defer resp.Body.Close()
f.events.Printf("Get(%q) = %s", path, resp.Status)
body, err := io.ReadAll(resp.Body)
if err != nil {
f.events.Errorf("ReadAll(%q) = %v", path, err)
return "", err
}
return string(body), nil
}
func (f *Fetcher) Close() error {
f.events.Finish()
return nil
}func handlerWithContext(w http.ResponseWriter, req *http.Request) {
tr := trace.New("myapp.Handler", req.URL.Path)
defer tr.Finish()
// Attach trace to context
ctx := trace.NewContext(req.Context(), tr)
// Pass context to other functions
processRequest(ctx, req)
}
func processRequest(ctx context.Context, req *http.Request) {
// Retrieve trace from context
if tr, ok := trace.FromContext(ctx); ok {
tr.LazyPrintf("processing in nested function")
}
// Do work...
}import "net"
func init() {
// Custom authorization for trace endpoints
trace.AuthRequest = func(req *http.Request) (any, sensitive bool) {
// Allow from specific IPs
host, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
host = req.RemoteAddr
}
// Check if IP is in allowed list
allowedIPs := map[string]bool{
"127.0.0.1": true,
"::1": true,
"192.168.1.100": true,
}
allowed := allowedIPs[host]
return allowed, allowed
}
}type RequestInfo struct {
Method string
Path string
User string
}
func (r RequestInfo) String() string {
return fmt.Sprintf("%s %s (user: %s)", r.Method, r.Path, r.User)
}
func tracedHandler(w http.ResponseWriter, req *http.Request) {
tr := trace.New("app.API", req.URL.Path)
defer tr.Finish()
info := RequestInfo{
Method: req.Method,
Path: req.URL.Path,
User: getUserID(req),
}
// Lazy evaluation - String() called only when viewing /debug/requests
tr.LazyLog(info, false)
// Process request...
}func traceLongRunning(id string) {
tr := trace.New("app.LongTask", id)
defer tr.Finish()
// Only keep last 100 events
tr.SetMaxEvents(100)
for i := 0; i < 10000; i++ {
tr.LazyPrintf("processing item %d", i)
// Do work...
}
}