Skip to content
This repository was archived by the owner on Apr 2, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ We use the following categories for changes:
- The `chunks_created` metrics was removed. [#1634]
- Stop logging as an error grpc NotFound and Canceled status codes [#1645]
- TimescaleDB is now mandatory [#1660].
- When querying for Jaeger tags with binary values the binary data will be
returned instead of the base64 representation of the string [#1649].

### Fixed
- Do not collect telemetry if `timescaledb.telemetry_level=off` [#1612]
Expand Down
95 changes: 95 additions & 0 deletions pkg/jaeger/store/binary_tags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package store

import (
"encoding/base64"
"fmt"
"strings"

"github.com/jaegertracing/jaeger/model"
)

const MEDIA_TYPE_ENCODED_BINARY = "data:application/octet-stream; base64,"
const MEDIA_TYPE_ENCODED_BINARY_LEN = len(MEDIA_TYPE_ENCODED_BINARY)

// decodeSpanBinaryTags decodes the tags with binary values that are present in
// the binaryTags sets for the span, process and logs.
//
// When writing binary tags we encode the slice of bytes into a base64 string
// representation and add the prefix `__ValueType_BINARY__`. Decoding implies
// removing the prefix and decoding the base64 string.
func decodeSpanBinaryTags(span *model.Span) {
decodeBinaryTags(span.Tags)
decodeBinaryTags(span.Process.Tags)
for _, log := range span.Logs {
decodeBinaryTags(log.Fields)
}
}

func decodeBinaryTags(tags []model.KeyValue) {
for i, tag := range tags {
if tag.GetVType() != model.ValueType_STRING {
continue
}

encoded := tag.VStr
if !strings.HasPrefix(encoded, MEDIA_TYPE_ENCODED_BINARY) {
continue
}

vBin, err := decodeBinaryTagValue(encoded)

// If we can't decode it means that we didn't encode it in the
// first place, so we should keep it as is.
if err != nil {
continue
}
tags[i] = model.KeyValue{
Key: tag.Key,
VType: model.ValueType_BINARY,
VBinary: vBin,
}
}
}

func decodeBinaryTagValue(encoded string) ([]byte, error) {
v := encoded[MEDIA_TYPE_ENCODED_BINARY_LEN:]
return base64.StdEncoding.DecodeString(v)
}

func encodeBinaryTagToStr(tag model.KeyValue) model.KeyValue {
value := fmt.Sprintf("%s%s", MEDIA_TYPE_ENCODED_BINARY, base64.StdEncoding.EncodeToString(tag.GetVBinary()))
return model.KeyValue{
Key: tag.Key,
VType: model.ValueType_STRING,
VStr: value,
}
}

func encodeBinaryTags(span *model.Span) {
for i, tag := range span.Tags {
if !isBinaryTag(tag) {
continue
}
span.Tags[i] = encodeBinaryTagToStr(tag)
}

for _, log := range span.Logs {
for i, tag := range log.Fields {
if !isBinaryTag(tag) {
continue
}
log.Fields[i] = encodeBinaryTagToStr(tag)
}
}

for i, tag := range span.Process.Tags {
if !isBinaryTag(tag) {
continue
}
span.Process.Tags[i] = encodeBinaryTagToStr(tag)
}
}

func isBinaryTag(tag model.KeyValue) bool {
return tag.GetVType() == model.ValueType_BINARY
}
181 changes: 181 additions & 0 deletions pkg/jaeger/store/binary_tags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package store

import (
"encoding/base64"
"fmt"
"testing"

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

var binaryValue1 = []byte{66, 105, 110, 97, 114, 121}
var binaryValue2 = []byte{66, 105, 110, 97, 114, 121}

func keyValuesFixture(prefix string) []model.KeyValue {
return []model.KeyValue{
{
Key: fmt.Sprintf("%s-binary1-data", prefix),
VBinary: binaryValue1,
VType: model.ValueType_BINARY,
},
{
Key: fmt.Sprintf("%s-string-data", prefix),
VStr: "My string",
VType: model.ValueType_STRING,
},
{
Key: fmt.Sprintf("%s-int64-data", prefix),
VInt64: 42,
VType: model.ValueType_INT64,
},
{
Key: fmt.Sprintf("%s-binary2-data", prefix),
VBinary: binaryValue2,
VType: model.ValueType_BINARY,
},
{
Key: fmt.Sprintf("%s-float64-data", prefix),
VFloat64: 42.42,
VType: model.ValueType_FLOAT64,
},
{
Key: fmt.Sprintf("%s-bool-data", prefix),
VBool: true,
VType: model.ValueType_BOOL,
},
}
}

func getExpectedStrV(key string, binaryValue []byte) model.KeyValue {
return model.KeyValue{
Key: key,
VStr: fmt.Sprintf("%s%s", MEDIA_TYPE_ENCODED_BINARY, base64.StdEncoding.EncodeToString(binaryValue)),
VType: model.ValueType_STRING,
}
}

func assertEncoded(t *testing.T, prefix string, encodedTags []model.KeyValue) {
// Binary values are at position 0 and 3
key1 := fmt.Sprintf("%s-binary1-data", prefix)
assert.Equal(t, getExpectedStrV(key1, binaryValue1), encodedTags[0])

key2 := fmt.Sprintf("%s-binary2-data", prefix)
assert.Equal(t, getExpectedStrV(key2, binaryValue2), encodedTags[3])
}

func assertOnlyBinariesModified(t *testing.T, binaryVIdx map[int]struct{}, expected []model.KeyValue, actual []model.KeyValue) {
assert.Equal(t, len(expected), len(actual))
for i, tag := range actual {
_, isBinary := binaryVIdx[i]
if isBinary {
assert.NotEqual(t, expected[i], tag)
} else {
assert.Equal(t, expected[i], tag)
}
}

}

func TestEncodeBinaryTag(t *testing.T) {
logs := []model.Log{
{
Fields: keyValuesFixture("log1"),
},
{
Fields: keyValuesFixture("log2"),
},
}
process := model.Process{
Tags: keyValuesFixture("process"),
}
span := model.Span{
Tags: keyValuesFixture("span"),
Process: &process,
Logs: logs,
}

encodeBinaryTags(&span)

// All the binary items are in the same index position because we are using
// the same []KeyValue fixture for span, process and logs;
binaryVIdxs := map[int]struct{}{0: {}, 3: {}}

assertEncoded(t, "span", span.Tags)
assertOnlyBinariesModified(t, binaryVIdxs, keyValuesFixture("span"), span.Tags)
assertEncoded(t, "process", span.Process.Tags)
assertOnlyBinariesModified(t, binaryVIdxs, keyValuesFixture("process"), span.Process.Tags)
assertEncoded(t, "log1", span.Logs[0].Fields)
assertOnlyBinariesModified(t, binaryVIdxs, keyValuesFixture("log1"), span.Logs[0].Fields)
assertEncoded(t, "log2", span.Logs[1].Fields)
assertOnlyBinariesModified(t, binaryVIdxs, keyValuesFixture("log2"), span.Logs[1].Fields)
}

func keyValuesEncodedFixture() []model.KeyValue {
return []model.KeyValue{
{
Key: "binary1-data",
VStr: fmt.Sprintf("%s%s", MEDIA_TYPE_ENCODED_BINARY, base64.StdEncoding.EncodeToString(binaryValue1)),
VType: model.ValueType_STRING,
},
{
Key: "string-data",
VStr: "My string",
VType: model.ValueType_STRING,
},
{
Key: "int64-data",
VInt64: 42,
VType: model.ValueType_INT64,
},
{
Key: "binary2-data",
VStr: fmt.Sprintf("%s%s", MEDIA_TYPE_ENCODED_BINARY, base64.StdEncoding.EncodeToString(binaryValue2)),
VType: model.ValueType_STRING,
},
{
Key: "float64-data",
VFloat64: 42.42,
VType: model.ValueType_FLOAT64,
},
{
Key: "bool-data",
VBool: true,
VType: model.ValueType_BOOL,
},
{
Key: "no-prefix",
VStr: base64.StdEncoding.EncodeToString(binaryValue1),
VType: model.ValueType_STRING,
},
{
Key: "no-binary-with-prefix",
VStr: MEDIA_TYPE_ENCODED_BINARY + "a normal string tag",
VType: model.ValueType_STRING,
},
}
}

func TestDecodeBinaryTags(t *testing.T) {
fixture := keyValuesEncodedFixture()
decodeBinaryTags(fixture)

expectedBinaryV := model.KeyValue{
Key: "binary1-data",
VBinary: binaryValue1,
VType: model.ValueType_BINARY,
}
expectedBinaryV2 := model.KeyValue{
Key: "binary2-data",
VBinary: binaryValue2,
VType: model.ValueType_BINARY,
}

assert.Equal(t, expectedBinaryV, fixture[0])
assert.Equal(t, expectedBinaryV2, fixture[3])

// Only the string values that have the MEDIA_TYPE_ENCODED_BINARY prefix
// and are followed by a valid base64 encoded string are modified.
binaryVIdxs := map[int]struct{}{0: {}, 3: {}}
assertOnlyBinariesModified(t, binaryVIdxs, keyValuesEncodedFixture(), fixture)
}
1 change: 1 addition & 0 deletions pkg/jaeger/store/find_traces.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func batchSliceToTraceSlice(bSlice []*model.Batch) []*model.Trace {
}
//copy over the process from the batch
span.Process = batch.Process
decodeSpanBinaryTags(span)
trace.Spans = append(trace.Spans, span)
}
}
Expand Down
1 change: 1 addition & 0 deletions pkg/jaeger/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func (p *Store) StreamingSpanWriter() spanstore.Writer {
}

func (p *Store) WriteSpan(ctx context.Context, span *model.Span) error {
encodeBinaryTags(span)
batches := []*model.Batch{
{
Spans: []*model.Span{span},
Expand Down