...

Source file src/go.opencensus.io/tag/map.go

Documentation: go.opencensus.io/tag

     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  
    16  package tag
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"fmt"
    22  	"sort"
    23  )
    24  
    25  // Tag is a key value pair that can be propagated on wire.
    26  type Tag struct {
    27  	Key   Key
    28  	Value string
    29  }
    30  
    31  type 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.
    38  type Map struct {
    39  	m map[Key]tagContent
    40  }
    41  
    42  // Value returns the value for the key if a value for the key exists.
    43  func (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  
    51  func (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  
    70  func (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  
    77  func (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  
    83  func (m *Map) upsert(k Key, v string, md metadatas) {
    84  	m.m[k] = tagContent{value: v, m: md}
    85  }
    86  
    87  func (m *Map) delete(k Key) {
    88  	delete(m.m, k)
    89  }
    90  
    91  func newMap() *Map {
    92  	return &Map{m: make(map[Key]tagContent)}
    93  }
    94  
    95  // Mutator modifies a tag map.
    96  type 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.
   107  func 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.
   126  func 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.
   146  func 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  
   158  func 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.
   175  func 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.
   187  func 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.
   219  func Do(ctx context.Context, f func(ctx context.Context)) {
   220  	do(ctx, f)
   221  }
   222  
   223  type mutator struct {
   224  	fn func(t *Map) (*Map, error)
   225  }
   226  
   227  func (m *mutator) Mutate(t *Map) (*Map, error) {
   228  	return m.fn(t)
   229  }
   230  

View as plain text