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