httptrace
httptrace

1. 初始化 httptrace.ClientTrace

httptraceClientTrace
httptrace.ClientTrace
ClientTrace
type ClientTrace struct {
    // GetConn is called before a connection is created or
    // retrieved from an idle pool. The hostPort is the
    // "host:port" of the target or proxy. GetConn is called even
    // if there's already an idle cached connection available.
    GetConn func(hostPort string)

    // GotConn is called after a successful connection is
    // obtained. There is no hook for failure to obtain a
    // connection; instead, use the error from
    // Transport.RoundTrip.
    GotConn func(GotConnInfo)

    [...]

    // DNSStart is called when a DNS lookup begins.
    DNSStart func(DNSStartInfo)

    // DNSDone is called when a DNS lookup ends.
    DNSDone func(DNSDoneInfo)

    // ConnectStart is called when a new connection's Dial begins.
    // If net.Dialer.DualStack (IPv6 "Happy Eyeballs") support is
    // enabled, this may be called multiple times.
    ConnectStart func(network, addr string)

    // ConnectDone is called when a new connection's Dial
    // completes. The provided err indicates whether the
    // connection completedly successfully.
    // If net.Dialer.DualStack ("Happy Eyeballs") support is
    // enabled, this may be called multiple times.
    ConnectDone func(network, addr string, err error)

    [...]
}
ClientTrace
package main

import (
    "fmt"
    "log"
    "net/http"
    "net/http/httptrace"
)

func main() {
    req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
    clientTrace := &httptrace.ClientTrace{
        GetConn:      func(hostPort string) { fmt.Println("starting to create conn ", hostPort) },
        DNSStart:     func(info httptrace.DNSStartInfo) { fmt.Println("starting to look up dns", info) },
        DNSDone:      func(info httptrace.DNSDoneInfo) { fmt.Println("done looking up dns", info) },
        ConnectStart: func(network, addr string) { fmt.Println("starting tcp connection", network, addr) },
        ConnectDone:  func(network, addr string, err error) { fmt.Println("tcp connection created", network, addr, err) },
        GotConn:      func(info httptrace.GotConnInfo) { fmt.Println("connection established", info) },
    }
}

2. 使用 httptrace.WithClientTrace 创建上下文

httptrace.WithClientTrace
func WithClientTrace(ctx context.Context, trace *ClientTrace) context.Context
WithClientTrace

这是我们需要添加到示例中的内容

clientTraceCtx := httptrace.WithClientTrace(req.Context(), clientTrace)

3. 将 httptrace.ClientTrace 与我们的 HTTP 客户端一起使用

我们的最终结果如下。 定义客户端跟踪钩子,使用客户端跟踪创建上下文并将其传递到我们的请求对象中。

package main

import (
    "fmt"
    "log"
    "net/http"
    "net/http/httptrace"
)

func main() {
    req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
    clientTrace := &httptrace.ClientTrace{
        GetConn:      func(hostPort string) { fmt.Println("starting to create conn ", hostPort) },
        DNSStart:     func(info httptrace.DNSStartInfo) { fmt.Println("starting to look up dns", info) },
        DNSDone:      func(info httptrace.DNSDoneInfo) { fmt.Println("done looking up dns", info) },
        ConnectStart: func(network, addr string) { fmt.Println("starting tcp connection", network, addr) },
        ConnectDone:  func(network, addr string, err error) { fmt.Println("tcp connection created", network, addr, err) },
        GotConn:      func(info httptrace.GotConnInfo) { fmt.Println("connection established", info) },
    }
    clientTraceCtx := httptrace.WithClientTrace(req.Context(), clientTrace)
    req = req.WithContext(clientTraceCtx)
    _, err := http.DefaultClient.Do(req)
    if err != nil {
        log.Fatal(err)
    }
}

预期结果如下

starting to create conn  example.com:80
starting to look up dns {example.com}
done looking up dns {[{93.184.216.34 } {2606:2800:220:1:248:1893:25c8:1946 }]  false}
starting tcp connection tcp 93.184.216.34:80
tcp connection created tcp 93.184.216.34:80 
connection established {0xc000010090 false false 0s}

这些只是我们可以用来在 Go 中跟踪传出 HTTP 请求的可能事件的一个子集。 但是正如我们从这个示例中看到的那样,这对于跟踪正在发生的事情和对我们的 http 客户端进行故障排除已经非常有用