-
Notifications
You must be signed in to change notification settings - Fork 165
[Serverless] Spawn mini agent in GCP & Azure Functions #4204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
84 commits
Select commit
Hold shift + click to select a range
18a5e71
Update Tracer.cs
thedavl d99700b
mini agent dotnet tracing
thedavl 25f19e4
Update Serverless.cs
thedavl 865495c
testing
thedavl 161a843
unit tests
thedavl a1f76fc
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl fbe5375
add unit tests
thedavl c793f56
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl 600f7d4
Update TracerManagerFactory.cs
thedavl 2fef8f0
fix mini agent path & log redirection
thedavl 1007974
fix mini agent log handling
thedavl df23e5b
fix gcp dotnet path, unit test
thedavl 0c6c462
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl f8d0bbc
fix unit test
thedavl 8d8dbb0
Update ServerlessTests.cs
thedavl 257ae75
Update MiniAgentManager.cs
thedavl 1b87cc8
clean up
thedavl bead95f
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl 4a91000
remove unecessary azure function env var check
thedavl dfcf5a8
default logging paths for azure & gcp functions
thedavl 2a93f23
cleanup
thedavl 42af89f
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl 99d7d2e
Update ServerlessTests.cs
thedavl bde9bc4
refactor
thedavl 6bd2410
remove unused vars
thedavl 66ed2ca
rename mini agent manager tests
thedavl 8925271
remove generated
thedavl f992bd2
add auto gen files
thedavl 6081595
test
thedavl 8a0afcf
Update AspectsDefinitions.g.cs
thedavl 06bcc51
re-add auto gen files
thedavl ee3fd49
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl c175333
generated files bug
thedavl c5b797d
Update ServerlessTests.cs
thedavl 8c7d1b2
refactor & address PR comments
thedavl 12d7c67
keep a reference to mini agent process, so it doesnt get GC-ed
thedavl a714a85
revert accidentally committing auto-gen files
thedavl 6a1effa
address PR comments, refactor #2
thedavl 43ef86f
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl 2aa4f0a
check specifically for consumption plan
thedavl 30db120
extra unit tests
thedavl 6bf1858
Update Tracer.cs
thedavl 933663c
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl b3ec283
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl ad38598
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl f3cf5f8
address PR comments
thedavl 756b550
more fluent assertions
thedavl ce5fd6b
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl 9df6d1a
move mini agent spawning to trace manager
thedavl 40fb69d
Update ConfigurationKeys.AzureAppService.cs
thedavl fb358b1
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl fee0b66
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl bc7da60
cleanup unused imports + newline
thedavl 5620a0f
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl 1cc5a32
address PR comments
thedavl 8aa499f
Apply suggestions from code review
thedavl 95512b8
Apply suggestions from code review
thedavl 70fb7d4
address more PR comments
thedavl bef9495
address PR comments
thedavl 1412986
fix bugs
thedavl 13aca05
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl f9cd577
Update tracer/src/Datadog.Trace/ClrProfiler/ServerlessInstrumentation…
thedavl 5b2485c
remove unnecessary StatsComputationEnabled tests
thedavl f5bcff7
fix unit tests failing on windows (because of hardcoded "/" in paths)
thedavl 902c99e
go all in on the tracer configuration system
thedavl c270108
don't need setters for these fields
thedavl 8f8afdf
or this one
thedavl d388dc8
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl fb9ea5b
remove unused variable
thedavl b196d63
Merge branch 'david.lee/spawn-mini-agent-gcp-azure' of github.com:Dat…
thedavl 73cc3ea
Update TracerSettings.cs
thedavl 0984e26
test az (AAS) function integration test failing
thedavl 7a95b95
revert testing changes
thedavl 14b5c45
add WEBSITE_SKU var to function integration tests
thedavl 881b788
Merge branch 'master' into david.lee/spawn-mini-agent-gcp-azure
thedavl 6fca67a
make WebsiteSKU nullable var
thedavl e397bd8
Merge branch 'david.lee/spawn-mini-agent-gcp-azure' of github.com:Dat…
thedavl cbd1c80
Update ImmutableAzureAppServiceSettings.cs
thedavl 388206d
further mysterious debugging
thedavl 4e868ff
revert testing changes
thedavl 9b5604f
fix integration tests, once and for all
thedavl 410231f
remove unneeded integration test launch settings
thedavl 9372dee
address PR comments
thedavl e2d5767
address nit
thedavl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
165 changes: 165 additions & 0 deletions
165
tracer/src/Datadog.Trace/ClrProfiler/ServerlessInstrumentation/ServerlessMiniAgent.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| // <copyright file="ServerlessMiniAgent.cs" company="Datadog"> | ||
| // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. | ||
| // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. | ||
| // </copyright> | ||
|
|
||
| using System; | ||
| using System.Diagnostics; | ||
| using System.IO; | ||
| using Datadog.Trace.Configuration; | ||
| using Datadog.Trace.Logging; | ||
| using Datadog.Trace.Util; | ||
|
|
||
| namespace Datadog.Trace.ClrProfiler.ServerlessInstrumentation; | ||
|
|
||
| internal static class ServerlessMiniAgent | ||
| { | ||
| private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(ServerlessMiniAgent)); | ||
|
|
||
| internal static string GetMiniAgentPath(PlatformID os, ImmutableTracerSettings settings) | ||
| { | ||
| if (!settings.IsRunningInGCPFunctions && !settings.IsRunningInAzureFunctionsConsumptionPlan) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| if (EnvironmentHelpers.GetEnvironmentVariable("DD_MINI_AGENT_PATH") is { } path) | ||
| { | ||
| return path; | ||
| } | ||
|
|
||
| // Environment.OSVersion.Platform can return PlatformID.Unix on MacOS, this is OK as GCP & Azure don't have MacOs functions. | ||
| if (os != PlatformID.Unix && os != PlatformID.Win32NT) | ||
| { | ||
| Log.Error("Serverless Mini Agent is only supported on Windows and Linux."); | ||
| return null; | ||
| } | ||
|
|
||
| string rustBinaryPathRoot; | ||
| if (settings.IsRunningInGCPFunctions) | ||
| { | ||
| rustBinaryPathRoot = Path.Combine(Path.DirectorySeparatorChar.ToString(), "layers", "google.dotnet.publish", "publish", "bin"); | ||
| } | ||
| else | ||
| { | ||
| rustBinaryPathRoot = Path.Combine(Path.DirectorySeparatorChar.ToString(), "home", "site", "wwwroot"); | ||
| } | ||
|
|
||
| var isWindows = os == PlatformID.Win32NT; | ||
|
|
||
| string rustBinaryPathOsFolder = isWindows ? "datadog-serverless-agent-windows-amd64" : "datadog-serverless-agent-linux-amd64"; | ||
| string rustBinaryName = isWindows ? "datadog-serverless-trace-mini-agent.exe" : "datadog-serverless-trace-mini-agent"; | ||
| return Path.Combine(rustBinaryPathRoot, rustBinaryPathOsFolder, rustBinaryName); | ||
| } | ||
|
|
||
| internal static void StartServerlessMiniAgent(ImmutableTracerSettings settings) | ||
| { | ||
| var serverlessMiniAgentPath = ServerlessMiniAgent.GetMiniAgentPath(Environment.OSVersion.Platform, settings); | ||
| if (string.IsNullOrEmpty(serverlessMiniAgentPath)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| if (!File.Exists(serverlessMiniAgentPath)) | ||
| { | ||
| Log.Error("Serverless Mini Agent does not exist: {Path}", serverlessMiniAgentPath); | ||
| return; | ||
| } | ||
|
|
||
| try | ||
| { | ||
| Log.Debug("Trying to spawn the Serverless Mini Agent at path: {Path}", serverlessMiniAgentPath); | ||
| Process process = new Process(); | ||
| process.StartInfo.FileName = serverlessMiniAgentPath; | ||
| process.StartInfo.UseShellExecute = false; | ||
| process.StartInfo.RedirectStandardOutput = true; | ||
| process.OutputDataReceived += MiniAgentDataReceivedHandler; | ||
|
|
||
| process.Start(); | ||
| process.BeginOutputReadLine(); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| Log.Error(ex, "Error spawning the Serverless Mini Agent."); | ||
| } | ||
| } | ||
|
|
||
| // Tries to clean Mini Agent logs and log to the correct level, otherwise just logs the data as-is to Info | ||
| // Mini Agent logs will be prefixed with "[Datadog Serverless Mini Agent]" | ||
| private static void MiniAgentDataReceivedHandler(object sender, DataReceivedEventArgs outLine) | ||
| { | ||
| var data = outLine.Data; | ||
| if (string.IsNullOrEmpty(data)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var level = "INFO"; | ||
| var log = data; | ||
| ProcessMiniAgentLog(data, out level, out log); | ||
|
|
||
| switch (level) | ||
| { | ||
| case "ERROR": | ||
| Log.Error("[Datadog Serverless Mini Agent] {Data}", log); | ||
| break; | ||
| case "WARN": | ||
| Log.Warning("[Datadog Serverless Mini Agent] {Data}", log); | ||
| break; | ||
| case "INFO": | ||
| Log.Information("[Datadog Serverless Mini Agent] {Data}", log); | ||
| break; | ||
| case "DEBUG": | ||
| Log.Debug("[Datadog Serverless Mini Agent] {Data}", log); | ||
| break; | ||
| default: | ||
| Log.Information("[Datadog Serverless Mini Agent] {Data}", data); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| // Processes a raw log from the mini agent, modifying two "out" parameters level and log. | ||
| // For example, given this raw log: | ||
| // [2023-06-06T01:31:30Z DEBUG datadog_trace_mini_agent::mini_agent] Random log | ||
| // level and log will be the following values: | ||
| // level == "DEBUG", log == "Random log" | ||
| internal static void ProcessMiniAgentLog(string rawLog, out string level, out string log) | ||
| { | ||
| level = "INFO"; | ||
| log = rawLog; | ||
|
|
||
| int logPrefixCutoff = rawLog.IndexOf("]"); | ||
| if (logPrefixCutoff < 0 || logPrefixCutoff == rawLog.Length - 1) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| int levelLeftBound = rawLog.IndexOf(" "); | ||
| if (levelLeftBound < 0) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| int levelRightBound = rawLog.IndexOf(" ", levelLeftBound + 1); | ||
| if (levelRightBound < 0 || levelRightBound - levelLeftBound < 1) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| string parsedLevel = rawLog.Substring(levelLeftBound + 1, levelRightBound - levelLeftBound - 1); | ||
|
|
||
| if (!(parsedLevel is "ERROR" or "WARN" or "INFO" or "DEBUG")) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| level = parsedLevel; | ||
| log = rawLog.Substring(logPrefixCutoff + 2); | ||
|
|
||
| if (level is "DEBUG") | ||
| { | ||
| level = "INFO"; | ||
| log = $"[DEBUG] {log}"; | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,6 +75,12 @@ internal class AzureAppService | |
| /// Used to force the loader to start dogstatsd (in case automatic instrumentation is disabled) | ||
| /// </summary> | ||
| public const string AasEnableCustomMetrics = "DD_AAS_ENABLE_CUSTOM_METRICS"; | ||
|
|
||
| /// <summary> | ||
| /// Used to identify consumption plan functions. Consumption plans will either not have this variable, | ||
| /// or will have a value of "dynamic". | ||
| /// </summary> | ||
| public const string WebsiteSKU = "WEBSITE_SKU"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI, please make sure you add all these new configuration keys to the intake allow list 🙂 You can create a PR for the changes to this file: and then tag apm-dotnet and instrumentation-telemetry channels for review 🙂 Thanks! |
||
| } | ||
| } | ||
| } | ||
36 changes: 36 additions & 0 deletions
36
tracer/src/Datadog.Trace/Configuration/ConfigurationKeys.GCPFunction.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| // <copyright file="ConfigurationKeys.GCPFunction.cs" company="Datadog"> | ||
| // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. | ||
| // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. | ||
| // </copyright> | ||
|
|
||
| namespace Datadog.Trace.Configuration | ||
| { | ||
| internal partial class ConfigurationKeys | ||
| { | ||
| // GCP Function auto-set env var reference: https://cloud.google.com/functions/docs/configuring/env-var#runtime_environment_variables_set_automatically | ||
| internal class GCPFunction | ||
| { | ||
| /// <summary> | ||
| /// The name of functions running deprecated runtimes. | ||
| /// </summary> | ||
| internal const string DeprecatedFunctionNameKey = "FUNCTION_NAME"; | ||
|
|
||
| /// <summary> | ||
| /// The name of the gcp project a deprecated function belongs to. Only set in functions running deprecated runtimes. | ||
| /// Used to tell whether or not we are in a deprecated function environment. | ||
| /// </summary> | ||
| internal const string DeprecatedProjectKey = "GCP_PROJECT"; | ||
|
|
||
| /// <summary> | ||
| /// The name of functions running non-deprecated runtimes. | ||
| /// </summary> | ||
| internal const string FunctionNameKey = "K_SERVICE"; | ||
|
|
||
| /// <summary> | ||
| /// The name of the function handler to be executed when the function is invoked. Only set in functions running non-deprecated runtimes. | ||
| /// Used to tell whether or not we are in a non-deprecated function environment. | ||
| /// </summary> | ||
| internal const string FunctionTargetKey = "FUNCTION_TARGET"; | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
tracer/src/Datadog.Trace/Configuration/ImmutableGCPFunctionSettings.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| // <copyright file="ImmutableGCPFunctionSettings.cs" company="Datadog"> | ||
| // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. | ||
| // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. | ||
| // </copyright> | ||
|
|
||
| #nullable enable | ||
|
|
||
| using System; | ||
| using Datadog.Trace.Configuration.Telemetry; | ||
|
|
||
| namespace Datadog.Trace.Configuration | ||
| { | ||
| /// <summary> | ||
| /// Settings class for gathering metadata about the execution context in Google Cloud Functions | ||
| /// References: | ||
| /// https://cloud.google.com/functions/docs/configuring/env-var#runtime_environment_variables_set_automatically | ||
| /// </summary> | ||
| internal class ImmutableGCPFunctionSettings | ||
|
thedavl marked this conversation as resolved.
|
||
| { | ||
| public ImmutableGCPFunctionSettings(IConfigurationSource? source, IConfigurationTelemetry telemetry) | ||
| { | ||
| source ??= NullConfigurationSource.Instance; | ||
| var config = new ConfigurationBuilder(source, telemetry); | ||
|
|
||
| var deprecatedFunctionKey = config.WithKeys(ConfigurationKeys.GCPFunction.DeprecatedFunctionNameKey).AsString(); | ||
| var deprecatedProjectKey = config.WithKeys(ConfigurationKeys.GCPFunction.DeprecatedProjectKey).AsString(); | ||
| IsDeprecatedFunction = deprecatedFunctionKey != null && deprecatedProjectKey != null; | ||
|
|
||
| var functionNameKey = config.WithKeys(ConfigurationKeys.GCPFunction.FunctionNameKey).AsString(); | ||
| var functionTargetKey = config.WithKeys(ConfigurationKeys.GCPFunction.FunctionTargetKey).AsString(); | ||
| IsNewerFunction = functionNameKey != null && functionTargetKey != null; | ||
|
|
||
| IsGCPFunction = IsDeprecatedFunction || IsNewerFunction; | ||
| } | ||
|
|
||
| public bool IsGCPFunction { get; } | ||
|
|
||
| public bool IsDeprecatedFunction { get; } | ||
|
|
||
| public bool IsNewerFunction { get; } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume it's impossible for a process to stay alive after the execution of a function, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After a function executes azure/gcp will scale down the instance & throttle CPU, though we still have time after the function execution to flush data. So the process will stay alive until the instance is fully stopped (which takes about 15 mins for azure, and a bit longer for GCP)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So is there any risk to start several processes on the same instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New requests routed to an existing instance shouldn't trigger the mini agent to be launched again, as the Tracer instance should still be alive. But if an additional process does try to start, it will shut down quickly when it detects the port is already being used.