Skip to content

Commit c53398c

Browse files
committed
Improve request logger middleware.
* Add "from" key with client IP * Only log controller and action in "started" log * Use controller name instead of resource name
1 parent b724c94 commit c53398c

File tree

5 files changed

+45
-8
lines changed

5 files changed

+45
-8
lines changed

context.go

+23
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
const (
1313
reqKey key = iota + 1
1414
respKey
15+
ctrlKey
16+
actionKey
1517
paramsKey
1618
logKey
1719
logContextKey
@@ -58,11 +60,32 @@ func NewContext(ctx context.Context, rw http.ResponseWriter, req *http.Request,
5860
return ctx
5961
}
6062

63+
// ActionContext creates a context with the given action name.
64+
func ActionContext(ctx context.Context, action string) context.Context {
65+
return context.WithValue(ctx, actionKey, action)
66+
}
67+
6168
// UseLogger sets the request context logger and returns the resulting new context.
6269
func UseLogger(ctx context.Context, logger Logger) context.Context {
6370
return context.WithValue(ctx, logKey, logger)
6471
}
6572

73+
// ContextController extracts the controller name from the given context.
74+
func ContextController(ctx context.Context) string {
75+
if c := ctx.Value(ctrlKey); c != nil {
76+
return c.(string)
77+
}
78+
return "<unknown>"
79+
}
80+
81+
// ContextAction extracts the action name from the given context.
82+
func ContextAction(ctx context.Context) string {
83+
if a := ctx.Value(actionKey); a != nil {
84+
return a.(string)
85+
}
86+
return "<unknown>"
87+
}
88+
6689
// ContextRequest extracts the request data from the given context.
6790
func ContextRequest(ctx context.Context) *RequestData {
6891
if r := ctx.Value(reqKey); r != nil {

goagen/gen_main/generator.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ type {{ $ctrlName }} struct {
260260
261261
// New{{ $ctrlName }} creates a {{ .Name }} controller.
262262
func New{{ $ctrlName }}(service *goa.Service) *{{ $ctrlName }} {
263-
return &{{ $ctrlName }}{Controller: service.NewController("{{ .Name }}")}
263+
return &{{ $ctrlName }}{Controller: service.NewController("{{ $ctrlName }}")}
264264
}
265265
`
266266

middleware/log_request.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/base64"
66
"encoding/json"
77
"io"
8+
"net"
89
"net/http"
910
"strings"
1011
"time"
@@ -28,7 +29,8 @@ func LogRequest(verbose bool) goa.Middleware {
2829
ctx = goa.LogWith(ctx, "req_id", reqID)
2930
startedAt := time.Now()
3031
r := goa.ContextRequest(ctx)
31-
goa.LogInfo(ctx, "started", r.Method, r.URL.String())
32+
goa.LogInfo(ctx, "started", r.Method, r.URL.String(), "from", from(req),
33+
"ctrl", goa.ContextController(ctx), "action", goa.ContextAction(ctx))
3234
if verbose {
3335
if len(r.Params) > 0 {
3436
logCtx := make([]interface{}, 2*len(r.Params))
@@ -76,3 +78,16 @@ func shortID() string {
7678
io.ReadFull(rand.Reader, b)
7779
return base64.StdEncoding.EncodeToString(b)
7880
}
81+
82+
// from makes a best effort to compute the request client IP.
83+
func from(req *http.Request) string {
84+
if f := req.Header.Get("X-Forwarded-For"); f != "" {
85+
return f
86+
}
87+
f := req.RemoteAddr
88+
ip, _, err := net.SplitHostPort(f)
89+
if err != nil {
90+
return f
91+
}
92+
return ip
93+
}

middleware/log_request_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ var _ = Describe("LogRequest", func() {
4545
Ω(lg(ctx, rw, req)).ShouldNot(HaveOccurred())
4646
Ω(logger.InfoEntries).Should(HaveLen(4))
4747

48-
Ω(logger.InfoEntries[0].Data).Should(HaveLen(4))
48+
Ω(logger.InfoEntries[0].Data).Should(HaveLen(10))
4949
Ω(logger.InfoEntries[0].Data[0]).Should(Equal("req_id"))
5050
Ω(logger.InfoEntries[0].Data[2]).Should(Equal("POST"))
5151
Ω(logger.InfoEntries[0].Data[3]).Should(Equal("/goo?param=value"))

service.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,11 @@ func (service *Service) ListenAndServeTLS(addr, certFile, keyFile string) error
148148

149149
// NewController returns a controller for the given resource. This method is mainly intended for
150150
// use by the generated code. User code shouldn't have to call it directly.
151-
func (service *Service) NewController(resName string) *Controller {
151+
func (service *Service) NewController(name string) *Controller {
152152
return &Controller{
153-
Name: resName,
153+
Name: name,
154154
Service: service,
155-
Context: context.WithValue(service.Context, "ctrl", resName),
155+
Context: context.WithValue(service.Context, ctrlKey, name),
156156
}
157157
}
158158

@@ -269,10 +269,9 @@ func (ctrl *Controller) MuxHandler(name string, hdlr Handler, unm Unmarshaler) M
269269
for i := range chain {
270270
middleware = chain[ml-i-1](middleware)
271271
}
272-
baseCtx := LogWith(ctrl.Context, "ctrl", ctrl.Name, "action", name)
273272
return func(rw http.ResponseWriter, req *http.Request, params url.Values) {
274273
// Build context
275-
ctx := NewContext(baseCtx, rw, req, params)
274+
ctx := NewContext(ActionContext(ctrl.Context, name), rw, req, params)
276275

277276
// Protect against request bodies with unreasonable length
278277
if MaxRequestBodyLength > 0 {

0 commit comments

Comments
 (0)