Skip to content

Commit 0e4655e

Browse files
authored
[SVLS-8160] Add serverless-identifying tag to Azure App Service Windows profiles (#44488)
### What does this PR do? - Adds `_dd.origin:appservice` tag to profiles for Azure App Service Windows Apps - The tag gets passed via `apm_config.additional_profile_tags`, which is set in [this PR](DataDog/datadog-aas-extension#419) in the AAS Site Extension - Refactors how extra tags get added to profiles - Turns AzureServerlessTags into a map called `AdditionalProfileTags` for clearer future use ### Motivation - This will help the profiling backend filter out and track serverless profiles for billing purposes. [See related PR](#44361) ### Describe how you validated your changes Deployed Node.js and .NET Azure App Service Windows Apps with the Site Extension with `DD_PROFILING_ENABLED=true` and looked at network request to validate that `_dd.origin` is set correctly 1. Downloaded `datadog-trace-agent.exe` and `dogstatsd.exe` from the `package_build > windows_zip_agent_binaries_x64-a7` job 2. Stopped an existing AAS Windows app using the site extension 3. In Kudu, replaced `datadog-trace-agent.exe` and `dogstatsd.exe` with the dev version 4. Copied and pasted the changes in `datadog.yaml` from the related [AAS Extension PR](DataDog/datadog-aas-extension#419) over 5. Started the app 6. Saw `_dd.origin` in the profile tags! <img width="204" height="127" alt="image" src="https://github.com/user-attachments/assets/6a08a29c-be18-4604-9d6f-af7b43c87fa7" /> - Also tested with an Azure Container App to make sure the refactoring didn't affect anything there Co-authored-by: kathie.huang <kathie.huang@datadoghq.com>
1 parent 4125770 commit 0e4655e

File tree

12 files changed

+76
-26
lines changed

12 files changed

+76
-26
lines changed

cmd/serverless-init/main.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ package main
99

1010
import (
1111
"context"
12-
"fmt"
1312
"os"
1413
"strings"
1514
"sync"
@@ -183,20 +182,20 @@ var azureServerlessTags = []string{
183182
}
184183

185184
func setupTraceAgent(tags map[string]string, configuredTags []string, tagger tagger.Component) trace.ServerlessTraceAgent {
186-
var azureTags strings.Builder
185+
azureTags := make(map[string]string)
187186
for _, azureServerlessTag := range azureServerlessTags {
188187
if value, ok := tags[azureServerlessTag]; ok {
189-
azureTags.WriteString(fmt.Sprintf(",%s:%s", azureServerlessTag, value))
188+
azureTags[azureServerlessTag] = value
190189
}
191190
}
192191

193192
// Note: serverless trace tag logic also in comp/trace/payload-modifier/impl/payloadmodifier_test.go
194193
functionTags := strings.Join(configuredTags, ",")
195194
traceAgent := trace.StartServerlessTraceAgent(trace.StartServerlessTraceAgentArgs{
196-
Enabled: pkgconfigsetup.Datadog().GetBool("apm_config.enabled"),
197-
LoadConfig: &trace.LoadConfig{Path: datadogConfigPath, Tagger: tagger},
198-
AzureServerlessTags: azureTags.String(),
199-
FunctionTags: functionTags,
195+
Enabled: pkgconfigsetup.Datadog().GetBool("apm_config.enabled"),
196+
LoadConfig: &trace.LoadConfig{Path: datadogConfigPath, Tagger: tagger},
197+
AdditionalProfileTags: azureTags,
198+
FunctionTags: functionTags,
200199
})
201200
traceAgent.SetTags(tags)
202201
go func() {

comp/trace/config/config_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,11 @@ func TestFullYamlConfig(t *testing.T) {
627627
}, cfg.InstallSignature)
628628

629629
assert.Equal(t, "edge", cfg.APMMode)
630+
631+
assert.Equal(t, map[string]string{
632+
"_dd.origin": "appservice",
633+
"env": "staging",
634+
}, cfg.AdditionalProfileTags)
630635
}
631636

632637
func TestFileLoggingDisabled(t *testing.T) {

comp/trace/config/setup.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ func applyDatadogConfig(c *config.AgentConfig, core corecompcfg.Component) error
681681
c.SendAllInternalStats = core.GetBool("apm_config.send_all_internal_stats") // default is false
682682
c.DebugServerPort = core.GetInt("apm_config.debug.port")
683683
c.ContainerTagsBuffer = core.GetBool("apm_config.enable_container_tags_buffer")
684+
c.AdditionalProfileTags = core.GetStringMapString("apm_config.additional_profile_tags")
684685
return nil
685686
}
686687

comp/trace/config/testdata/full.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,6 @@ apm_config:
9898
max_size: 5555555
9999

100100
mode: edge
101+
additional_profile_tags:
102+
_dd.origin: appservice
103+
env: staging

pkg/config/config_template.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,23 @@ api_key:
17251725
# # Increase this value if you experience timeouts with large profile uploads.
17261726
#
17271727
# profiling_receiver_timeout: 5
1728+
#
1729+
# # @param additional_profile_tags - map of strings - optional
1730+
# # @env DD_APM_ADDITIONAL_PROFILE_TAGS - JSON string - optional
1731+
# # Additional tags to add to all profiles. These tags are added on the agent side
1732+
# # before forwarding profiles to Datadog. This is useful for environment-identifying
1733+
# # tags that should be applied to all profiles (e.g., origin).
1734+
# #
1735+
# # Note: For Azure App Service in Windows, this configuration is set in the AAS Site Extension.
1736+
# # Overriding the default tags in AAS Windows should be done with reference to https://datadoghq.atlassian.net/wiki/spaces/SLS/pages/6007685143/Profiling.
1737+
# #
1738+
# # additional_profile_tags:
1739+
# # _dd.origin: appservice
1740+
# # <TAG_KEY>: <TAG_VALUE>
1741+
#
1742+
# ## DD_APM_ADDITIONAL_PROFILE_TAGS='{"_dd.origin":"appservice","secondtag":"custom"}'
1743+
#
1744+
# # additional_profile_tags: {}
17281745
{{ if .InternalProfiling }}
17291746
# # @param profiling - custom object - optional
17301747
# # Enter specific configurations for internal profiling.

pkg/config/setup/apm.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,12 @@ func setupAPM(config pkgconfigmodel.Setup) {
141141
config.BindEnv("apm_config.profiling_dd_url", "DD_APM_PROFILING_DD_URL") //nolint:forbidigo // TODO: replace by 'SetDefaultAndBindEnv'
142142
config.BindEnv("apm_config.profiling_additional_endpoints", "DD_APM_PROFILING_ADDITIONAL_ENDPOINTS") //nolint:forbidigo // TODO: replace by 'SetDefaultAndBindEnv'
143143
config.BindEnv("apm_config.profiling_receiver_timeout", "DD_APM_PROFILING_RECEIVER_TIMEOUT") //nolint:forbidigo // TODO: replace by 'SetDefaultAndBindEnv'
144-
config.BindEnv("apm_config.additional_endpoints", "DD_APM_ADDITIONAL_ENDPOINTS") //nolint:forbidigo // TODO: replace by 'SetDefaultAndBindEnv'
145-
config.BindEnv("apm_config.replace_tags", "DD_APM_REPLACE_TAGS") //nolint:forbidigo // TODO: replace by 'SetDefaultAndBindEnv'
146-
config.BindEnv("apm_config.analyzed_spans", "DD_APM_ANALYZED_SPANS") //nolint:forbidigo // TODO: replace by 'SetDefaultAndBindEnv'
147-
config.BindEnv("apm_config.ignore_resources", "DD_APM_IGNORE_RESOURCES", "DD_IGNORE_RESOURCE") //nolint:forbidigo // TODO: replace by 'SetDefaultAndBindEnv'
148-
config.BindEnv("apm_config.instrumentation.targets", "DD_APM_INSTRUMENTATION_TARGETS") //nolint:forbidigo // TODO: replace by 'SetDefaultAndBindEnv'
144+
config.BindEnvAndSetDefault("apm_config.additional_profile_tags", map[string]string{}, "DD_APM_ADDITIONAL_PROFILE_TAGS")
145+
config.BindEnv("apm_config.additional_endpoints", "DD_APM_ADDITIONAL_ENDPOINTS") //nolint:forbidigo // TODO: replace by 'SetDefaultAndBindEnv'
146+
config.BindEnv("apm_config.replace_tags", "DD_APM_REPLACE_TAGS") //nolint:forbidigo // TODO: replace by 'SetDefaultAndBindEnv'
147+
config.BindEnv("apm_config.analyzed_spans", "DD_APM_ANALYZED_SPANS") //nolint:forbidigo // TODO: replace by 'SetDefaultAndBindEnv'
148+
config.BindEnv("apm_config.ignore_resources", "DD_APM_IGNORE_RESOURCES", "DD_IGNORE_RESOURCE") //nolint:forbidigo // TODO: replace by 'SetDefaultAndBindEnv'
149+
config.BindEnv("apm_config.instrumentation.targets", "DD_APM_INSTRUMENTATION_TARGETS") //nolint:forbidigo // TODO: replace by 'SetDefaultAndBindEnv'
149150
config.ParseEnvAsSlice("apm_config.instrumentation.targets", func(in string) []interface{} {
150151
var mappings []interface{}
151152
if err := json.Unmarshal([]byte(in), &mappings); err != nil {

pkg/config/setup/config_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,18 @@ func TestPeerTagsEnv(t *testing.T) {
10601060
require.Equal(t, []string{"aws.s3.bucket", "db.instance", "db.system"}, testConfig.GetStringSlice("apm_config.peer_tags"))
10611061
}
10621062

1063+
func TestAdditionalProfileTagsEnv(t *testing.T) {
1064+
testConfig := newTestConf(t)
1065+
require.Empty(t, testConfig.GetStringMapString("apm_config.additional_profile_tags"))
1066+
1067+
t.Setenv("DD_APM_ADDITIONAL_PROFILE_TAGS", `{"_dd.origin":"appservice","env":"staging"}`)
1068+
testConfig = newTestConf(t)
1069+
require.Equal(t, map[string]string{
1070+
"_dd.origin": "appservice",
1071+
"env": "staging",
1072+
}, testConfig.GetStringMapString("apm_config.additional_profile_tags"))
1073+
}
1074+
10631075
func TestLogDefaults(t *testing.T) {
10641076
// New config
10651077
c := newEmptyMockConf(t)

pkg/serverless/trace/trace.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,11 @@ func (l *LoadConfig) Load() (*config.AgentConfig, error) {
9797

9898
// StartServerlessTraceAgentArgs are the arguments for the StartServerlessTraceAgent method
9999
type StartServerlessTraceAgentArgs struct {
100-
Enabled bool
101-
LoadConfig Load
102-
AzureServerlessTags string
103-
FunctionTags string
104-
RCService *remoteconfig.CoreAgentService
100+
Enabled bool
101+
LoadConfig Load
102+
AdditionalProfileTags map[string]string
103+
FunctionTags string
104+
RCService *remoteconfig.CoreAgentService
105105
}
106106

107107
// Start starts the agent
@@ -124,7 +124,7 @@ func StartServerlessTraceAgent(args StartServerlessTraceAgentArgs) ServerlessTra
124124
context, cancel := context.WithCancel(context.Background())
125125
tc.Hostname = ""
126126
tc.SynchronousFlushing = true
127-
tc.AzureServerlessTags = args.AzureServerlessTags
127+
tc.AdditionalProfileTags = args.AdditionalProfileTags
128128
ta := agent.NewAgent(context, tc, telemetry.NewNoopCollector(), &statsd.NoOpClient{}, zstd.NewComponent())
129129

130130
// Check if trace stats should be disabled for serverless

pkg/trace/api/profiles.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,9 @@ func (r *HTTPReceiver) profileProxyHandler() http.Handler {
8686
if orch := r.conf.FargateOrchestrator; orch != config.OrchestratorUnknown {
8787
tags.WriteString(",orchestrator:fargate_" + strings.ToLower(string(orch)))
8888
}
89-
if r.conf.AzureServerlessTags != "" {
90-
tags.WriteString(r.conf.AzureServerlessTags)
89+
// Add any additional environment-identifying tags
90+
for k, v := range r.conf.AdditionalProfileTags {
91+
tags.WriteString(fmt.Sprintf(",%s:%s", k, v))
9192
}
9293

9394
return newProfileProxy(r.conf, targets, keys, tags.String(), r.statsd)

pkg/trace/api/profiles_test.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,15 @@ func TestProfileProxyHandler(t *testing.T) {
300300
}))
301301
conf := newTestReceiverConfig()
302302
conf.ProfilingProxy = config.ProfilingProxyConfig{DDURL: srv.URL}
303-
conf.AzureServerlessTags = ",subscription_id:123,resource_group:test-rg,resource_id:456,aca.subscription.id:123,aca.resource.group:test-rg,aca.resource.id:456,aca.replica.name:test-replica"
303+
conf.AdditionalProfileTags = map[string]string{
304+
"subscription_id": "123",
305+
"resource_group": "test-rg",
306+
"resource_id": "456",
307+
"aca.subscription.id": "123",
308+
"aca.resource.group": "test-rg",
309+
"aca.resource.id": "456",
310+
"aca.replica.name": "test-replica",
311+
}
304312
req, err := http.NewRequest("POST", "/some/path", nil)
305313
if err != nil {
306314
t.Fatal(err)
@@ -331,7 +339,11 @@ func TestProfileProxyHandler(t *testing.T) {
331339
}))
332340
conf := newTestReceiverConfig()
333341
conf.ProfilingProxy = config.ProfilingProxyConfig{DDURL: srv.URL}
334-
conf.AzureServerlessTags = ",aas.subscription.id:123,aas.resource.group:test-rg,aas.resource.id:456"
342+
conf.AdditionalProfileTags = map[string]string{
343+
"aas.subscription.id": "123",
344+
"aas.resource.group": "test-rg",
345+
"aas.resource.id": "456",
346+
}
335347
req, err := http.NewRequest("POST", "/some/path", nil)
336348
if err != nil {
337349
t.Fatal(err)

0 commit comments

Comments
 (0)