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:
commit
b2ec53f483
@ -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
|
||||
```
|
||||
|
@ -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) {},
|
||||
)
|
||||
|
@ -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) {}))
|
||||
},
|
||||
}
|
||||
)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
303
api/standard/http.go
Normal 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
777
api/standard/http_test.go
Normal 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()
|
||||
}
|
@ -33,7 +33,6 @@ func (ns alwaysOffSampler) ShouldSample(
|
||||
_ SpanContext,
|
||||
_ bool,
|
||||
_ ID,
|
||||
_ SpanID,
|
||||
_ string,
|
||||
_ SpanKind,
|
||||
_ []kv.KeyValue,
|
||||
|
@ -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)
|
||||
|
@ -33,7 +33,6 @@ func (as alwaysOnSampler) ShouldSample(
|
||||
_ SpanContext,
|
||||
_ bool,
|
||||
_ ID,
|
||||
_ SpanID,
|
||||
_ string,
|
||||
_ SpanKind,
|
||||
_ []kv.KeyValue,
|
||||
|
@ -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)
|
||||
|
@ -24,7 +24,6 @@ type Sampler interface {
|
||||
sc SpanContext,
|
||||
remote bool,
|
||||
traceID ID,
|
||||
spanID SpanID,
|
||||
spanName string,
|
||||
spanKind SpanKind,
|
||||
attributes []kv.KeyValue,
|
||||
|
@ -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
|
||||
|
@ -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"),
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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`.
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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=
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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"),
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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...)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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=
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
//
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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()),
|
||||
)
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
129
sdk/metric/aggregator/histogram/benchmark_test.go
Normal file
129
sdk/metric/aggregator/histogram/benchmark_test.go
Normal 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)
|
||||
}
|
@ -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]
|
||||
}
|
||||
|
@ -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]++
|
||||
|
@ -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
|
||||
|
@ -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...)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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}
|
||||
}
|
||||
|
||||
|
@ -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) })
|
||||
|
@ -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"
|
||||
}
|
||||
|
84
sdk/resource/benchmark_test.go
Normal file
84
sdk/resource/benchmark_test.go
Normal 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)
|
||||
}
|
@ -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": ... }
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
)
|
||||
|
36
tools/go.sum
36
tools/go.sum
@ -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=
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user