# HTTP Server-Timing for Go [![Godoc](https://godoc.org/github.com/mitchellh/go-server-timing?status.svg)](https://godoc.org/github.com/mitchellh/go-server-timing) This is a library including middleware for using [HTTP Server-Timing](https://www.w3.org/TR/server-timing) with Go. This header allows a server to send timing information from the backend, such as database access time, file reads, etc. The timing information can be then be inspected in the standard browser developer tools: ![Server Timing Example](https://raw.githubusercontent.com/mitchellh/go-server-timing/master/example/screenshot.png) ## Features * Middleware for injecting the server timing struct into the request `Context` and writing the `Server-Timing` header. * Concurrency-safe structures for easily recording timings of multiple concurrency tasks. * Parse `Server-Timing` headers as a client. * Note: No browser properly supports sending the Server-Timing header as an [HTTP Trailer](https://tools.ietf.org/html/rfc7230#section-4.4) so the Middleware only supports a normal header currently. ## Browser Support Browser support is required to **view** server timings easily. Because server timings are sent as an HTTP header, there is no negative impact to sending the header to unsupported browsers. * **Chrome 65 or higher** is required to properly display server timings in the devtools. * **Firefox is pending** with an [open bug report (ID 1403051)](https://bugzilla.mozilla.org/show_bug.cgi?id=1403051) * IE, Opera, and others are unknown at this time. ## Usage Example usage is shown below. A fully runnable example is available in the `example/` directory. ```go func main() { // Our handler. In a real application this might be your root router, // or some subset of your router. Wrapping this ensures that all routes // handled by this handler have access to the server timing header struct. var h http.Handler = http.HandlerFunc(handler) // Wrap our handler with the server timing middleware h = servertiming.Middleware(h, nil) // Start! http.ListenAndServe(":8080", h) } func handler(w http.ResponseWriter, r *http.Request) { // Get our timing header builder from the context timing := servertiming.FromContext(r.Context()) // Imagine your handler performs some tasks in a goroutine, such as // accessing some remote service. timing is concurrency safe so we can // record how long that takes. Let's simulate making 5 concurrent requests // to various servicse. var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) name := fmt.Sprintf("service-%d", i) go func(name string) { // This creats a new metric and starts the timer. The Stop is // deferred so when the function exits it'll record the duration. defer timing.NewMetric(name).Start().Stop() time.Sleep(random(25, 75)) wg.Done() }(name) } // Imagine this is just some blocking code in your main handler such // as a SQL query. Let's record that. m := timing.NewMetric("sql").WithDesc("SQL query").Start() time.Sleep(random(20, 50)) m.Stop() // Wait for the goroutine to end wg.Wait() // You could continue recording more metrics, but let's just return now w.WriteHeader(200) w.Write([]byte("Done. Check your browser inspector timing details.")) } func random(min, max int) time.Duration { return (time.Duration(rand.Intn(max-min) + min)) * time.Millisecond } ```