1// Copyright 2017, OpenCensus Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14// 15 16package tag 17 18import ( 19 "bytes" 20 "context" 21 "fmt" 22 "sort" 23) 24 25// Tag is a key value pair that can be propagated on wire. 26type Tag struct { 27 Key Key 28 Value string 29} 30 31type tagContent struct { 32 value string 33 m metadatas 34} 35 36// Map is a map of tags. Use New to create a context containing 37// a new Map. 38type Map struct { 39 m map[Key]tagContent 40} 41 42// Value returns the value for the key if a value for the key exists. 43func (m *Map) Value(k Key) (string, bool) { 44 if m == nil { 45 return "", false 46 } 47 v, ok := m.m[k] 48 return v.value, ok 49} 50 51func (m *Map) String() string { 52 if m == nil { 53 return "nil" 54 } 55 keys := make([]Key, 0, len(m.m)) 56 for k := range m.m { 57 keys = append(keys, k) 58 } 59 sort.Slice(keys, func(i, j int) bool { return keys[i].Name() < keys[j].Name() }) 60 61 var buffer bytes.Buffer 62 buffer.WriteString("{ ") 63 for _, k := range keys { 64 buffer.WriteString(fmt.Sprintf("{%v %v}", k.name, m.m[k])) 65 } 66 buffer.WriteString(" }") 67 return buffer.String() 68} 69 70func (m *Map) insert(k Key, v string, md metadatas) { 71 if _, ok := m.m[k]; ok { 72 return 73 } 74 m.m[k] = tagContent{value: v, m: md} 75} 76 77func (m *Map) update(k Key, v string, md metadatas) { 78 if _, ok := m.m[k]; ok { 79 m.m[k] = tagContent{value: v, m: md} 80 } 81} 82 83func (m *Map) upsert(k Key, v string, md metadatas) { 84 m.m[k] = tagContent{value: v, m: md} 85} 86 87func (m *Map) delete(k Key) { 88 delete(m.m, k) 89} 90 91func newMap() *Map { 92 return &Map{m: make(map[Key]tagContent)} 93} 94 95// Mutator modifies a tag map. 96type Mutator interface { 97 Mutate(t *Map) (*Map, error) 98} 99 100// Insert returns a mutator that inserts a 101// value associated with k. If k already exists in the tag map, 102// mutator doesn't update the value. 103// Metadata applies metadata to the tag. It is optional. 104// Metadatas are applied in the order in which it is provided. 105// If more than one metadata updates the same attribute then 106// the update from the last metadata prevails. 107func Insert(k Key, v string, mds ...Metadata) Mutator { 108 return &mutator{ 109 fn: func(m *Map) (*Map, error) { 110 if !checkValue(v) { 111 return nil, errInvalidValue 112 } 113 m.insert(k, v, createMetadatas(mds...)) 114 return m, nil 115 }, 116 } 117} 118 119// Update returns a mutator that updates the 120// value of the tag associated with k with v. If k doesn't 121// exists in the tag map, the mutator doesn't insert the value. 122// Metadata applies metadata to the tag. It is optional. 123// Metadatas are applied in the order in which it is provided. 124// If more than one metadata updates the same attribute then 125// the update from the last metadata prevails. 126func Update(k Key, v string, mds ...Metadata) Mutator { 127 return &mutator{ 128 fn: func(m *Map) (*Map, error) { 129 if !checkValue(v) { 130 return nil, errInvalidValue 131 } 132 m.update(k, v, createMetadatas(mds...)) 133 return m, nil 134 }, 135 } 136} 137 138// Upsert returns a mutator that upserts the 139// value of the tag associated with k with v. It inserts the 140// value if k doesn't exist already. It mutates the value 141// if k already exists. 142// Metadata applies metadata to the tag. It is optional. 143// Metadatas are applied in the order in which it is provided. 144// If more than one metadata updates the same attribute then 145// the update from the last metadata prevails. 146func Upsert(k Key, v string, mds ...Metadata) Mutator { 147 return &mutator{ 148 fn: func(m *Map) (*Map, error) { 149 if !checkValue(v) { 150 return nil, errInvalidValue 151 } 152 m.upsert(k, v, createMetadatas(mds...)) 153 return m, nil 154 }, 155 } 156} 157 158func createMetadatas(mds ...Metadata) metadatas { 159 var metas metadatas 160 if len(mds) > 0 { 161 for _, md := range mds { 162 if md != nil { 163 md(&metas) 164 } 165 } 166 } else { 167 WithTTL(TTLUnlimitedPropagation)(&metas) 168 } 169 return metas 170 171} 172 173// Delete returns a mutator that deletes 174// the value associated with k. 175func Delete(k Key) Mutator { 176 return &mutator{ 177 fn: func(m *Map) (*Map, error) { 178 m.delete(k) 179 return m, nil 180 }, 181 } 182} 183 184// New returns a new context that contains a tag map 185// originated from the incoming context and modified 186// with the provided mutators. 187func New(ctx context.Context, mutator ...Mutator) (context.Context, error) { 188 m := newMap() 189 orig := FromContext(ctx) 190 if orig != nil { 191 for k, v := range orig.m { 192 if !checkKeyName(k.Name()) { 193 return ctx, fmt.Errorf("key:%q: %v", k, errInvalidKeyName) 194 } 195 if !checkValue(v.value) { 196 return ctx, fmt.Errorf("key:%q value:%q: %v", k.Name(), v, errInvalidValue) 197 } 198 m.insert(k, v.value, v.m) 199 } 200 } 201 var err error 202 for _, mod := range mutator { 203 m, err = mod.Mutate(m) 204 if err != nil { 205 return ctx, err 206 } 207 } 208 return NewContext(ctx, m), nil 209} 210 211// Do is similar to pprof.Do: a convenience for installing the tags 212// from the context as Go profiler labels. This allows you to 213// correlated runtime profiling with stats. 214// 215// It converts the key/values from the given map to Go profiler labels 216// and calls pprof.Do. 217// 218// Do is going to do nothing if your Go version is below 1.9. 219func Do(ctx context.Context, f func(ctx context.Context)) { 220 do(ctx, f) 221} 222 223type mutator struct { 224 fn func(t *Map) (*Map, error) 225} 226 227func (m *mutator) Mutate(t *Map) (*Map, error) { 228 return m.fn(t) 229} 230