Skip to content

Commit

Permalink
allow fully random trace ids in xray exporter
Browse files Browse the repository at this point in the history
  • Loading branch information
jj22ee committed Aug 22, 2023
1 parent 23f286c commit 6408d36
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 111 deletions.
27 changes: 27 additions & 0 deletions .chloggen/xray-exporter-allow-random-trace-id.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: 'enhancement'

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: 'awsxrayexporter'

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: "remove xray timestamp restriction on first 32 bits of trace id, allow any random 128-bit trace id"

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [26041]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [user]
7 changes: 1 addition & 6 deletions exporter/awsxrayexporter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,7 @@ propagated by them using the `X-Amzn-Trace-Id` HTTP header. However, other gener
supported by replacing fully-random Trace IDs with X-Ray formatted Trace IDs where necessary:

> AWS X-Ray IDs are the same size as W3C Trace Context IDs but differ in that the first 32 bits of a Trace ID
> is the Unix epoch time when the trace was started. Since X-Ray only allows submission of Trace IDs from the
> past 30 days, received Trace IDs are checked and spans without a valid timestamp are dropped.
This means in order for spans to appear in X-Ray, the client SDK MUST use an X-Ray ID generator. For more
information, see
[configuring the X-Ray exporter](https://aws-otel.github.io/docs/getting-started/x-ray#configuring-the-aws-x-ray-exporter).
> is the Unix epoch time when the trace was started.
The `http` object is populated when the `component` attribute value is `grpc` as well as `http`. Other
synchronous call types should also result in the `http` object being populated.
Expand Down
4 changes: 2 additions & 2 deletions exporter/awsxrayexporter/awsxray_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ func TestXrayAndW3CSpanTraceExport(t *testing.T) {
func TestXrayAndW3CSpanTraceResourceExtraction(t *testing.T) {
td := constructXrayAndW3CSpanData()
logger, _ := zap.NewProduction()
assert.Len(t, extractResourceSpans(generateConfig(t), logger, td), 2, "2 spans have xay trace id")
assert.Len(t, extractResourceSpans(generateConfig(t), logger, td), 4, "4 spans have w3c/xray trace id")
}

func TestW3CSpanTraceResourceExtraction(t *testing.T) {
t.Skip("Flaky test, see https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/9255")
td := constructW3CSpanData()
logger, _ := zap.NewProduction()
assert.Len(t, extractResourceSpans(generateConfig(t), logger, td), 0, "0 spans have xray trace id")
assert.Len(t, extractResourceSpans(generateConfig(t), logger, td), 2, "2 spans have w3c/xray trace id")
}

func TestTelemetryEnabled(t *testing.T) {
Expand Down
39 changes: 7 additions & 32 deletions exporter/awsxrayexporter/internal/translator/segment.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"crypto/rand"
"encoding/binary"
"encoding/hex"
"fmt"
"net/url"
"regexp"
"strings"
Expand Down Expand Up @@ -89,10 +88,7 @@ func MakeSegment(span ptrace.Span, resource pcommon.Resource, indexedAttrs []str
}

// convert trace id
traceID, err := convertToAmazonTraceID(span.TraceID())
if err != nil {
return nil, err
}
traceID := convertToAmazonTraceID(span.TraceID())

attributes := span.Attributes()

Expand All @@ -107,15 +103,11 @@ func MakeSegment(span ptrace.Span, resource pcommon.Resource, indexedAttrs []str
sqlfiltered, sql = makeSQL(span, awsfiltered)
additionalAttrs = addSpecialAttributes(sqlfiltered, indexedAttrs, attributes)
user, annotations, metadata = makeXRayAttributes(additionalAttrs, resource, storeResource, indexedAttrs, indexAllAttrs)
spanLinks, makeSpanLinkErr = makeSpanLinks(span.Links())
spanLinks = makeSpanLinks(span.Links())
name string
namespace string
)

if makeSpanLinkErr != nil {
return nil, makeSpanLinkErr
}

// X-Ray segment names are service names, unlike span names which are methods. Try to find a service name.

// support x-ray specific service name attributes as segment name if it exists
Expand Down Expand Up @@ -291,37 +283,20 @@ func determineAwsOrigin(resource pcommon.Resource) string {
// - A trace_id consists of three numbers separated by hyphens. For example,
// 1-58406520-a006649127e371903a2de979. This includes:
// - The version number, that is, 1.
// - The time of the original request, in Unix epoch time, in 8 hexadecimal digits.
// - 8 hexadecimal digits. If the trace ID originated from XRay SDK or XRay ID generator,
// it will represent the time of the original request, in Unix epoch time.
// - For example, 10:00AM December 2nd, 2016 PST in epoch time is 1480615200 seconds,
// or 58406520 in hexadecimal.
// - Otherwise, the 8 hexadecimal digits will be random.
// - A 96-bit identifier for the trace, globally unique, in 24 hexadecimal digits.
func convertToAmazonTraceID(traceID pcommon.TraceID) (string, error) {
const (
// maxAge of 28 days. AWS has a 30 day limit, let's be conservative rather than
// hit the limit
maxAge = 60 * 60 * 24 * 28

// maxSkew allows for 5m of clock skew
maxSkew = 60 * 5
)

func convertToAmazonTraceID(traceID pcommon.TraceID) string {
var (
content = [traceIDLength]byte{}
epochNow = time.Now().Unix()
traceIDBytes = traceID
epoch = int64(binary.BigEndian.Uint32(traceIDBytes[0:4]))
b = [4]byte{}
)

// If AWS traceID originally came from AWS, no problem. However, if oc generated
// the traceID, then the epoch may be outside the accepted AWS range of within the
// past 30 days.
//
// In that case, we return invalid traceid error
if delta := epochNow - epoch; delta > maxAge || delta < -maxSkew {
return "", fmt.Errorf("invalid xray traceid: %s", traceID)
}

binary.BigEndian.PutUint32(b[0:4], uint32(epoch))

content[0] = '1'
Expand All @@ -330,7 +305,7 @@ func convertToAmazonTraceID(traceID pcommon.TraceID) (string, error) {
content[10] = '-'
hex.Encode(content[identifierOffset:], traceIDBytes[4:16]) // overwrite with identifier

return string(content[0:traceIDLength]), nil
return string(content[0:traceIDLength])
}

func timestampToFloatSeconds(ts pcommon.Timestamp) float64 {
Expand Down
33 changes: 0 additions & 33 deletions exporter/awsxrayexporter/internal/translator/segment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,39 +315,6 @@ func TestClientSpanWithRpcHost(t *testing.T) {
assert.Equal(t, "com.foo.AnimalService", *segment.Name)
}

func TestSpanWithInvalidTraceId(t *testing.T) {
spanName := "platformapi.widgets.searchWidgets"
attributes := make(map[string]interface{})
attributes[conventions.AttributeHTTPMethod] = "GET"
attributes[conventions.AttributeHTTPScheme] = "ipv6"
attributes[conventions.AttributeNetPeerIP] = "2607:f8b0:4000:80c::2004"
attributes[conventions.AttributeNetPeerPort] = "9443"
attributes[conventions.AttributeHTTPTarget] = spanName
resource := constructDefaultResource()
span := constructClientSpan(pcommon.NewSpanIDEmpty(), spanName, ptrace.StatusCodeUnset, "OK", attributes)
timeEvents := constructTimedEventsWithSentMessageEvent(span.StartTimestamp())
timeEvents.CopyTo(span.Events())
traceID := span.TraceID()
traceID[0] = 0x11
span.SetTraceID(traceID)

_, err := MakeSegmentDocumentString(span, resource, nil, false, nil)

assert.NotNil(t, err)
}

func TestSpanWithExpiredTraceId(t *testing.T) {
// First Build expired TraceId
const maxAge = 60 * 60 * 24 * 30
ExpiredEpoch := time.Now().Unix() - maxAge - 1

tempTraceID := newTraceID()
binary.BigEndian.PutUint32(tempTraceID[0:4], uint32(ExpiredEpoch))

_, err := convertToAmazonTraceID(tempTraceID)
assert.NotNil(t, err)
}

func TestFixSegmentName(t *testing.T) {
validName := "EP @ test_15.testing-d\u00F6main.org#GO"
fixedName := fixSegmentName(validName)
Expand Down
10 changes: 3 additions & 7 deletions exporter/awsxrayexporter/internal/translator/span_links.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,15 @@ import (
awsxray "github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/xray"
)

func makeSpanLinks(links ptrace.SpanLinkSlice) ([]awsxray.SpanLinkData, error) {
func makeSpanLinks(links ptrace.SpanLinkSlice) []awsxray.SpanLinkData {
var spanLinkDataArray []awsxray.SpanLinkData

for i := 0; i < links.Len(); i++ {
var spanLinkData awsxray.SpanLinkData
var link = links.At(i)

var spanID = link.SpanID().String()
traceID, err := convertToAmazonTraceID(link.TraceID())

if err != nil {
return nil, err
}
traceID := convertToAmazonTraceID(link.TraceID())

spanLinkData.SpanID = &spanID
spanLinkData.TraceID = &traceID
Expand All @@ -39,5 +35,5 @@ func makeSpanLinks(links ptrace.SpanLinkSlice) ([]awsxray.SpanLinkData, error) {
spanLinkDataArray = append(spanLinkDataArray, spanLinkData)
}

return spanLinkDataArray, nil
return spanLinkDataArray
}
34 changes: 3 additions & 31 deletions exporter/awsxrayexporter/internal/translator/span_links_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
package translator // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsxrayexporter/internal/translator"

import (
"encoding/binary"
"strings"
"testing"
"time"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/pdata/ptrace"
Expand All @@ -28,7 +26,7 @@ func TestSpanLinkSimple(t *testing.T) {

segment, _ := MakeSegment(span, resource, nil, false, nil)

var convertedTraceID, _ = convertToAmazonTraceID(traceID)
var convertedTraceID = convertToAmazonTraceID(traceID)

assert.Equal(t, 1, len(segment.Links))
assert.Equal(t, spanLink.SpanID().String(), *segment.Links[0].SpanID)
Expand Down Expand Up @@ -59,32 +57,6 @@ func TestSpanLinkEmpty(t *testing.T) {
assert.False(t, strings.Contains(jsonStr, "links"))
}

func TestOldSpanLinkError(t *testing.T) {
spanName := "ProcessingMessage"
parentSpanID := newSegmentID()
attributes := make(map[string]interface{})
resource := constructDefaultResource()
span := constructServerSpan(parentSpanID, spanName, ptrace.StatusCodeOk, "OK", attributes)

const maxAge = 60 * 60 * 24 * 30
ExpiredEpoch := time.Now().Unix() - maxAge - 1

var traceID = newTraceID()
binary.BigEndian.PutUint32(traceID[0:4], uint32(ExpiredEpoch))

spanLink := span.Links().AppendEmpty()
spanLink.SetTraceID(traceID)
spanLink.SetSpanID(newSegmentID())

_, error1 := MakeSegment(span, resource, nil, false, nil)

assert.NotNil(t, error1)

_, error2 := MakeSegmentDocumentString(span, resource, nil, false, nil)

assert.NotNil(t, error2)
}

func TestTwoSpanLinks(t *testing.T) {
spanName := "ProcessingMessage"
parentSpanID := newSegmentID()
Expand All @@ -108,8 +80,8 @@ func TestTwoSpanLinks(t *testing.T) {

segment, _ := MakeSegment(span, resource, nil, false, nil)

var convertedTraceID1, _ = convertToAmazonTraceID(traceID1)
var convertedTraceID2, _ = convertToAmazonTraceID(traceID2)
var convertedTraceID1 = convertToAmazonTraceID(traceID1)
var convertedTraceID2 = convertToAmazonTraceID(traceID2)

assert.Equal(t, 2, len(segment.Links))
assert.Equal(t, spanLink1.SpanID().String(), *segment.Links[0].SpanID)
Expand Down

0 comments on commit 6408d36

Please sign in to comment.