mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-03-03 14:52:56 +02:00
Make tag.Map a concrete type. (#89)
This is to make tag.Map an immutable type, so it is safe to use concurrently. The safety is not yet fully achieved because of the functions returning contents of the map (Value and Foreach). The functions give callers an access to core.Value objects, which contain a byte slice, which has pointer like semantics. So to avoid accidental changes, we will need to copy the value if it is of BYTES type. Fixes #59
This commit is contained in:
parent
fafb3daf1e
commit
670b0365d8
@ -16,8 +16,7 @@ package tag
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
"runtime/pprof"
|
||||
)
|
||||
|
||||
type ctxTagsType struct{}
|
||||
@ -26,56 +25,6 @@ var (
|
||||
ctxTagsKey = &ctxTagsType{}
|
||||
)
|
||||
|
||||
type MutatorOp int
|
||||
|
||||
const (
|
||||
INSERT MutatorOp = iota
|
||||
UPDATE
|
||||
UPSERT
|
||||
DELETE
|
||||
)
|
||||
|
||||
type Mutator struct {
|
||||
MutatorOp
|
||||
core.KeyValue
|
||||
MeasureMetadata
|
||||
}
|
||||
|
||||
type MeasureMetadata struct {
|
||||
TTL int // -1 == infinite, 0 == do not propagate
|
||||
}
|
||||
|
||||
func (m Mutator) WithTTL(hops int) Mutator {
|
||||
m.TTL = hops
|
||||
return m
|
||||
}
|
||||
|
||||
type MapUpdate struct {
|
||||
SingleKV core.KeyValue
|
||||
MultiKV []core.KeyValue
|
||||
SingleMutator Mutator
|
||||
MultiMutator []Mutator
|
||||
}
|
||||
|
||||
type Map interface {
|
||||
Apply(MapUpdate) Map
|
||||
|
||||
Value(core.Key) (core.Value, bool)
|
||||
HasValue(core.Key) bool
|
||||
|
||||
Len() int
|
||||
|
||||
Foreach(func(kv core.KeyValue) bool)
|
||||
}
|
||||
|
||||
func NewEmptyMap() Map {
|
||||
return tagMap{}
|
||||
}
|
||||
|
||||
func NewMap(update MapUpdate) Map {
|
||||
return NewEmptyMap().Apply(update)
|
||||
}
|
||||
|
||||
func WithMap(ctx context.Context, m Map) context.Context {
|
||||
return context.WithValue(ctx, ctxTagsKey, m)
|
||||
}
|
||||
@ -90,5 +39,16 @@ func FromContext(ctx context.Context) Map {
|
||||
if m, ok := ctx.Value(ctxTagsKey).(Map); ok {
|
||||
return m
|
||||
}
|
||||
return tagMap{}
|
||||
return NewEmptyMap()
|
||||
}
|
||||
|
||||
// Note: the golang pprof.Do API forces this memory allocation, we
|
||||
// should file an issue about that. (There's a TODO in the source.)
|
||||
func Do(ctx context.Context, f func(ctx context.Context)) {
|
||||
m := FromContext(ctx)
|
||||
keyvals := make([]string, 0, 2*len(m.m))
|
||||
for k, v := range m.m {
|
||||
keyvals = append(keyvals, k.Variable.Name, v.value.Emit())
|
||||
}
|
||||
pprof.Do(ctx, pprof.Labels(keyvals...), f)
|
||||
}
|
127
api/tag/map.go
127
api/tag/map.go
@ -15,64 +15,91 @@
|
||||
package tag
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime/pprof"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
)
|
||||
|
||||
type MeasureMetadata struct {
|
||||
TTL int // -1 == infinite, 0 == do not propagate
|
||||
}
|
||||
|
||||
type tagContent struct {
|
||||
value core.Value
|
||||
meta MeasureMetadata
|
||||
}
|
||||
|
||||
type tagMap map[core.Key]tagContent
|
||||
type rawMap map[core.Key]tagContent
|
||||
|
||||
var _ Map = tagMap{}
|
||||
type Map struct {
|
||||
m rawMap
|
||||
}
|
||||
|
||||
func (t tagMap) Apply(update MapUpdate) Map {
|
||||
m := make(tagMap, len(t)+len(update.MultiKV)+len(update.MultiMutator))
|
||||
for k, v := range t {
|
||||
m[k] = v
|
||||
type MapUpdate struct {
|
||||
SingleKV core.KeyValue
|
||||
MultiKV []core.KeyValue
|
||||
SingleMutator Mutator
|
||||
MultiMutator []Mutator
|
||||
}
|
||||
|
||||
func newMap(raw rawMap) Map {
|
||||
return Map{
|
||||
m: raw,
|
||||
}
|
||||
}
|
||||
|
||||
func NewEmptyMap() Map {
|
||||
return newMap(nil)
|
||||
}
|
||||
|
||||
func NewMap(update MapUpdate) Map {
|
||||
return NewEmptyMap().Apply(update)
|
||||
}
|
||||
|
||||
func (m Map) Apply(update MapUpdate) Map {
|
||||
r := make(rawMap, len(m.m)+len(update.MultiKV)+len(update.MultiMutator))
|
||||
for k, v := range m.m {
|
||||
r[k] = v
|
||||
}
|
||||
if update.SingleKV.Key.Defined() {
|
||||
m[update.SingleKV.Key] = tagContent{
|
||||
r[update.SingleKV.Key] = tagContent{
|
||||
value: update.SingleKV.Value,
|
||||
}
|
||||
}
|
||||
for _, kv := range update.MultiKV {
|
||||
m[kv.Key] = tagContent{
|
||||
r[kv.Key] = tagContent{
|
||||
value: kv.Value,
|
||||
}
|
||||
}
|
||||
if update.SingleMutator.Key.Defined() {
|
||||
m.apply(update.SingleMutator)
|
||||
r.apply(update.SingleMutator)
|
||||
}
|
||||
for _, mutator := range update.MultiMutator {
|
||||
m.apply(mutator)
|
||||
r.apply(mutator)
|
||||
}
|
||||
return m
|
||||
if len(r) == 0 {
|
||||
r = nil
|
||||
}
|
||||
return newMap(r)
|
||||
}
|
||||
|
||||
func (m tagMap) Value(k core.Key) (core.Value, bool) {
|
||||
entry, ok := m[k]
|
||||
func (m Map) Value(k core.Key) (core.Value, bool) {
|
||||
entry, ok := m.m[k]
|
||||
if !ok {
|
||||
entry.value.Type = core.INVALID
|
||||
}
|
||||
return entry.value, ok
|
||||
}
|
||||
|
||||
func (m tagMap) HasValue(k core.Key) bool {
|
||||
func (m Map) HasValue(k core.Key) bool {
|
||||
_, has := m.Value(k)
|
||||
return has
|
||||
}
|
||||
|
||||
func (m tagMap) Len() int {
|
||||
return len(m)
|
||||
func (m Map) Len() int {
|
||||
return len(m.m)
|
||||
}
|
||||
|
||||
func (m tagMap) Foreach(f func(kv core.KeyValue) bool) {
|
||||
for k, v := range m {
|
||||
func (m Map) Foreach(f func(kv core.KeyValue) bool) {
|
||||
for k, v := range m.m {
|
||||
if !f(core.KeyValue{
|
||||
Key: k,
|
||||
Value: v.value,
|
||||
@ -82,10 +109,7 @@ func (m tagMap) Foreach(f func(kv core.KeyValue) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (m tagMap) apply(mutator Mutator) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
func (r rawMap) apply(mutator Mutator) {
|
||||
key := mutator.KeyValue.Key
|
||||
content := tagContent{
|
||||
value: mutator.KeyValue.Value,
|
||||
@ -93,57 +117,16 @@ func (m tagMap) apply(mutator Mutator) {
|
||||
}
|
||||
switch mutator.MutatorOp {
|
||||
case INSERT:
|
||||
if _, ok := m[key]; !ok {
|
||||
m[key] = content
|
||||
if _, ok := r[key]; !ok {
|
||||
r[key] = content
|
||||
}
|
||||
case UPDATE:
|
||||
if _, ok := m[key]; ok {
|
||||
m[key] = content
|
||||
if _, ok := r[key]; ok {
|
||||
r[key] = content
|
||||
}
|
||||
case UPSERT:
|
||||
m[key] = content
|
||||
r[key] = content
|
||||
case DELETE:
|
||||
delete(m, key)
|
||||
delete(r, key)
|
||||
}
|
||||
}
|
||||
|
||||
func Insert(kv core.KeyValue) Mutator {
|
||||
return Mutator{
|
||||
MutatorOp: INSERT,
|
||||
KeyValue: kv,
|
||||
}
|
||||
}
|
||||
|
||||
func Update(kv core.KeyValue) Mutator {
|
||||
return Mutator{
|
||||
MutatorOp: UPDATE,
|
||||
KeyValue: kv,
|
||||
}
|
||||
}
|
||||
|
||||
func Upsert(kv core.KeyValue) Mutator {
|
||||
return Mutator{
|
||||
MutatorOp: UPSERT,
|
||||
KeyValue: kv,
|
||||
}
|
||||
}
|
||||
|
||||
func Delete(k core.Key) Mutator {
|
||||
return Mutator{
|
||||
MutatorOp: DELETE,
|
||||
KeyValue: core.KeyValue{
|
||||
Key: k,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Note: the golang pprof.Do API forces this memory allocation, we
|
||||
// should file an issue about that. (There's a TODO in the source.)
|
||||
func Do(ctx context.Context, f func(ctx context.Context)) {
|
||||
m := FromContext(ctx).(tagMap)
|
||||
keyvals := make([]string, 0, 2*len(m))
|
||||
for k, v := range m {
|
||||
keyvals = append(keyvals, k.Variable.Name, v.value.Emit())
|
||||
}
|
||||
pprof.Do(ctx, pprof.Labels(keyvals...), f)
|
||||
}
|
||||
|
69
api/tag/mutator.go
Normal file
69
api/tag/mutator.go
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tag
|
||||
|
||||
import (
|
||||
"go.opentelemetry.io/api/core"
|
||||
)
|
||||
|
||||
type MutatorOp int
|
||||
|
||||
const (
|
||||
INSERT MutatorOp = iota
|
||||
UPDATE
|
||||
UPSERT
|
||||
DELETE
|
||||
)
|
||||
|
||||
type Mutator struct {
|
||||
MutatorOp
|
||||
core.KeyValue
|
||||
MeasureMetadata
|
||||
}
|
||||
|
||||
func (m Mutator) WithTTL(hops int) Mutator {
|
||||
m.TTL = hops
|
||||
return m
|
||||
}
|
||||
|
||||
func Insert(kv core.KeyValue) Mutator {
|
||||
return Mutator{
|
||||
MutatorOp: INSERT,
|
||||
KeyValue: kv,
|
||||
}
|
||||
}
|
||||
|
||||
func Update(kv core.KeyValue) Mutator {
|
||||
return Mutator{
|
||||
MutatorOp: UPDATE,
|
||||
KeyValue: kv,
|
||||
}
|
||||
}
|
||||
|
||||
func Upsert(kv core.KeyValue) Mutator {
|
||||
return Mutator{
|
||||
MutatorOp: UPSERT,
|
||||
KeyValue: kv,
|
||||
}
|
||||
}
|
||||
|
||||
func Delete(k core.Key) Mutator {
|
||||
return Mutator{
|
||||
MutatorOp: DELETE,
|
||||
KeyValue: core.KeyValue{
|
||||
Key: k,
|
||||
},
|
||||
}
|
||||
}
|
@ -55,7 +55,7 @@ func AppendEvent(buf *strings.Builder, data reader.Event) {
|
||||
} else {
|
||||
buf.WriteString(" <")
|
||||
f(false)(parentSpanIDKey.String(data.Parent.SpanIDString()))
|
||||
if data.ParentAttributes != nil {
|
||||
if data.ParentAttributes.Len() > 0 {
|
||||
data.ParentAttributes.Foreach(f(false))
|
||||
}
|
||||
buf.WriteString(" >")
|
||||
@ -113,10 +113,10 @@ func AppendEvent(buf *strings.Builder, data reader.Event) {
|
||||
|
||||
// Attach the scope (span) attributes and context tags.
|
||||
buf.WriteString(" [")
|
||||
if data.Attributes != nil {
|
||||
if data.Attributes.Len() > 0 {
|
||||
data.Attributes.Foreach(f(false))
|
||||
}
|
||||
if data.Tags != nil {
|
||||
if data.Tags.Len() > 0 {
|
||||
data.Tags.Foreach(f(true))
|
||||
}
|
||||
if data.SpanContext.HasSpanID() {
|
||||
|
@ -311,7 +311,7 @@ func (ro *readerObserver) addMeasurement(e *Event, m stats.Measurement) {
|
||||
|
||||
func (ro *readerObserver) readMeasureScope(m stats.Measure) (tag.Map, *readerSpan) {
|
||||
// TODO
|
||||
return nil, nil
|
||||
return tag.NewEmptyMap(), nil
|
||||
}
|
||||
|
||||
func (ro *readerObserver) readScope(id observer.ScopeID) (tag.Map, *readerSpan) {
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
"go.opentelemetry.io/api/tag"
|
||||
apitrace "go.opentelemetry.io/api/trace"
|
||||
)
|
||||
|
||||
@ -94,5 +95,5 @@ func (tr *tracer) WithComponent(component string) apitrace.Tracer {
|
||||
}
|
||||
|
||||
func (tr *tracer) Inject(ctx context.Context, span apitrace.Span, injector apitrace.Injector) {
|
||||
injector.Inject(span.SpanContext(), nil)
|
||||
injector.Inject(span.SpanContext(), tag.NewEmptyMap())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user