-
Notifications
You must be signed in to change notification settings - Fork 3
/
logger.go
147 lines (115 loc) · 3.46 KB
/
logger.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package gojilogger
import (
"bytes"
"fmt"
"log"
"net/http"
"net/url"
"os"
"time"
"goji.io/pattern"
"goji.io"
"golang.org/x/net/context"
"github.com/derekdowling/go-stdlogger"
"github.com/zenazn/goji/web/mutil"
)
const (
// FastResponse is anything under this duration
FastResponse = 500 * time.Millisecond
// AcceptableResponse is anything under this duration
AcceptableResponse = 5 * time.Second
)
// Logger contains instance state for a goji2logger to avoid configuration
// collisions if this middleware is used in multiple places
type Logger struct {
// Logger is a https://github.com/derekdowling/go-stdlogger
Logger std.Logger
// Debug will increase verbosity of logging information, and causes Query params not
// to be omitted. Do NOT use in production otherwise you risk logging sensitive
// information.
Debug bool
}
// New creates a new goji2logger instance
func New(logger std.Logger, debug bool) *Logger {
l := &Logger{
Debug: debug,
Logger: logger,
}
if l.Logger == nil {
l.Logger = log.New(os.Stderr, "", log.LstdFlags)
}
return l
}
// Middleware logs the start and end of each request, along
// with some useful data about what was requested, what the response status was,
// and how long it took to return. When standard output is a TTY, Logger will
// print in color, otherwise it will print in black and white.
//
// Use like so with Goji2:
// gLogger := gojilogger.New(nil, false)
// yourGoji.UseC(gLogger.Middleware)
func (l *Logger) Middleware(next goji.Handler) goji.Handler {
middleware := func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
l.printRequest(ctx, r)
// WrapWriter lets us peek at ResponseWriter outputs
lw := mutil.WrapWriter(w)
startTime := time.Now()
next.ServeHTTPC(ctx, lw, r)
if lw.Status() == 0 {
lw.WriteHeader(http.StatusOK)
}
finishTime := time.Now()
l.printResponse(lw, finishTime.Sub(startTime))
}
return goji.HandlerFunc(middleware)
}
func (l *Logger) printRequest(ctx context.Context, r *http.Request) {
var buf bytes.Buffer
if l.Debug {
buf.WriteString("[DEBUG]")
}
buf.WriteString("Serving route: ")
// Goji routing details
colorWrite(&buf, bGreen, "%s", pattern.Path(ctx))
// Server details
buf.WriteString(fmt.Sprintf(" from %s ", r.RemoteAddr))
// Request details
buf.WriteString("for ")
colorWrite(&buf, bMagenta, "%s ", r.Method)
urlStr := r.URL.String()
// if not in debug mode, remove Query params from logging as not to include any
// sensitive information inadvertantly into user's logs
if !l.Debug && r.URL.RawQuery != "" {
tempURL := &url.URL{}
*tempURL = *r.URL
tempURL.RawQuery = "<omitted>"
urlStr = tempURL.String()
}
colorWrite(&buf, bBlue, "%q", urlStr)
log.Print(buf.String())
}
func (l *Logger) printResponse(w mutil.WriterProxy, delta time.Duration) {
var buf bytes.Buffer
buf.WriteString("Returning HTTP ")
status := w.Status()
if status < 200 {
colorWrite(&buf, bBlue, "%03d", status)
} else if status < 300 {
colorWrite(&buf, bGreen, "%03d", status)
} else if status < 400 {
colorWrite(&buf, bCyan, "%03d", status)
} else if status < 500 {
colorWrite(&buf, bYellow, "%03d", status)
} else {
colorWrite(&buf, bRed, "%03d", status)
}
buf.WriteString(" in ")
if delta < FastResponse {
colorWrite(&buf, nGreen, "%s", delta.String())
} else if delta < AcceptableResponse {
colorWrite(&buf, nYellow, "%s", delta.String())
} else {
colorWrite(&buf, nRed, "%s", delta.String())
}
log.Print(buf.String())
}