Skip to content

Commit

Permalink
[jaeger-v2] Implement UTF-8 sanitizer for OTLP (jaegertracing#6078)
Browse files Browse the repository at this point in the history
## Which problem is this PR solving?
- Resolves jaegertracing#5545 

## Description of the changes
- Continuation of jaegertracing#6077 to
implement the UTF-8 sanitizer to operate on OTLP data.

## How was this change tested?
- Unit tests 

## Checklist
- [x] I have read
https://github.com/jaegertracing/jaeger/blob/master/CONTRIBUTING_GUIDELINES.md
- [x] I have signed all commits
- [x] I have added unit tests for the new functionality
- [x] I have run lint and test steps successfully
  - for `jaeger`: `make lint test`
  - for `jaeger-ui`: `yarn lint` and `yarn test`

---------

Signed-off-by: Mahad Zaryab <[email protected]>
  • Loading branch information
mahadzaryab1 authored Oct 14, 2024
1 parent 20d91c2 commit c7e9f3d
Show file tree
Hide file tree
Showing 4 changed files with 399 additions and 1 deletion.
1 change: 1 addition & 0 deletions cmd/jaeger/internal/sanitizer/sanitizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Func func(traces ptrace.Traces) ptrace.Traces
func NewStandardSanitizers() []Func {
return []Func{
NewEmptyServiceNameSanitizer(),
NewUTF8Sanitizer(),
}
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/jaeger/internal/sanitizer/sanitizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

func TestNewStandardSanitizers(t *testing.T) {
sanitizers := NewStandardSanitizers()
require.Len(t, sanitizers, 1)
require.Len(t, sanitizers, 2)
}

func TestNewChainedSanitizer(t *testing.T) {
Expand Down
95 changes: 95 additions & 0 deletions cmd/jaeger/internal/sanitizer/utf8.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) 2024 The Jaeger Authors.
// SPDX-License-Identifier: Apache-2.0

package sanitizer

import (
"fmt"
"unicode/utf8"

"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/ptrace"
)

const (
invalidSpanName = "invalid-span-name"
invalidTagKey = "invalid-tag-key"
)

// NewUTF8Sanitizer returns a sanitizer function that performs the following sanitizations
// on the trace data:
// - Checks all attributes of the span to ensure that the keys are valid UTF-8 strings
// and all string typed values are valid UTF-8 strings. If any keys or values are invalid,
// they are replaced with a valid UTF-8 string containing debugging data related to the invalidations.
// - Explicitly checks that all span names are valid UTF-8 strings. If they are not, they are replaced
// with a valid UTF-8 string and debugging information is put into the attributes of the span.
func NewUTF8Sanitizer() Func {
return sanitizeUTF8
}

func sanitizeUTF8(traces ptrace.Traces) ptrace.Traces {
resourceSpans := traces.ResourceSpans()
for i := 0; i < resourceSpans.Len(); i++ {
resourceSpan := resourceSpans.At(i)
sanitizeAttributes(resourceSpan.Resource().Attributes())

scopeSpans := resourceSpan.ScopeSpans()
for j := 0; j < scopeSpans.Len(); j++ {
scopeSpan := scopeSpans.At(j)
sanitizeAttributes(scopeSpan.Scope().Attributes())

spans := scopeSpan.Spans()

for k := 0; k < spans.Len(); k++ {
span := spans.At(k)

if !utf8.ValidString(span.Name()) {
sanitized := []byte(span.Name())
newVal := span.Attributes().PutEmptyBytes(invalidSpanName)
newVal.Append(sanitized...)
span.SetName(invalidSpanName)
}

sanitizeAttributes(span.Attributes())
}
}
}

return traces
}

func sanitizeAttributes(attributes pcommon.Map) {
// collect invalid keys during iteration to avoid changing the keys of the map
// while iterating over the map using Range
invalidKeys := make(map[string]pcommon.Value)

attributes.Range(func(k string, v pcommon.Value) bool {
if !utf8.ValidString(k) {
invalidKeys[k] = v
}

if v.Type() == pcommon.ValueTypeStr && !utf8.ValidString(v.Str()) {
sanitized := []byte(v.Str())
newVal := attributes.PutEmptyBytes(k)
newVal.Append(sanitized...)
}
return true
})

i := 1
for k, v := range invalidKeys {
sanitized := []byte(k + ":")
switch v.Type() {
case pcommon.ValueTypeBytes:
sanitized = append(sanitized, v.Bytes().AsRaw()...)
default:
sanitized = append(sanitized, []byte(v.AsString())...)
}

newKey := fmt.Sprintf("%s-%d", invalidTagKey, i)
newVal := attributes.PutEmptyBytes(newKey)
newVal.Append(sanitized...)
i++
attributes.Remove(k)
}
}
Loading

0 comments on commit c7e9f3d

Please sign in to comment.