httptrace, a new Go debugging tool

Today I was investigating why HTTP redirects resulted in more persistent connections staying open than I was expecting. I found myself digging around deep inside net/http/transport.go and I noticed the new net/http/httptrace package. It is new in Go 1.7, which is currently in beta.

net/http/httptrace is lightly documented, and because it is new, there are no examples to look at. So I decided to share what I came up with here.

package main

import (

func main() {
	ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{
		PutIdleConn: func(err error) {
			if err == nil {
				fmt.Println("put idle: no err")
			} else {
				fmt.Println("put idle:", err.Error())
	req, err := http.NewRequest("GET", "", nil)
	if err != nil {
		log.Fatal("new req", err)
	req = req.WithContext(ctx)
	_, err = http.DefaultClient.Do(req)
	if err != nil {
		fmt.Fprintf(os.Stderr, "fetch: %v\n", err)

The key things I had to figure out (and hopefully you won’t have to since you found this post) were:

  • How to get a base context to add the tracer to (context.Background())
  • Which callback to use in┬áthe ClientTrace structure (for my purposes, PutIdleConn)
  • How to do an HTTP request with the context attached (NewRequest -> req.WithContext -> http.(*Client).Do)

If you are used to using the simple http.(*Client).Get method to do HTTP you’ll need to learn to use http.NewRequest in order to make a request object, and then pass that to a client. In this simple case, I used the DefaultClient. In a library, you should be using the http.Client passed into you by the caller, so that your library would work in contexts like Google App Engine.