Add middleware for logging each request to the app.

This commit is contained in:
Tom Wilkie
2016-04-06 14:30:34 +01:00
parent 392589744c
commit a8f0d64b3c
3 changed files with 84 additions and 4 deletions

View File

@@ -0,0 +1,45 @@
package middleware
import (
"bufio"
"fmt"
"net"
"net/http"
"time"
log "github.com/Sirupsen/logrus"
)
// Logging middleware logs each HTTP request method, path, response code and duration.
var Logging = Func(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
begin := time.Now()
i := &interceptor{ResponseWriter: w, statusCode: http.StatusOK}
next.ServeHTTP(i, r)
log.Infof("%s %s (%d) %s", r.Method, r.RequestURI, i.statusCode, time.Since(begin))
})
})
// interceptor implements WriteHeader to intercept status codes. WriteHeader
// may not be called on success, so initialize statusCode with the status you
// want to report on success, i.e. http.StatusOK.
//
// interceptor also implements net.Hijacker, to let the downstream Handler
// hijack the connection. This is needed by the app-mapper's proxy.
type interceptor struct {
http.ResponseWriter
statusCode int
}
func (i *interceptor) WriteHeader(code int) {
i.statusCode = code
i.ResponseWriter.WriteHeader(code)
}
func (i *interceptor) Hijack() (net.Conn, *bufio.ReadWriter, error) {
hj, ok := i.ResponseWriter.(http.Hijacker)
if !ok {
return nil, nil, fmt.Errorf("interceptor: can't cast parent ResponseWriter to Hijacker")
}
return hj.Hijack()
}

View File

@@ -0,0 +1,30 @@
package middleware
import (
"net/http"
)
// Interface is the shared contract for all middlesware, and allows middlesware
// to wrap handlers.
type Interface interface {
Wrap(http.Handler) http.Handler
}
// Func is to Interface as http.HandlerFunc is to http.Handler
type Func func(http.Handler) http.Handler
// Wrap implements Interface
func (m Func) Wrap(next http.Handler) http.Handler {
return m(next)
}
// Merge produces a middleware that applies multiple middlesware in turn;
// ie Merge(f,g,h).Wrap(handler) == f.Wrap(g.Wrap(h.Wrap(handler)))
func Merge(middlesware ...Interface) Interface {
return Func(func(next http.Handler) http.Handler {
for i := len(middlesware) - 1; i >= 0; i-- {
next = middlesware[i].Wrap(next)
}
return next
})
}

View File

@@ -20,6 +20,7 @@ import (
"github.com/weaveworks/scope/app"
"github.com/weaveworks/scope/app/multitenant"
"github.com/weaveworks/scope/common/middleware"
"github.com/weaveworks/scope/common/weave"
"github.com/weaveworks/scope/common/xfer"
"github.com/weaveworks/scope/probe/docker"
@@ -110,10 +111,11 @@ func pipeRouterFactory(userIDer multitenant.UserIDer, pipeRouterURL, consulInf s
// Main runs the app
func appMain() {
var (
window = flag.Duration("window", 15*time.Second, "window")
listen = flag.String("http.address", ":"+strconv.Itoa(xfer.AppPort), "webserver listen address")
logLevel = flag.String("log.level", "info", "logging threshold level: debug|info|warn|error|fatal|panic")
logPrefix = flag.String("log.prefix", "<app>", "prefix for each log line")
window = flag.Duration("window", 15*time.Second, "window")
listen = flag.String("http.address", ":"+strconv.Itoa(xfer.AppPort), "webserver listen address")
logLevel = flag.String("log.level", "info", "logging threshold level: debug|info|warn|error|fatal|panic")
logPrefix = flag.String("log.prefix", "<app>", "prefix for each log line")
logRequests = flag.Bool("log.requests", false, "Log individual HTTP requests")
weaveAddr = flag.String("weave.addr", app.DefaultWeaveURL, "Address on which to contact WeaveDNS")
weaveHostname = flag.String("weave.hostname", app.DefaultHostname, "Hostname to advertise in WeaveDNS")
@@ -189,6 +191,9 @@ func appMain() {
}
handler := router(collector, controlRouter, pipeRouter)
if *logRequests {
handler = middleware.Logging.Wrap(handler)
}
go func() {
log.Infof("listening on %s", *listen)
log.Info(http.ListenAndServe(*listen, handler))