1package log
2
3import "errors"
4
5// Logger is the fundamental interface for all log operations. Log creates a
6// log event from keyvals, a variadic sequence of alternating keys and values.
7// Implementations must be safe for concurrent use by multiple goroutines. In
8// particular, any implementation of Logger that appends to keyvals or
9// modifies or retains any of its elements must make a copy first.
10type Logger interface {
11	Log(keyvals ...interface{}) error
12}
13
14// ErrMissingValue is appended to keyvals slices with odd length to substitute
15// the missing value.
16var ErrMissingValue = errors.New("(MISSING)")
17
18// With returns a new contextual logger with keyvals prepended to those passed
19// to calls to Log. If logger is also a contextual logger created by With,
20// WithPrefix, or WithSuffix, keyvals is appended to the existing context.
21//
22// The returned Logger replaces all value elements (odd indexes) containing a
23// Valuer with their generated value for each call to its Log method.
24func With(logger Logger, keyvals ...interface{}) Logger {
25	if len(keyvals) == 0 {
26		return logger
27	}
28	l := newContext(logger)
29	kvs := append(l.keyvals, keyvals...)
30	if len(kvs)%2 != 0 {
31		kvs = append(kvs, ErrMissingValue)
32	}
33	return &context{
34		logger: l.logger,
35		// Limiting the capacity of the stored keyvals ensures that a new
36		// backing array is created if the slice must grow in Log or With.
37		// Using the extra capacity without copying risks a data race that
38		// would violate the Logger interface contract.
39		keyvals:    kvs[:len(kvs):len(kvs)],
40		hasValuer:  l.hasValuer || containsValuer(keyvals),
41		sKeyvals:   l.sKeyvals,
42		sHasValuer: l.sHasValuer,
43	}
44}
45
46// WithPrefix returns a new contextual logger with keyvals prepended to those
47// passed to calls to Log. If logger is also a contextual logger created by
48// With, WithPrefix, or WithSuffix, keyvals is prepended to the existing context.
49//
50// The returned Logger replaces all value elements (odd indexes) containing a
51// Valuer with their generated value for each call to its Log method.
52func WithPrefix(logger Logger, keyvals ...interface{}) Logger {
53	if len(keyvals) == 0 {
54		return logger
55	}
56	l := newContext(logger)
57	// Limiting the capacity of the stored keyvals ensures that a new
58	// backing array is created if the slice must grow in Log or With.
59	// Using the extra capacity without copying risks a data race that
60	// would violate the Logger interface contract.
61	n := len(l.keyvals) + len(keyvals)
62	if len(keyvals)%2 != 0 {
63		n++
64	}
65	kvs := make([]interface{}, 0, n)
66	kvs = append(kvs, keyvals...)
67	if len(kvs)%2 != 0 {
68		kvs = append(kvs, ErrMissingValue)
69	}
70	kvs = append(kvs, l.keyvals...)
71	return &context{
72		logger:     l.logger,
73		keyvals:    kvs,
74		hasValuer:  l.hasValuer || containsValuer(keyvals),
75		sKeyvals:   l.sKeyvals,
76		sHasValuer: l.sHasValuer,
77	}
78}
79
80// WithSuffix returns a new contextual logger with keyvals appended to those
81// passed to calls to Log. If logger is also a contextual logger created by
82// With, WithPrefix, or WithSuffix, keyvals is appended to the existing context.
83//
84// The returned Logger replaces all value elements (odd indexes) containing a
85// Valuer with their generated value for each call to its Log method.
86func WithSuffix(logger Logger, keyvals ...interface{}) Logger {
87	if len(keyvals) == 0 {
88		return logger
89	}
90	l := newContext(logger)
91	// Limiting the capacity of the stored keyvals ensures that a new
92	// backing array is created if the slice must grow in Log or With.
93	// Using the extra capacity without copying risks a data race that
94	// would violate the Logger interface contract.
95	n := len(l.sKeyvals) + len(keyvals)
96	if len(keyvals)%2 != 0 {
97		n++
98	}
99	kvs := make([]interface{}, 0, n)
100	kvs = append(kvs, keyvals...)
101	if len(kvs)%2 != 0 {
102		kvs = append(kvs, ErrMissingValue)
103	}
104	kvs = append(l.sKeyvals, kvs...)
105	return &context{
106		logger:     l.logger,
107		keyvals:    l.keyvals,
108		hasValuer:  l.hasValuer,
109		sKeyvals:   kvs,
110		sHasValuer: l.sHasValuer || containsValuer(keyvals),
111	}
112}
113
114// context is the Logger implementation returned by With, WithPrefix, and
115// WithSuffix. It wraps a Logger and holds keyvals that it includes in all
116// log events. Its Log method calls bindValues to generate values for each
117// Valuer in the context keyvals.
118//
119// A context must always have the same number of stack frames between calls to
120// its Log method and the eventual binding of Valuers to their value. This
121// requirement comes from the functional requirement to allow a context to
122// resolve application call site information for a Caller stored in the
123// context. To do this we must be able to predict the number of logging
124// functions on the stack when bindValues is called.
125//
126// Two implementation details provide the needed stack depth consistency.
127//
128//    1. newContext avoids introducing an additional layer when asked to
129//       wrap another context.
130//    2. With, WithPrefix, and WithSuffix avoid introducing an additional
131//       layer by returning a newly constructed context with a merged keyvals
132//       rather than simply wrapping the existing context.
133type context struct {
134	logger     Logger
135	keyvals    []interface{}
136	sKeyvals   []interface{} // suffixes
137	hasValuer  bool
138	sHasValuer bool
139}
140
141func newContext(logger Logger) *context {
142	if c, ok := logger.(*context); ok {
143		return c
144	}
145	return &context{logger: logger}
146}
147
148// Log replaces all value elements (odd indexes) containing a Valuer in the
149// stored context with their generated value, appends keyvals, and passes the
150// result to the wrapped Logger.
151func (l *context) Log(keyvals ...interface{}) error {
152	kvs := append(l.keyvals, keyvals...)
153	if len(kvs)%2 != 0 {
154		kvs = append(kvs, ErrMissingValue)
155	}
156	if l.hasValuer {
157		// If no keyvals were appended above then we must copy l.keyvals so
158		// that future log events will reevaluate the stored Valuers.
159		if len(keyvals) == 0 {
160			kvs = append([]interface{}{}, l.keyvals...)
161		}
162		bindValues(kvs[:(len(l.keyvals))])
163	}
164	kvs = append(kvs, l.sKeyvals...)
165	if l.sHasValuer {
166		bindValues(kvs[len(kvs)-len(l.sKeyvals):])
167	}
168	return l.logger.Log(kvs...)
169}
170
171// LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If
172// f is a function with the appropriate signature, LoggerFunc(f) is a Logger
173// object that calls f.
174type LoggerFunc func(...interface{}) error
175
176// Log implements Logger by calling f(keyvals...).
177func (f LoggerFunc) Log(keyvals ...interface{}) error {
178	return f(keyvals...)
179}
180