1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-01-20 03:30:02 +02:00

Merge remote-tracking branch 'upstream/master' into otlp

This commit is contained in:
Tyler Yahn 2020-05-29 10:26:41 -07:00
commit b2ec53f483
No known key found for this signature in database
GPG Key ID: 42AA23B0BC85B798
67 changed files with 1853 additions and 352 deletions

View File

@ -12,7 +12,7 @@ depending on the master. So it is not a concern.
4. Create a PR on github and merge the PR once approved.
```
./pre-release.sh -t <new tag>
./pre_release.sh -t <new tag>
git diff master
git push
```

View File

@ -86,12 +86,12 @@ func TestDirect(t *testing.T) {
valuerecorder.Record(ctx, 1, labels1...)
valuerecorder.Record(ctx, 2, labels1...)
_ = Must(meter1).RegisterFloat64ValueObserver("test.valueobserver.float", func(_ context.Context, result metric.Float64ObserverResult) {
_ = Must(meter1).NewFloat64ValueObserver("test.valueobserver.float", func(_ context.Context, result metric.Float64ObserverResult) {
result.Observe(1., labels1...)
result.Observe(2., labels2...)
})
_ = Must(meter1).RegisterInt64ValueObserver("test.valueobserver.int", func(_ context.Context, result metric.Int64ObserverResult) {
_ = Must(meter1).NewInt64ValueObserver("test.valueobserver.int", func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(1, labels1...)
result.Observe(2, labels2...)
})
@ -331,7 +331,7 @@ func TestImplementationIndirection(t *testing.T) {
require.False(t, ok)
// Async: no SDK yet
valueobserver := Must(meter1).RegisterFloat64ValueObserver(
valueobserver := Must(meter1).NewFloat64ValueObserver(
"interface.valueobserver",
func(_ context.Context, result metric.Float64ObserverResult) {},
)

View File

@ -44,10 +44,10 @@ var (
return unwrap(MeterProvider().Meter(libraryName).NewFloat64ValueRecorder(name))
},
"valueobserver.int64": func(name, libraryName string) (metric.InstrumentImpl, error) {
return unwrap(MeterProvider().Meter(libraryName).RegisterInt64ValueObserver(name, func(context.Context, metric.Int64ObserverResult) {}))
return unwrap(MeterProvider().Meter(libraryName).NewInt64ValueObserver(name, func(context.Context, metric.Int64ObserverResult) {}))
},
"valueobserver.float64": func(name, libraryName string) (metric.InstrumentImpl, error) {
return unwrap(MeterProvider().Meter(libraryName).RegisterFloat64ValueObserver(name, func(context.Context, metric.Float64ObserverResult) {}))
return unwrap(MeterProvider().Meter(libraryName).NewFloat64ValueObserver(name, func(context.Context, metric.Float64ObserverResult) {}))
},
}
)

View File

@ -25,6 +25,21 @@ type Iterator struct {
idx int
}
// MergeIterator supports iterating over two sets of labels while
// eliminating duplicate values from the combined set. The first
// iterator value takes precedence.
type MergeItererator struct {
one oneIterator
two oneIterator
current kv.KeyValue
}
type oneIterator struct {
iter Iterator
done bool
label kv.KeyValue
}
// Next moves the iterator to the next position. Returns false if there
// are no more labels.
func (i *Iterator) Next() bool {
@ -75,3 +90,63 @@ func (i *Iterator) ToSlice() []kv.KeyValue {
}
return slice
}
// NewMergeIterator returns a MergeIterator for merging two label sets
// Duplicates are resolved by taking the value from the first set.
func NewMergeIterator(s1, s2 *Set) MergeItererator {
mi := MergeItererator{
one: makeOne(s1.Iter()),
two: makeOne(s2.Iter()),
}
return mi
}
func makeOne(iter Iterator) oneIterator {
oi := oneIterator{
iter: iter,
}
oi.advance()
return oi
}
func (oi *oneIterator) advance() {
if oi.done = !oi.iter.Next(); !oi.done {
oi.label = oi.iter.Label()
}
}
// Next returns true if there is another label available.
func (m *MergeItererator) Next() bool {
if m.one.done && m.two.done {
return false
}
if m.one.done {
m.current = m.two.label
m.two.advance()
return true
}
if m.two.done {
m.current = m.one.label
m.one.advance()
return true
}
if m.one.label.Key == m.two.label.Key {
m.current = m.one.label // first iterator label value wins
m.one.advance()
m.two.advance()
return true
}
if m.one.label.Key < m.two.label.Key {
m.current = m.one.label
m.one.advance()
return true
}
m.current = m.two.label
m.two.advance()
return true
}
// Label returns the current value after Next() returns true.
func (m *MergeItererator) Label() kv.KeyValue {
return m.current
}

View File

@ -15,6 +15,7 @@
package label_test
import (
"fmt"
"testing"
"go.opentelemetry.io/otel/api/kv"
@ -55,3 +56,96 @@ func TestEmptyIterator(t *testing.T) {
require.Equal(t, 0, iter.Len())
require.False(t, iter.Next())
}
func TestMergedIterator(t *testing.T) {
type inputs struct {
name string
keys1 []string
keys2 []string
expect []string
}
makeLabels := func(keys []string, num int) (result []kv.KeyValue) {
for _, k := range keys {
result = append(result, kv.Int(k, num))
}
return
}
for _, input := range []inputs{
{
name: "one overlap",
keys1: []string{"A", "B"},
keys2: []string{"B", "C"},
expect: []string{"A/1", "B/1", "C/2"},
},
{
name: "reversed one overlap",
keys1: []string{"B", "A"},
keys2: []string{"C", "B"},
expect: []string{"A/1", "B/1", "C/2"},
},
{
name: "one empty",
keys1: nil,
keys2: []string{"C", "B"},
expect: []string{"B/2", "C/2"},
},
{
name: "two empty",
keys1: []string{"C", "B"},
keys2: nil,
expect: []string{"B/1", "C/1"},
},
{
name: "no overlap both",
keys1: []string{"C"},
keys2: []string{"B"},
expect: []string{"B/2", "C/1"},
},
{
name: "one empty single two",
keys1: nil,
keys2: []string{"B"},
expect: []string{"B/2"},
},
{
name: "two empty single one",
keys1: []string{"A"},
keys2: nil,
expect: []string{"A/1"},
},
{
name: "all empty",
keys1: nil,
keys2: nil,
expect: nil,
},
{
name: "full overlap",
keys1: []string{"A", "B", "C", "D"},
keys2: []string{"A", "B", "C", "D"},
expect: []string{"A/1", "B/1", "C/1", "D/1"},
},
} {
t.Run(input.name, func(t *testing.T) {
labels1 := makeLabels(input.keys1, 1)
labels2 := makeLabels(input.keys2, 2)
set1 := label.NewSet(labels1...)
set2 := label.NewSet(labels2...)
merge := label.NewMergeIterator(&set1, &set2)
var result []string
for merge.Next() {
label := merge.Label()
result = append(result, fmt.Sprint(label.Key, "/", label.Value.Emit()))
}
require.Equal(t, input.expect, result)
})
}
}

View File

@ -183,7 +183,7 @@ func TestObserverInstruments(t *testing.T) {
t.Run("float valueobserver", func(t *testing.T) {
labels := []kv.KeyValue{kv.String("O", "P")}
mockSDK, meter := mockTest.NewMeter()
o := Must(meter).RegisterFloat64ValueObserver("test.valueobserver.float", func(_ context.Context, result metric.Float64ObserverResult) {
o := Must(meter).NewFloat64ValueObserver("test.valueobserver.float", func(_ context.Context, result metric.Float64ObserverResult) {
result.Observe(42.1, labels...)
})
mockSDK.RunAsyncInstruments()
@ -194,7 +194,7 @@ func TestObserverInstruments(t *testing.T) {
t.Run("int valueobserver", func(t *testing.T) {
labels := []kv.KeyValue{}
mockSDK, meter := mockTest.NewMeter()
o := Must(meter).RegisterInt64ValueObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) {
o := Must(meter).NewInt64ValueObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(-142, labels...)
})
mockSDK.RunAsyncInstruments()
@ -205,7 +205,7 @@ func TestObserverInstruments(t *testing.T) {
t.Run("float sumobserver", func(t *testing.T) {
labels := []kv.KeyValue{kv.String("O", "P")}
mockSDK, meter := mockTest.NewMeter()
o := Must(meter).RegisterFloat64SumObserver("test.sumobserver.float", func(_ context.Context, result metric.Float64ObserverResult) {
o := Must(meter).NewFloat64SumObserver("test.sumobserver.float", func(_ context.Context, result metric.Float64ObserverResult) {
result.Observe(42.1, labels...)
})
mockSDK.RunAsyncInstruments()
@ -216,7 +216,7 @@ func TestObserverInstruments(t *testing.T) {
t.Run("int sumobserver", func(t *testing.T) {
labels := []kv.KeyValue{}
mockSDK, meter := mockTest.NewMeter()
o := Must(meter).RegisterInt64SumObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) {
o := Must(meter).NewInt64SumObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(-142, labels...)
})
mockSDK.RunAsyncInstruments()
@ -227,7 +227,7 @@ func TestObserverInstruments(t *testing.T) {
t.Run("float updownsumobserver", func(t *testing.T) {
labels := []kv.KeyValue{kv.String("O", "P")}
mockSDK, meter := mockTest.NewMeter()
o := Must(meter).RegisterFloat64UpDownSumObserver("test.updownsumobserver.float", func(_ context.Context, result metric.Float64ObserverResult) {
o := Must(meter).NewFloat64UpDownSumObserver("test.updownsumobserver.float", func(_ context.Context, result metric.Float64ObserverResult) {
result.Observe(42.1, labels...)
})
mockSDK.RunAsyncInstruments()
@ -238,7 +238,7 @@ func TestObserverInstruments(t *testing.T) {
t.Run("int updownsumobserver", func(t *testing.T) {
labels := []kv.KeyValue{}
mockSDK, meter := mockTest.NewMeter()
o := Must(meter).RegisterInt64UpDownSumObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) {
o := Must(meter).NewInt64UpDownSumObserver("test.observer.int", func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(-142, labels...)
})
mockSDK.RunAsyncInstruments()
@ -309,8 +309,8 @@ func TestBatchObserverInstruments(t *testing.T) {
)
},
)
obs1 = cb.RegisterInt64ValueObserver("test.observer.int")
obs2 = cb.RegisterFloat64ValueObserver("test.observer.float")
obs1 = cb.NewInt64ValueObserver("test.observer.int")
obs2 = cb.NewFloat64ValueObserver("test.observer.float")
mockSDK.RunAsyncInstruments()
@ -394,7 +394,7 @@ func TestWrappedInstrumentError(t *testing.T) {
require.Equal(t, err, metric.ErrSDKReturnedNilImpl)
require.NotNil(t, valuerecorder.SyncImpl())
observer, err := meter.RegisterInt64ValueObserver("test.observer", func(_ context.Context, result metric.Int64ObserverResult) {})
observer, err := meter.NewInt64ValueObserver("test.observer", func(_ context.Context, result metric.Int64ObserverResult) {})
require.NotNil(t, err)
require.NotNil(t, observer.AsyncImpl())
@ -404,7 +404,7 @@ func TestNilCallbackObserverNoop(t *testing.T) {
// Tests that a nil callback yields a no-op observer without error.
_, meter := mockTest.NewMeter()
observer := Must(meter).RegisterInt64ValueObserver("test.observer", nil)
observer := Must(meter).NewInt64ValueObserver("test.observer", nil)
_, ok := observer.AsyncImpl().(metric.NoopAsync)
require.True(t, ok)

View File

@ -33,7 +33,7 @@
// and the asynchronous instruments are:
//
// SumObserver: additive, monotonic
// UpDownSumOnserver: additive
// UpDownSumObserver: additive
// ValueObserver: non-additive
//
// All instruments are provided with support for either float64 or

View File

@ -118,11 +118,11 @@ func (m Meter) NewFloat64ValueRecorder(name string, opts ...Option) (Float64Valu
m.newSync(name, ValueRecorderKind, Float64NumberKind, opts))
}
// RegisterInt64ValueObserver creates a new integer ValueObserver instrument
// NewInt64ValueObserver creates a new integer ValueObserver instrument
// with the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) RegisterInt64ValueObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64ValueObserver, error) {
func (m Meter) NewInt64ValueObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64ValueObserver, error) {
if callback == nil {
return wrapInt64ValueObserverInstrument(NoopAsync{}, nil)
}
@ -131,11 +131,11 @@ func (m Meter) RegisterInt64ValueObserver(name string, callback Int64ObserverCal
newInt64AsyncRunner(callback)))
}
// RegisterFloat64ValueObserver creates a new floating point ValueObserver with
// NewFloat64ValueObserver creates a new floating point ValueObserver with
// the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) RegisterFloat64ValueObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64ValueObserver, error) {
func (m Meter) NewFloat64ValueObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64ValueObserver, error) {
if callback == nil {
return wrapFloat64ValueObserverInstrument(NoopAsync{}, nil)
}
@ -144,11 +144,11 @@ func (m Meter) RegisterFloat64ValueObserver(name string, callback Float64Observe
newFloat64AsyncRunner(callback)))
}
// RegisterInt64SumObserver creates a new integer SumObserver instrument
// NewInt64SumObserver creates a new integer SumObserver instrument
// with the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) RegisterInt64SumObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64SumObserver, error) {
func (m Meter) NewInt64SumObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64SumObserver, error) {
if callback == nil {
return wrapInt64SumObserverInstrument(NoopAsync{}, nil)
}
@ -157,11 +157,11 @@ func (m Meter) RegisterInt64SumObserver(name string, callback Int64ObserverCallb
newInt64AsyncRunner(callback)))
}
// RegisterFloat64SumObserver creates a new floating point SumObserver with
// NewFloat64SumObserver creates a new floating point SumObserver with
// the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) RegisterFloat64SumObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64SumObserver, error) {
func (m Meter) NewFloat64SumObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64SumObserver, error) {
if callback == nil {
return wrapFloat64SumObserverInstrument(NoopAsync{}, nil)
}
@ -170,11 +170,11 @@ func (m Meter) RegisterFloat64SumObserver(name string, callback Float64ObserverC
newFloat64AsyncRunner(callback)))
}
// RegisterInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument
// NewInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument
// with the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) RegisterInt64UpDownSumObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64UpDownSumObserver, error) {
func (m Meter) NewInt64UpDownSumObserver(name string, callback Int64ObserverCallback, opts ...Option) (Int64UpDownSumObserver, error) {
if callback == nil {
return wrapInt64UpDownSumObserverInstrument(NoopAsync{}, nil)
}
@ -183,11 +183,11 @@ func (m Meter) RegisterInt64UpDownSumObserver(name string, callback Int64Observe
newInt64AsyncRunner(callback)))
}
// RegisterFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with
// NewFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with
// the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) RegisterFloat64UpDownSumObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64UpDownSumObserver, error) {
func (m Meter) NewFloat64UpDownSumObserver(name string, callback Float64ObserverCallback, opts ...Option) (Float64UpDownSumObserver, error) {
if callback == nil {
return wrapFloat64UpDownSumObserverInstrument(NoopAsync{}, nil)
}
@ -196,11 +196,11 @@ func (m Meter) RegisterFloat64UpDownSumObserver(name string, callback Float64Obs
newFloat64AsyncRunner(callback)))
}
// RegisterInt64ValueObserver creates a new integer ValueObserver instrument
// NewInt64ValueObserver creates a new integer ValueObserver instrument
// with the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) RegisterInt64ValueObserver(name string, opts ...Option) (Int64ValueObserver, error) {
func (b BatchObserver) NewInt64ValueObserver(name string, opts ...Option) (Int64ValueObserver, error) {
if b.runner == nil {
return wrapInt64ValueObserverInstrument(NoopAsync{}, nil)
}
@ -208,11 +208,11 @@ func (b BatchObserver) RegisterInt64ValueObserver(name string, opts ...Option) (
b.meter.newAsync(name, ValueObserverKind, Int64NumberKind, opts, b.runner))
}
// RegisterFloat64ValueObserver creates a new floating point ValueObserver with
// NewFloat64ValueObserver creates a new floating point ValueObserver with
// the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) RegisterFloat64ValueObserver(name string, opts ...Option) (Float64ValueObserver, error) {
func (b BatchObserver) NewFloat64ValueObserver(name string, opts ...Option) (Float64ValueObserver, error) {
if b.runner == nil {
return wrapFloat64ValueObserverInstrument(NoopAsync{}, nil)
}
@ -221,11 +221,11 @@ func (b BatchObserver) RegisterFloat64ValueObserver(name string, opts ...Option)
b.runner))
}
// RegisterInt64SumObserver creates a new integer SumObserver instrument
// NewInt64SumObserver creates a new integer SumObserver instrument
// with the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) RegisterInt64SumObserver(name string, opts ...Option) (Int64SumObserver, error) {
func (b BatchObserver) NewInt64SumObserver(name string, opts ...Option) (Int64SumObserver, error) {
if b.runner == nil {
return wrapInt64SumObserverInstrument(NoopAsync{}, nil)
}
@ -233,11 +233,11 @@ func (b BatchObserver) RegisterInt64SumObserver(name string, opts ...Option) (In
b.meter.newAsync(name, SumObserverKind, Int64NumberKind, opts, b.runner))
}
// RegisterFloat64SumObserver creates a new floating point SumObserver with
// NewFloat64SumObserver creates a new floating point SumObserver with
// the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) RegisterFloat64SumObserver(name string, opts ...Option) (Float64SumObserver, error) {
func (b BatchObserver) NewFloat64SumObserver(name string, opts ...Option) (Float64SumObserver, error) {
if b.runner == nil {
return wrapFloat64SumObserverInstrument(NoopAsync{}, nil)
}
@ -246,11 +246,11 @@ func (b BatchObserver) RegisterFloat64SumObserver(name string, opts ...Option) (
b.runner))
}
// RegisterInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument
// NewInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument
// with the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) RegisterInt64UpDownSumObserver(name string, opts ...Option) (Int64UpDownSumObserver, error) {
func (b BatchObserver) NewInt64UpDownSumObserver(name string, opts ...Option) (Int64UpDownSumObserver, error) {
if b.runner == nil {
return wrapInt64UpDownSumObserverInstrument(NoopAsync{}, nil)
}
@ -258,11 +258,11 @@ func (b BatchObserver) RegisterInt64UpDownSumObserver(name string, opts ...Optio
b.meter.newAsync(name, UpDownSumObserverKind, Int64NumberKind, opts, b.runner))
}
// RegisterFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with
// NewFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with
// the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) RegisterFloat64UpDownSumObserver(name string, opts ...Option) (Float64UpDownSumObserver, error) {
func (b BatchObserver) NewFloat64UpDownSumObserver(name string, opts ...Option) (Float64UpDownSumObserver, error) {
if b.runner == nil {
return wrapFloat64UpDownSumObserverInstrument(NoopAsync{}, nil)
}

View File

@ -93,60 +93,60 @@ func (mm MeterMust) NewFloat64ValueRecorder(name string, mos ...Option) Float64V
}
}
// RegisterInt64ValueObserver calls `Meter.RegisterInt64ValueObserver` and
// NewInt64ValueObserver calls `Meter.NewInt64ValueObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) RegisterInt64ValueObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64ValueObserver {
if inst, err := mm.meter.RegisterInt64ValueObserver(name, callback, oos...); err != nil {
func (mm MeterMust) NewInt64ValueObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64ValueObserver {
if inst, err := mm.meter.NewInt64ValueObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// RegisterFloat64ValueObserver calls `Meter.RegisterFloat64ValueObserver` and
// NewFloat64ValueObserver calls `Meter.NewFloat64ValueObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) RegisterFloat64ValueObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64ValueObserver {
if inst, err := mm.meter.RegisterFloat64ValueObserver(name, callback, oos...); err != nil {
func (mm MeterMust) NewFloat64ValueObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64ValueObserver {
if inst, err := mm.meter.NewFloat64ValueObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// RegisterInt64SumObserver calls `Meter.RegisterInt64SumObserver` and
// NewInt64SumObserver calls `Meter.NewInt64SumObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) RegisterInt64SumObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64SumObserver {
if inst, err := mm.meter.RegisterInt64SumObserver(name, callback, oos...); err != nil {
func (mm MeterMust) NewInt64SumObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64SumObserver {
if inst, err := mm.meter.NewInt64SumObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// RegisterFloat64SumObserver calls `Meter.RegisterFloat64SumObserver` and
// NewFloat64SumObserver calls `Meter.NewFloat64SumObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) RegisterFloat64SumObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64SumObserver {
if inst, err := mm.meter.RegisterFloat64SumObserver(name, callback, oos...); err != nil {
func (mm MeterMust) NewFloat64SumObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64SumObserver {
if inst, err := mm.meter.NewFloat64SumObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// RegisterInt64UpDownSumObserver calls `Meter.RegisterInt64UpDownSumObserver` and
// NewInt64UpDownSumObserver calls `Meter.NewInt64UpDownSumObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) RegisterInt64UpDownSumObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64UpDownSumObserver {
if inst, err := mm.meter.RegisterInt64UpDownSumObserver(name, callback, oos...); err != nil {
func (mm MeterMust) NewInt64UpDownSumObserver(name string, callback Int64ObserverCallback, oos ...Option) Int64UpDownSumObserver {
if inst, err := mm.meter.NewInt64UpDownSumObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// RegisterFloat64UpDownSumObserver calls `Meter.RegisterFloat64UpDownSumObserver` and
// NewFloat64UpDownSumObserver calls `Meter.NewFloat64UpDownSumObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) RegisterFloat64UpDownSumObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64UpDownSumObserver {
if inst, err := mm.meter.RegisterFloat64UpDownSumObserver(name, callback, oos...); err != nil {
func (mm MeterMust) NewFloat64UpDownSumObserver(name string, callback Float64ObserverCallback, oos ...Option) Float64UpDownSumObserver {
if inst, err := mm.meter.NewFloat64UpDownSumObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
@ -161,60 +161,60 @@ func (mm MeterMust) NewBatchObserver(callback BatchObserverCallback) BatchObserv
}
}
// RegisterInt64ValueObserver calls `BatchObserver.RegisterInt64ValueObserver` and
// NewInt64ValueObserver calls `BatchObserver.NewInt64ValueObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) RegisterInt64ValueObserver(name string, oos ...Option) Int64ValueObserver {
if inst, err := bm.batch.RegisterInt64ValueObserver(name, oos...); err != nil {
func (bm BatchObserverMust) NewInt64ValueObserver(name string, oos ...Option) Int64ValueObserver {
if inst, err := bm.batch.NewInt64ValueObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// RegisterFloat64ValueObserver calls `BatchObserver.RegisterFloat64ValueObserver` and
// NewFloat64ValueObserver calls `BatchObserver.NewFloat64ValueObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) RegisterFloat64ValueObserver(name string, oos ...Option) Float64ValueObserver {
if inst, err := bm.batch.RegisterFloat64ValueObserver(name, oos...); err != nil {
func (bm BatchObserverMust) NewFloat64ValueObserver(name string, oos ...Option) Float64ValueObserver {
if inst, err := bm.batch.NewFloat64ValueObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// RegisterInt64SumObserver calls `BatchObserver.RegisterInt64SumObserver` and
// NewInt64SumObserver calls `BatchObserver.NewInt64SumObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) RegisterInt64SumObserver(name string, oos ...Option) Int64SumObserver {
if inst, err := bm.batch.RegisterInt64SumObserver(name, oos...); err != nil {
func (bm BatchObserverMust) NewInt64SumObserver(name string, oos ...Option) Int64SumObserver {
if inst, err := bm.batch.NewInt64SumObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// RegisterFloat64SumObserver calls `BatchObserver.RegisterFloat64SumObserver` and
// NewFloat64SumObserver calls `BatchObserver.NewFloat64SumObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) RegisterFloat64SumObserver(name string, oos ...Option) Float64SumObserver {
if inst, err := bm.batch.RegisterFloat64SumObserver(name, oos...); err != nil {
func (bm BatchObserverMust) NewFloat64SumObserver(name string, oos ...Option) Float64SumObserver {
if inst, err := bm.batch.NewFloat64SumObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// RegisterInt64UpDownSumObserver calls `BatchObserver.RegisterInt64UpDownSumObserver` and
// NewInt64UpDownSumObserver calls `BatchObserver.NewInt64UpDownSumObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) RegisterInt64UpDownSumObserver(name string, oos ...Option) Int64UpDownSumObserver {
if inst, err := bm.batch.RegisterInt64UpDownSumObserver(name, oos...); err != nil {
func (bm BatchObserverMust) NewInt64UpDownSumObserver(name string, oos ...Option) Int64UpDownSumObserver {
if inst, err := bm.batch.NewInt64UpDownSumObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// RegisterFloat64UpDownSumObserver calls `BatchObserver.RegisterFloat64UpDownSumObserver` and
// NewFloat64UpDownSumObserver calls `BatchObserver.NewFloat64UpDownSumObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) RegisterFloat64UpDownSumObserver(name string, oos ...Option) Float64UpDownSumObserver {
if inst, err := bm.batch.RegisterFloat64UpDownSumObserver(name, oos...); err != nil {
func (bm BatchObserverMust) NewFloat64UpDownSumObserver(name string, oos ...Option) Float64UpDownSumObserver {
if inst, err := bm.batch.NewFloat64UpDownSumObserver(name, oos...); err != nil {
panic(err)
} else {
return inst

View File

@ -45,10 +45,10 @@ var (
return unwrap(m.NewFloat64ValueRecorder(name))
},
"valueobserver.int64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) {
return unwrap(m.RegisterInt64ValueObserver(name, func(context.Context, metric.Int64ObserverResult) {}))
return unwrap(m.NewInt64ValueObserver(name, func(context.Context, metric.Int64ObserverResult) {}))
},
"valueobserver.float64": func(m metric.Meter, name string) (metric.InstrumentImpl, error) {
return unwrap(m.RegisterFloat64ValueObserver(name, func(context.Context, metric.Float64ObserverResult) {}))
return unwrap(m.NewFloat64ValueObserver(name, func(context.Context, metric.Float64ObserverResult) {}))
},
}
)

303
api/standard/http.go Normal file
View File

@ -0,0 +1,303 @@
// Copyright The 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 standard
import (
"fmt"
"net"
"net/http"
"strconv"
"strings"
"google.golang.org/grpc/codes"
"go.opentelemetry.io/otel/api/kv"
)
// NetAttributesFromHTTPRequest generates attributes of the net
// namespace as specified by the OpenTelemetry specification for a
// span. The network parameter is a string that net.Dial function
// from standard library can understand.
func NetAttributesFromHTTPRequest(network string, request *http.Request) []kv.KeyValue {
attrs := []kv.KeyValue{}
switch network {
case "tcp", "tcp4", "tcp6":
attrs = append(attrs, NetTransportTCP)
case "udp", "udp4", "udp6":
attrs = append(attrs, NetTransportUDP)
case "ip", "ip4", "ip6":
attrs = append(attrs, NetTransportIP)
case "unix", "unixgram", "unixpacket":
attrs = append(attrs, NetTransportUnix)
default:
attrs = append(attrs, NetTransportOther)
}
peerName, peerIP, peerPort := "", "", 0
{
hostPart := request.RemoteAddr
portPart := ""
if idx := strings.LastIndex(hostPart, ":"); idx >= 0 {
hostPart = request.RemoteAddr[:idx]
portPart = request.RemoteAddr[idx+1:]
}
if hostPart != "" {
if ip := net.ParseIP(hostPart); ip != nil {
peerIP = ip.String()
} else {
peerName = hostPart
}
if portPart != "" {
numPort, err := strconv.ParseUint(portPart, 10, 16)
if err == nil {
peerPort = (int)(numPort)
} else {
peerName, peerIP = "", ""
}
}
}
}
if peerName != "" {
attrs = append(attrs, NetPeerNameKey.String(peerName))
}
if peerIP != "" {
attrs = append(attrs, NetPeerIPKey.String(peerIP))
}
if peerPort != 0 {
attrs = append(attrs, NetPeerPortKey.Int(peerPort))
}
hostIP, hostName, hostPort := "", "", 0
for _, someHost := range []string{request.Host, request.Header.Get("Host"), request.URL.Host} {
hostPart := ""
if idx := strings.LastIndex(someHost, ":"); idx >= 0 {
strPort := someHost[idx+1:]
numPort, err := strconv.ParseUint(strPort, 10, 16)
if err == nil {
hostPort = (int)(numPort)
}
hostPart = someHost[:idx]
} else {
hostPart = someHost
}
if hostPart != "" {
ip := net.ParseIP(hostPart)
if ip != nil {
hostIP = ip.String()
} else {
hostName = hostPart
}
break
} else {
hostPort = 0
}
}
if hostIP != "" {
attrs = append(attrs, NetHostIPKey.String(hostIP))
}
if hostName != "" {
attrs = append(attrs, NetHostNameKey.String(hostName))
}
if hostPort != 0 {
attrs = append(attrs, NetHostPortKey.Int(hostPort))
}
return attrs
}
// EndUserAttributesFromHTTPRequest generates attributes of the
// enduser namespace as specified by the OpenTelemetry specification
// for a span.
func EndUserAttributesFromHTTPRequest(request *http.Request) []kv.KeyValue {
if username, _, ok := request.BasicAuth(); ok {
return []kv.KeyValue{EnduserIDKey.String(username)}
}
return nil
}
// HTTPClientAttributesFromHTTPRequest generates attributes of the
// http namespace as specified by the OpenTelemetry specification for
// a span on the client side.
func HTTPClientAttributesFromHTTPRequest(request *http.Request) []kv.KeyValue {
attrs := []kv.KeyValue{}
if request.Method != "" {
attrs = append(attrs, HTTPMethodKey.String(request.Method))
} else {
attrs = append(attrs, HTTPMethodKey.String(http.MethodGet))
}
attrs = append(attrs, HTTPUrlKey.String(request.URL.String()))
return append(attrs, httpCommonAttributesFromHTTPRequest(request)...)
}
func httpCommonAttributesFromHTTPRequest(request *http.Request) []kv.KeyValue {
attrs := []kv.KeyValue{}
if request.TLS != nil {
attrs = append(attrs, HTTPSchemeHTTPS)
} else {
attrs = append(attrs, HTTPSchemeHTTP)
}
if request.Host != "" {
attrs = append(attrs, HTTPHostKey.String(request.Host))
}
if ua := request.UserAgent(); ua != "" {
attrs = append(attrs, HTTPUserAgentKey.String(ua))
}
flavor := ""
if request.ProtoMajor == 1 {
flavor = fmt.Sprintf("1.%d", request.ProtoMinor)
} else if request.ProtoMajor == 2 {
flavor = "2"
}
if flavor != "" {
attrs = append(attrs, HTTPFlavorKey.String(flavor))
}
return attrs
}
// HTTPServerAttributesFromHTTPRequest generates attributes of the
// http namespace as specified by the OpenTelemetry specification for
// a span on the server side. Currently, only basic authentication is
// supported.
func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []kv.KeyValue {
attrs := []kv.KeyValue{
HTTPMethodKey.String(request.Method),
HTTPTargetKey.String(request.RequestURI),
}
if serverName != "" {
attrs = append(attrs, HTTPServerNameKey.String(serverName))
}
if route != "" {
attrs = append(attrs, HTTPRouteKey.String(route))
}
if values, ok := request.Header["X-Forwarded-For"]; ok && len(values) > 0 {
attrs = append(attrs, HTTPClientIPKey.String(values[0]))
}
return append(attrs, httpCommonAttributesFromHTTPRequest(request)...)
}
// HTTPAttributesFromHTTPStatusCode generates attributes of the http
// namespace as specified by the OpenTelemetry specification for a
// span.
func HTTPAttributesFromHTTPStatusCode(code int) []kv.KeyValue {
attrs := []kv.KeyValue{
HTTPStatusCodeKey.Int(code),
}
text := http.StatusText(code)
if text != "" {
attrs = append(attrs, HTTPStatusTextKey.String(text))
}
return attrs
}
type codeRange struct {
fromInclusive int
toInclusive int
}
func (r codeRange) contains(code int) bool {
return r.fromInclusive <= code && code <= r.toInclusive
}
var validRangesPerCategory = map[int][]codeRange{
1: {
{http.StatusContinue, http.StatusEarlyHints},
},
2: {
{http.StatusOK, http.StatusAlreadyReported},
{http.StatusIMUsed, http.StatusIMUsed},
},
3: {
{http.StatusMultipleChoices, http.StatusUseProxy},
{http.StatusTemporaryRedirect, http.StatusPermanentRedirect},
},
4: {
{http.StatusBadRequest, http.StatusTeapot}, // yes, teapot is so useful…
{http.StatusMisdirectedRequest, http.StatusUpgradeRequired},
{http.StatusPreconditionRequired, http.StatusTooManyRequests},
{http.StatusRequestHeaderFieldsTooLarge, http.StatusRequestHeaderFieldsTooLarge},
{http.StatusUnavailableForLegalReasons, http.StatusUnavailableForLegalReasons},
},
5: {
{http.StatusInternalServerError, http.StatusLoopDetected},
{http.StatusNotExtended, http.StatusNetworkAuthenticationRequired},
},
}
// SpanStatusFromHTTPStatusCode generates a status code and a message
// as specified by the OpenTelemetry specification for a span.
func SpanStatusFromHTTPStatusCode(code int) (codes.Code, string) {
spanCode := func() codes.Code {
category := code / 100
ranges, ok := validRangesPerCategory[category]
if !ok {
return codes.Unknown
}
ok = false
for _, crange := range ranges {
ok = crange.contains(code)
if ok {
break
}
}
if !ok {
return codes.Unknown
}
switch code {
case http.StatusUnauthorized:
return codes.Unauthenticated
case http.StatusForbidden:
return codes.PermissionDenied
case http.StatusNotFound:
return codes.NotFound
case http.StatusTooManyRequests:
return codes.ResourceExhausted
case http.StatusNotImplemented:
return codes.Unimplemented
case http.StatusServiceUnavailable:
return codes.Unavailable
case http.StatusGatewayTimeout:
return codes.DeadlineExceeded
}
if category > 0 && category < 4 {
return codes.OK
}
if category == 4 {
return codes.InvalidArgument
}
if category == 5 {
return codes.Internal
}
// this really should not happen, if we get there then
// it means that the code got out of sync with
// validRangesPerCategory map
return codes.Unknown
}()
if spanCode == codes.Unknown {
return spanCode, fmt.Sprintf("Invalid HTTP status code %d", code)
}
return spanCode, fmt.Sprintf("HTTP status code: %d", code)
}

777
api/standard/http_test.go Normal file
View File

@ -0,0 +1,777 @@
// Copyright The 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 standard
import (
"crypto/tls"
"net/http"
"net/url"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/codes"
otelkv "go.opentelemetry.io/otel/api/kv"
)
type tlsOption int
const (
noTLS tlsOption = iota
withTLS
)
func TestNetAttributesFromHTTPRequest(t *testing.T) {
type testcase struct {
name string
network string
method string
requestURI string
proto string
remoteAddr string
host string
url *url.URL
header http.Header
expected []otelkv.KeyValue
}
testcases := []testcase{
{
name: "stripped, tcp",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
},
},
{
name: "stripped, udp",
network: "udp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.UDP"),
},
},
{
name: "stripped, ip",
network: "ip",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP"),
},
},
{
name: "stripped, unix",
network: "unix",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "Unix"),
},
},
{
name: "stripped, other",
network: "nih",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "other"),
},
},
{
name: "with remote ip and port",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "1.2.3.4:56",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
otelkv.String("net.peer.ip", "1.2.3.4"),
otelkv.Int("net.peer.port", 56),
},
},
{
name: "with remote name and port",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "example.com:56",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
otelkv.String("net.peer.name", "example.com"),
otelkv.Int("net.peer.port", 56),
},
},
{
name: "with remote ip only",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "1.2.3.4",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
otelkv.String("net.peer.ip", "1.2.3.4"),
},
},
{
name: "with remote name only",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "example.com",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
otelkv.String("net.peer.name", "example.com"),
},
},
{
name: "with remote port only",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: ":56",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
},
},
{
name: "with host name only",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "1.2.3.4:56",
host: "example.com",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
otelkv.String("net.peer.ip", "1.2.3.4"),
otelkv.Int("net.peer.port", 56),
otelkv.String("net.host.name", "example.com"),
},
},
{
name: "with host ip only",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "1.2.3.4:56",
host: "4.3.2.1",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
otelkv.String("net.peer.ip", "1.2.3.4"),
otelkv.Int("net.peer.port", 56),
otelkv.String("net.host.ip", "4.3.2.1"),
},
},
{
name: "with host name and port",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "1.2.3.4:56",
host: "example.com:78",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
otelkv.String("net.peer.ip", "1.2.3.4"),
otelkv.Int("net.peer.port", 56),
otelkv.String("net.host.name", "example.com"),
otelkv.Int("net.host.port", 78),
},
},
{
name: "with host ip and port",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "1.2.3.4:56",
host: "4.3.2.1:78",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
otelkv.String("net.peer.ip", "1.2.3.4"),
otelkv.Int("net.peer.port", 56),
otelkv.String("net.host.ip", "4.3.2.1"),
otelkv.Int("net.host.port", 78),
},
},
{
name: "with host name and bogus port",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "1.2.3.4:56",
host: "example.com:qwerty",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
otelkv.String("net.peer.ip", "1.2.3.4"),
otelkv.Int("net.peer.port", 56),
otelkv.String("net.host.name", "example.com"),
},
},
{
name: "with host ip and bogus port",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "1.2.3.4:56",
host: "4.3.2.1:qwerty",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
otelkv.String("net.peer.ip", "1.2.3.4"),
otelkv.Int("net.peer.port", 56),
otelkv.String("net.host.ip", "4.3.2.1"),
},
},
{
name: "with empty host and port",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "1.2.3.4:56",
host: ":80",
url: &url.URL{
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
otelkv.String("net.peer.ip", "1.2.3.4"),
otelkv.Int("net.peer.port", 56),
},
},
{
name: "with host ip and port in headers",
network: "tcp",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "1.2.3.4:56",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: http.Header{
"Host": []string{"4.3.2.1:78"},
},
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
otelkv.String("net.peer.ip", "1.2.3.4"),
otelkv.Int("net.peer.port", 56),
otelkv.String("net.host.ip", "4.3.2.1"),
otelkv.Int("net.host.port", 78),
},
},
{
name: "with host ip and port in url",
network: "tcp",
method: "GET",
requestURI: "http://4.3.2.1:78/user/123",
proto: "HTTP/1.0",
remoteAddr: "1.2.3.4:56",
host: "",
url: &url.URL{
Host: "4.3.2.1:78",
Path: "/user/123",
},
header: nil,
expected: []otelkv.KeyValue{
otelkv.String("net.transport", "IP.TCP"),
otelkv.String("net.peer.ip", "1.2.3.4"),
otelkv.Int("net.peer.port", 56),
otelkv.String("net.host.ip", "4.3.2.1"),
otelkv.Int("net.host.port", 78),
},
},
}
for idx, tc := range testcases {
r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, noTLS)
got := NetAttributesFromHTTPRequest(tc.network, r)
assertElementsMatch(t, tc.expected, got, "testcase %d - %s", idx, tc.name)
}
}
func TestEndUserAttributesFromHTTPRequest(t *testing.T) {
r := testRequest("GET", "/user/123", "HTTP/1.1", "", "", nil, http.Header{}, withTLS)
var expected []otelkv.KeyValue
got := EndUserAttributesFromHTTPRequest(r)
assert.ElementsMatch(t, expected, got)
r.SetBasicAuth("admin", "password")
expected = []otelkv.KeyValue{otelkv.String("enduser.id", "admin")}
got = EndUserAttributesFromHTTPRequest(r)
assert.ElementsMatch(t, expected, got)
}
func TestHTTPServerAttributesFromHTTPRequest(t *testing.T) {
type testcase struct {
name string
serverName string
route string
method string
requestURI string
proto string
remoteAddr string
host string
url *url.URL
header http.Header
tls tlsOption
expected []otelkv.KeyValue
}
testcases := []testcase{
{
name: "stripped",
serverName: "",
route: "",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: noTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.target", "/user/123"),
otelkv.String("http.scheme", "http"),
otelkv.String("http.flavor", "1.0"),
},
},
{
name: "with server name",
serverName: "my-server-name",
route: "",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: noTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.target", "/user/123"),
otelkv.String("http.scheme", "http"),
otelkv.String("http.flavor", "1.0"),
otelkv.String("http.server_name", "my-server-name"),
},
},
{
name: "with tls",
serverName: "my-server-name",
route: "",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: withTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.target", "/user/123"),
otelkv.String("http.scheme", "https"),
otelkv.String("http.flavor", "1.0"),
otelkv.String("http.server_name", "my-server-name"),
},
},
{
name: "with route",
serverName: "my-server-name",
route: "/user/:id",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: withTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.target", "/user/123"),
otelkv.String("http.scheme", "https"),
otelkv.String("http.flavor", "1.0"),
otelkv.String("http.server_name", "my-server-name"),
otelkv.String("http.route", "/user/:id"),
},
},
{
name: "with host",
serverName: "my-server-name",
route: "/user/:id",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "example.com",
url: &url.URL{
Path: "/user/123",
},
header: nil,
tls: withTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.target", "/user/123"),
otelkv.String("http.scheme", "https"),
otelkv.String("http.flavor", "1.0"),
otelkv.String("http.server_name", "my-server-name"),
otelkv.String("http.route", "/user/:id"),
otelkv.String("http.host", "example.com"),
},
},
{
name: "with user agent",
serverName: "my-server-name",
route: "/user/:id",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "example.com",
url: &url.URL{
Path: "/user/123",
},
header: http.Header{
"User-Agent": []string{"foodownloader"},
},
tls: withTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.target", "/user/123"),
otelkv.String("http.scheme", "https"),
otelkv.String("http.flavor", "1.0"),
otelkv.String("http.server_name", "my-server-name"),
otelkv.String("http.route", "/user/:id"),
otelkv.String("http.host", "example.com"),
otelkv.String("http.user_agent", "foodownloader"),
},
},
{
name: "with proxy info",
serverName: "my-server-name",
route: "/user/:id",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.0",
remoteAddr: "",
host: "example.com",
url: &url.URL{
Path: "/user/123",
},
header: http.Header{
"User-Agent": []string{"foodownloader"},
"X-Forwarded-For": []string{"1.2.3.4"},
},
tls: withTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.target", "/user/123"),
otelkv.String("http.scheme", "https"),
otelkv.String("http.flavor", "1.0"),
otelkv.String("http.server_name", "my-server-name"),
otelkv.String("http.route", "/user/:id"),
otelkv.String("http.host", "example.com"),
otelkv.String("http.user_agent", "foodownloader"),
otelkv.String("http.client_ip", "1.2.3.4"),
},
},
{
name: "with http 1.1",
serverName: "my-server-name",
route: "/user/:id",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/1.1",
remoteAddr: "",
host: "example.com",
url: &url.URL{
Path: "/user/123",
},
header: http.Header{
"User-Agent": []string{"foodownloader"},
"X-Forwarded-For": []string{"1.2.3.4"},
},
tls: withTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.target", "/user/123"),
otelkv.String("http.scheme", "https"),
otelkv.String("http.flavor", "1.1"),
otelkv.String("http.server_name", "my-server-name"),
otelkv.String("http.route", "/user/:id"),
otelkv.String("http.host", "example.com"),
otelkv.String("http.user_agent", "foodownloader"),
otelkv.String("http.client_ip", "1.2.3.4"),
},
},
{
name: "with http 2",
serverName: "my-server-name",
route: "/user/:id",
method: "GET",
requestURI: "/user/123",
proto: "HTTP/2.0",
remoteAddr: "",
host: "example.com",
url: &url.URL{
Path: "/user/123",
},
header: http.Header{
"User-Agent": []string{"foodownloader"},
"X-Forwarded-For": []string{"1.2.3.4"},
},
tls: withTLS,
expected: []otelkv.KeyValue{
otelkv.String("http.method", "GET"),
otelkv.String("http.target", "/user/123"),
otelkv.String("http.scheme", "https"),
otelkv.String("http.flavor", "2"),
otelkv.String("http.server_name", "my-server-name"),
otelkv.String("http.route", "/user/:id"),
otelkv.String("http.host", "example.com"),
otelkv.String("http.user_agent", "foodownloader"),
otelkv.String("http.client_ip", "1.2.3.4"),
},
},
}
for idx, tc := range testcases {
r := testRequest(tc.method, tc.requestURI, tc.proto, tc.remoteAddr, tc.host, tc.url, tc.header, tc.tls)
got := HTTPServerAttributesFromHTTPRequest(tc.serverName, tc.route, r)
assertElementsMatch(t, tc.expected, got, "testcase %d - %s", idx, tc.name)
}
}
func TestHTTPAttributesFromHTTPStatusCode(t *testing.T) {
expected := []otelkv.KeyValue{
otelkv.Int("http.status_code", 404),
otelkv.String("http.status_text", "Not Found"),
}
got := HTTPAttributesFromHTTPStatusCode(http.StatusNotFound)
assertElementsMatch(t, expected, got, "with valid HTTP status code")
assert.ElementsMatch(t, expected, got)
expected = []otelkv.KeyValue{
otelkv.Int("http.status_code", 499),
}
got = HTTPAttributesFromHTTPStatusCode(499)
assertElementsMatch(t, expected, got, "with invalid HTTP status code")
}
func TestSpanStatusFromHTTPStatusCode(t *testing.T) {
for code := 0; code < 1000; code++ {
expected := getExpectedGRPCCodeForHTTPCode(code)
got, _ := SpanStatusFromHTTPStatusCode(code)
assert.Equalf(t, expected, got, "%s vs %s", expected, got)
}
}
func getExpectedGRPCCodeForHTTPCode(code int) codes.Code {
if http.StatusText(code) == "" {
return codes.Unknown
}
switch code {
case http.StatusUnauthorized:
return codes.Unauthenticated
case http.StatusForbidden:
return codes.PermissionDenied
case http.StatusNotFound:
return codes.NotFound
case http.StatusTooManyRequests:
return codes.ResourceExhausted
case http.StatusNotImplemented:
return codes.Unimplemented
case http.StatusServiceUnavailable:
return codes.Unavailable
case http.StatusGatewayTimeout:
return codes.DeadlineExceeded
}
category := code / 100
if category < 4 {
return codes.OK
}
if category < 5 {
return codes.InvalidArgument
}
return codes.Internal
}
func assertElementsMatch(t *testing.T, expected, got []otelkv.KeyValue, format string, args ...interface{}) {
if !assert.ElementsMatchf(t, expected, got, format, args...) {
t.Log("expected:", kvStr(expected))
t.Log("got:", kvStr(got))
}
}
func testRequest(method, requestURI, proto, remoteAddr, host string, u *url.URL, header http.Header, tlsopt tlsOption) *http.Request {
major, minor := protoToInts(proto)
var tlsConn *tls.ConnectionState
switch tlsopt {
case noTLS:
case withTLS:
tlsConn = &tls.ConnectionState{}
}
return &http.Request{
Method: method,
URL: u,
Proto: proto,
ProtoMajor: major,
ProtoMinor: minor,
Header: header,
Host: host,
RemoteAddr: remoteAddr,
RequestURI: requestURI,
TLS: tlsConn,
}
}
func protoToInts(proto string) (int, int) {
switch proto {
case "HTTP/1.0":
return 1, 0
case "HTTP/1.1":
return 1, 1
case "HTTP/2.0":
return 2, 0
}
// invalid proto
return 13, 42
}
func kvStr(kvs []otelkv.KeyValue) string {
sb := strings.Builder{}
sb.WriteRune('[')
for idx, kv := range kvs {
if idx > 0 {
sb.WriteString(", ")
}
sb.WriteString((string)(kv.Key))
sb.WriteString(": ")
sb.WriteString(kv.Value.Emit())
}
sb.WriteRune(']')
return sb.String()
}

