Skip to content

Commit a51aa9a

Browse files
[SVLS-6247][serverless] Support metric origins in Serverless agents (#35844)
1 parent da19b2c commit a51aa9a

File tree

14 files changed

+337
-9
lines changed

14 files changed

+337
-9
lines changed

comp/dogstatsd/server/default.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2016-present Datadog, Inc.
5+
6+
//go:build !serverless
7+
// +build !serverless
8+
9+
package server
10+
11+
import "github.com/DataDog/datadog-agent/pkg/metrics"
12+
13+
// GetDefaultMetricSource returns the default metric source based on build tags
14+
func GetDefaultMetricSource() metrics.MetricSource {
15+
return metrics.MetricSourceDogstatsd
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2016-present Datadog, Inc.
5+
6+
//go:build serverless
7+
// +build serverless
8+
9+
package server
10+
11+
import (
12+
"os"
13+
14+
"github.com/DataDog/datadog-agent/pkg/metrics"
15+
)
16+
17+
const (
18+
awsLambdaFunctionNameEnvVar = "AWS_LAMBDA_FUNCTION_NAME"
19+
googleCloudRunServiceNameEnvVar = "K_SERVICE"
20+
azureContainerAppNameEnvVar = "CONTAINER_APP_NAME"
21+
azureAppServiceNameEnvVar = "WEBSITE_STACK"
22+
)
23+
24+
// GetDefaultMetricSource returns the default metric source based on build tags
25+
func GetDefaultMetricSource() metrics.MetricSource {
26+
if _, ok := os.LookupEnv(awsLambdaFunctionNameEnvVar); ok {
27+
return metrics.MetricSourceAwsLambdaCustom
28+
}
29+
if _, ok := os.LookupEnv(googleCloudRunServiceNameEnvVar); ok {
30+
return metrics.MetricSourceGoogleCloudRunCustom
31+
}
32+
if _, ok := os.LookupEnv(azureContainerAppNameEnvVar); ok {
33+
return metrics.MetricSourceAzureContainerAppCustom
34+
}
35+
if _, ok := os.LookupEnv(azureAppServiceNameEnvVar); ok {
36+
return metrics.MetricSourceAzureAppServiceCustom
37+
}
38+
39+
return metrics.MetricSourceServerless
40+
}

comp/dogstatsd/server/enrich.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ type enrichConfig struct {
3939
// extractTagsMetadata returns tags (client tags + host tag) and information needed to query tagger (origins, cardinality).
4040
func extractTagsMetadata(tags []string, originFromUDS string, processID uint32, localData origindetection.LocalData, externalData origindetection.ExternalData, cardinality string, conf enrichConfig) ([]string, string, taggertypes.OriginInfo, metrics.MetricSource) {
4141
host := conf.defaultHostname
42-
metricSource := metrics.MetricSourceDogstatsd
42+
metricSource := GetDefaultMetricSource()
4343

4444
// Add Origin Detection metadata
4545
origin := taggertypes.OriginInfo{
@@ -76,6 +76,21 @@ func extractTagsMetadata(tags []string, originFromUDS string, processID uint32,
7676
return tags, host, origin, metricSource
7777
}
7878

79+
// serverlessSourceCustomToRuntime converts Serverless custom metric source to its corresponding runtime metric source
80+
func serverlessSourceCustomToRuntime(metricSource metrics.MetricSource) metrics.MetricSource {
81+
switch metricSource {
82+
case metrics.MetricSourceAwsLambdaCustom:
83+
metricSource = metrics.MetricSourceAwsLambdaRuntime
84+
case metrics.MetricSourceAzureAppServiceCustom:
85+
metricSource = metrics.MetricSourceAzureAppServiceRuntime
86+
case metrics.MetricSourceAzureContainerAppCustom:
87+
metricSource = metrics.MetricSourceAzureContainerAppRuntime
88+
case metrics.MetricSourceGoogleCloudRunCustom:
89+
metricSource = metrics.MetricSourceGoogleCloudRunRuntime
90+
}
91+
return metricSource
92+
}
93+
7994
func enrichMetricType(dogstatsdMetricType metricType) metrics.MetricType {
8095
switch dogstatsdMetricType {
8196
case gaugeType:

comp/dogstatsd/server/server.go

+6
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,12 @@ func (s *server) parseMetricMessage(metricSamples []metrics.MetricSample, parser
773773
} else {
774774
metricSamples[idx].Tags = metricSamples[0].Tags
775775
}
776+
777+
// If we're receiving runtime metrics, we need to convert the default source to the runtime source
778+
if s.enrichConfig.serverlessMode && strings.HasPrefix(metricSamples[idx].Name, "runtime.") {
779+
metricSamples[idx].Source = serverlessSourceCustomToRuntime(metricSamples[idx].Source)
780+
}
781+
776782
dogstatsdMetricPackets.Add(1)
777783
okCnt.Inc()
778784
}

pkg/metrics/metricsource.go

+41
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,21 @@ const (
361361
MetricSourceOpenTelemetryCollectorBigipReceiver
362362
MetricSourceOpenTelemetryCollectorChronyReceiver
363363
MetricSourceOpenTelemetryCollectorCouchdbReceiver
364+
365+
// Serverless
366+
MetricSourceServerless
367+
MetricSourceAwsLambdaCustom
368+
MetricSourceAwsLambdaEnhanced
369+
MetricSourceAwsLambdaRuntime
370+
MetricSourceAzureContainerAppCustom
371+
MetricSourceAzureContainerAppEnhanced
372+
MetricSourceAzureContainerAppRuntime
373+
MetricSourceAzureAppServiceCustom
374+
MetricSourceAzureAppServiceEnhanced
375+
MetricSourceAzureAppServiceRuntime
376+
MetricSourceGoogleCloudRunCustom
377+
MetricSourceGoogleCloudRunEnhanced
378+
MetricSourceGoogleCloudRunRuntime
364379
)
365380

366381
// String returns a string representation of MetricSource
@@ -1028,6 +1043,32 @@ func (ms MetricSource) String() string {
10281043
return "opentelemetry_collector_chronyreceiver"
10291044
case MetricSourceOpenTelemetryCollectorCouchdbReceiver:
10301045
return "opentelemetry_collector_couchdbreceiver"
1046+
case MetricSourceServerless:
1047+
return "serverless"
1048+
case MetricSourceAwsLambdaCustom:
1049+
return "aws_lambda_custom"
1050+
case MetricSourceAwsLambdaEnhanced:
1051+
return "aws_lambda_enhanced"
1052+
case MetricSourceAwsLambdaRuntime:
1053+
return "aws_lambda_runtime"
1054+
case MetricSourceAzureContainerAppCustom:
1055+
return "azure_container_app_custom"
1056+
case MetricSourceAzureContainerAppEnhanced:
1057+
return "azure_container_app_enhanced"
1058+
case MetricSourceAzureContainerAppRuntime:
1059+
return "azure_container_app_runtime"
1060+
case MetricSourceAzureAppServiceCustom:
1061+
return "azure_app_service_custom"
1062+
case MetricSourceAzureAppServiceEnhanced:
1063+
return "azure_app_service_enhanced"
1064+
case MetricSourceAzureAppServiceRuntime:
1065+
return "azure_app_service_runtime"
1066+
case MetricSourceGoogleCloudRunCustom:
1067+
return "google_cloud_run_custom"
1068+
case MetricSourceGoogleCloudRunEnhanced:
1069+
return "google_cloud_run_enhanced"
1070+
case MetricSourceGoogleCloudRunRuntime:
1071+
return "google_cloud_run_runtime"
10311072
default:
10321073
return "<unknown>"
10331074
}

pkg/metrics/series.go

+12
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ type Serie struct {
6262
Source MetricSource `json:"-"` // This is only used by api V2
6363
}
6464

65+
// Metadata holds metadata about the metric
66+
type Metadata struct {
67+
Origin Origin `json:"origin,omitempty"`
68+
}
69+
70+
// Origin holds the metric origins metadata
71+
type Origin struct {
72+
OriginProduct int32 `json:"origin_product,omitempty"`
73+
OriginSubProduct int32 `json:"origin_sub_product,omitempty"`
74+
OriginProductDetail int32 `json:"origin_product_detail,omitempty"`
75+
}
76+
6577
// SeriesAPIV2Enum returns the enumeration value for MetricPayload.MetricType in
6678
// https://github.com/DataDog/agent-payload/blob/master/proto/metrics/agent_payload.proto
6779
func (a APIMetricType) SeriesAPIV2Enum() int32 {

pkg/serializer/internal/metrics/iterable_series.go

+23-6
Original file line numberDiff line numberDiff line change
@@ -499,19 +499,36 @@ func (pb *PayloadsBuilder) finishPayload() error {
499499
// MarshalJSON serializes timeseries to JSON so it can be sent to V1 endpoints
500500
// FIXME(maxime): to be removed when v2 endpoints are available
501501
func (series *IterableSeries) MarshalJSON() ([]byte, error) {
502-
// use an alias to avoid infinite recursion while serializing a Series
503-
type SeriesAlias Series
502+
type SeriesWithMetadata struct {
503+
*metrics.Serie
504+
Metadata metrics.Metadata `json:"metadata,omitempty"`
505+
}
504506

505-
seriesAlias := make(SeriesAlias, 0)
507+
seriesWithMetadata := make([]SeriesWithMetadata, 0)
506508
for series.MoveNext() {
507509
serie := series.source.Current()
508510
serie.PopulateDeviceField()
509511
serie.PopulateResources()
510-
seriesAlias = append(seriesAlias, serie)
512+
513+
serieWithMetadata := SeriesWithMetadata{
514+
Serie: serie,
515+
}
516+
517+
if serie.Source != 0 {
518+
serieWithMetadata.Metadata = metrics.Metadata{
519+
Origin: metrics.Origin{
520+
OriginProduct: metricSourceToOriginProduct(serie.Source),
521+
OriginSubProduct: metricSourceToOriginCategory(serie.Source),
522+
OriginProductDetail: metricSourceToOriginService(serie.Source),
523+
},
524+
}
525+
}
526+
527+
seriesWithMetadata = append(seriesWithMetadata, serieWithMetadata)
511528
}
512529

513-
data := map[string][]*metrics.Serie{
514-
"series": seriesAlias,
530+
data := map[string][]SeriesWithMetadata{
531+
"series": seriesWithMetadata,
515532
}
516533
reqBody := &bytes.Buffer{}
517534
err := json.NewEncoder(reqBody).Encode(data)

pkg/serializer/internal/metrics/origin_mapping.go

+48
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
)
1111

1212
func metricSourceToOriginProduct(ms metrics.MetricSource) int32 {
13+
const serieMetadataOriginOriginProductServerlessType = 1
1314
const serieMetadataOriginOriginProductAgentType = 10
1415
const serieMetadataOriginOriginProductDatadogExporterType = 19
1516
const serieMetadataOriginOriginProductGPU = 38 // ref: https://github.com/DataDog/dd-source/blob/276882b71d84785ec89c31973046ab66d5a01807/domains/metrics/shared/libs/proto/origin/origin.proto#L277
@@ -19,6 +20,22 @@ func metricSourceToOriginProduct(ms metrics.MetricSource) int32 {
1920
if ms == metrics.MetricSourceGPU {
2021
return serieMetadataOriginOriginProductGPU
2122
}
23+
switch ms {
24+
case metrics.MetricSourceServerless,
25+
metrics.MetricSourceAwsLambdaCustom,
26+
metrics.MetricSourceAwsLambdaEnhanced,
27+
metrics.MetricSourceAwsLambdaRuntime,
28+
metrics.MetricSourceAzureContainerAppCustom,
29+
metrics.MetricSourceAzureContainerAppEnhanced,
30+
metrics.MetricSourceAzureContainerAppRuntime,
31+
metrics.MetricSourceAzureAppServiceCustom,
32+
metrics.MetricSourceAzureAppServiceEnhanced,
33+
metrics.MetricSourceAzureAppServiceRuntime,
34+
metrics.MetricSourceGoogleCloudRunCustom,
35+
metrics.MetricSourceGoogleCloudRunEnhanced,
36+
metrics.MetricSourceGoogleCloudRunRuntime:
37+
return serieMetadataOriginOriginProductServerlessType
38+
}
2239
return serieMetadataOriginOriginProductAgentType
2340
}
2441

@@ -332,6 +349,22 @@ func metricSourceToOriginCategory(ms metrics.MetricSource) int32 {
332349
return 11 // integrationMetrics
333350
case metrics.MetricSourceGPU:
334351
return 72 // ref: https://github.com/DataDog/dd-source/blob/276882b71d84785ec89c31973046ab66d5a01807/domains/metrics/shared/libs/proto/origin/origin.proto#L427
352+
case metrics.MetricSourceAzureAppServiceCustom,
353+
metrics.MetricSourceAzureAppServiceEnhanced,
354+
metrics.MetricSourceAzureAppServiceRuntime:
355+
return 35
356+
case metrics.MetricSourceGoogleCloudRunCustom,
357+
metrics.MetricSourceGoogleCloudRunEnhanced,
358+
metrics.MetricSourceGoogleCloudRunRuntime:
359+
return 36
360+
case metrics.MetricSourceAzureContainerAppCustom,
361+
metrics.MetricSourceAzureContainerAppEnhanced,
362+
metrics.MetricSourceAzureContainerAppRuntime:
363+
return 37
364+
case metrics.MetricSourceAwsLambdaCustom,
365+
metrics.MetricSourceAwsLambdaEnhanced,
366+
metrics.MetricSourceAwsLambdaRuntime:
367+
return 38
335368
default:
336369
return 0
337370
}
@@ -1028,6 +1061,21 @@ func metricSourceToOriginService(ms metrics.MetricSource) int32 {
10281061
return 464
10291062
case metrics.MetricSourceInfiniband:
10301063
return 465
1064+
case metrics.MetricSourceAwsLambdaCustom,
1065+
metrics.MetricSourceAzureContainerAppCustom,
1066+
metrics.MetricSourceAzureAppServiceCustom,
1067+
metrics.MetricSourceGoogleCloudRunCustom:
1068+
return 472
1069+
case metrics.MetricSourceAwsLambdaEnhanced,
1070+
metrics.MetricSourceAzureContainerAppEnhanced,
1071+
metrics.MetricSourceAzureAppServiceEnhanced,
1072+
metrics.MetricSourceGoogleCloudRunEnhanced:
1073+
return 473
1074+
case metrics.MetricSourceAwsLambdaRuntime,
1075+
metrics.MetricSourceAzureContainerAppRuntime,
1076+
metrics.MetricSourceAzureAppServiceRuntime,
1077+
metrics.MetricSourceGoogleCloudRunRuntime:
1078+
return 474
10311079
default:
10321080
return 0
10331081
}

pkg/serializer/internal/metrics/sketch_series_list.go

+15-2
Original file line numberDiff line numberDiff line change
@@ -443,12 +443,25 @@ func (sl SketchSeriesList) Marshal() ([]byte, error) {
443443
})
444444
}
445445

446-
pb.Sketches = append(pb.Sketches, gogen.SketchPayload_Sketch{
446+
sketch := gogen.SketchPayload_Sketch{
447447
Metric: ss.Name,
448448
Host: ss.Host,
449449
Tags: ss.Tags.UnsafeToReadOnlySliceString(),
450450
Dogsketches: dsl,
451-
})
451+
}
452+
453+
// Add origin mapping to metadata
454+
if ss.Source != 0 {
455+
sketch.Metadata = &gogen.Metadata{
456+
Origin: &gogen.Origin{
457+
OriginProduct: uint32(metricSourceToOriginProduct(ss.Source)),
458+
OriginCategory: uint32(metricSourceToOriginCategory(ss.Source)),
459+
OriginService: uint32(metricSourceToOriginService(ss.Source)),
460+
},
461+
}
462+
}
463+
464+
pb.Sketches = append(pb.Sketches, sketch)
452465
}
453466
return pb.Marshal()
454467
}

pkg/serializer/internal/metrics/sketch_series_test.go

+30
Original file line numberDiff line numberDiff line change
@@ -335,3 +335,33 @@ func TestSketchSeriesMarshalSplitCompressMultiple(t *testing.T) {
335335
}
336336

337337
}
338+
339+
func TestSketchSeriesListMarshalWithOriginMapping(t *testing.T) {
340+
sl := metrics.NewSketchesSourceTest()
341+
342+
// Create a sketch series with a specific source
343+
ss := Makeseries(0)
344+
ss.Source = metrics.MetricSourceDogstatsd
345+
sl.Append(ss)
346+
347+
serializer := SketchSeriesList{SketchesSource: sl}
348+
b, err := serializer.Marshal()
349+
if err != nil {
350+
t.Fatal(err)
351+
}
352+
353+
pl := new(gogen.SketchPayload)
354+
if err := pl.Unmarshal(b); err != nil {
355+
t.Fatal(err)
356+
}
357+
358+
require.Len(t, pl.Sketches, 1)
359+
pb := pl.Sketches[0]
360+
361+
// Verify the metadata and origin mapping
362+
require.NotNil(t, pb.Metadata)
363+
require.NotNil(t, pb.Metadata.Origin)
364+
assert.Equal(t, uint32(metricSourceToOriginProduct(metrics.MetricSourceDogstatsd)), pb.Metadata.Origin.OriginProduct)
365+
assert.Equal(t, uint32(metricSourceToOriginCategory(metrics.MetricSourceDogstatsd)), pb.Metadata.Origin.OriginCategory)
366+
assert.Equal(t, uint32(metricSourceToOriginService(metrics.MetricSourceDogstatsd)), pb.Metadata.Origin.OriginService)
367+
}

pkg/serverless/invocationlifecycle/lifecycle_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ func TestGenerateEnhancedErrorMetricOnInvocationEnd(t *testing.T) {
6060
Tags: extraTags.Tags,
6161
SampleRate: 1,
6262
Timestamp: float64(endInvocationTime.UnixNano()) / float64(time.Second),
63+
Source: metrics.MetricSourceAwsLambdaEnhanced,
6364
}})
6465
}
6566

@@ -789,6 +790,7 @@ func TestTriggerTypesLifecycleEventForAPIGateway5xxResponse(t *testing.T) {
789790
Tags: extraTags.Tags,
790791
SampleRate: 1,
791792
Timestamp: float64(endTime.Unix()),
793+
Source: metrics.MetricSourceAwsLambdaEnhanced,
792794
}})
793795
assert.Len(t, lateMetrics, 0)
794796

