Skip to content

Commit

Permalink
Merge branch 'mike/baggage-predicate' of github.com:honeycombio/opent…
Browse files Browse the repository at this point in the history
…elemetry-go-contrib into mike/baggage-predicate
  • Loading branch information
MikeGoldsmith committed Jun 5, 2024
2 parents 1e9c77a + b55754a commit 0c9ddbe
Show file tree
Hide file tree
Showing 35 changed files with 862 additions and 172 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Added

- The `go.opentelemetry.io/contrib/config` add support to configure periodic reader interval and timeout. (#5661)
- Add support to configure views when creating MeterProvider using the config package. (#5654)

### Fixed

Expand Down
63 changes: 61 additions & 2 deletions bridges/otelzap/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package otelzap // import "go.opentelemetry.io/contrib/bridges/otelzap"

import (
"fmt"
"reflect"
"time"

"go.uber.org/zap/zapcore"
Expand Down Expand Up @@ -93,8 +95,12 @@ func (m *objectEncoder) AddUint64(k string, v uint64) {
})
}

// TODO.
func (m *objectEncoder) AddReflected(k string, v interface{}) error {
m.kv = append(m.kv,
log.KeyValue{
Key: k,
Value: convertValue(v),
})
return nil
}

Expand Down Expand Up @@ -177,8 +183,8 @@ func (a *arrayEncoder) AppendObject(v zapcore.ObjectMarshaler) error {
return err
}

// TODO.
func (a *arrayEncoder) AppendReflected(v interface{}) error {
a.elems = append(a.elems, convertValue(v))
return nil
}

Expand Down Expand Up @@ -231,3 +237,56 @@ func (a *arrayEncoder) AppendUint32(v uint32) { a.AppendInt64(int64(v))
func (a *arrayEncoder) AppendUint16(v uint16) { a.AppendInt64(int64(v)) }
func (a *arrayEncoder) AppendUint8(v uint8) { a.AppendInt64(int64(v)) }
func (a *arrayEncoder) AppendUintptr(v uintptr) { a.AppendUint64(uint64(v)) }

func convertValue(v interface{}) log.Value {
switch v := v.(type) {
case bool:
return log.BoolValue(v)
case []byte:
return log.BytesValue(v)
case float64:
return log.Float64Value(v)
case int:
return log.IntValue(v)
case int64:
return log.Int64Value(v)
case string:
return log.StringValue(v)
}

t := reflect.TypeOf(v)
if t == nil {
return log.Value{}
}
val := reflect.ValueOf(v)
switch t.Kind() {
case reflect.Struct:
return log.StringValue(fmt.Sprintf("%+v", v))
case reflect.Slice, reflect.Array:
items := make([]log.Value, 0, val.Len())
for i := 0; i < val.Len(); i++ {
items = append(items, convertValue(val.Index(i).Interface()))
}
return log.SliceValue(items...)
case reflect.Map:
kvs := make([]log.KeyValue, 0, val.Len())
for _, k := range val.MapKeys() {
var key string
// If the key is a struct, use %+v to print the struct fields.
if k.Kind() == reflect.Struct {
key = fmt.Sprintf("%+v", k.Interface())
} else {
key = fmt.Sprintf("%v", k.Interface())
}
kvs = append(kvs, log.KeyValue{
Key: key,
Value: convertValue(val.MapIndex(k).Interface()),
})
}
return log.MapValue(kvs...)
case reflect.Ptr, reflect.Interface:
return convertValue(val.Elem().Interface())
}

return log.StringValue(fmt.Sprintf("unhandled attribute type: (%s) %+v", t, v))
}
14 changes: 14 additions & 0 deletions bridges/otelzap/encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ func TestObjectEncoder(t *testing.T) {
},
expected: []interface{}{wantTurducken, wantTurducken},
},
{
desc: "AddReflected",
f: func(e zapcore.ObjectEncoder) {
assert.NoError(t, e.AddReflected("k", map[string]interface{}{"foo": 5}), "Expected AddReflected to succeed.")
},
expected: map[string]interface{}{"foo": int64(5)},
},
{
desc: "AddBinary",
f: func(e zapcore.ObjectEncoder) { e.AddBinary("k", []byte("foo")) },
Expand Down Expand Up @@ -207,6 +214,13 @@ func TestArrayEncoder(t *testing.T) {
},
expected: []interface{}{true, false},
},
{
desc: "AppendReflected",
f: func(e zapcore.ArrayEncoder) {
assert.NoError(t, e.AppendReflected(map[string]interface{}{"foo": 5}))
},
expected: map[string]interface{}{"foo": int64(5)},
},
{"AppendBool", func(e zapcore.ArrayEncoder) { e.AppendBool(true) }, true},
{"AppendByteString", func(e zapcore.ArrayEncoder) { e.AppendByteString([]byte("foo")) }, "foo"},
{"AppendFloat64", func(e zapcore.ArrayEncoder) { e.AppendFloat64(3.14) }, 3.14},
Expand Down
6 changes: 3 additions & 3 deletions config/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ require (
github.com/prometheus/procfs v0.15.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect
go.opentelemetry.io/proto/otlp v1.2.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/grpc v1.64.0 // indirect
Expand Down
12 changes: 6 additions & 6 deletions config/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IO
go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw=
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8=
Expand Down
162 changes: 161 additions & 1 deletion config/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,22 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
otelprom "go.opentelemetry.io/otel/exporters/prometheus"
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/noop"
"go.opentelemetry.io/otel/sdk/instrumentation"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
)

var zeroScope instrumentation.Scope

const instrumentKindUndefined = sdkmetric.InstrumentKind(0)

func meterProvider(cfg configOptions, res *resource.Resource) (metric.MeterProvider, shutdownFunc, error) {
if cfg.opentelemetryConfig.MeterProvider == nil {
return noop.NewMeterProvider(), noopShutdown, nil
Expand All @@ -45,6 +51,15 @@ func meterProvider(cfg configOptions, res *resource.Resource) (metric.MeterProvi
errs = append(errs, err)
}
}
for _, vw := range cfg.opentelemetryConfig.MeterProvider.Views {
v, err := view(vw)
if err == nil {
opts = append(opts, sdkmetric.WithView(v))
} else {
errs = append(errs, err)
}
}

if len(errs) > 0 {
return noop.NewMeterProvider(), noopShutdown, errors.Join(errs...)
}
Expand Down Expand Up @@ -156,7 +171,7 @@ func otlpHTTPMetricExporter(ctx context.Context, otlpConfig *OTLPMetric) (sdkmet
}

func otlpGRPCMetricExporter(ctx context.Context, otlpConfig *OTLPMetric) (sdkmetric.Exporter, error) {
opts := []otlpmetricgrpc.Option{}
var opts []otlpmetricgrpc.Option

if len(otlpConfig.Endpoint) > 0 {
u, err := url.ParseRequestURI(otlpConfig.Endpoint)
Expand Down Expand Up @@ -266,3 +281,148 @@ func (rws readerWithServer) Shutdown(ctx context.Context) error {
rws.server.Shutdown(ctx),
)
}

func view(v View) (sdkmetric.View, error) {
if v.Selector == nil {
return nil, errors.New("view: no selector provided")
}

inst, err := instrument(*v.Selector)
if err != nil {
return nil, err
}

return sdkmetric.NewView(inst, stream(v.Stream)), nil
}

func instrument(vs ViewSelector) (sdkmetric.Instrument, error) {
kind, err := instrumentKind(vs.InstrumentType)
if err != nil {
return sdkmetric.Instrument{}, fmt.Errorf("view_selector: %w", err)
}
inst := sdkmetric.Instrument{
Name: strOrEmpty(vs.InstrumentName),
Unit: strOrEmpty(vs.Unit),
Kind: kind,
Scope: instrumentation.Scope{
Name: strOrEmpty(vs.MeterName),
Version: strOrEmpty(vs.MeterVersion),
SchemaURL: strOrEmpty(vs.MeterSchemaUrl),
},
}

if instrumentIsEmpty(inst) {
return sdkmetric.Instrument{}, errors.New("view_selector: empty selector not supporter")
}
return inst, nil
}

func stream(vs *ViewStream) sdkmetric.Stream {
if vs == nil {
return sdkmetric.Stream{}
}

return sdkmetric.Stream{
Name: strOrEmpty(vs.Name),
Description: strOrEmpty(vs.Description),
Aggregation: aggregation(vs.Aggregation),
AttributeFilter: attributeFilter(vs.AttributeKeys),
}
}

func attributeFilter(attributeKeys []string) attribute.Filter {
var attrKeys []attribute.Key
for _, attrStr := range attributeKeys {
attrKeys = append(attrKeys, attribute.Key(attrStr))
}
return attribute.NewAllowKeysFilter(attrKeys...)
}

func aggregation(aggr *ViewStreamAggregation) sdkmetric.Aggregation {
if aggr == nil {
return nil
}

if aggr.Base2ExponentialBucketHistogram != nil {
return sdkmetric.AggregationBase2ExponentialHistogram{
MaxSize: int32(intOrZero(aggr.Base2ExponentialBucketHistogram.MaxSize)),
MaxScale: int32(intOrZero(aggr.Base2ExponentialBucketHistogram.MaxScale)),
// Need to negate because config has the positive action RecordMinMax.
NoMinMax: !boolOrFalse(aggr.Base2ExponentialBucketHistogram.RecordMinMax),
}
}
if aggr.Default != nil {
// TODO: Understand what to set here.
return nil
}
if aggr.Drop != nil {
return sdkmetric.AggregationDrop{}
}
if aggr.ExplicitBucketHistogram != nil {
return sdkmetric.AggregationExplicitBucketHistogram{
Boundaries: aggr.ExplicitBucketHistogram.Boundaries,
// Need to negate because config has the positive action RecordMinMax.
NoMinMax: !boolOrFalse(aggr.ExplicitBucketHistogram.RecordMinMax),
}
}
if aggr.LastValue != nil {
return sdkmetric.AggregationLastValue{}
}
if aggr.Sum != nil {
return sdkmetric.AggregationSum{}
}
return nil
}

func instrumentKind(vsit *ViewSelectorInstrumentType) (sdkmetric.InstrumentKind, error) {
if vsit == nil {
// Equivalent to instrumentKindUndefined.
return instrumentKindUndefined, nil
}

switch *vsit {
case ViewSelectorInstrumentTypeCounter:
return sdkmetric.InstrumentKindCounter, nil
case ViewSelectorInstrumentTypeUpDownCounter:
return sdkmetric.InstrumentKindUpDownCounter, nil
case ViewSelectorInstrumentTypeHistogram:
return sdkmetric.InstrumentKindHistogram, nil
case ViewSelectorInstrumentTypeObservableCounter:
return sdkmetric.InstrumentKindObservableCounter, nil
case ViewSelectorInstrumentTypeObservableUpDownCounter:
return sdkmetric.InstrumentKindObservableUpDownCounter, nil
case ViewSelectorInstrumentTypeObservableGauge:
return sdkmetric.InstrumentKindObservableGauge, nil
}

return instrumentKindUndefined, errors.New("instrument_type: invalid value")
}

func instrumentIsEmpty(i sdkmetric.Instrument) bool {
return i.Name == "" &&
i.Description == "" &&
i.Kind == instrumentKindUndefined &&
i.Unit == "" &&
i.Scope == zeroScope
}

func boolOrFalse(pBool *bool) bool {
if pBool == nil {
return false
}
return *pBool
}

func intOrZero(pInt *int) int {
if pInt == nil {
return 0
}
return *pInt
}

func strOrEmpty(pStr *string) string {
if pStr == nil {
return ""
}
return *pStr
}
Loading

0 comments on commit 0c9ddbe

Please sign in to comment.