View File

@ -33,7 +33,6 @@ func (ns alwaysOffSampler) ShouldSample(
_ SpanContext,
_ bool,
_ ID,
_ SpanID,
_ string,
_ SpanKind,
_ []kv.KeyValue,

View File

@ -24,7 +24,7 @@ import (
func TestNeverSamperShouldSample(t *testing.T) {
gotD := AlwaysOffSampler().ShouldSample(
SpanContext{}, false, ID{}, SpanID{}, "span", SpanKindClient, []kv.KeyValue{}, []Link{})
SpanContext{}, false, ID{}, "span", SpanKindClient, []kv.KeyValue{}, []Link{})
wantD := Decision{Sampled: false}
if diff := cmp.Diff(wantD, gotD); diff != "" {
t.Errorf("Decision: +got, -want%v", diff)

View File

@ -33,7 +33,6 @@ func (as alwaysOnSampler) ShouldSample(
_ SpanContext,
_ bool,
_ ID,
_ SpanID,
_ string,
_ SpanKind,
_ []kv.KeyValue,

View File

@ -24,7 +24,7 @@ import (
func TestAlwaysOnSamplerShouldSample(t *testing.T) {
gotD := AlwaysOnSampler().ShouldSample(
SpanContext{}, false, ID{}, SpanID{}, "span", SpanKindClient, []kv.KeyValue{}, []Link{})
SpanContext{}, false, ID{}, "span", SpanKindClient, []kv.KeyValue{}, []Link{})
wantD := Decision{Sampled: true}
if diff := cmp.Diff(wantD, gotD); diff != "" {
t.Errorf("Decision: +got, -want%v", diff)

View File

@ -24,7 +24,6 @@ type Sampler interface {
sc SpanContext,
remote bool,
traceID ID,
spanID SpanID,
spanName string,
spanKind SpanKind,
attributes []kv.KeyValue,

View File

@ -4,4 +4,4 @@ go 1.13
replace go.opentelemetry.io/otel => ../..
require go.opentelemetry.io/otel v0.5.0
require go.opentelemetry.io/otel v0.6.0

View File

@ -26,6 +26,7 @@ import (
metricstdout "go.opentelemetry.io/otel/exporters/metric/stdout"
tracestdout "go.opentelemetry.io/otel/exporters/trace/stdout"
"go.opentelemetry.io/otel/sdk/metric/controller/push"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
@ -46,7 +47,7 @@ func initTracer() {
}
tp, err := sdktrace.NewProvider(sdktrace.WithSyncer(exp),
sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}),
sdktrace.WithResourceAttributes(kv.String("rk1", "rv11"), kv.Int64("rk2", 5)))
sdktrace.WithResource(resource.New(kv.String("rk1", "rv11"), kv.Int64("rk2", 5))))
if err != nil {
log.Panicf("failed to initialize trace provider %v", err)
}
@ -76,7 +77,7 @@ func main() {
oneMetricCB := func(_ context.Context, result metric.Float64ObserverResult) {
result.Observe(1, commonLabels...)
}
_ = metric.Must(meter).RegisterFloat64ValueObserver("ex.com.one", oneMetricCB,
_ = metric.Must(meter).NewFloat64ValueObserver("ex.com.one", oneMetricCB,
metric.WithDescription("A ValueObserver set to 1.0"),
)

View File

@ -6,7 +6,7 @@ replace go.opentelemetry.io/otel => ../..
require (
github.com/golang/protobuf v1.3.2
go.opentelemetry.io/otel v0.5.0
go.opentelemetry.io/otel v0.6.0
golang.org/x/net v0.0.0-20190311183353-d8887717615a
google.golang.org/grpc v1.27.1
)

View File

@ -4,4 +4,4 @@ go 1.13
replace go.opentelemetry.io/otel => ../..
require go.opentelemetry.io/otel v0.5.0
require go.opentelemetry.io/otel v0.6.0

View File

@ -8,6 +8,6 @@ replace (
)
require (
go.opentelemetry.io/otel v0.5.0
go.opentelemetry.io/otel/exporters/trace/jaeger v0.5.0
go.opentelemetry.io/otel v0.6.0
go.opentelemetry.io/otel/exporters/trace/jaeger v0.6.0
)

View File

@ -4,4 +4,4 @@ go 1.13
replace go.opentelemetry.io/otel => ../..
require go.opentelemetry.io/otel v0.5.0
require go.opentelemetry.io/otel v0.6.0

View File

@ -156,14 +156,11 @@ The next step is to create the TraceProvider:
```go
tp, err := sdktrace.NewProvider(
sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}),
sdktrace.WithResourceAttributes(
sdktrace.WithResource(resource.New(
// the service name used to display traces in Jaeger
core.Key(conventions.AttributeServiceName).String("test-service"),
),
sdktrace.WithBatcher(exp, // add following two options to ensure flush
sdktrace.WithScheduleDelayMillis(5),
sdktrace.WithMaxExportBatchSize(2),
))
kv.Key(conventions.AttributeServiceName).String("test-service"),
)),
sdktrace.WithSyncer(exp))
if err != nil {
log.Fatalf("error creating trace provider: %v\n", err)
}
@ -175,7 +172,7 @@ After this, you can simply start sending traces:
```go
tracer := tp.Tracer("test-tracer")
ctx, span := tracer.Start(context.Background(), "CollectorExporter-Example")
defer span.End()
defer span.End()
```
The traces should now be visible from the Jaeger UI (if you have it installed), or thorough the jaeger-query service, under the name `test-service`.

View File

@ -4,7 +4,12 @@ go 1.14
require (
github.com/open-telemetry/opentelemetry-collector v0.3.0
go.opentelemetry.io/otel v0.5.0
go.opentelemetry.io/otel/exporters/otlp v0.5.0
go.opentelemetry.io/otel v0.6.0
go.opentelemetry.io/otel/exporters/otlp v0.6.0
google.golang.org/grpc v1.29.1
)
replace (
go.opentelemetry.io/otel => ../..
go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp
)

View File

@ -414,7 +414,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
@ -929,10 +928,6 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/otel v0.5.0 h1:tdIR1veg/z+VRJaw/6SIxz+QX3l+m+BDleYLTs+GC1g=
go.opentelemetry.io/otel v0.5.0/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek=
go.opentelemetry.io/otel/exporters/otlp v0.5.0 h1:dfS89YmU0e6HmmULuJQ9s3xnfz2uu1LHz29wseFt0Jc=
go.opentelemetry.io/otel/exporters/otlp v0.5.0/go.mod h1:uQseOXa3qUrjJRaRl8At4ISGr55GgKPkILaovWY5EI4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=

View File

@ -25,6 +25,7 @@ import (
"go.opentelemetry.io/otel/api/kv"
"go.opentelemetry.io/otel/exporters/otlp"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"github.com/open-telemetry/opentelemetry-collector/translator/conventions"
@ -50,14 +51,11 @@ func main() {
tp, err := sdktrace.NewProvider(
sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}),
sdktrace.WithResourceAttributes(
sdktrace.WithResource(resource.New(
// the service name used to display traces in Jaeger
kv.Key(conventions.AttributeServiceName).String("test-service"),
),
sdktrace.WithBatcher(exp, // add following two options to ensure flush
sdktrace.WithScheduleDelayMillis(5),
sdktrace.WithMaxExportBatchSize(2),
))
)),
sdktrace.WithSyncer(exp))
if err != nil {
log.Fatalf("error creating trace provider: %v\n", err)
}
@ -74,6 +72,4 @@ func main() {
}
span.End()
// Wait 1 second before ending
<-time.After(time.Second)
}

View File

@ -8,6 +8,6 @@ replace (
)
require (
go.opentelemetry.io/otel v0.5.0
go.opentelemetry.io/otel/exporters/metric/prometheus v0.5.0
go.opentelemetry.io/otel v0.6.0
go.opentelemetry.io/otel/exporters/metric/prometheus v0.6.0
)

View File

@ -56,7 +56,7 @@ func main() {
(*observerLock).RUnlock()
result.Observe(value, labels...)
}
_ = metric.Must(meter).RegisterFloat64ValueObserver("ex.com.one", cb,
_ = metric.Must(meter).NewFloat64ValueObserver("ex.com.one", cb,
metric.WithDescription("A ValueObserver set to 1.0"),
)

View File

@ -8,6 +8,6 @@ replace (
)
require (
go.opentelemetry.io/otel v0.5.0
go.opentelemetry.io/otel/exporters/trace/zipkin v0.5.0
go.opentelemetry.io/otel v0.6.0
go.opentelemetry.io/otel/exporters/trace/zipkin v0.6.0
)

View File

@ -25,6 +25,8 @@ import (
"go.opentelemetry.io/otel/api/kv"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/exporters/metric/prometheus"
"go.opentelemetry.io/otel/sdk/metric/controller/pull"
"go.opentelemetry.io/otel/sdk/resource"
)
// This test demonstrates that it is relatively difficult to setup a
@ -32,11 +34,14 @@ import (
//
// 1. The default boundaries are difficult to pass, should be []float instead of []metric.Number
//
// TODO: Address this issue; add Resources to the test.
// TODO: Address this issue.
func ExampleNewExportPipeline() {
// Create a meter
exporter, err := prometheus.NewExportPipeline(prometheus.Config{})
exporter, err := prometheus.NewExportPipeline(
prometheus.Config{},
pull.WithResource(resource.New(kv.String("R", "V"))),
)
if err != nil {
panic(err)
}
@ -73,10 +78,10 @@ func ExampleNewExportPipeline() {
// Output:
// # HELP a_counter Counts things
// # TYPE a_counter counter
// a_counter{key="value"} 100
// a_counter{R="V",key="value"} 100
// # HELP a_valuerecorder Records values
// # TYPE a_valuerecorder histogram
// a_valuerecorder_bucket{key="value",le="+Inf"} 1
// a_valuerecorder_sum{key="value"} 100
// a_valuerecorder_count{key="value"} 1
// a_valuerecorder_bucket{R="V",key="value",le="+Inf"} 1
// a_valuerecorder_sum{R="V",key="value"} 100
// a_valuerecorder_count{R="V",key="value"} 1
}

View File

@ -9,6 +9,6 @@ require (
github.com/prometheus/client_golang v1.5.0
github.com/prometheus/procfs v0.0.10 // indirect
github.com/stretchr/testify v1.4.0
go.opentelemetry.io/otel v0.5.0
go.opentelemetry.io/otel v0.6.0
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect
)

View File

@ -20,13 +20,12 @@ import (
"net/http"
"sync"
"go.opentelemetry.io/otel/api/metric"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/api/label"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/controller/pull"
@ -54,7 +53,7 @@ type Exporter struct {
onError func(error)
defaultSummaryQuantiles []float64
defaultHistogramBoundaries []metric.Number
defaultHistogramBoundaries []float64
}
var _ http.Handler = &Exporter{}
@ -85,7 +84,7 @@ type Config struct {
// DefaultHistogramBoundaries defines the default histogram bucket
// boundaries.
DefaultHistogramBoundaries []metric.Number
DefaultHistogramBoundaries []float64
// OnError is a function that handle errors that may occur while exporting metrics.
// TODO: This should be refactored or even removed once we have a better error handling mechanism.
@ -203,7 +202,9 @@ func (c *collector) Describe(ch chan<- *prometheus.Desc) {
defer c.exp.lock.RUnlock()
_ = c.exp.Controller().ForEach(func(record export.Record) error {
ch <- c.toDesc(&record)
var labelKeys []string
mergeLabels(record, &labelKeys, nil)
ch <- c.toDesc(record, labelKeys)
return nil
})
}
@ -222,9 +223,11 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
err := ctrl.ForEach(func(record export.Record) error {
agg := record.Aggregator()
numberKind := record.Descriptor().NumberKind()
// TODO: Use the resource value in this record.
labels := labelValues(record.Labels())
desc := c.toDesc(&record)
var labelKeys, labels []string
mergeLabels(record, &labelKeys, &labels)
desc := c.toDesc(record, labelKeys)
if hist, ok := agg.(aggregator.Histogram); ok {
if err := c.exportHistogram(ch, hist, numberKind, desc, labels); err != nil {
@ -330,12 +333,12 @@ func (c *collector) exportHistogram(ch chan<- prometheus.Metric, hist aggregator
// The bucket with upper-bound +inf is not included.
counts := make(map[float64]uint64, len(buckets.Boundaries))
for i := range buckets.Boundaries {
boundary := buckets.Boundaries[i].CoerceToFloat64(kind)
totalCount += buckets.Counts[i].AsUint64()
boundary := buckets.Boundaries[i]
totalCount += uint64(buckets.Counts[i])
counts[boundary] = totalCount
}
// Include the +inf bucket in the total count.
totalCount += buckets.Counts[len(buckets.Counts)-1].AsUint64()
totalCount += uint64(buckets.Counts[len(buckets.Counts)-1])
m, err := prometheus.NewConstHistogram(desc, totalCount, sum.CoerceToFloat64(kind), counts, labels...)
if err != nil {
@ -346,30 +349,34 @@ func (c *collector) exportHistogram(ch chan<- prometheus.Metric, hist aggregator
return nil
}
func (c *collector) toDesc(record *export.Record) *prometheus.Desc {
func (c *collector) toDesc(record export.Record, labelKeys []string) *prometheus.Desc {
desc := record.Descriptor()
labels := labelsKeys(record.Labels())
return prometheus.NewDesc(sanitize(desc.Name()), desc.Description(), labels, nil)
return prometheus.NewDesc(sanitize(desc.Name()), desc.Description(), labelKeys, nil)
}
func labelsKeys(labels *label.Set) []string {
iter := labels.Iter()
keys := make([]string, 0, iter.Len())
for iter.Next() {
kv := iter.Label()
keys = append(keys, sanitize(string(kv.Key)))
// mergeLabels merges the export.Record's labels and resources into a
// single set, giving precedence to the record's labels in case of
// duplicate keys. This outputs one or both of the keys and the
// values as a slice, and either argument may be nil to avoid
// allocating an unnecessary slice.
func mergeLabels(record export.Record, keys, values *[]string) {
if keys != nil {
*keys = make([]string, 0, record.Labels().Len()+record.Resource().Len())
}
if values != nil {
*values = make([]string, 0, record.Labels().Len()+record.Resource().Len())
}
return keys
}
func labelValues(labels *label.Set) []string {
// TODO(paivagustavo): parse the labels.Encoded() instead of calling `Emit()` directly
// this would avoid unnecessary allocations.
iter := labels.Iter()
values := make([]string, 0, iter.Len())
for iter.Next() {
label := iter.Label()
values = append(values, label.Value.Emit())
// Duplicate keys are resolved by taking the record label value over
// the resource value.
mi := label.NewMergeIterator(record.Labels(), record.Resource().LabelSet())
for mi.Next() {
label := mi.Label()
if keys != nil {
*keys = append(*keys, sanitize(string(label.Key)))
}
if values != nil {
*values = append(*values, label.Value.Emit())
}
}
return values
}

View File

@ -30,12 +30,13 @@ import (
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/exporters/metric/prometheus"
"go.opentelemetry.io/otel/sdk/metric/controller/pull"
"go.opentelemetry.io/otel/sdk/resource"
)
func TestPrometheusExporter(t *testing.T) {
exporter, err := prometheus.NewExportPipeline(prometheus.Config{
DefaultHistogramBoundaries: []metric.Number{metric.NewFloat64Number(-0.5), metric.NewFloat64Number(1)},
})
DefaultHistogramBoundaries: []float64{-0.5, 1},
}, pull.WithResource(resource.New(kv.String("R", "V"))))
require.NoError(t, err)
meter := exporter.Provider().Meter("test")
@ -54,18 +55,18 @@ func TestPrometheusExporter(t *testing.T) {
counter.Add(ctx, 10, labels...)
counter.Add(ctx, 5.3, labels...)
expected = append(expected, `counter{A="B",C="D"} 15.3`)
expected = append(expected, `counter{A="B",C="D",R="V"} 15.3`)
valuerecorder.Record(ctx, -0.6, labels...)
valuerecorder.Record(ctx, -0.4, labels...)
valuerecorder.Record(ctx, 0.6, labels...)
valuerecorder.Record(ctx, 20, labels...)
expected = append(expected, `valuerecorder_bucket{A="B",C="D",le="+Inf"} 4`)
expected = append(expected, `valuerecorder_bucket{A="B",C="D",le="-0.5"} 1`)
expected = append(expected, `valuerecorder_bucket{A="B",C="D",le="1"} 3`)
expected = append(expected, `valuerecorder_count{A="B",C="D"} 4`)
expected = append(expected, `valuerecorder_sum{A="B",C="D"} 19.6`)
expected = append(expected, `valuerecorder_bucket{A="B",C="D",R="V",le="+Inf"} 4`)
expected = append(expected, `valuerecorder_bucket{A="B",C="D",R="V",le="-0.5"} 1`)
expected = append(expected, `valuerecorder_bucket{A="B",C="D",R="V",le="1"} 3`)
expected = append(expected, `valuerecorder_count{A="B",C="D",R="V"} 4`)
expected = append(expected, `valuerecorder_sum{A="B",C="D",R="V"} 19.6`)
compareExport(t, exporter, expected)
}

View File

@ -97,7 +97,7 @@ func (p *CheckpointSet) AddValueRecorder(desc *metric.Descriptor, v float64, lab
p.updateAggregator(desc, array.New(), v, labels...)
}
func (p *CheckpointSet) AddHistogramValueRecorder(desc *metric.Descriptor, boundaries []metric.Number, v float64, labels ...kv.KeyValue) {
func (p *CheckpointSet) AddHistogramValueRecorder(desc *metric.Descriptor, boundaries []float64, v float64, labels ...kv.KeyValue) {
p.updateAggregator(desc, histogram.New(desc, boundaries), v, labels...)
}

View File

@ -11,8 +11,6 @@ require (
github.com/open-telemetry/opentelemetry-proto v0.3.0
github.com/stretchr/testify v1.4.0
go.opentelemetry.io/otel v0.6.0
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa // indirect
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 // indirect
golang.org/x/text v0.3.2 // indirect
google.golang.org/grpc v1.29.1
gopkg.in/yaml.v2 v2.2.8 // indirect

View File

@ -68,8 +68,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJV
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0 h1:2mqDk8w/o6UmeUCu5Qiq2y7iMf6anbx+YA8d1JFoFrs=
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -79,8 +77,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=

View File

@ -8,7 +8,7 @@ require (
github.com/apache/thrift v0.13.0
github.com/google/go-cmp v0.4.0
github.com/stretchr/testify v1.4.0
go.opentelemetry.io/otel v0.5.0
go.opentelemetry.io/otel v0.6.0
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
google.golang.org/api v0.20.0
google.golang.org/grpc v1.27.1

View File

@ -7,6 +7,6 @@ replace go.opentelemetry.io/otel => ../../..
require (
github.com/openzipkin/zipkin-go v0.2.2
github.com/stretchr/testify v1.4.0
go.opentelemetry.io/otel v0.5.0
go.opentelemetry.io/otel v0.6.0
google.golang.org/grpc v1.27.1
)

View File

@ -105,6 +105,7 @@ func (e *Exporter) ExportSpans(ctx context.Context, batch []*export.SpanData) {
e.logf("failed to create request to %s: %v", e.url, err)
return
}
req.Header.Set("Content-Type", "application/json")
resp, err := e.client.Do(req)
if err != nil {
e.logf("request to %s failed: %v", e.url, err)

View File

@ -133,9 +133,10 @@ const (
type clientStream struct {
grpc.ClientStream
desc *grpc.StreamDesc
events chan streamEvent
finished chan error
desc *grpc.StreamDesc
events chan streamEvent
eventsDone chan struct{}
finished chan error
receivedMessageID int
sentMessageID int
@ -147,11 +148,11 @@ func (w *clientStream) RecvMsg(m interface{}) error {
err := w.ClientStream.RecvMsg(m)
if err == nil && !w.desc.ServerStreams {
w.events <- streamEvent{receiveEndEvent, nil}
w.sendStreamEvent(receiveEndEvent, nil)
} else if err == io.EOF {
w.events <- streamEvent{receiveEndEvent, nil}
w.sendStreamEvent(receiveEndEvent, nil)
} else if err != nil {
w.events <- streamEvent{errorEvent, err}
w.sendStreamEvent(errorEvent, err)
} else {
w.receivedMessageID++
messageReceived.Event(w.Context(), w.receivedMessageID, m)
@ -167,7 +168,7 @@ func (w *clientStream) SendMsg(m interface{}) error {
messageSent.Event(w.Context(), w.sentMessageID, m)
if err != nil {
w.events <- streamEvent{errorEvent, err}
w.sendStreamEvent(errorEvent, err)
}
return err
@ -177,7 +178,7 @@ func (w *clientStream) Header() (metadata.MD, error) {
md, err := w.ClientStream.Header()
if err != nil {
w.events <- streamEvent{errorEvent, err}
w.sendStreamEvent(errorEvent, err)
}
return md, err
@ -187,9 +188,9 @@ func (w *clientStream) CloseSend() error {
err := w.ClientStream.CloseSend()
if err != nil {
w.events <- streamEvent{errorEvent, err}
w.sendStreamEvent(errorEvent, err)
} else {
w.events <- streamEvent{closeEvent, nil}
w.sendStreamEvent(closeEvent, nil)
}
return err
@ -201,10 +202,13 @@ const (
)
func wrapClientStream(s grpc.ClientStream, desc *grpc.StreamDesc) *clientStream {
events := make(chan streamEvent, 1)
events := make(chan streamEvent)
eventsDone := make(chan struct{})
finished := make(chan error)
go func() {
defer close(eventsDone)
// Both streams have to be closed
state := byte(0)
@ -216,12 +220,12 @@ func wrapClientStream(s grpc.ClientStream, desc *grpc.StreamDesc) *clientStream
state |= receiveEndedState
case errorEvent:
finished <- event.Err
close(events)
return
}
if state == clientClosedState|receiveEndedState {
finished <- nil
close(events)
return
}
}
}()
@ -230,10 +234,18 @@ func wrapClientStream(s grpc.ClientStream, desc *grpc.StreamDesc) *clientStream
ClientStream: s,
desc: desc,
events: events,
eventsDone: eventsDone,
finished: finished,
}
}
func (w *clientStream) sendStreamEvent(eventType streamEventType, err error) {
select {
case <-w.eventsDone:
case w.events <- streamEvent{Type: eventType, Err: err}:
}
}
// StreamClientInterceptor returns a grpc.StreamClientInterceptor suitable
// for use in a grpc.Dial call.
//

View File

@ -376,6 +376,9 @@ func TestStreamClientInterceptor(t *testing.T) {
validate("SENT", events[i].Attributes)
validate("RECEIVED", events[i+1].Attributes)
}
// ensure CloseSend can be subsequently called
_ = streamClient.CloseSend()
}
func TestServerInterceptorError(t *testing.T) {

View File

@ -22,6 +22,7 @@ import (
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/api/kv"
"go.opentelemetry.io/otel/api/propagation"
"go.opentelemetry.io/otel/api/standard"
"go.opentelemetry.io/otel/api/trace"
)
@ -34,10 +35,10 @@ var (
func Extract(ctx context.Context, req *http.Request) ([]kv.KeyValue, []kv.KeyValue, trace.SpanContext) {
ctx = propagation.ExtractHTTP(ctx, global.Propagators(), req.Header)
attrs := []kv.KeyValue{
URLKey.String(req.URL.String()),
// Etc.
}
attrs := append(
standard.HTTPServerAttributesFromHTTPRequest("", "", req),
standard.NetAttributesFromHTTPRequest("tcp", req)...,
)
var correlationCtxKVs []kv.KeyValue
correlation.MapFromContext(ctx).Foreach(func(kv kv.KeyValue) bool {

View File

@ -18,20 +18,10 @@ import (
"net/http"
"go.opentelemetry.io/otel/api/kv"
"go.opentelemetry.io/otel/api/trace"
)
// Attribute keys that can be added to a span.
const (
HostKey = kv.Key("http.host") // the HTTP host (http.Request.Host)
MethodKey = kv.Key("http.method") // the HTTP method (http.Request.Method)
PathKey = kv.Key("http.path") // the HTTP path (http.Request.URL.Path)
URLKey = kv.Key("http.url") // the HTTP URL (http.Request.URL.String())
UserAgentKey = kv.Key("http.user_agent") // the HTTP user agent (http.Request.UserAgent())
RouteKey = kv.Key("http.route") // the HTTP route (ex: /users/:id)
RemoteAddrKey = kv.Key("http.remote_addr") // the network address of the client that sent the HTTP request (http.Request.RemoteAddr)
StatusCodeKey = kv.Key("http.status_code") // if set, the HTTP status
ReadBytesKey = kv.Key("http.read_bytes") // if anything was read from the request body, the total number of bytes read
ReadErrorKey = kv.Key("http.read_error") // If an error occurred while reading a request, the string of the error (io.EOF is not recorded)
WroteBytesKey = kv.Key("http.wrote_bytes") // if anything was written to the response writer, the total number of bytes written
@ -41,15 +31,3 @@ const (
// Filter is a predicate used to determine whether a given http.request should
// be traced. A Filter must return true if the request should be traced.
type Filter func(*http.Request) bool
// Setup basic span attributes before so that they
// are available to be mutated if needed.
func setBasicAttributes(span trace.Span, r *http.Request) {
span.SetAttributes(
HostKey.String(r.Host),
MethodKey.String(r.Method),
PathKey.String(r.URL.Path),
URLKey.String(r.URL.String()),
UserAgentKey.String(r.UserAgent()),
)
}

View File

@ -21,6 +21,7 @@ import (
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/api/kv"
"go.opentelemetry.io/otel/api/propagation"
"go.opentelemetry.io/otel/api/standard"
"go.opentelemetry.io/otel/api/trace"
)
@ -88,7 +89,11 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
opts := append([]trace.StartOption{}, h.spanStartOptions...) // start with the configured options
opts := append([]trace.StartOption{
trace.WithAttributes(standard.NetAttributesFromHTTPRequest("tcp", r)...),
trace.WithAttributes(standard.EndUserAttributesFromHTTPRequest(r)...),
trace.WithAttributes(standard.HTTPServerAttributesFromHTTPRequest(h.operation, "", r)...),
}, h.spanStartOptions...) // start with the configured options
ctx := propagation.ExtractHTTP(r.Context(), h.propagators, r.Header)
ctx, span := h.tracer.Start(ctx, h.spanNameFormatter(h.operation, r), opts...)
@ -112,16 +117,14 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
rww := &respWriterWrapper{ResponseWriter: w, record: writeRecordFunc, ctx: ctx, props: h.propagators}
setBasicAttributes(span, r)
span.SetAttributes(RemoteAddrKey.String(r.RemoteAddr))
h.handler.ServeHTTP(rww, r.WithContext(ctx))
setAfterServeAttributes(span, bw.read, rww.written, int64(rww.statusCode), bw.err, rww.err)
setAfterServeAttributes(span, bw.read, rww.written, rww.statusCode, bw.err, rww.err)
}
func setAfterServeAttributes(span trace.Span, read, wrote, statusCode int64, rerr, werr error) {
kv := make([]kv.KeyValue, 0, 5)
func setAfterServeAttributes(span trace.Span, read, wrote int64, statusCode int, rerr, werr error) {
kv := []kv.KeyValue{}
// TODO: Consider adding an event after each read and write, possibly as an
// option (defaulting to off), so as to not create needlessly verbose spans.
if read > 0 {
@ -134,7 +137,7 @@ func setAfterServeAttributes(span trace.Span, read, wrote, statusCode int64, rer
kv = append(kv, WroteBytesKey.Int64(wrote))
}
if statusCode > 0 {
kv = append(kv, StatusCodeKey.Int64(statusCode))
kv = append(kv, standard.HTTPAttributesFromHTTPStatusCode(statusCode)...)
}
if werr != nil && werr != io.EOF {
kv = append(kv, WriteErrorKey.String(werr.Error()))
@ -147,7 +150,7 @@ func setAfterServeAttributes(span trace.Span, read, wrote, statusCode int64, rer
func WithRouteTag(route string, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
span := trace.SpanFromContext(r.Context())
span.SetAttributes(RouteKey.String(route))
span.SetAttributes(standard.HTTPRouteKey.String(route))
h.ServeHTTP(w, r)
})
}

View File

@ -21,6 +21,7 @@ import (
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/api/propagation"
"go.opentelemetry.io/otel/api/standard"
"go.opentelemetry.io/otel/api/trace"
"google.golang.org/grpc/codes"
@ -88,7 +89,7 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
ctx, span := t.tracer.Start(r.Context(), t.spanNameFormatter("", r), opts...)
r = r.WithContext(ctx)
setBasicAttributes(span, r)
span.SetAttributes(standard.HTTPClientAttributesFromHTTPRequest(r)...)
propagation.InjectHTTP(ctx, t.propagators, r.Header)
res, err := t.rt.RoundTrip(r)
@ -98,7 +99,8 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
return res, err
}
span.SetAttributes(StatusCodeKey.Int(res.StatusCode))
span.SetAttributes(standard.HTTPAttributesFromHTTPStatusCode(res.StatusCode)...)
span.SetStatus(standard.SpanStatusFromHTTPStatusCode(res.StatusCode))
res.Body = &wrappedBody{ctx: ctx, span: span, body: res.Body}
return res, err

View File

@ -68,8 +68,14 @@ type (
// For a Histogram with N defined boundaries, e.g, [x, y, z].
// There are N+1 counts: [-inf, x), [x, y), [y, z), [z, +inf]
Buckets struct {
Boundaries []metric.Number
Counts []metric.Number
// Boundaries are floating point numbers, even when
// aggregating integers.
Boundaries []float64
// Counts are floating point numbers to account for
// the possibility of sampling which allows for
// non-integer count values.
Counts []float64
}
// Histogram returns the count of events in pre-determined buckets.

View File

@ -0,0 +1,129 @@
// Copyright The 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 histogram_test
import (
"context"
"math/rand"
"testing"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
"go.opentelemetry.io/otel/sdk/metric/aggregator/test"
)
const inputRange = 1e6
func benchmarkHistogramSearchFloat64(b *testing.B, size int) {
boundaries := make([]float64, size)
for i := range boundaries {
boundaries[i] = rand.Float64() * inputRange
}
values := make([]float64, b.N)
for i := range values {
values[i] = rand.Float64() * inputRange
}
desc := test.NewAggregatorTest(metric.ValueRecorderKind, metric.Float64NumberKind)
agg := histogram.New(desc, boundaries)
ctx := context.Background()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = agg.Update(ctx, metric.NewFloat64Number(values[i]), desc)
}
}
func BenchmarkHistogramSearchFloat64_1(b *testing.B) {
benchmarkHistogramSearchFloat64(b, 1)
}
func BenchmarkHistogramSearchFloat64_8(b *testing.B) {
benchmarkHistogramSearchFloat64(b, 8)
}
func BenchmarkHistogramSearchFloat64_16(b *testing.B) {
benchmarkHistogramSearchFloat64(b, 16)
}
func BenchmarkHistogramSearchFloat64_32(b *testing.B) {
benchmarkHistogramSearchFloat64(b, 32)
}
func BenchmarkHistogramSearchFloat64_64(b *testing.B) {
benchmarkHistogramSearchFloat64(b, 64)
}
func BenchmarkHistogramSearchFloat64_128(b *testing.B) {
benchmarkHistogramSearchFloat64(b, 128)
}
func BenchmarkHistogramSearchFloat64_256(b *testing.B) {
benchmarkHistogramSearchFloat64(b, 256)
}
func BenchmarkHistogramSearchFloat64_512(b *testing.B) {
benchmarkHistogramSearchFloat64(b, 512)
}
func BenchmarkHistogramSearchFloat64_1024(b *testing.B) {
benchmarkHistogramSearchFloat64(b, 1024)
}
func benchmarkHistogramSearchInt64(b *testing.B, size int) {
boundaries := make([]float64, size)
for i := range boundaries {
boundaries[i] = rand.Float64() * inputRange
}
values := make([]int64, b.N)
for i := range values {
values[i] = int64(rand.Float64() * inputRange)
}
desc := test.NewAggregatorTest(metric.ValueRecorderKind, metric.Int64NumberKind)
agg := histogram.New(desc, boundaries)
ctx := context.Background()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = agg.Update(ctx, metric.NewInt64Number(values[i]), desc)
}
}
func BenchmarkHistogramSearchInt64_1(b *testing.B) {
benchmarkHistogramSearchInt64(b, 1)
}
func BenchmarkHistogramSearchInt64_8(b *testing.B) {
benchmarkHistogramSearchInt64(b, 8)
}
func BenchmarkHistogramSearchInt64_16(b *testing.B) {
benchmarkHistogramSearchInt64(b, 16)
}
func BenchmarkHistogramSearchInt64_32(b *testing.B) {
benchmarkHistogramSearchInt64(b, 32)
}
func BenchmarkHistogramSearchInt64_64(b *testing.B) {
benchmarkHistogramSearchInt64(b, 64)
}
func BenchmarkHistogramSearchInt64_128(b *testing.B) {
benchmarkHistogramSearchInt64(b, 128)
}
func BenchmarkHistogramSearchInt64_256(b *testing.B) {
benchmarkHistogramSearchInt64(b, 256)
}
func BenchmarkHistogramSearchInt64_512(b *testing.B) {
benchmarkHistogramSearchInt64(b, 512)
}
func BenchmarkHistogramSearchInt64_1024(b *testing.B) {
benchmarkHistogramSearchInt64(b, 1024)
}

View File

@ -36,7 +36,7 @@ type (
lock sync.Mutex
current state
checkpoint state
boundaries []metric.Number
boundaries []float64
kind metric.NumberKind
}
@ -44,7 +44,7 @@ type (
// the sum and counts for all observed values and
// the less than equal bucket count for the pre-determined boundaries.
state struct {
bucketCounts []metric.Number
bucketCounts []float64
count metric.Number
sum metric.Number
}
@ -63,23 +63,19 @@ var _ aggregator.Histogram = &Aggregator{}
// Note that this aggregator maintains each value using independent
// atomic operations, which introduces the possibility that
// checkpoints are inconsistent.
func New(desc *metric.Descriptor, boundaries []metric.Number) *Aggregator {
func New(desc *metric.Descriptor, boundaries []float64) *Aggregator {
// Boundaries MUST be ordered otherwise the histogram could not
// be properly computed.
sortedBoundaries := numbers{
numbers: make([]metric.Number, len(boundaries)),
kind: desc.NumberKind(),
}
sortedBoundaries := make([]float64, len(boundaries))
copy(sortedBoundaries.numbers, boundaries)
sort.Sort(&sortedBoundaries)
boundaries = sortedBoundaries.numbers
copy(sortedBoundaries, boundaries)
sort.Float64s(sortedBoundaries)
return &Aggregator{
kind: desc.NumberKind(),
boundaries: boundaries,
current: emptyState(boundaries),
checkpoint: emptyState(boundaries),
boundaries: sortedBoundaries,
current: emptyState(sortedBoundaries),
checkpoint: emptyState(sortedBoundaries),
}
}
@ -117,30 +113,42 @@ func (c *Aggregator) Checkpoint(ctx context.Context, desc *metric.Descriptor) {
c.lock.Unlock()
}
func emptyState(boundaries []metric.Number) state {
func emptyState(boundaries []float64) state {
return state{
bucketCounts: make([]metric.Number, len(boundaries)+1),
bucketCounts: make([]float64, len(boundaries)+1),
}
}
// Update adds the recorded measurement to the current data set.
func (c *Aggregator) Update(_ context.Context, number metric.Number, desc *metric.Descriptor) error {
kind := desc.NumberKind()
asFloat := number.CoerceToFloat64(kind)
bucketID := len(c.boundaries)
for i, boundary := range c.boundaries {
if number.CompareNumber(kind, boundary) < 0 {
if asFloat < boundary {
bucketID = i
break
}
}
// Note: Binary-search was compared using the benchmarks. The following
// code is equivalent to the linear search above:
//
// bucketID := sort.Search(len(c.boundaries), func(i int) bool {
// return asFloat < c.boundaries[i]
// })
//
// The binary search wins for very large boundary sets, but
// the linear search performs better up through arrays between
// 256 and 512 elements, which is a relatively large histogram, so we
// continue to prefer linear search.
c.lock.Lock()
defer c.lock.Unlock()
c.current.count.AddInt64(1)
c.current.sum.AddNumber(kind, number)
c.current.bucketCounts[bucketID].AddUint64(1)
c.current.bucketCounts[bucketID]++
return nil
}
@ -156,27 +164,7 @@ func (c *Aggregator) Merge(oa export.Aggregator, desc *metric.Descriptor) error
c.checkpoint.count.AddNumber(metric.Uint64NumberKind, o.checkpoint.count)
for i := 0; i < len(c.checkpoint.bucketCounts); i++ {
c.checkpoint.bucketCounts[i].AddNumber(metric.Uint64NumberKind, o.checkpoint.bucketCounts[i])
c.checkpoint.bucketCounts[i] += o.checkpoint.bucketCounts[i]
}
return nil
}
// numbers is an auxiliary struct to order histogram bucket boundaries (slice of kv.Number)
type numbers struct {
numbers []metric.Number
kind metric.NumberKind
}
var _ sort.Interface = (*numbers)(nil)
func (n *numbers) Len() int {
return len(n.numbers)
}
func (n *numbers) Less(i, j int) bool {
return -1 == n.numbers[i].CompareNumber(n.kind, n.numbers[j])
}
func (n *numbers) Swap(i, j int) {
n.numbers[i], n.numbers[j] = n.numbers[j], n.numbers[i]
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package histogram
package histogram_test
import (
"context"
@ -24,6 +24,7 @@ import (
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
"go.opentelemetry.io/otel/sdk/metric/aggregator/test"
)
@ -57,36 +58,33 @@ var (
},
}
boundaries = map[metric.NumberKind][]metric.Number{
metric.Float64NumberKind: {metric.NewFloat64Number(500), metric.NewFloat64Number(250), metric.NewFloat64Number(750)},
metric.Int64NumberKind: {metric.NewInt64Number(500), metric.NewInt64Number(250), metric.NewInt64Number(750)},
}
boundaries = []float64{500, 250, 750}
)
func TestHistogramAbsolute(t *testing.T) {
test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
histogram(t, profile, positiveOnly)
testHistogram(t, profile, positiveOnly)
})
}
func TestHistogramNegativeOnly(t *testing.T) {
test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
histogram(t, profile, negativeOnly)
testHistogram(t, profile, negativeOnly)
})
}
func TestHistogramPositiveAndNegative(t *testing.T) {
test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
histogram(t, profile, positiveAndNegative)
testHistogram(t, profile, positiveAndNegative)
})
}
// Validates count, sum and buckets for a given profile and policy
func histogram(t *testing.T, profile test.Profile, policy policy) {
func testHistogram(t *testing.T, profile test.Profile, policy policy) {
ctx := context.Background()
descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind)
agg := New(descriptor, boundaries[profile.NumberKind])
agg := histogram.New(descriptor, boundaries)
all := test.NewNumbers(profile.NumberKind)
@ -107,18 +105,21 @@ func histogram(t *testing.T, profile test.Profile, policy policy) {
asum.CoerceToFloat64(profile.NumberKind),
0.000000001,
"Same sum - "+policy.name)
require.Nil(t, err)
require.NoError(t, err)
count, err := agg.Count()
require.Equal(t, all.Count(), count, "Same count -"+policy.name)
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, len(agg.checkpoint.bucketCounts), len(boundaries[profile.NumberKind])+1, "There should be b + 1 counts, where b is the number of boundaries")
buckets, err := agg.Histogram()
require.NoError(t, err)
require.Equal(t, len(buckets.Counts), len(boundaries)+1, "There should be b + 1 counts, where b is the number of boundaries")
counts := calcBuckets(all.Points(), profile)
for i, v := range counts {
bCount := agg.checkpoint.bucketCounts[i].AsUint64()
require.Equal(t, v, bCount, "Wrong bucket #%d count: %v != %v", i, counts, agg.checkpoint.bucketCounts)
bCount := uint64(buckets.Counts[i])
require.Equal(t, v, bCount, "Wrong bucket #%d count: %v != %v", i, counts, buckets.Counts)
}
}
@ -126,12 +127,12 @@ func TestHistogramInitial(t *testing.T) {
test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind)
agg := New(descriptor, boundaries[profile.NumberKind])
agg := histogram.New(descriptor, boundaries)
buckets, err := agg.Histogram()
require.NoError(t, err)
require.Equal(t, len(buckets.Counts), len(boundaries[profile.NumberKind])+1)
require.Equal(t, len(buckets.Boundaries), len(boundaries[profile.NumberKind]))
require.Equal(t, len(buckets.Counts), len(boundaries)+1)
require.Equal(t, len(buckets.Boundaries), len(boundaries))
})
}
@ -141,8 +142,8 @@ func TestHistogramMerge(t *testing.T) {
test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind)
agg1 := New(descriptor, boundaries[profile.NumberKind])
agg2 := New(descriptor, boundaries[profile.NumberKind])
agg1 := histogram.New(descriptor, boundaries)
agg2 := histogram.New(descriptor, boundaries)
all := test.NewNumbers(profile.NumberKind)
@ -171,18 +172,21 @@ func TestHistogramMerge(t *testing.T) {
asum.CoerceToFloat64(profile.NumberKind),
0.000000001,
"Same sum - absolute")
require.Nil(t, err)
require.NoError(t, err)
count, err := agg1.Count()
require.Equal(t, all.Count(), count, "Same count - absolute")
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, len(agg1.checkpoint.bucketCounts), len(boundaries[profile.NumberKind])+1, "There should be b + 1 counts, where b is the number of boundaries")
buckets, err := agg1.Histogram()
require.NoError(t, err)
require.Equal(t, len(buckets.Counts), len(boundaries)+1, "There should be b + 1 counts, where b is the number of boundaries")
counts := calcBuckets(all.Points(), profile)
for i, v := range counts {
bCount := agg1.checkpoint.bucketCounts[i].AsUint64()
require.Equal(t, v, bCount, "Wrong bucket #%d count: %v != %v", i, counts, agg1.checkpoint.bucketCounts)
bCount := uint64(buckets.Counts[i])
require.Equal(t, v, bCount, "Wrong bucket #%d count: %v != %v", i, counts, buckets.Counts)
}
})
}
@ -193,38 +197,37 @@ func TestHistogramNotSet(t *testing.T) {
test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
descriptor := test.NewAggregatorTest(metric.ValueRecorderKind, profile.NumberKind)
agg := New(descriptor, boundaries[profile.NumberKind])
agg := histogram.New(descriptor, boundaries)
agg.Checkpoint(ctx, descriptor)
asum, err := agg.Sum()
require.Equal(t, metric.Number(0), asum, "Empty checkpoint sum = 0")
require.Nil(t, err)
require.NoError(t, err)
count, err := agg.Count()
require.Equal(t, int64(0), count, "Empty checkpoint count = 0")
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, len(agg.checkpoint.bucketCounts), len(boundaries[profile.NumberKind])+1, "There should be b + 1 counts, where b is the number of boundaries")
for i, bCount := range agg.checkpoint.bucketCounts {
require.Equal(t, uint64(0), bCount.AsUint64(), "Bucket #%d must have 0 observed values", i)
buckets, err := agg.Histogram()
require.NoError(t, err)
require.Equal(t, len(buckets.Counts), len(boundaries)+1, "There should be b + 1 counts, where b is the number of boundaries")
for i, bCount := range buckets.Counts {
require.Equal(t, uint64(0), uint64(bCount), "Bucket #%d must have 0 observed values", i)
}
})
}
func calcBuckets(points []metric.Number, profile test.Profile) []uint64 {
sortedBoundaries := numbers{
numbers: make([]metric.Number, len(boundaries[profile.NumberKind])),
kind: profile.NumberKind,
}
sortedBoundaries := make([]float64, len(boundaries))
copy(sortedBoundaries.numbers, boundaries[profile.NumberKind])
sort.Sort(&sortedBoundaries)
boundaries := sortedBoundaries.numbers
copy(sortedBoundaries, boundaries)
sort.Float64s(sortedBoundaries)
counts := make([]uint64, len(boundaries)+1)
counts := make([]uint64, len(sortedBoundaries)+1)
idx := 0
for _, p := range points {
for idx < len(boundaries) && p.CompareNumber(profile.NumberKind, boundaries[idx]) != -1 {
for idx < len(sortedBoundaries) && p.CoerceToFloat64(profile.NumberKind) >= sortedBoundaries[idx] {
idx++
}
counts[idx]++

View File

@ -81,6 +81,8 @@ func TestMain(m *testing.M) {
os.Exit(m.Run())
}
// TODO: Expose Numbers in api/metric for sorting support
type Numbers struct {
// numbers has to be aligned for 64-bit atomic operations.
numbers []metric.Number

View File

@ -430,7 +430,7 @@ func BenchmarkObserverRegistration(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
fix.meter.RegisterInt64ValueObserver(names[i], cb)
fix.meter.NewInt64ValueObserver(names[i], cb)
}
}
@ -438,7 +438,7 @@ func BenchmarkValueObserverObservationInt64(b *testing.B) {
ctx := context.Background()
fix := newFixture(b)
labs := makeLabels(1)
_ = fix.meter.RegisterInt64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) {
_ = fix.meter.NewInt64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) {
for i := 0; i < b.N; i++ {
result.Observe((int64)(i), labs...)
}
@ -453,7 +453,7 @@ func BenchmarkValueObserverObservationFloat64(b *testing.B) {
ctx := context.Background()
fix := newFixture(b)
labs := makeLabels(1)
_ = fix.meter.RegisterFloat64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Float64ObserverResult) {
_ = fix.meter.NewFloat64ValueObserver("test.valueobserver", func(_ context.Context, result metric.Float64ObserverResult) {
for i := 0; i < b.N; i++ {
result.Observe((float64)(i), labs...)
}

View File

@ -320,13 +320,13 @@ func TestObserverCollection(t *testing.T) {
ctx := context.Background()
meter, sdk, integrator := newSDK(t)
_ = Must(meter).RegisterFloat64ValueObserver("float.valueobserver", func(_ context.Context, result metric.Float64ObserverResult) {
_ = Must(meter).NewFloat64ValueObserver("float.valueobserver", func(_ context.Context, result metric.Float64ObserverResult) {
result.Observe(1, kv.String("A", "B"))
// last value wins
result.Observe(-1, kv.String("A", "B"))
result.Observe(-1, kv.String("C", "D"))
})
_ = Must(meter).RegisterInt64ValueObserver("int.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) {
_ = Must(meter).NewInt64ValueObserver("int.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(-1, kv.String("A", "B"))
result.Observe(1)
// last value wins
@ -334,12 +334,12 @@ func TestObserverCollection(t *testing.T) {
result.Observe(1)
})
_ = Must(meter).RegisterFloat64SumObserver("float.sumobserver", func(_ context.Context, result metric.Float64ObserverResult) {
_ = Must(meter).NewFloat64SumObserver("float.sumobserver", func(_ context.Context, result metric.Float64ObserverResult) {
result.Observe(1, kv.String("A", "B"))
result.Observe(2, kv.String("A", "B"))
result.Observe(1, kv.String("C", "D"))
})
_ = Must(meter).RegisterInt64SumObserver("int.sumobserver", func(_ context.Context, result metric.Int64ObserverResult) {
_ = Must(meter).NewInt64SumObserver("int.sumobserver", func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(2, kv.String("A", "B"))
result.Observe(1)
// last value wins
@ -347,12 +347,12 @@ func TestObserverCollection(t *testing.T) {
result.Observe(1)
})
_ = Must(meter).RegisterFloat64UpDownSumObserver("float.updownsumobserver", func(_ context.Context, result metric.Float64ObserverResult) {
_ = Must(meter).NewFloat64UpDownSumObserver("float.updownsumobserver", func(_ context.Context, result metric.Float64ObserverResult) {
result.Observe(1, kv.String("A", "B"))
result.Observe(-2, kv.String("A", "B"))
result.Observe(1, kv.String("C", "D"))
})
_ = Must(meter).RegisterInt64UpDownSumObserver("int.updownsumobserver", func(_ context.Context, result metric.Int64ObserverResult) {
_ = Must(meter).NewInt64UpDownSumObserver("int.updownsumobserver", func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(2, kv.String("A", "B"))
result.Observe(1)
// last value wins
@ -360,7 +360,7 @@ func TestObserverCollection(t *testing.T) {
result.Observe(-1)
})
_ = Must(meter).RegisterInt64ValueObserver("empty.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) {
_ = Must(meter).NewInt64ValueObserver("empty.valueobserver", func(_ context.Context, result metric.Int64ObserverResult) {
})
collected := sdk.Collect(ctx)
@ -393,13 +393,13 @@ func TestSumObserverInputRange(t *testing.T) {
ctx := context.Background()
meter, sdk, integrator := newSDK(t)
_ = Must(meter).RegisterFloat64SumObserver("float.sumobserver", func(_ context.Context, result metric.Float64ObserverResult) {
_ = Must(meter).NewFloat64SumObserver("float.sumobserver", func(_ context.Context, result metric.Float64ObserverResult) {
result.Observe(-2, kv.String("A", "B"))
require.Equal(t, aggregator.ErrNegativeInput, integrator.sdkErr())
result.Observe(-1, kv.String("C", "D"))
require.Equal(t, aggregator.ErrNegativeInput, integrator.sdkErr())
})
_ = Must(meter).RegisterInt64SumObserver("int.sumobserver", func(_ context.Context, result metric.Int64ObserverResult) {
_ = Must(meter).NewInt64SumObserver("int.sumobserver", func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(-1, kv.String("A", "B"))
require.Equal(t, aggregator.ErrNegativeInput, integrator.sdkErr())
result.Observe(-1)
@ -458,12 +458,12 @@ func TestObserverBatch(t *testing.T) {
intUpDownSumObs.Observation(10),
)
})
floatValueObs = batch.RegisterFloat64ValueObserver("float.valueobserver")
intValueObs = batch.RegisterInt64ValueObserver("int.valueobserver")
floatSumObs = batch.RegisterFloat64SumObserver("float.sumobserver")
intSumObs = batch.RegisterInt64SumObserver("int.sumobserver")
floatUpDownSumObs = batch.RegisterFloat64UpDownSumObserver("float.updownsumobserver")
intUpDownSumObs = batch.RegisterInt64UpDownSumObserver("int.updownsumobserver")
floatValueObs = batch.NewFloat64ValueObserver("float.valueobserver")
intValueObs = batch.NewInt64ValueObserver("int.valueobserver")
floatSumObs = batch.NewFloat64SumObserver("float.sumobserver")
intSumObs = batch.NewInt64SumObserver("int.sumobserver")
floatUpDownSumObs = batch.NewFloat64UpDownSumObserver("float.updownsumobserver")
intUpDownSumObs = batch.NewInt64UpDownSumObserver("int.updownsumobserver")
collected := sdk.Collect(ctx)
@ -570,7 +570,7 @@ func TestIncorrectInstruments(t *testing.T) {
counter = metric.Must(noopMeter).NewInt64Counter("counter")
observer = metric.Must(noopMeter).NewBatchObserver(
func(context.Context, metric.BatchObserverResult) {},
).RegisterInt64ValueObserver("observer")
).NewInt64ValueObserver("observer")
meter.RecordBatch(ctx, nil, counter.Measurement(1))
meter.NewBatchObserver(func(_ context.Context, result metric.BatchObserverResult) {
@ -587,7 +587,7 @@ func TestSyncInAsync(t *testing.T) {
meter, sdk, integrator := newSDK(t)
counter := Must(meter).NewFloat64Counter("counter")
_ = Must(meter).RegisterInt64ValueObserver("observer",
_ = Must(meter).NewInt64ValueObserver("observer",
func(ctx context.Context, result metric.Int64ObserverResult) {
result.Observe(10)
counter.Add(ctx, 100)

View File

@ -26,7 +26,7 @@ import (
func TestStressInt64Histogram(t *testing.T) {
desc := metric.NewDescriptor("some_metric", metric.ValueRecorderKind, metric.Int64NumberKind)
h := histogram.New(&desc, []metric.Number{metric.NewInt64Number(25), metric.NewInt64Number(50), metric.NewInt64Number(75)})
h := histogram.New(&desc, []float64{25, 50, 75})
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
@ -51,7 +51,7 @@ func TestStressInt64Histogram(t *testing.T) {
var realCount int64
for _, c := range b.Counts {
v := c.AsInt64()
v := int64(c)
realCount += v
}

View File

@ -31,7 +31,7 @@ type (
config *ddsketch.Config
}
selectorHistogram struct {
boundaries []metric.Number
boundaries []float64
}
)
@ -75,7 +75,7 @@ func NewWithExactDistribution() export.AggregationSelector {
// histogram, and histogram aggregators for the three kinds of metric. This
// selector uses more memory than the NewWithInexpensiveDistribution because it
// uses a counter per bucket.
func NewWithHistogramDistribution(boundaries []metric.Number) export.AggregationSelector {
func NewWithHistogramDistribution(boundaries []float64) export.AggregationSelector {
return selectorHistogram{boundaries: boundaries}
}

View File

@ -56,7 +56,7 @@ func TestExactDistribution(t *testing.T) {
}
func TestHistogramDistribution(t *testing.T) {
ex := simple.NewWithHistogramDistribution([]metric.Number{})
ex := simple.NewWithHistogramDistribution(nil)
require.NotPanics(t, func() { _ = ex.AggregatorFor(&testCounterDesc).(*sum.Aggregator) })
require.NotPanics(t, func() { _ = ex.AggregatorFor(&testValueRecorderDesc).(*histogram.Aggregator) })
require.NotPanics(t, func() { _ = ex.AggregatorFor(&testValueObserverDesc).(*histogram.Aggregator) })

View File

@ -17,5 +17,5 @@ package opentelemetry // import "go.opentelemetry.io/otel/sdk"
// Version is the current release version of OpenTelemetry in use.
func Version() string {
return "0.5.0"
return "0.6.0"
}

View File

@ -0,0 +1,84 @@
// Copyright The 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 resource_test
import (
"fmt"
"math/rand"
"testing"
"go.opentelemetry.io/otel/api/kv"
"go.opentelemetry.io/otel/sdk/resource"
)
const conflict = 0.5
func makeLabels(n int) (_, _ *resource.Resource) {
used := map[string]bool{}
l1 := make([]kv.KeyValue, n)
l2 := make([]kv.KeyValue, n)
for i := 0; i < n; i++ {
var k string
for {
k = fmt.Sprint("k", rand.Intn(1000000000))
if !used[k] {
used[k] = true
break
}
}
l1[i] = kv.String(k, fmt.Sprint("v", rand.Intn(1000000000)))
if rand.Float64() < conflict {
l2[i] = l1[i]
} else {
l2[i] = kv.String(k, fmt.Sprint("v", rand.Intn(1000000000)))
}
}
return resource.New(l1...), resource.New(l2...)
}
func benchmarkMergeResource(b *testing.B, size int) {
r1, r2 := makeLabels(size)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = resource.Merge(r1, r2)
}
}
func BenchmarkMergeResource_1(b *testing.B) {
benchmarkMergeResource(b, 1)
}
func BenchmarkMergeResource_2(b *testing.B) {
benchmarkMergeResource(b, 2)
}
func BenchmarkMergeResource_3(b *testing.B) {
benchmarkMergeResource(b, 3)
}
func BenchmarkMergeResource_4(b *testing.B) {
benchmarkMergeResource(b, 4)
}
func BenchmarkMergeResource_6(b *testing.B) {
benchmarkMergeResource(b, 6)
}
func BenchmarkMergeResource_8(b *testing.B) {
benchmarkMergeResource(b, 8)
}
func BenchmarkMergeResource_16(b *testing.B) {
benchmarkMergeResource(b, 16)
}

View File

@ -89,15 +89,23 @@ func (r *Resource) Equal(eq *Resource) bool {
// If there are common keys between resource a and b, then the value
// from resource a is preserved.
func Merge(a, b *Resource) *Resource {
if a == nil && b == nil {
return Empty()
}
if a == nil {
a = Empty()
return b
}
if b == nil {
b = Empty()
return a
}
// Note: 'a' labels will overwrite 'b' with last-value-wins in label.Key()
// Meaning this is equivalent to: append(b.Attributes(), a.Attributes()...)
mi := label.NewMergeIterator(a.LabelSet(), b.LabelSet())
combine := make([]kv.KeyValue, 0, a.Len()+b.Len())
for mi.Next() {
combine = append(combine, mi.Label())
}
// Note: 'b' is listed first so that 'a' will overwrite with
// last-value-wins in label.Key()
combine := append(b.Attributes(), a.Attributes()...)
return New(combine...)
}
@ -111,10 +119,15 @@ func Empty() *Resource {
// between two resources. This value is suitable for use as a key in
// a map.
func (r *Resource) Equivalent() label.Distinct {
return r.LabelSet().Equivalent()
}
// LabelSet returns the equivalent *label.Set.
func (r *Resource) LabelSet() *label.Set {
if r == nil {
r = Empty()
}
return r.labels.Equivalent()
return &r.labels
}
// MarshalJSON encodes labels as a JSON list of { "Key": "...", "Value": ... }

View File

@ -21,7 +21,6 @@ import (
export "go.opentelemetry.io/otel/sdk/export/trace"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/api/kv"
apitrace "go.opentelemetry.io/otel/api/trace"
)
@ -195,10 +194,10 @@ func WithConfig(config Config) ProviderOption {
}
}
// WithResourceAttributes option sets the resource attributes to the provider.
// Resource is added to the span when it is started.
func WithResourceAttributes(attrs ...kv.KeyValue) ProviderOption {
// WithResource option attaches a resource to the provider.
// The resource is added to the span when it is started.
func WithResource(r *resource.Resource) ProviderOption {
return func(opts *ProviderOptions) {
opts.config.Resource = resource.New(attrs...)
opts.config.Resource = r
}
}

View File

@ -32,7 +32,6 @@ type Sampler interface {
type SamplingParameters struct {
ParentContext api.SpanContext
TraceID api.ID
SpanID api.SpanID
Name string
HasRemoteParent bool
Kind api.SpanKind

View File

@ -394,7 +394,6 @@ func makeSamplingDecision(data samplingData) SamplingResult {
sampled := sampler.ShouldSample(SamplingParameters{
ParentContext: data.parent,
TraceID: spanContext.TraceID,
SpanID: spanContext.SpanID,
Name: data.name,
HasRemoteParent: data.remoteParent,
Kind: data.kind,

View File

@ -1067,7 +1067,7 @@ func TestWithResource(t *testing.T) {
var te testExporter
tp, _ := NewProvider(WithSyncer(&te),
WithConfig(Config{DefaultSampler: AlwaysSample()}),
WithResourceAttributes(kv.String("rk1", "rv1"), kv.Int64("rk2", 5)))
WithResource(resource.New(kv.String("rk1", "rv1"), kv.Int64("rk2", 5))))
span := startSpan(tp, "WithResource")
span.SetAttributes(kv.String("key1", "value1"))
got, err := endSpan(&te, span)

View File

@ -4,9 +4,7 @@ go 1.13
require (
github.com/client9/misspell v0.3.4
github.com/fatih/color v1.9.0 // indirect
github.com/golangci/golangci-lint v1.25.1
github.com/mattn/go-isatty v0.0.12 // indirect
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 // indirect
github.com/itchyny/gojq v0.10.1
golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e
)

View File

@ -5,8 +5,15 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us=
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/alecthomas/go-thrift v0.0.0-20170109061633-7914173639b2/go.mod h1:CxCgO+NdpMdi9SsTlGbc0W+/UNxO3I0AabOEJZ3w61w=
github.com/alecthomas/kong v0.2.1/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
github.com/alecthomas/participle v0.4.2-0.20191220090139-9fbceec1d131 h1:iPgE4wTIM/fgSreWdpxnKXxaGOgGwfPqc2aVPq2BFSU=
github.com/alecthomas/participle v0.4.2-0.20191220090139-9fbceec1d131/go.mod h1:T8u4bQOSMwrkTWOSyt8/jSFPEnRtd0FKFMjVfYBlqPs=
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@ -29,6 +36,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU=
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
@ -127,10 +136,18 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hokaccha/go-prettyjson v0.0.0-20190818114111-108c894c2c0e/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/itchyny/astgen-go v0.0.0-20200116103543-aaa595cf980e h1:PupVBrJNomt2fTXto19vW8Jh1ftn1oKxgtjSzSuLBZI=
github.com/itchyny/astgen-go v0.0.0-20200116103543-aaa595cf980e/go.mod h1:9Gyr9nZoENI+woes+xm+BFhmvYmAp6bPtXD866pQH9g=
github.com/itchyny/go-flags v1.5.0/go.mod h1:lenkYuCobuxLBAd/HGFE4LRoW8D3B6iXRQfWYJ+MNbA=
github.com/itchyny/gojq v0.10.1 h1:52TnrHnzmenfqUtJ52OfjG16uDoFSu1xYmfVQ5kRMuQ=
github.com/itchyny/gojq v0.10.1/go.mod h1:dJzXXNL1A+1rjDF8tDTzW5vOe4i9iIkKSH21HxV76Sw=
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3 h1:jNYPNLe3d8smommaoQlK7LOA5ESyUJJ+Wf79ZtA7Vp4=
@ -158,6 +175,10 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
github.com/lestrrat-go/strftime v1.0.1 h1:o7qz5pmLzPDLyGW4lG6JvTKPUfTFXwe+vOamIYWtnVU=
github.com/lestrrat-go/strftime v1.0.1/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
@ -171,11 +192,14 @@ github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb h1:RHba4YImhrUVQDHUC
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@ -199,6 +223,8 @@ github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/pbnjay/strptime v0.0.0-20140226051138-5c05b0d668c9 h1:4lfz0keanz7/gAlvJ7lAe9zmE08HXxifBZJC0AdeGKo=
github.com/pbnjay/strptime v0.0.0-20140226051138-5c05b0d668c9/go.mod h1:6Hr+C/olSdkdL3z68MlyXWzwhvwmwN7KuUFXGb3PoOk=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@ -206,6 +232,8 @@ github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@ -264,12 +292,15 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.0/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2 h1:Xr9gkxfOP0KQWXKNqmwe8vEeSUiUj4Rlee9CMVX2ZUQ=
github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto=
github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=
github.com/tetafro/godot v0.2.5 h1:7+EYJM/Z4gYZhBFdRrVm6JTj5ZLw/QI1j4RfEOXJviE=
github.com/tetafro/godot v0.2.5/go.mod h1:pT6/T8+h6//L/LwQcFc4C0xpfy1euZwzS1sHdrFCms0=
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q=
@ -343,8 +374,10 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@ -403,6 +436,7 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

View File

@ -19,5 +19,6 @@ package tools
import (
_ "github.com/client9/misspell/cmd/misspell"
_ "github.com/golangci/golangci-lint/cmd/golangci-lint"
_ "github.com/itchyny/gojq"
_ "golang.org/x/tools/cmd/stringer"
)

View File

@ -63,16 +63,15 @@ for dir in $PACKAGE_DIRS; do
# strip double quotes
replaces=("${replaces[@]%\"}") && \
replaces=("${replaces[@]#\"}") && \
# make an array (-dropreplace=mod1 -dropreplace=mod2 …)
# make an array (-dropreplace=mod1 -dropreplace=mod2 …)
dropreplaces=("${replaces[@]/#/-dropreplace=}") && \
go mod edit -module "oteltmp/${dir}" "${dropreplaces[@]}" && \
go mod tidy)
done
printf "Update done:\n\n"
# Build directories that contain main package. These directories are different than
# Build directories that contain main package. These directories are different than
# directories that contain go.mod files.
#
printf "Build examples:\n"
EXAMPLES=$(./get_main_pkgs.sh ./example)
for ex in $EXAMPLES; do