Merge pull request #2086 from weaveworks/log-request-headers

Add flag for logging headers
This commit is contained in:
Jonathan Lange
2016-12-14 17:29:38 +00:00
committed by GitHub
6 changed files with 75 additions and 145 deletions

View File

@@ -270,7 +270,10 @@ func appMain(flags appFlags) {
handler := router(collector, controlRouter, pipeRouter, flags.externalUI)
if flags.logHTTP {
handler = middleware.LogFailed.Wrap(handler)
handler = middleware.Log{
LogRequestHeaders: flags.logHTTPHeaders,
LogSuccess: false,
}.Wrap(handler)
}
server := &graceful.Server{

View File

@@ -112,12 +112,13 @@ type probeFlags struct {
}
type appFlags struct {
window time.Duration
listen string
stopTimeout time.Duration
logLevel string
logPrefix string
logHTTP bool
window time.Duration
listen string
stopTimeout time.Duration
logLevel string
logPrefix string
logHTTP bool
logHTTPHeaders bool
weaveEnabled bool
weaveAddr string
@@ -298,6 +299,7 @@ func main() {
flag.StringVar(&flags.app.logLevel, "app.log.level", "info", "logging threshold level: debug|info|warn|error|fatal|panic")
flag.StringVar(&flags.app.logPrefix, "app.log.prefix", "<app>", "prefix for each log line")
flag.BoolVar(&flags.app.logHTTP, "app.log.http", false, "Log individual HTTP requests")
flag.BoolVar(&flags.app.logHTTPHeaders, "app.log.httpHeaders", false, "Log HTTP headers. Needs app.log.http to be enabled.")
flag.StringVar(&flags.app.weaveAddr, "app.weave.addr", app.DefaultWeaveURL, "Address on which to contact WeaveDNS")
flag.StringVar(&flags.app.weaveHostname, "app.weave.hostname", app.DefaultHostname, "Hostname to advertise in WeaveDNS")

View File

@@ -1,135 +0,0 @@
package logging
import (
"fmt"
"net"
"net/http"
"strconv"
log "github.com/Sirupsen/logrus"
"github.com/fluent/fluent-logger-golang/fluent"
)
const maxBufferedEvents = 100
// Event is a user event to be sent to out analytics system
type Event struct {
ID string `msg:"event"`
SessionID string `msg:"session_id"`
Product string `msg:"product"`
Version string `msg:"version"`
UserAgent string `msg:"user_agent"`
ClientID string `msg:"client_id"`
OrganizationID string `msg:"org_id"`
UserID string `msg:"user_id"`
Values string `msg:"values"`
}
// EventLogger logs events to the analytics system
type EventLogger struct {
stop chan struct{}
events chan Event
logger *fluent.Fluent
}
// NewEventLogger creates a new EventLogger.
func NewEventLogger(fluentHostPort string) (*EventLogger, error) {
host, port, err := net.SplitHostPort(fluentHostPort)
if err != nil {
return nil, err
}
intPort, err := strconv.Atoi(port)
if err != nil {
return nil, err
}
logger, err := fluent.New(fluent.Config{
FluentPort: intPort,
FluentHost: host,
AsyncConnect: true,
MaxRetry: -1,
})
if err != nil {
return nil, err
}
el := &EventLogger{
stop: make(chan struct{}),
events: make(chan Event, maxBufferedEvents),
logger: logger,
}
go el.logLoop()
return el, nil
}
func (el *EventLogger) post(e Event) {
if err := el.logger.Post("events", e); err != nil {
log.Warnf("EventLogger: failed to log event: %v", e)
}
}
func (el *EventLogger) logLoop() {
for done := false; !done; {
select {
case event := <-el.events:
el.post(event)
case <-el.stop:
done = true
}
}
// flush remaining events
for done := false; !done; {
select {
case event := <-el.events:
el.post(event)
default:
done = true
}
}
el.logger.Close()
}
// Close closes and deallocates the event logger
func (el *EventLogger) Close() error {
close(el.stop)
return nil
}
// LogEvent logs an event to the analytics system
func (el *EventLogger) LogEvent(e Event) error {
select {
case <-el.stop:
return fmt.Errorf("Stopping, discarding event: %v", e)
default:
}
select {
case el.events <- e: // Put event in the channel unless it is full
return nil
default:
// full
}
return fmt.Errorf("Reached event buffer limit (%d), discarding event: %v", maxBufferedEvents, e)
}
// HTTPEventExtractor extracts an event from an http requests indicating whether it should be loggged
type HTTPEventExtractor func(*http.Request) (Event, bool)
// HTTPEventLogger logs an events extracted from an http request
type HTTPEventLogger struct {
Extractor HTTPEventExtractor
Logger *EventLogger
}
// Wrap implements middleware.Wrap()
func (el HTTPEventLogger) Wrap(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if event, shouldLog := el.Extractor(r); shouldLog {
if err := el.Logger.LogEvent(event); err != nil {
log.Warnf("HTTPEventLogger: failed to log event: %v", err)
}
}
next.ServeHTTP(w, r)
})
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"net"
"net/http"
"net/http/httputil"
"time"
log "github.com/Sirupsen/logrus"
@@ -12,7 +13,8 @@ import (
// Log middleware logs http requests
type Log struct {
LogSuccess bool // LogSuccess true -> log successful queries; false -> only log failed queries
LogSuccess bool // LogSuccess true -> log successful queries; false -> only log failed queries
LogRequestHeaders bool // LogRequestHeaders true -> dump http headers at debug log level
}
// Wrap implements Middleware
@@ -20,6 +22,15 @@ func (l Log) Wrap(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
begin := time.Now()
uri := r.RequestURI // capture the URI before running next, as it may get rewritten
if l.LogRequestHeaders {
// Log headers before running 'next' in case other interceptors change the data.
headers, err := httputil.DumpRequest(r, false)
if err != nil {
log.Warnf("Could not dump request headers: %v", err)
return
}
log.Debugf("Is websocket request: %v\n%s", IsWSHandshakeRequest(r), string(headers))
}
i := &interceptor{ResponseWriter: w, statusCode: http.StatusOK}
next.ServeHTTP(i, r)
if l.LogSuccess || !(100 <= i.statusCode && i.statusCode < 400) {
@@ -31,7 +42,8 @@ func (l Log) Wrap(next http.Handler) http.Handler {
// Logging middleware logs each HTTP request method, path, response code and
// duration for all HTTP requests.
var Logging = Log{
LogSuccess: true,
LogSuccess: true,
LogRequestHeaders: false,
}
// LogFailed middleware logs each HTTP request method, path, response code and

View File

@@ -0,0 +1,48 @@
package middleware
import (
"net/http"
"net/url"
)
// Redirect middleware, will redirect requests to hosts which match any of the
// Matches to RedirectScheme://RedirectHost
type Redirect struct {
Matches []Match
RedirectHost string
RedirectScheme string
}
// Match specifies a match for a redirect. Host and/or Scheme can be empty
// signify match-all.
type Match struct {
Host, Scheme string
}
func (m Match) match(u *url.URL) bool {
if m.Host != "" && m.Host != u.Host {
return false
}
if m.Scheme != "" && m.Scheme != u.Scheme {
return false
}
return true
}
// Wrap implements Middleware
func (m Redirect) Wrap(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
for _, match := range m.Matches {
if match.match(r.URL) {
r.URL.Host = m.RedirectHost
r.URL.Scheme = m.RedirectScheme
http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently)
return
}
}
next.ServeHTTP(w, r)
})
}

2
vendor/manifest vendored
View File

@@ -1361,7 +1361,7 @@
"importpath": "github.com/weaveworks/common",
"repository": "https://github.com/weaveworks/common",
"vcs": "git",
"revision": "cc20acf03ebf74be0facaae4259dff6cde01ce77",
"revision": "139d0313ac15170e9de8187b26e7df03b4cb910e",
"branch": "master",
"notests": true
},