Skip to content

Commit

Permalink
Merge pull request #170 from signalfx/translator
Browse files Browse the repository at this point in the history
moving translator from signalfx/sapm-proto to golib to avoid circular
  • Loading branch information
jgheewala authored Jan 17, 2020
2 parents 0895983 + 6b83399 commit d0a2adb
Show file tree
Hide file tree
Showing 11 changed files with 1,227 additions and 4 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ script:
- export GO111MODULE=on
- go mod download
- golangci-lint run
- go test -v -covermode atomic -race -timeout 60s -cpu 4 -parallel 8 -coverprofile ./coverage.out ./...
- go test -v -covermode atomic -race -timeout 60s -cpu 4 -parallel 8 -coverprofile ./coverage.out $(go list ./... | grep -v translator)
- go test -v -covermode atomic -race -timeout 60s -cpu 4 -parallel 8 $(go list ./... | grep -i translator)
- "! go tool cover -func coverage.out | grep -v 100.0%"

after_script:
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ module github.com/signalfx/golib/v3

go 1.13

replace git.apache.org/thrift.git => github.com/signalfx/thrift v0.0.0-20181211001559-3838fa316492

require (
github.com/boltdb/bolt v1.3.1
github.com/dropbox/godropbox v0.0.0-20180512210157-31879d3884b9
github.com/facebookgo/stackerr v0.0.0-20150612192056-c2fcf88613f4
github.com/go-kit/kit v0.9.0
github.com/go-logfmt/logfmt v0.4.0
github.com/go-stack/stack v1.8.0
github.com/gogo/protobuf v1.3.1
github.com/golang/protobuf v1.3.2
github.com/jaegertracing/jaeger v1.15.1
github.com/juju/errors v0.0.0-20181012004132-a4583d0a56ea
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e
github.com/opentracing/opentracing-go v1.1.0
Expand Down
3 changes: 1 addition & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,10 @@ github.com/signalfx/com_signalfx_metrics_protobuf v0.0.0-20190222193949-1fb69526
github.com/signalfx/com_signalfx_metrics_protobuf v0.0.0-20190222193949-1fb69526e884/go.mod h1:muYA2clvwCdj7nzAJ5vJIXYpJsUumhAl4Uu1wUNpWzA=
github.com/signalfx/gohistogram v0.0.0-20160107210732-1ccfd2ff5083 h1:WsShHmu12ZztYPfh9b+I+VjYD1o8iOHhB67WZCMEEE8=
github.com/signalfx/gohistogram v0.0.0-20160107210732-1ccfd2ff5083/go.mod h1:adPDS6s7WaajdFBV9mQ7i0dKfQ8xiDnF9ZNETVPpp7c=
github.com/signalfx/golib v2.5.1+incompatible h1:rw16Flfx5Z29DahDSNGAA3ch8dOeNc3oOMUJm493yao=
github.com/signalfx/golib/v3 v3.0.0/go.mod h1:p+krygP/cDlWvCBEgdkQp3H16rbP4NW7YQa81TDMRe8=
github.com/signalfx/gomemcache v0.0.0-20180823214636-4f7ef64c72a9 h1:M3hb9iDDlB3LrR1xI6wiWIA75Ol9eenD5fbV5d3bxjc=
github.com/signalfx/gomemcache v0.0.0-20180823214636-4f7ef64c72a9/go.mod h1:Ytb8KfCSyuwy/VILnROdgCvbQLA5ch0nkbG7lKT0BXw=
github.com/signalfx/sapm-proto v0.1.1-0.20191217203625-02349a3badae h1:plYuQx78OfsLSQxWxq6NroClbuQhZ4BAF9no5zPhWP4=
github.com/signalfx/sapm-proto v0.1.1-0.20191217203625-02349a3badae/go.mod h1:X/wS1ofuOAW+OTFhCALiHVZvihqMDiNPhzmbusWCQi8=
github.com/signalfx/sapm-proto v0.2.0 h1:uaNhSN9qE8g16YtXSbryPmbkRiyCt8Bw4XTjV/MC5Xg=
github.com/signalfx/sapm-proto v0.2.0/go.mod h1:X/wS1ofuOAW+OTFhCALiHVZvihqMDiNPhzmbusWCQi8=
github.com/signalfx/thrift v0.0.0-20181211001559-3838fa316492 h1:xWH5x1J8QgMdxs1q+jcuVyBKmazAee82UWeS9BPMxzY=
Expand Down
2 changes: 1 addition & 1 deletion sfxclient/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# sfxclient

import "github.com/signalfx/golib/sfxclient"
import "github.com/signalfx/golib/v3/sfxclient"

Package signalfx creates convenient go functions and wrappers to send metrics to
SignalFx.
Expand Down
81 changes: 81 additions & 0 deletions translator/batcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright 2019 Splunk, Inc.
//
// 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 translator

import (
"crypto/sha256"
"fmt"

"github.com/gogo/protobuf/proto"
jaegerpb "github.com/jaegertracing/jaeger/model"
)

type bucketID [32]byte

// spanBatcher is simpler version of OpenTelemetry's Node Batcher.
// spanBatcher takes spans and groups them into Jaeger Batches using
// the Span Process objects.
type spanBatcher struct {
buckets map[bucketID]*jaegerpb.Batch
}

func (b *spanBatcher) add(span *jaegerpb.Span) {
if b.buckets == nil {
b.buckets = make(map[bucketID]*jaegerpb.Batch)
}

batchByProcess := span.Process
id, err := b.genBucketID(span.Process)
if err != nil {
batchByProcess = nil
}

batch := b.getOrAddBatch(id, batchByProcess)
if batch.Process != nil {
span.Process = nil
}
batch.Spans = append(batch.Spans, span)
}

func (b *spanBatcher) batches() []*jaegerpb.Batch {
batches := make([]*jaegerpb.Batch, 0, len(b.buckets))
for _, b := range b.buckets {
batches = append(batches, b)
}
return batches
}

func (b *spanBatcher) genBucketID(process *jaegerpb.Process) (bucketID, error) {
if process != nil {
sortTags(process.Tags)
key, err := proto.Marshal(process)
if err != nil {
return bucketID{}, fmt.Errorf("error generating bucket ID: %s", err.Error())
}
return sha256.Sum256(key), nil
}
return bucketID{}, nil
}

func (b *spanBatcher) getOrAddBatch(id bucketID, p *jaegerpb.Process) *jaegerpb.Batch {
batch, ok := b.buckets[id]
if !ok {
batch = &jaegerpb.Batch{
Process: p,
}
b.buckets[id] = batch
}
return batch
}
120 changes: 120 additions & 0 deletions translator/batcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright 2019 Splunk, Inc.
//
// 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 translator

import (
"testing"

jaegerpb "github.com/jaegertracing/jaeger/model"
"github.com/stretchr/testify/assert"
)

func TestBatcher(t *testing.T) {
p1 := &jaegerpb.Process{
ServiceName: "s1",
Tags: []jaegerpb.KeyValue{
{
Key: "k1",
VType: jaegerpb.ValueType_STRING,
VStr: "v1",
},
{
Key: "k2",
VType: jaegerpb.ValueType_STRING,
VStr: "v2",
},
},
}
p2 := &jaegerpb.Process{
ServiceName: "s1",
Tags: []jaegerpb.KeyValue{
{
Key: "k2",
VType: jaegerpb.ValueType_STRING,
VStr: "v2",
},
{
Key: "k1",
VType: jaegerpb.ValueType_STRING,
VStr: "v1",
},
},
}
p3 := &jaegerpb.Process{ServiceName: "s2"}
p4 := &jaegerpb.Process{ServiceName: "s3"}

spans := []*jaegerpb.Span{
{Process: p1, SpanID: jaegerpb.SpanID(1)},
{Process: p1, SpanID: jaegerpb.SpanID(2)},
{Process: p2, SpanID: jaegerpb.SpanID(3)},
{Process: p3, SpanID: jaegerpb.SpanID(4)},
{Process: p3, SpanID: jaegerpb.SpanID(5)},
{Process: p4, SpanID: jaegerpb.SpanID(6)},
{Process: p4, SpanID: jaegerpb.SpanID(7)},
{SpanID: jaegerpb.SpanID(8)},
{SpanID: jaegerpb.SpanID(9)},
}

b := &spanBatcher{}
for _, s := range spans {
b.add(s)
}

batches := b.batches()
assert.Len(t, batches, 4)

b1 := findBatchWithProcessServiceName(batches, p1)
assert.NotNil(t, b1)
assert.Equal(t, b1.Process, p1)
assertSpansAreEqual(t, b1.Spans, []*jaegerpb.Span{spans[0], spans[1], spans[2]})

b2 := findBatchWithProcessServiceName(batches, p2)
assert.Equal(t, b1, b2)

b3 := findBatchWithProcessServiceName(batches, p3)
assert.NotNil(t, b3)
assert.Equal(t, b3.Process, p3)
assertSpansAreEqual(t, b3.Spans, []*jaegerpb.Span{spans[3], spans[4]})

b4 := findBatchWithProcessServiceName(batches, p4)
assert.NotNil(t, b4)
assert.Equal(t, b4.Process, p4)
assertSpansAreEqual(t, b4.Spans, []*jaegerpb.Span{spans[5], spans[6]})

var nilProcess *jaegerpb.Process
b5 := findBatchWithProcessServiceName(batches, nil)
assert.NotNil(t, b5)
assert.Equal(t, b5.Process, nilProcess)
assertSpansAreEqual(t, b5.Spans, []*jaegerpb.Span{spans[7], spans[8]})

for _, s := range spans {
assert.Nil(t, s.Process)
}
}

func findBatchWithProcessServiceName(batches []*jaegerpb.Batch, p *jaegerpb.Process) *jaegerpb.Batch {
for _, b := range batches {
if p == nil {
if b.Process == nil {
return b
}
} else {
if b.Process != nil && b.Process.ServiceName == p.ServiceName {
return b
}
}
}
return nil
}
26 changes: 26 additions & 0 deletions translator/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2019 Splunk, Inc.
//
// 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 translator

const (
peerHostIPv4 = "peer.ipv4"
peerHostIPv6 = "peer.ipv6"
peerPort = "peer.port"
spanKind = "span.kind"
spanKindRPCClient = "client"
spanKindRPCServer = "server"
spanKindProducer = "producer"
spanKindConsumer = "consumer"
)
100 changes: 100 additions & 0 deletions translator/grpc_http_mapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2019 Splunk, Inc.
//
// 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 translator

import (
"net/http"
)

// This code is taken from OpenTelemetry project in order to avoid adding the whole project as a dependency.

// https://github.com/googleapis/googleapis/blob/bee79fbe03254a35db125dc6d2f1e9b752b390fe/google/rpc/code.proto#L33-L186
const (
OCOK = 0
OCCancelled = 1
OCUnknown = 2
OCInvalidArgument = 3
OCDeadlineExceeded = 4
OCNotFound = 5
OCAlreadyExists = 6
OCPermissionDenied = 7
OCResourceExhausted = 8
OCFailedPrecondition = 9
OCAborted = 10
OCOutOfRange = 11
OCUnimplemented = 12
OCInternal = 13
OCUnavailable = 14
OCDataLoss = 15
OCUnauthenticated = 16
)

var httpToOCCodeMap = map[int32]int32{
401: OCUnauthenticated,
403: OCPermissionDenied,
404: OCNotFound,
429: OCResourceExhausted,
499: OCCancelled,
501: OCUnimplemented,
503: OCUnavailable,
504: OCDeadlineExceeded,
}

// OCStatusCodeFromHTTP takes an HTTP status code and return the appropriate OpenTelemetry status code
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-http.md
func OCStatusCodeFromHTTP(code int32) int32 {
if code >= 100 && code < 400 {
return OCOK
}
if rvCode, ok := httpToOCCodeMap[code]; ok {
return rvCode
}
if code >= 400 && code < 500 {
return OCInvalidArgument
}
if code >= 500 && code < 600 {
return OCInternal
}
return OCUnknown
}

var ocToHTTPCodeMap = map[int32]int32{
OCOK: http.StatusOK,
OCCancelled: 499,
OCUnknown: http.StatusInternalServerError,
OCInvalidArgument: http.StatusBadRequest,
OCDeadlineExceeded: http.StatusGatewayTimeout,
OCNotFound: http.StatusNotFound,
OCAlreadyExists: http.StatusConflict,
OCPermissionDenied: http.StatusForbidden,
OCResourceExhausted: http.StatusTooManyRequests,
OCFailedPrecondition: http.StatusPreconditionFailed,
OCAborted: http.StatusConflict,
OCOutOfRange: http.StatusRequestedRangeNotSatisfiable,
OCUnimplemented: http.StatusNotImplemented,
OCInternal: http.StatusInternalServerError,
OCUnavailable: http.StatusServiceUnavailable,
OCDataLoss: http.StatusUnprocessableEntity,
OCUnauthenticated: http.StatusUnauthorized,
}

// HTTPStatusCodeFromOCStatus takes an OpenTelemetry status code and return the appropriate HTTP status code
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-http.md
func HTTPStatusCodeFromOCStatus(code int32) int32 {
if rvCode, ok := ocToHTTPCodeMap[code]; ok {
return rvCode
}
return http.StatusInternalServerError
}
Loading

0 comments on commit d0a2adb

Please sign in to comment.