Skip to content

Commit

Permalink
Add OTLP translation
Browse files Browse the repository at this point in the history
This adds the protocol/otlp package that can translate from OTLP metric types
to SignalFx datapoints.  It follows the same conversion process as the OTEL
collector, but that code cannot be reused due to the internal format used within
the OTEL Collector.

This also adds a decoder that reads from an HTTP request and sends to a
sink.
  • Loading branch information
benkeith-splunk committed Feb 17, 2022
1 parent ccfdf7b commit b304cf4
Show file tree
Hide file tree
Showing 10 changed files with 1,370 additions and 7 deletions.
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ require (
github.com/smartystreets/assertions v1.0.1
github.com/smartystreets/goconvey v1.6.4
github.com/stretchr/testify v1.7.0
google.golang.org/grpc v1.40.0
go.opentelemetry.io/proto/otlp v0.12.0
google.golang.org/grpc v1.43.0
google.golang.org/protobuf v1.27.1
)

require (
Expand All @@ -39,6 +41,5 @@ require (
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/text v0.3.6 // indirect
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
10 changes: 9 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,11 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
Expand Down Expand Up @@ -224,6 +228,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/esimonov/ifshort v1.0.1/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
Expand Down Expand Up @@ -1120,6 +1125,8 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/collector v0.28.0/go.mod h1:AP/BTXwo1eedoJO7V+HQ68CSvJU1lcdqOzJCgt1VsNs=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.12.0 h1:CMJ/3Wp7iOWES+CYLfnBv+DVmPbB+kmy9PJ92XvlR6c=
go.opentelemetry.io/proto/otlp v0.12.0/go.mod h1:TsIjwGWIx5VFYv9KGVlOpxoBl5Dy+63SUguV7GGvlSQ=
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.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
Expand Down Expand Up @@ -1623,8 +1630,9 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
Expand Down
52 changes: 52 additions & 0 deletions protocol/otlp/decoder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package otlp

import (
"bytes"
"context"
"net/http"
"sync"

"github.com/signalfx/golib/v3/datapoint/dpsink"
"github.com/signalfx/golib/v3/log"
"github.com/signalfx/ingest-protocols/logkey"
"github.com/signalfx/ingest-protocols/protocol"
"github.com/signalfx/ingest-protocols/protocol/signalfx"
metricsservicev1 "go.opentelemetry.io/proto/otlp/collector/metrics/v1"
"google.golang.org/protobuf/proto"
)

type httpMetricDecoder struct {
sink dpsink.Sink
logger log.Logger
buffs sync.Pool
}

func NewHTTPMetricDecoder(sink dpsink.Sink, logger log.Logger) signalfx.ErrorReader {
return &httpMetricDecoder{
sink: sink,
logger: log.NewContext(logger).With(logkey.Protocol, "otlp"),
buffs: sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
},
}
}

func (d *httpMetricDecoder) Read(ctx context.Context, req *http.Request) (err error) {
jeff := d.buffs.Get().(*bytes.Buffer)
defer d.buffs.Put(jeff)
jeff.Reset()
if err = protocol.ReadFromRequest(jeff, req, d.logger); err != nil {
return err
}
var msg metricsservicev1.ExportMetricsServiceRequest
if err = proto.Unmarshal(jeff.Bytes(), &msg); err != nil {
return err
}
dps := FromOTLPMetricRequest(&msg)
if len(dps) > 0 {
err = d.sink.AddDatapoints(ctx, dps)
}
return nil
}
99 changes: 99 additions & 0 deletions protocol/otlp/decoder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package otlp

import (
"bytes"
"context"
"errors"
"io"
"net/http"
"sync"
"testing"

"github.com/signalfx/golib/v3/datapoint/dptest"
"github.com/signalfx/golib/v3/log"
. "github.com/smartystreets/goconvey/convey"
metricsservicev1 "go.opentelemetry.io/proto/otlp/collector/metrics/v1"
commonv1 "go.opentelemetry.io/proto/otlp/common/v1"
metricsv1 "go.opentelemetry.io/proto/otlp/metrics/v1"
"google.golang.org/protobuf/proto"
)

var errReadErr = errors.New("could not read")

type errorReader struct{}

func (errorReader *errorReader) Read([]byte) (int, error) {
return 0, errReadErr
}

func TestDecoder(t *testing.T) {
Convey("httpMetricDecoder", t, func() {
sendTo := dptest.NewBasicSink()
decoder := NewHTTPMetricDecoder(sendTo, log.Discard)

Convey("Bad request reading", func() {
req := &http.Request{
Body: io.NopCloser(&errorReader{}),
}
req.ContentLength = 1
ctx := context.Background()
So(decoder.Read(ctx, req), ShouldEqual, errReadErr)
})

Convey("Bad request content", func() {
req := &http.Request{
Body: io.NopCloser(bytes.NewBufferString("asdf")),
}
req.ContentLength = 4
ctx := context.Background()
So(decoder.Read(ctx, req), ShouldNotBeNil)
})

Convey("Good request", func(c C) {
var msg metricsservicev1.ExportMetricsServiceRequest
msg.ResourceMetrics = []*metricsv1.ResourceMetrics{
{
InstrumentationLibraryMetrics: []*metricsv1.InstrumentationLibraryMetrics{
{
Metrics: []*metricsv1.Metric{
{
Name: "test",
Data: &metricsv1.Metric_Gauge{
Gauge: &metricsv1.Gauge{
DataPoints: []*metricsv1.NumberDataPoint{
{
Attributes: []*commonv1.KeyValue{},
StartTimeUnixNano: 1000,
TimeUnixNano: 1000,
Value: &metricsv1.NumberDataPoint_AsInt{AsInt: 4},
},
},
},
},
},
},
},
},
},
}
b, _ := proto.Marshal(&msg)
req := &http.Request{
Body: io.NopCloser(bytes.NewBuffer(b)),
}
req.ContentLength = int64(len(b))
ctx := context.Background()

var wg sync.WaitGroup
wg.Add(1)
go func() {
dp := <-sendTo.PointsChan
c.So(dp, ShouldNotBeNil)
wg.Done()
}()

So(decoder.Read(ctx, req), ShouldBeNil)

wg.Wait()
})
})
}
Loading

0 comments on commit b304cf4

Please sign in to comment.