1
0
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:
Krzesimir Nowak 2019-08-23 18:01:52 +02:00 committed by rghetia
parent fafb3daf1e
commit 670b0365d8
6 changed files with 143 additions and 130 deletions

View File

@ -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)
}

View File

@ -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
View 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,
},
}
}

View File

@ -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() {

View File

@ -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) {

View File

@ -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())
}