Skip to content
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
9 changes: 8 additions & 1 deletion ddtrace/tracer/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,10 @@ type config struct {

// llmobs contains the LLM Observability config
llmobs llmobsconfig.Config

// isLambdaFunction, if true, indicates we are in a lambda function
// It is set by checking for a nonempty LAMBDA_FUNCTION_NAME env var.
isLambdaFunction bool
}

// orchestrionConfig contains Orchestrion configuration.
Expand Down Expand Up @@ -463,10 +467,13 @@ func newConfig(opts ...StartOption) (*config, error) {
// TODO: should we track the origin of these tags individually?
c.globalTags.cfgOrigin = telemetry.OriginEnvVar
}
if _, ok := env.Lookup("AWS_LAMBDA_FUNCTION_NAME"); ok {
if v, ok := env.Lookup("AWS_LAMBDA_FUNCTION_NAME"); ok {
// AWS_LAMBDA_FUNCTION_NAME being set indicates that we're running in an AWS Lambda environment.
// See: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html
c.logToStdout = true
if v != "" {
c.isLambdaFunction = true
}
}
c.logStartup = internal.BoolEnv("DD_TRACE_STARTUP_LOGS", true)
c.runtimeMetrics = internal.BoolVal(getDDorOtelConfig("metrics"), false)
Expand Down
70 changes: 64 additions & 6 deletions ddtrace/tracer/spancontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ func (t *trace) finishedOne(s *Span) {
return
}
tc := tr.TracerConf()
setPeerService(s, tc.PeerServiceDefaults, tc.PeerServiceMappings)
setPeerService(s, tc)

// attach the _dd.base_service tag only when the globally configured service name is different from the
// span service name.
Expand Down Expand Up @@ -614,13 +614,24 @@ func (t *trace) finishChunk(tr *tracer, ch *chunk) {

// setPeerService sets the peer.service, _dd.peer.service.source, and _dd.peer.service.remapped_from
// tags as applicable for the given span.
func setPeerService(s *Span, peerServiceDefaults bool, peerServiceMappings map[string]string) {
func setPeerService(s *Span, tc TracerConf) {
spanKind := s.meta[ext.SpanKind]
isOutboundRequest := spanKind == ext.SpanKindClient || spanKind == ext.SpanKindProducer

if _, ok := s.meta[ext.PeerService]; ok { // peer.service already set on the span
s.setMeta(keyPeerServiceSource, ext.PeerService)
} else if isServerless(tc) {
// Set peerService only in outbound Lambda requests
if isOutboundRequest {
if ps := deriveAWSPeerService(s.meta); ps != "" {
s.setMeta(ext.PeerService, ps)
s.setMeta(keyPeerServiceSource, ext.PeerService)
} else {
log.Debug("Unable to set peer.service tag for serverless span %q", s.name)
}
}
} else { // no peer.service currently set
spanKind := s.meta[ext.SpanKind]
isOutboundRequest := spanKind == ext.SpanKindClient || spanKind == ext.SpanKindProducer
shouldSetDefaultPeerService := isOutboundRequest && peerServiceDefaults
shouldSetDefaultPeerService := isOutboundRequest && tc.PeerServiceDefaults
if !shouldSetDefaultPeerService {
return
}
Expand All @@ -633,12 +644,59 @@ func setPeerService(s *Span, peerServiceDefaults bool, peerServiceMappings map[s
}
// Overwrite existing peer.service value if remapped by the user
ps := s.meta[ext.PeerService]
if to, ok := peerServiceMappings[ps]; ok {
if to, ok := tc.PeerServiceMappings[ps]; ok {
s.setMeta(keyPeerServiceRemappedFrom, ps)
s.setMeta(ext.PeerService, to)
}
}

/*
checks if we are in a serverless environment

TODO add checks for Azure functions and other serverless environments
*/
func isServerless(tc TracerConf) bool {
return tc.isLambdaFunction
}

/*
deriveAWSPeerService returns the host name of the
outbound aws service call based on the span metadata,
or an empty string if it cannot be determined.

The mapping is as follows:
- eventbridge: events.<region>.amazonaws.com
- sqs: sqs.<region>.amazonaws.com
- sns: sns.<region>.amazonaws.com
- kinesis: kinesis.<region>.amazonaws.com
- dynamodb: dynamodb.<region>.amazonaws.com
- s3: <bucket>.s3.<region>.amazonaws.com (if Bucket param present)
s3.<region>.amazonaws.com (otherwise)
*/
func deriveAWSPeerService(sm map[string]string) string {
service, region := sm[ext.AWSService], sm[ext.AWSRegion]
if service == "" || region == "" {
return ""
}

s := strings.ToLower(service)
switch s {

case "s3":
if bucket := sm[ext.S3BucketName]; bucket != "" {
return bucket + ".s3." + region + ".amazonaws.com"
}
return "s3." + region + ".amazonaws.com"

case "eventbridge":
return "events." + region + ".amazonaws.com"

case "sqs", "sns", "dynamodb", "kinesis":
return s + "." + region + ".amazonaws.com"
}
return ""
}

// setPeerServiceFromSource sets peer.service from the sources determined
// by the tags on the span. It returns the source tag name that it used for
// the peer.service value, or the empty string if no valid source tag was available.
Expand Down
161 changes: 160 additions & 1 deletion ddtrace/tracer/spancontext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"fmt"
"math"
"strconv"
"strings"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -529,6 +530,160 @@ func TestSpanPeerService(t *testing.T) {
wantPeerServiceSource: "bucketname",
wantPeerServiceRemappedFrom: "",
},
{
name: "AWS-No-Service",
spanOpts: []StartSpanOption{
Tag("span.kind", "client"),
Tag("region", "us-east-2"),
Tag("db.system", "db-system"),
Tag("db.name", "db-name"),
},
peerServiceDefaultsEnabled: true,
peerServiceMappings: nil,
wantPeerService: "",
wantPeerServiceSource: "",
wantPeerServiceRemappedFrom: "",
},
{
name: "AWS-No-Region",
spanOpts: []StartSpanOption{
Tag("span.kind", "client"),
Tag("aws_service", "S3"),
Tag("db.system", "db-system"),
Tag("db.name", "db-name"),
},
peerServiceDefaultsEnabled: true,
peerServiceMappings: nil,
wantPeerService: "",
wantPeerServiceSource: "",
wantPeerServiceRemappedFrom: "",
},
{
name: "AWS-Nonexistent-Service-And-Region",
spanOpts: []StartSpanOption{
Tag("span.kind", "client"),
Tag("aws_service", "notarealservice"),
Tag("region", "notarealregion"),
Tag("db.system", "db-system"),
Tag("db.name", "db-name"),
},
peerServiceDefaultsEnabled: true,
peerServiceMappings: nil,
wantPeerService: "",
wantPeerServiceSource: "",
wantPeerServiceRemappedFrom: "",
},
{
name: "AWS-No-Outbound-Request",
spanOpts: []StartSpanOption{
Tag("aws_service", "S3"),
Tag("db.system", "db-system"),
Tag("db.name", "db-name"),
},
peerServiceDefaultsEnabled: true,
peerServiceMappings: nil,
wantPeerService: "",
wantPeerServiceSource: "",
wantPeerServiceRemappedFrom: "",
},
{
name: "AWS-S3",
spanOpts: []StartSpanOption{
Tag("span.kind", "client"),
Tag("aws_service", "S3"),
Tag("region", "us-east-2"),
Tag("bucketname", "some-bucket"),
Tag("db.system", "db-system"),
Tag("db.name", "db-name"),
},
peerServiceDefaultsEnabled: true,
peerServiceMappings: nil,
wantPeerService: "some-bucket.s3.us-east-2.amazonaws.com",
wantPeerServiceSource: "peer.service",
wantPeerServiceRemappedFrom: "",
},
{
name: "AWS-S3-No-Bucket",
spanOpts: []StartSpanOption{
Tag("span.kind", "client"),
Tag("aws_service", "S3"),
Tag("region", "us-east-2"),
Tag("db.system", "db-system"),
Tag("db.name", "db-name"),
},
peerServiceDefaultsEnabled: true,
peerServiceMappings: nil,
wantPeerService: "s3.us-east-2.amazonaws.com",
wantPeerServiceSource: "peer.service",
wantPeerServiceRemappedFrom: "",
},
{
name: "AWS-DynamoDB",
spanOpts: []StartSpanOption{
Tag("span.kind", "client"),
Tag("aws_service", "DynamoDB"),
Tag("region", "us-east-2"),
Tag("db.system", "db-system"),
Tag("db.name", "db-name"),
},
peerServiceDefaultsEnabled: true,
peerServiceMappings: nil,
wantPeerService: "dynamodb.us-east-2.amazonaws.com",
wantPeerServiceSource: "peer.service",
wantPeerServiceRemappedFrom: "",
},
{
name: "AWS-Kinesis",
spanOpts: []StartSpanOption{
Tag("span.kind", "client"),
Tag("aws_service", "Kinesis"),
Tag("region", "us-east-2"),
},
peerServiceDefaultsEnabled: true,
peerServiceMappings: nil,
wantPeerService: "kinesis.us-east-2.amazonaws.com",
wantPeerServiceSource: "peer.service",
wantPeerServiceRemappedFrom: "",
},
{
name: "AWS-SNS",
spanOpts: []StartSpanOption{
Tag("span.kind", "client"),
Tag("aws_service", "SNS"),
Tag("region", "us-east-2"),
},
peerServiceDefaultsEnabled: true,
peerServiceMappings: nil,
wantPeerService: "sns.us-east-2.amazonaws.com",
wantPeerServiceSource: "peer.service",
wantPeerServiceRemappedFrom: "",
},
{
name: "AWS-SQS",
spanOpts: []StartSpanOption{
Tag("span.kind", "client"),
Tag("aws_service", "SQS"),
Tag("region", "us-east-2"),
},
peerServiceDefaultsEnabled: true,
peerServiceMappings: nil,
wantPeerService: "sqs.us-east-2.amazonaws.com",
wantPeerServiceSource: "peer.service",
wantPeerServiceRemappedFrom: "",
},
{
name: "AWS-Events",
spanOpts: []StartSpanOption{
Tag("span.kind", "client"),
Tag("aws_service", "EventBridge"),
Tag("region", "us-east-2"),
},
peerServiceDefaultsEnabled: true,
peerServiceMappings: nil,
wantPeerService: "events.us-east-2.amazonaws.com",
wantPeerServiceSource: "peer.service",
wantPeerServiceRemappedFrom: "",
},
{
name: "DBClient",
spanOpts: []StartSpanOption{
Expand Down Expand Up @@ -658,7 +813,11 @@ func TestSpanPeerService(t *testing.T) {
}
}
t.Run(tc.name, func(t *testing.T) {
tracer, transport, flush, stop, err := startTestTracer(t)
if strings.Contains(tc.name, "AWS-") {
t.Setenv("AWS_LAMBDA_FUNCTION_NAME", "test_name")
}

tracer, transport, flush, stop, err := startTestTracer(t, WithLambdaMode(false))
assert.Nil(t, err)
defer stop()

Expand Down
2 changes: 2 additions & 0 deletions ddtrace/tracer/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type TracerConf struct { //nolint:revive
VersionTag string
ServiceTag string
TracingAsTransport bool
isLambdaFunction bool
}

// Tracer specifies an implementation of the Datadog tracer which allows starting
Expand Down Expand Up @@ -954,6 +955,7 @@ func (t *tracer) TracerConf() TracerConf {
VersionTag: t.config.version,
ServiceTag: t.config.serviceName,
TracingAsTransport: t.config.tracingAsTransport,
isLambdaFunction: t.config.isLambdaFunction,
}
}

Expand Down
Loading