-
Notifications
You must be signed in to change notification settings - Fork 1
/
logger.go
104 lines (90 loc) · 3.38 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
// Package log provides context-aware and structured logging capabilities.
package log
import (
"context"
"github.com/google/uuid"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"go.uber.org/zap/zaptest/observer"
"net/http"
)
// Logger is a logger that supports log levels, context and structured logging.
type Logger interface {
// With returns a logger based off the root logger and decorates it with the given context and arguments.
With(ctx context.Context, args ...interface{}) Logger
// Debug uses fmt.Sprint to construct and log a message at DEBUG level
Debug(args ...interface{})
// Info uses fmt.Sprint to construct and log a message at INFO level
Info(args ...interface{})
// Error uses fmt.Sprint to construct and log a message at ERROR level
Error(args ...interface{})
// Debugf uses fmt.Sprintf to construct and log a message at DEBUG level
Debugf(format string, args ...interface{})
// Infof uses fmt.Sprintf to construct and log a message at INFO level
Infof(format string, args ...interface{})
// Errorf uses fmt.Sprintf to construct and log a message at ERROR level
Errorf(format string, args ...interface{})
}
type logger struct {
*zap.SugaredLogger
}
type contextKey int
const (
requestIDKey contextKey = iota
correlationIDKey
)
// New creates a new logger using the default configuration.
func New() Logger {
l, _ := zap.NewProduction()
return NewWithZap(l)
}
// NewWithZap creates a new logger using the preconfigured zap logger.
func NewWithZap(l *zap.Logger) Logger {
return &logger{l.Sugar()}
}
// NewForTest returns a new logger and the corresponding observed logs which can be used in unit tests to verify log entries.
func NewForTest() (Logger, *observer.ObservedLogs) {
core, recorded := observer.New(zapcore.InfoLevel)
return NewWithZap(zap.New(core)), recorded
}
// With returns a logger based off the root logger and decorates it with the given context and arguments.
//
// If the context contains request ID and/or correlation ID information (recorded via WithRequestID()
// and WithCorrelationID()), they will be added to every log message generated by the new logger.
//
// The arguments should be specified as a sequence of name, value pairs with names being strings.
// The arguments will also be added to every log message generated by the logger.
func (l *logger) With(ctx context.Context, args ...interface{}) Logger {
if ctx != nil {
if id, ok := ctx.Value(requestIDKey).(string); ok {
args = append(args, zap.String("request_id", id))
}
if id, ok := ctx.Value(correlationIDKey).(string); ok {
args = append(args, zap.String("correlation_id", id))
}
}
if len(args) > 0 {
return &logger{l.SugaredLogger.With(args...)}
}
return l
}
// WithRequest returns a context which knows the request ID and correlation ID in the given request.
func WithRequest(ctx context.Context, req *http.Request) context.Context {
id := getRequestID(req)
if id == "" {
id = uuid.New().String()
}
ctx = context.WithValue(ctx, requestIDKey, id)
if id := getCorrelationID(req); id != "" {
ctx = context.WithValue(ctx, correlationIDKey, id)
}
return ctx
}
// getCorrelationID extracts the correlation ID from the HTTP request
func getCorrelationID(req *http.Request) string {
return req.Header.Get("X-Correlation-ID")
}
// getRequestID extracts the correlation ID from the HTTP request
func getRequestID(req *http.Request) string {
return req.Header.Get("X-Request-ID")
}