@@ -882,6 +884,7 @@ func TestTriggerTypesLifecycleEventForAPIGatewayNonProxy5xxResponse(t *testing.T
882884
Tags: extraTags.Tags,
883885
SampleRate: 1,
884886
Timestamp: float64(endTime.Unix()),
887+
Source: metrics.MetricSourceAwsLambdaEnhanced,
885888
}})
886889
assert.Len(t, lateMetrics, 0)
887890

@@ -965,6 +968,7 @@ func TestTriggerTypesLifecycleEventForAPIGatewayWebsocket5xxResponse(t *testing.
965968
Tags: extraTags.Tags,
966969
SampleRate: 1,
967970
Timestamp: float64(endTime.Unix()),
971+
Source: metrics.MetricSourceAwsLambdaEnhanced,
968972
}})
969973
assert.Len(t, lateMetrics, 0)
970974

@@ -1052,6 +1056,7 @@ func TestTriggerTypesLifecycleEventForALB5xxResponse(t *testing.T) {
10521056
Tags: extraTags.Tags,
10531057
SampleRate: 1,
10541058
Timestamp: float64(endTime.Unix()),
1059+
Source: metrics.MetricSourceAwsLambdaEnhanced,
10551060
}})
10561061
assert.Len(t, lateMetrics, 0)
10571062

0 commit comments

Comments
 (0)