forked from google/periph
/
loghttp.go
120 lines (108 loc) · 2.39 KB
/
loghttp.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
// Copyright 2018 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
// Inspired by:
// https://github.com/maruel/serve-dir/blob/master/loghttp/loghttp.go
package main
import (
"bufio"
"log"
"net"
"net/http"
"time"
)
func loggingHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
received := time.Now()
rwh := responseWriteHijacker{responseWriter: responseWriter{ResponseWriter: w}}
w = &rwh
// Not all ResponseWriter implement Hijack, so query its support upfront.
if _, ok := w.(http.Hijacker); !ok {
w = &rwh.responseWriter
}
defer func() {
m := r.Method
if rwh.hijacked {
m = "HIJACKED"
}
log.Printf("%s - %3d %6db %-4s %6s %s", r.RemoteAddr, rwh.status, rwh.length, m, roundDuration(time.Since(received)), r.RequestURI)
}()
h.ServeHTTP(w, r)
})
}
type responseWriter struct {
http.ResponseWriter
length int
status int
}
func (r *responseWriter) Write(data []byte) (size int, err error) {
if r.status == 0 {
r.status = 200
}
size, err = r.ResponseWriter.Write(data)
r.length += size
return
}
func (r *responseWriter) WriteHeader(status int) {
r.ResponseWriter.WriteHeader(status)
r.status = status
}
type responseWriteHijacker struct {
responseWriter
hijacked bool
}
// Hijack is needed for websocket.
func (r *responseWriteHijacker) Hijack() (net.Conn, *bufio.ReadWriter, error) {
r.hijacked = true
return r.ResponseWriter.(http.Hijacker).Hijack()
}
//
func roundDuration(d time.Duration) time.Duration {
if l := log10(int64(d)); l > 3 {
m := time.Duration(1)
for i := uint(3); i < l; i++ {
m *= 10
}
d = (d + (m / 2)) / m * m
}
return d
}
// log10 is a cheap way to find the most significant digit.
func log10(i int64) uint {
switch {
case i < 10:
return 0
case i < 100:
return 1
case i < 1000:
return 2
case i < 10000:
return 3
case i < 100000:
return 4
case i < 1000000:
return 5
case i < 10000000:
return 6
case i < 100000000:
return 7
case i < 1000000000:
return 8
case i < 10000000000:
return 9
case i < 100000000000:
return 10
case i < 1000000000000:
return 11
case i < 10000000000000:
return 12
case i < 100000000000000:
return 13
case i < 1000000000000000:
return 14
case i < 10000000000000000:
return 15
default:
return 16
}
}