or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

bpf.mdcontext-ctxhttp.mdcontext.mddict.mddns-dnsmessage.mdhtml-atom.mdhtml-charset.mdhtml.mdhttp-httpguts.mdhttp-httpproxy.mdhttp2-h2c.mdhttp2-hpack.mdhttp2.mdicmp.mdidna.mdindex.mdipv4.mdipv6.mdnettest.mdnetutil.mdproxy.mdpublicsuffix.mdquic-qlog.mdquic.mdtrace.mdwebdav.mdwebsocket.mdxsrftoken.md
tile.json

trace.mddocs/

Request Tracing

Package trace implements tracing of requests and long-lived objects. It exports HTTP interfaces on /debug/requests and /debug/events.

Import

import "golang.org/x/net/trace"

Overview

  • Trace: Provides tracing for short-lived objects, usually requests
  • EventLog: Provides tracing for long-lived objects, such as RPC connections
  • HTTP Endpoints: /debug/requests and /debug/events for viewing traces

Variables

// 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 = false

AuthRequest 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].

Types

// 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()
}

Functions

// 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)

Trace Methods

  • LazyLog: Adds an event to the log (evaluated when /debug/requests is rendered)
  • LazyPrintf: Formats and adds an event to the log
  • SetError: Declares that this trace resulted in an error
  • SetRecycler: Sets a recycler function for trace events
  • SetTraceInfo: Sets trace ID and span ID (currently unused)
  • SetMaxEvents: Sets maximum number of stored events
  • Finish: Declares that this trace is complete

EventLog Methods

  • Printf: Formats and adds a result to the event log
  • Errorf: Like Printf, but marks the event as an error
  • Finish: Declares that this event log is complete

Usage Examples

Tracing HTTP Requests

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"))
}

EventLog for Long-Lived Objects

// 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
}

Using Context

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...
}

Custom Authorization

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
    }
}

Lazy Logging with Custom Stringers

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...
}

Setting Maximum Events

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...
    }
}