3
votes

I'm using Gorilla mux for all my routing. Now my app is working fine, I want to find a way to log all my response codes to -for example- statds. I have found this package: https://godoc.org/github.com/gorilla/handlers#LoggingHandler

Which allows me to output all responses into apache format. Although this is nice, it's not 100% what I want. I just want to extract the response statusses and send them to statds. Now what's the best/easiest way to achieve this?

package main

import (
    "log"
    "net/http"
    "os"

    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
    "github.com/rogierlommers/mux-status-handler/articles"
    "github.com/rogierlommers/mux-status-handler/users"
)

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/products", articles.Handler)
    r.HandleFunc("/users", users.Handler)

    loggedRouter := handlers.LoggingHandler(os.Stdout, r)
    log.Println("listening on 8080")
    http.ListenAndServe(":8080", loggedRouter)
}

Above code gives me this:

apache logformat output

So I'm looking for something similar, but instead of outputting the Apache access logs to stdout, I would like to be able to "do something" with the response code. I have also created a simple repo which contains my sample code. You can find it here.

3
I don't exactly understand what you mean but if you want to log the response code, and have an output like this: 2017/02/10 17:25:48 127.0.0.1:63747 [/foo] 405 19 15.059µs you could try violetear - violetear.orgnbari
My goal is to report http response codes to another service (to be specific: statds). So I need some kind of mechanism which extracts the response codes from all handlers and sends them to statds.Rogier Lommers
Copy what the LoggingHandler does: create your own type that satisfies the http.ResponseWriter interface, and in the Write method, log/inspect what you need to.elithrar

3 Answers

6
votes

I found this useful Blog post from Tim Andersson. First he builds a new struct that satisfies the interface:

type loggingResponseWriter struct {
    http.ResponseWriter
    statusCode int
}

func NewLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter {
    return &loggingResponseWriter{w, http.StatusOK}
}

func (lrw *loggingResponseWriter) WriteHeader(code int) {
    lrw.statusCode = code
    lrw.ResponseWriter.WriteHeader(code)
}

Then he's using it as a wrapper (or middleware):

func wrapHandlerWithLogging(wrappedHandler http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        log.Printf("--> %s %s", req.Method, req.URL.Path)

        lrw := NewLoggingResponseWriter(w)
        wrappedHandler.ServeHTTP(lrw, req)

        statusCode := lrw.statusCode
        log.Printf("<-- %d %s", statusCode, http.StatusText(statusCode))
    })
}
0
votes

This is how it can be made with violetear, probably can give you a hint about how to deal with the status code within the handler:

package main

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

    "github.com/nbari/violetear"
)

func handleGET(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("I handle GET requests\n"))
    // do anything here with the Status code
    cw := w.(*violetear.ResponseWriter)
    fmt.Printf("The status code is: %d\n", cw.Status())
}

func main() {
    router := violetear.New()
    router.HandleFunc("/", handleGET, "GET")
    log.Fatal(http.ListenAndServe(":8080", router))
}

By using:

cw := w.(*violetear.ResponseWriter)

You can access the violetear.ResponseWriter which exposes the status code by using cw.Status()

-1
votes

You can write your own middleware, here's a very base example

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/mux"
    "github.com/rogierlommers/mux-status-handler/articles"
    "github.com/rogierlommers/mux-status-handler/users"
)

// middleWare ...
func middleWare(handler http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // right not all this does is log like
        // "github.com/gorilla/handlers"
        log.Printf("%s %s %s", r.RemoteAddr, r.Method, r.URL)
        // However since this is middleware you can have it do other things
        // Examples, auth users, write to file, redirects, handle panics, ect
        // add code to log to statds, remove log.Printf if you want


        handler.ServeHTTP(w, r)
    })
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/products", articles.Handler)
    r.HandleFunc("/users", users.Handler)

    log.Println("listening on 8080")
    http.ListenAndServe(":8080", middleWare(r))
}