-
Notifications
You must be signed in to change notification settings - Fork 13
/
utils.go
115 lines (99 loc) · 3.87 KB
/
utils.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
package common
import (
"context"
"crypto/rand"
"encoding/base64"
"net/http"
"strings"
"time"
"github.com/Comcast/webpa-common/logging"
"github.com/Comcast/webpa-common/secure/handler"
kitlog "github.com/go-kit/kit/log"
kithttp "github.com/go-kit/kit/transport/http"
)
//HeaderWPATID is the header key for the WebPA transaction UUID
const HeaderWPATID = "X-WebPA-Transaction-Id"
//TransactionLogging is used by the different Tr1d1um services to
//keep track of incoming requests and their corresponding responses
func TransactionLogging(logger kitlog.Logger) kithttp.ServerFinalizerFunc {
var infoLogger = logging.Info(logger)
return func(ctx context.Context, code int, r *http.Request) {
var satClientID = "N/A"
// retrieve satClientID from request context
if reqContextValues, ok := handler.FromContext(r.Context()); ok {
satClientID = reqContextValues.SatClientID
}
var rCtx = r.Context()
transactionLogger := kitlog.WithPrefix(infoLogger,
logging.MessageKey(), "Bookkeeping response",
"requestAddress", r.RemoteAddr,
"requestURLPath", r.URL.Path,
"requestURLQuery", r.URL.RawQuery,
"requestMethod", r.Method,
"responseCode", code,
"responseHeaders", ctx.Value(kithttp.ContextKeyResponseHeaders),
"tid", ctx.Value(ContextKeyRequestTID),
"satClientID", satClientID,
)
//For a request R, lantency includes time from points A to B where:
//A: as soon as R is authorized
//B: as soon as Tr1d1um is done sending the response for R
var latency time.Duration
if requestArrivalTime, ok := rCtx.Value(ContextKeyRequestArrivalTime).(time.Time); ok {
latency = time.Since(requestArrivalTime)
} else {
logging.Error(logger).Log("tid", ctx.Value(ContextKeyRequestTID), logging.MessageKey(), "latency value could not be derived")
}
transactionLogger.Log("latency", latency)
}
}
//ForwardHeadersByPrefix copies headers h where the source and target are 'from' and 'to' respectively such that key(h) has p as prefix
func ForwardHeadersByPrefix(p string, from http.Header, to http.Header) {
for headerKey, headerValues := range from {
if strings.HasPrefix(headerKey, p) {
for _, headerValue := range headerValues {
to.Add(headerKey, headerValue)
}
}
}
}
//ErrorLogEncoder decorates the errorEncoder in such a way that
//errors are logged with their corresponding unique request identifier
func ErrorLogEncoder(logger kitlog.Logger, ee kithttp.ErrorEncoder) kithttp.ErrorEncoder {
var errorLogger = logging.Error(logger)
return func(ctx context.Context, e error, w http.ResponseWriter) {
errorLogger.Log(logging.ErrorKey(), e.Error(), "tid", ctx.Value(ContextKeyRequestTID).(string))
ee(ctx, e, w)
}
}
//Welcome is an Alice-style constructor that defines necessary request
//context values assumed to exist by the delegate. These values should
//be those expected to be used both in and outside the gokit server flow
func Welcome(delegate http.Handler) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
var ctx = r.Context()
ctx = context.WithValue(ctx, ContextKeyRequestArrivalTime, time.Now())
delegate.ServeHTTP(w, r.WithContext(ctx))
})
}
//Capture (for lack of a better name) captures context values of interest
//from the incoming request. Unlike Welcome, values captured here are
//intended to be used only throughout the gokit server flow: (request decoding, business logic, response encoding)
func Capture(ctx context.Context, r *http.Request) context.Context {
var tid string
if tid = r.Header.Get(HeaderWPATID); tid == "" {
tid = genTID()
}
return context.WithValue(ctx, ContextKeyRequestTID, tid)
}
//genTID generates a 16-byte long string
//it returns "N/A" in the extreme case the random string could not be generated
func genTID() (tid string) {
buf := make([]byte, 16)
tid = "N/A"
if _, err := rand.Read(buf); err == nil {
tid = base64.RawURLEncoding.EncodeToString(buf)
}
return
}