-
Notifications
You must be signed in to change notification settings - Fork 10
/
logging.go
169 lines (152 loc) · 5.18 KB
/
logging.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package goa
import (
"bytes"
"context"
"fmt"
"log"
)
// ErrMissingLogValue is the value used to log keys with missing values
const ErrMissingLogValue = "MISSING"
type (
// LogAdapter is the logger interface used by goa to log informational and error messages.
// Adapters to different logging backends are provided in the logging sub-packages.
// goa takes care of initializing the logging context with the service, controller and
// action names.
LogAdapter interface {
// Info logs an informational message.
Info(msg string, keyvals ...interface{})
// Error logs an error.
Error(msg string, keyvals ...interface{})
// New appends to the logger context and returns the updated logger logger.
New(keyvals ...interface{}) LogAdapter
}
// WarningLogAdapter is the logger interface used by goa to log informational, warning and error messages.
// Adapters to different logging backends are provided in the logging sub-packages.
// goa takes care of initializing the logging context with the service, controller and
// action names.
WarningLogAdapter interface {
LogAdapter
// Warn logs a warning message.
Warn(mgs string, keyvals ...interface{})
}
// ContextLogAdapter is the logger interface used by goa to log informational, warning and error messages.
// It allows to pass a context.Context to the logger.
ContextLogAdapter interface {
WarningLogAdapter
// InfoContext is same as Info but with context.
InfoContext(ctx context.Context, msg string, keyvals ...interface{})
// ErrorContext is same as Error but with context.
ErrorContext(ctx context.Context, msg string, keyvals ...interface{})
// WarnContext is same as Warn but with context.
WarnContext(ctx context.Context, mgs string, keyvals ...interface{})
}
// adapter is the stdlib logger adapter.
adapter struct {
*log.Logger
keyvals []interface{}
}
)
// NewLogger returns a goa log adapter backed by a log logger.
func NewLogger(logger *log.Logger) LogAdapter {
return &adapter{Logger: logger}
}
// Logger returns the logger stored in the context if any, nil otherwise.
func Logger(ctx context.Context) *log.Logger {
logger := ContextLogger(ctx)
if a, ok := logger.(*adapter); ok {
return a.Logger
}
return nil
}
func (a *adapter) Info(msg string, keyvals ...interface{}) {
a.logit(msg, keyvals, "INFO")
}
func (a *adapter) Warn(msg string, keyvals ...interface{}) {
a.logit(msg, keyvals, "WARN")
}
func (a *adapter) Error(msg string, keyvals ...interface{}) {
a.logit(msg, keyvals, "EROR")
}
func (a *adapter) New(keyvals ...interface{}) LogAdapter {
if len(keyvals) == 0 {
return a
}
kvs := append(a.keyvals, keyvals...)
if len(kvs)%2 != 0 {
kvs = append(kvs, ErrMissingLogValue)
}
return &adapter{
Logger: a.Logger,
// Limiting the capacity of the stored keyvals ensures that a new
// backing array is created if the slice must grow.
keyvals: kvs[:len(kvs):len(kvs)],
}
}
func (a *adapter) logit(msg string, keyvals []interface{}, level string) {
n := (len(keyvals) + 1) / 2
if len(keyvals)%2 != 0 {
keyvals = append(keyvals, ErrMissingLogValue)
}
m := (len(a.keyvals) + 1) / 2
n += m
var fm bytes.Buffer
fm.WriteString(fmt.Sprintf("[%s] %s", level, msg))
vals := make([]interface{}, n)
offset := len(a.keyvals)
for i := 0; i < offset; i += 2 {
k := a.keyvals[i]
v := a.keyvals[i+1]
vals[i/2] = v
fm.WriteString(fmt.Sprintf(" %s=%%+v", k))
}
for i := 0; i < len(keyvals); i += 2 {
k := keyvals[i]
v := keyvals[i+1]
vals[i/2+offset/2] = v
fm.WriteString(fmt.Sprintf(" %s=%%+v", k))
}
a.Logger.Printf(fm.String(), vals...)
}
// LogInfo extracts the logger from the given context and calls Info on it.
// This is intended for code that needs portable logging such as the internal code of goa and
// middleware. User code should use the log adapters instead.
func LogInfo(ctx context.Context, msg string, keyvals ...interface{}) {
// This block should be synced with Service.LogInfo
if l := ctx.Value(logKey); l != nil {
switch logger := l.(type) {
case ContextLogAdapter:
logger.InfoContext(ctx, msg, keyvals...)
case LogAdapter:
logger.Info(msg, keyvals...)
}
}
}
// LogWarn extracts the logger from the given context and calls Warn on it.
// This is intended for code that needs portable logging such as the internal code of goa and
// middleware. User code should use the log adapters instead.
func LogWarn(ctx context.Context, msg string, keyvals ...interface{}) {
if l := ctx.Value(logKey); l != nil {
switch logger := l.(type) {
case ContextLogAdapter:
logger.WarnContext(ctx, msg, keyvals...)
case WarningLogAdapter:
logger.Warn(msg, keyvals...)
case LogAdapter:
logger.Info(msg, keyvals...)
}
}
}
// LogError extracts the logger from the given context and calls Error on it.
// This is intended for code that needs portable logging such as the internal code of goa and
// middleware. User code should use the log adapters instead.
func LogError(ctx context.Context, msg string, keyvals ...interface{}) {
// this block should be synced with Service.LogError
if l := ctx.Value(logKey); l != nil {
switch logger := l.(type) {
case ContextLogAdapter:
logger.ErrorContext(ctx, msg, keyvals...)
case LogAdapter:
logger.Error(msg, keyvals...)
}
}
}