-
Notifications
You must be signed in to change notification settings - Fork 0
/
attribs.go
174 lines (156 loc) · 4.56 KB
/
attribs.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
170
171
172
173
174
package golog
import (
"context"
"net/http"
)
var (
_ Loggable = Attribs(nil)
)
// Attribs is a Attrib slice with methods to manage and log them.
// Usually only one Attrib with a given key is present in the slice,
// but this is not enforced.
// A slice is used instead of a map to preserve the order
// of attributes and to maximize allocation performance.
//
// Attribs implements the Loggable interface by logging
// the attributes in the slice in the given order.
type Attribs []Attrib
// Log implements the Loggable interface by logging
// the attributes in the slice in the given order.
func (a Attribs) Log(m *Message) {
for _, attrib := range a {
attrib.Log(m)
}
}
// Get returns the first Attrib with the passed key
// or nil if not Attrib was found.
func (a Attribs) Get(key string) Attrib {
for _, attrib := range a {
if attrib.GetKey() == key {
return attrib
}
}
return nil
}
// Has indicates if the Attribs contain an Attrib with the passed key
func (a Attribs) Has(key string) bool {
for _, attrib := range a {
if attrib.GetKey() == key {
return true
}
}
return false
}
// Len returns the length of the Attribs slice
func (a Attribs) Len() int {
return len(a)
}
/*
// ReplaceOrAppend replaces the first value in the slice with the same key
// than the passed value, or else appends the passed value to the slice.
func (v *Attribs) ReplaceOrAppend(value Attrib) {
name := value.Name()
for i, existing := range *v {
if existing.Name() == name {
(*v)[i] = value
return
}
}
*v = append(*v, value)
}
*/
var attribsCtxKey int
// AttribsFromContext returns the Attribs that were added
// to a context or nil.
func AttribsFromContext(ctx context.Context) Attribs {
attribs, _ := ctx.Value(&attribsCtxKey).(Attribs)
return attribs
}
// AttribFromContext returns an attrib with a given key and type
// from a context or false for ok if no such attribute was
// added to the context.
func AttribFromContext[T Attrib](ctx context.Context, key string) (attrib T, ok bool) {
attrib, ok = AttribsFromContext(ctx).Get(key).(T)
return attrib, ok
}
// ContextWithAttribs returns a context with the passed attribs
// added to it, overwriting any attribs with the same keys
// already added to the context.
//
// The added attribs can be retrieved from the context
// with AttribsFromContext.
func ContextWithAttribs(ctx context.Context, attribs ...Attrib) context.Context {
return Attribs(attribs).AddToContext(ctx)
}
// RequestWithAttribs returns an http.Request with the Attribs
// added to its context, overwriting any attribs with
// the same keys already added to the request context.
func RequestWithAttribs(request *http.Request, attribs ...Attrib) *http.Request {
return Attribs(attribs).AddToRequest(request)
}
// AddToContext returns a context with the Attribs
// added to it, overwriting any attribs with the same keys
// already added to the context.
//
// The added attribs can be retrieved from the context
// with AttribsFromContext.
func (a Attribs) AddToContext(ctx context.Context) context.Context {
if len(a) == 0 {
return ctx
}
mergedAttribs := a.AppendUnique(AttribsFromContext(ctx)...)
return context.WithValue(ctx, &attribsCtxKey, mergedAttribs)
}
// AddToRequest returns an http.Request with the Attribs
// added to its context, overwriting any attribs with
// the same keys already added to the request context.
func (a Attribs) AddToRequest(request *http.Request) *http.Request {
if len(a) == 0 {
return request
}
ctx := a.AddToContext(request.Context())
return request.WithContext(ctx)
}
// AppendUnique merges a and b so that keys are unique
// using attribs from a in case of identical keyed attribs in b.
//
// The slices left and right will never be modified,
// in case of a merge the result is always a new slice.
func (a Attribs) AppendUnique(b ...Attrib) Attribs {
// Remove nil interfaces. They should not happen but robustness of logging is important!
for i := len(a) - 1; i >= 0; i-- {
if a[i] == nil {
a = append(a[:i], a[i+1:]...)
}
}
for i := len(b) - 1; i >= 0; i-- {
if b[i] == nil {
b = append(b[:i], b[i+1:]...)
}
}
// No merge cases
switch {
case len(a) == 0:
return b
case len(b) == 0:
return a
}
var result Attribs
for _, bAttrib := range b {
if a.Has(bAttrib.GetKey()) {
// Ignore bAttrib value because the value from a is preferred
continue
}
if result == nil {
// Allocate new slice for merged result
result = append(result, a...)
}
result = append(result, bAttrib)
}
if result == nil {
// All keys of b were present in a
// so result is identical to a
return a
}
return result
}