Search code examples
httpgoservertrace

HTTP Tracing from server perspective


In Go 1.7 http tracing was introduced, but it works only from client perspective. Is it possible somehow trace request from server perspective, I mean I want to add some hooks, for example when connection was established. Should it be implemented as middleware or what?

http.HandlerFunc(func(w http.ResponseWriter, r *http.Request)

Solution

  • Support for server-side tracing was added a lot earlier, in Go 1.3. Create your own http.Server, and set a callback function in the Server.ConnState field.

    // ConnState specifies an optional callback function that is
    // called when a client connection changes state. See the
    // ConnState type and associated constants for details.
    ConnState func(net.Conn, ConnState) // Go 1.3
    

    http.ConnState details when / what connection states the callback is notified about.

    // StateNew represents a new connection that is expected to
    // send a request immediately. Connections begin at this
    // state and then transition to either StateActive or
    // StateClosed.
    StateNew ConnState = iota
    
    // StateActive represents a connection that has read 1 or more
    // bytes of a request. The Server.ConnState hook for
    // StateActive fires before the request has entered a handler
    // and doesn't fire again until the request has been
    // handled. After the request is handled, the state
    // transitions to StateClosed, StateHijacked, or StateIdle.
    // For HTTP/2, StateActive fires on the transition from zero
    // to one active request, and only transitions away once all
    // active requests are complete. That means that ConnState
    // cannot be used to do per-request work; ConnState only notes
    // the overall state of the connection.
    StateActive
    
    // StateIdle represents a connection that has finished
    // handling a request and is in the keep-alive state, waiting
    // for a new request. Connections transition from StateIdle
    // to either StateActive or StateClosed.
    StateIdle
    
    // StateHijacked represents a hijacked connection.
    // This is a terminal state. It does not transition to StateClosed.
    StateHijacked
    
    // StateClosed represents a closed connection.
    // This is a terminal state. Hijacked connections do not
    // transition to StateClosed.
    StateClosed
    

    A simple example:

    srv := &http.Server{
        Addr: ":8080",
        ConnState: func(conn net.Conn, cs http.ConnState) {
            fmt.Println("Client:", conn.RemoteAddr(), "- new state:", cs)
        },
    }
    log.Fatal(srv.ListenAndServe())
    

    Making a request to the above server:

    curl localhost:8080
    

    Server output will be:

    Client: 127.0.0.1:39778 - new state: new
    Client: 127.0.0.1:39778 - new state: active
    Client: 127.0.0.1:39778 - new state: idle
    Client: 127.0.0.1:39778 - new state: closed