Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
12f6483
Add serverless compat layer instrumentation for pipe coordination
Lewis-E Feb 5, 2026
91f1c92
Improve validation for windows pipe name
Lewis-E Feb 5, 2026
bfb08b6
Lazy compat layer named pipe generation
Lewis-E Feb 5, 2026
bfeee23
Minor cleanup
Lewis-E Feb 5, 2026
9bdde5b
Target compat layer specifically
Lewis-E Feb 5, 2026
3ae5285
Fix compilation and StyleCop errors
Lewis-E Feb 5, 2026
9b6f2b0
Now with generated files
Lewis-E Feb 5, 2026
b380a24
Add named pipes to packages map
Lewis-E Feb 6, 2026
46cef63
Include serverless classes in .net standard 2.0 build
Lewis-E Feb 6, 2026
b7346a6
Update source-generated files for Serverless compat instrumentation
Lewis-E Feb 6, 2026
5b54c47
Modify unit tests to ignore compat values
Lewis-E Feb 6, 2026
e4c217b
More test fixes
Lewis-E Feb 6, 2026
4ae1405
Fix test helpers to use backing fields for ExporterSettings pipe names
Lewis-E Feb 11, 2026
4295303
Fix ExporterSettings readonly test by making Azure Functions pipe nam…
Lewis-E Mar 2, 2026
79a4b5e
Widen ServerlessCompat version range to 0.0.0 and add trimming entries
Lewis-E Mar 4, 2026
2bda3a7
Regenerate calltargets and trimming files for MinimumVersion 0.0.0
Lewis-E Mar 4, 2026
b8a2f7e
Deduplicate pipe name generation, remove double-logging, use collecti…
Lewis-E Mar 4, 2026
e3c299c
Add unit tests for ServerlessCompat pipe name generation and integrat…
Lewis-E Mar 5, 2026
bf99610
Gate pipe transport on compat layer presence and version
Lewis-E Mar 6, 2026
197cabf
Fix compilation errors in IsCompatLayerAvailableWithPipeSupport
Lewis-E Mar 6, 2026
e924fc8
Generate unique pipe name in integrations when no pre-generated name …
Lewis-E Mar 6, 2026
ca3e07a
Fix typos, apply code standards, and deduplicate tests
Lewis-E Mar 6, 2026
06132ae
Use DD_TRACE_PIPE_NAME and DD_DOGSTATSD_PIPE_NAME as base names for u…
Lewis-E Mar 6, 2026
976857b
Remove lazy caching and redundant pipe name generation from pipe name…
Lewis-E Mar 24, 2026
0829237
Move Azure Functions pipe name generation from static init to instanc…
Lewis-E Mar 24, 2026
8c925c3
Hoist Azure Functions check into constructor and consolidate pipe nam…
Lewis-E Mar 24, 2026
ded3bd4
Move IsCompatLayerAvailableWithPipeSupport to ServerlessCompatPipeNam…
Lewis-E Mar 24, 2026
bc51a8e
Fix build errors: add DD_SERVERLESS_COMPAT_PATH to ConfigurationKeys,…
Lewis-E Mar 24, 2026
1df4f5a
Update generated ConfigurationKeys files for DD_SERVERLESS_COMPAT_PATH
Lewis-E Mar 24, 2026
64ced11
Apply suggestions from code review
Lewis-E Mar 24, 2026
0922e07
Merge origin/master into lewis/SVLS-8244/add-multifunction-named-pipes
Lewis-E Mar 24, 2026
72f2744
Remove unnecessary null-forgiving operator on configuredBaseName
Lewis-E Mar 24, 2026
32b730d
Update generated native calltargets for MaximumVersion 1.*.*, add DD_…
Lewis-E Mar 24, 2026
9bebc26
Add DD_SERVERLESS_COMPAT_PATH to telemetry normalization rules
Lewis-E Mar 24, 2026
a168e9d
Bump Datadog.Serverless.Compat dependency to 1.4.0
Lewis-E Mar 25, 2026
d8195fc
Fix stale docs, strengthen test assertions, and rewrite integration t…
Lewis-E Mar 25, 2026
6561d30
Move ServerlessCompatPipeNameHelper to Datadog.Trace.Serverless, fix …
Lewis-E Mar 25, 2026
6483747
Revert Datadog.Serverless.Compat dependency to 1.2.0
Lewis-E Mar 25, 2026
791e2e7
Fix generated_calltargets.g.cpp sig index (sig168 -> sig173)
Lewis-E Mar 26, 2026
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
5 changes: 4 additions & 1 deletion tracer/build/_build/Honeypot/IntegrationGroups.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ static IntegrationMap()
NugetPackages.Add("Datadog.Trace", new string[] { });
NugetPackages.Add("Datadog.Trace.Manual", new string[] { });
NugetPackages.Add("Datadog.Trace.OpenTracing", new string[] { });


// Serverless
NugetPackages.Add("Datadog.Serverless.Compat", new string[] { });

// Feature Flags
NugetPackages.Add("Datadog.FeatureFlags.OpenFeature", new string[] { });
NugetPackages.Add("OpenFeature", new string[] { });
Expand Down
44 changes: 44 additions & 0 deletions tracer/build/supported_calltargets.g.json
Original file line number Diff line number Diff line change
Expand Up @@ -16459,6 +16459,50 @@
"IsAdoNetIntegration": false,
"InstrumentationCategory": 1
},
{
"IntegrationName": "ServerlessCompat",
"AssemblyName": "Datadog.Serverless.Compat",
"TargetTypeName": "Datadog.Serverless.CompatibilityLayer",
"TargetMethodName": "CalculateDogStatsDPipeName",
"TargetReturnType": "System.String",
"TargetParameterTypes": [],
"MinimumVersion": {
"Item1": 0,
"Item2": 0,
"Item3": 0
},
"MaximumVersion": {
"Item1": 1,
"Item2": 65535,
"Item3": 65535
},
"InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.Serverless.CompatibilityLayer_CalculateDogStatsDPipeName_Integration",
"IntegrationKind": 0,
"IsAdoNetIntegration": false,
"InstrumentationCategory": 1
},
{
"IntegrationName": "ServerlessCompat",
"AssemblyName": "Datadog.Serverless.Compat",
"TargetTypeName": "Datadog.Serverless.CompatibilityLayer",
"TargetMethodName": "CalculateTracePipeName",
"TargetReturnType": "System.String",
"TargetParameterTypes": [],
"MinimumVersion": {
"Item1": 0,
"Item2": 0,
"Item3": 0
},
"MaximumVersion": {
"Item1": 1,
"Item2": 65535,
"Item3": 65535
},
"InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.Serverless.CompatibilityLayer_CalculateTracePipeName_Integration",
"IntegrationKind": 0,
"IsAdoNetIntegration": false,
"InstrumentationCategory": 1
},
{
"IntegrationName": "ServiceStackRedis",
"AssemblyName": "ServiceStack.Redis",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<assembly fullname="Couchbase.NetClient" />
<assembly fullname="coverlet.core" />
<assembly fullname="Datadog.FeatureFlags.OpenFeature" />
<assembly fullname="Datadog.Serverless.Compat" />
<assembly fullname="Datadog.Trace" />
<assembly fullname="Datadog.Trace.Manual" />
<assembly fullname="Datadog.Trace.OpenTracing" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// <copyright file="CompatibilityLayer_CalculateDogStatsDPipeName_Integration.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

#if !NETFRAMEWORK
using System;
using System.ComponentModel;
using Datadog.Trace.ClrProfiler.CallTarget;
using Datadog.Trace.Configuration;
using Datadog.Trace.Logging;

namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Serverless
{
/// <summary>
/// Instrumentation for Datadog.Serverless.CompatibilityLayer.CalculateDogStatsDPipeName method.
/// This instrumentation overrides the return value with the tracer's pre-generated pipe name,
/// ensuring the tracer's runtime metrics and the compat layer use the same pipe name.
/// Version range 0.0.0–1.*.* matches all 0.x and 1.x versions, including the 0.0.0 dev build.
/// If the target method doesn't exist (e.g. older/newer compat assembly), the native profiler
/// silently skips instrumentation — no exception is thrown.
/// </summary>
[InstrumentMethod(
AssemblyName = "Datadog.Serverless.Compat",
TypeName = "Datadog.Serverless.CompatibilityLayer",
MethodName = "CalculateDogStatsDPipeName",
ReturnTypeName = ClrNames.String,
ParameterTypeNames = [],
MinimumVersion = "0.0.0",
MaximumVersion = "1.*.*",
IntegrationName = nameof(IntegrationId.ServerlessCompat),
InstrumentationCategory = InstrumentationCategory.Tracing)]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class CompatibilityLayer_CalculateDogStatsDPipeName_Integration
{
private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(CompatibilityLayer_CalculateDogStatsDPipeName_Integration));

/// <summary>
/// OnMethodEnd callback - intercepts the return value and overrides it with a unique pipe name
/// </summary>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <param name="instance">Instance value, aka `this` of the instrumented method (null for static methods)</param>
/// <param name="returnValue">The pipe name calculated by the compat layer</param>
/// <param name="exception">Exception instance in case the original code threw an exception</param>
/// <param name="state">Calltarget state value</param>
/// <returns>A unique pipe name for coordination between tracer and compat layer</returns>
internal static CallTargetReturn<string> OnMethodEnd<TTarget>(
TTarget instance,
string returnValue,
Exception exception,
in CallTargetState state)
{
if (exception is not null)
{
// If there was an exception, pass it through
return new CallTargetReturn<string>(returnValue);
}

try
{
// Use the tracer's configured pipe name, which in Azure Functions will be the
// unique name generated by ExporterSettings. Fall back to the compat layer's
// own calculated name if the tracer isn't initialized yet.
var pipeName = Tracer.Instance.Settings?.Manager?.InitialExporterSettings?.MetricsPipeName ?? returnValue;

Log.Debug(
"ServerlessCompat integration: Overriding compat layer DogStatsD pipe name. " +
"Compat layer calculated: {CompatPipeName}, Tracer using: {TracerPipeName}",
returnValue,
pipeName);

return new CallTargetReturn<string>(pipeName);
}
catch (Exception ex)
{
Log.Error(ex, "ServerlessCompat integration: Error overriding DogStatsD pipe name");
}

// Fallback to compat layer's original value
return new CallTargetReturn<string>(returnValue);
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// <copyright file="CompatibilityLayer_CalculateTracePipeName_Integration.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

#if !NETFRAMEWORK
using System;
using System.ComponentModel;
using Datadog.Trace.ClrProfiler.CallTarget;
using Datadog.Trace.Configuration;
using Datadog.Trace.Logging;

namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Serverless
{
/// <summary>
/// Instrumentation for Datadog.Serverless.CompatibilityLayer.CalculateTracePipeName method.
/// This instrumentation overrides the return value with the tracer's pre-generated pipe name,
/// ensuring both the tracer and the compat layer use the same pipe name for communication.
/// Version range 0.0.0–1.*.* matches all 0.x and 1.x versions, including the 0.0.0 dev build.
/// If the target method doesn't exist (e.g. older/newer compat assembly), the native profiler
/// silently skips instrumentation — no exception is thrown.
/// </summary>
[InstrumentMethod(
AssemblyName = "Datadog.Serverless.Compat",
TypeName = "Datadog.Serverless.CompatibilityLayer",
MethodName = "CalculateTracePipeName",
ReturnTypeName = ClrNames.String,
ParameterTypeNames = [],
MinimumVersion = "0.0.0",
MaximumVersion = "1.*.*",
IntegrationName = nameof(IntegrationId.ServerlessCompat),
InstrumentationCategory = InstrumentationCategory.Tracing)]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class CompatibilityLayer_CalculateTracePipeName_Integration
{
private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(CompatibilityLayer_CalculateTracePipeName_Integration));

/// <summary>
/// OnMethodEnd callback - intercepts the return value and overrides it with a unique pipe name
/// </summary>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <param name="instance">Instance value, aka `this` of the instrumented method (null for static methods)</param>
/// <param name="returnValue">The pipe name calculated by the compat layer</param>
/// <param name="exception">Exception instance in case the original code threw an exception</param>
/// <param name="state">Calltarget state value</param>
/// <returns>A unique pipe name for coordination between tracer and compat layer</returns>
internal static CallTargetReturn<string> OnMethodEnd<TTarget>(
TTarget instance,
string returnValue,
Exception exception,
in CallTargetState state)
{
if (exception is not null)
{
// If there was an exception, pass it through
return new CallTargetReturn<string>(returnValue);
}

try
{
// Use the tracer's configured pipe name, which in Azure Functions will be the
// unique name generated by ExporterSettings. Fall back to the compat layer's
// own calculated name if the tracer isn't initialized yet.
var pipeName = Tracer.Instance.Settings?.Manager?.InitialExporterSettings?.TracesPipeName ?? returnValue;

Log.Debug(
"ServerlessCompat integration: Overriding compat layer trace pipe name. " +
"Compat layer calculated: {CompatPipeName}, Tracer using: {TracerPipeName}",
returnValue,
pipeName);

return new CallTargetReturn<string>(pipeName);
}
catch (Exception ex)
{
Log.Error(ex, "ServerlessCompat integration: Error overriding trace pipe name");
}

// Fallback to compat layer's original value
return new CallTargetReturn<string>(returnValue);
}
}
}
#endif
43 changes: 37 additions & 6 deletions tracer/src/Datadog.Trace/Configuration/ExporterSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Datadog.Trace.Configuration.ConfigurationSources;
using Datadog.Trace.Configuration.ConfigurationSources.Telemetry;
using Datadog.Trace.Configuration.Telemetry;
using Datadog.Trace.Logging;
using Datadog.Trace.SourceGenerators;
using Datadog.Trace.Telemetry;
using Datadog.Trace.Telemetry.Metrics;
Expand All @@ -27,6 +28,8 @@ namespace Datadog.Trace.Configuration
/// </summary>
public sealed partial class ExporterSettings
{
private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(ExporterSettings));

/// <summary>
/// Allows overriding of file system access for tests.
/// </summary>
Expand Down Expand Up @@ -88,12 +91,31 @@ internal ExporterSettings(Raw rawSettings, Func<string, bool> fileExists, IConfi

ValidationWarnings = new List<string>();

// In Azure Functions (without AAS Site Extension), generate unique pipe names so each
// function instance gets its own pipe even when sharing a hosting plan. The generated names
// use the user-configured base name (from any config source) with a unique GUID suffix.
string? tracesPipeName;
string? metricsPipeName;

if (Util.EnvironmentHelpers.IsAzureFunctions()
&& !Util.EnvironmentHelpers.IsUsingAzureAppServicesSiteExtension()
&& Serverless.ServerlessCompatPipeNameHelper.IsCompatLayerAvailableWithPipeSupport())
{
tracesPipeName = GenerateUniquePipeName(rawSettings.TracesPipeName, "dd_trace", ConfigurationKeys.TracesPipeName);
metricsPipeName = GenerateUniquePipeName(rawSettings.MetricsPipeName, "dd_dogstatsd", ConfigurationKeys.MetricsPipeName);
}
else
{
tracesPipeName = rawSettings.TracesPipeName;
metricsPipeName = rawSettings.MetricsPipeName;
}

var traceSettings = GetTraceTransport(
agentUri: rawSettings.TraceAgentUri,
tracesPipeName: rawSettings.TracesPipeName,
agentHost: rawSettings.TraceAgentHost,
agentPort: rawSettings.TraceAgentPort,
tracesUnixDomainSocketPath: rawSettings.TracesUnixDomainSocketPath);
agentUri: rawSettings.TraceAgentUri,
tracesPipeName: tracesPipeName,
agentHost: rawSettings.TraceAgentHost,
agentPort: rawSettings.TraceAgentPort,
tracesUnixDomainSocketPath: rawSettings.TracesUnixDomainSocketPath);

TracesEncoding = TracesEncoding.DatadogV0_4;
TracesTransport = traceSettings.Transport;
Expand Down Expand Up @@ -130,7 +152,7 @@ internal ExporterSettings(Raw rawSettings, Func<string, bool> fileExists, IConfi
traceAgentUrl: rawSettings.TraceAgentUri,
agentHost: rawSettings.TraceAgentHost,
dogStatsdPort: rawSettings.DogStatsdPort,
metricsPipeName: rawSettings.MetricsPipeName,
metricsPipeName: metricsPipeName,
metricsUnixDomainSocketPath: rawSettings.MetricsUnixDomainSocketPath);

MetricsHostname = metricsSettings.Hostname;
Expand Down Expand Up @@ -325,6 +347,15 @@ private static string GetMetricsHostNameFromAgentUri(Uri agentUri)
return string.IsNullOrEmpty(traceHostname) ? DefaultDogstatsdHostname : traceHostname;
}

private string GenerateUniquePipeName(string? configuredBaseName, string defaultBaseName, string configKey)
{
var baseName = StringUtil.IsNullOrEmpty(configuredBaseName) ? defaultBaseName : configuredBaseName;
var name = Serverless.ServerlessCompatPipeNameHelper.GenerateUniquePipeName(baseName, "ExporterSettings");
Log.Information("Azure Functions environment detected. Using pipe base name '{BaseName}', generated unique pipe name: {PipeName}", baseName, name);
_telemetry.Record(configKey, name, recordValue: true, ConfigurationOrigins.Calculated);
return name;
}

private MetricsTransportSettings ConfigureMetricsTransport(string? metricsUrl, string? traceAgentUrl, string? agentHost, int dogStatsdPort, string? metricsPipeName, string? metricsUnixDomainSocketPath)
{
if (!string.IsNullOrEmpty(metricsUrl) && TryGetMetricsUriAndTransport(metricsUrl!, out var settingsFromUri))
Expand Down
3 changes: 2 additions & 1 deletion tracer/src/Datadog.Trace/Configuration/IntegrationId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ internal enum IntegrationId
AzureEventHubs,
DatadogTraceVersionConflict,
Hangfire,
OpenFeature
OpenFeature,
ServerlessCompat
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,15 @@ supportedConfigurations:
Match all spans that start with "cart" without any limits and any operation name.
"[{"service": "cart*"}]"
<seealso cref="Datadog.Trace.Configuration.TracerSettings.SpanSamplingRules"/>
DD_SERVERLESS_COMPAT_PATH:
- implementation: A
type: string
default: null
const_name: ServerlessCompatPath
documentation: |-
Overrides the default path to the serverless compatibility layer binary.
Default value in windows is <c>C:\home\site\wwwroot\datadog\bin\windows-amd64\datadog-serverless-compat.exe</c>.
Default value in linux is <c>/bin/linux-amd64/datadog-serverless-compat</c>.
DD_SYMBOL_DATABASE_BATCH_SIZE_BYTES:
- implementation: A
type: int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,13 @@ internal static partial class ConfigurationKeys
/// </summary>
public const string RuntimeMetricsEnabled = "DD_RUNTIME_METRICS_ENABLED";

/// <summary>
/// Overrides the default path to the serverless compatibility layer binary.
/// Default value in windows is <c>C:\home\site\wwwroot\datadog\bin\windows-amd64\datadog-serverless-compat.exe</c>.
/// Default value in linux is <c>/bin/linux-amd64/datadog-serverless-compat</c>.
/// </summary>
public const string ServerlessCompatPath = "DD_SERVERLESS_COMPAT_PATH";

/// <summary>
/// Configuration key for the application's default service name.
/// Used as the service name for top-level spans,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal static partial class IntegrationIdExtensions
/// The number of members in the enum.
/// This is a non-distinct count of defined names.
/// </summary>
public const int Length = 79;
public const int Length = 80;

/// <summary>
/// Returns the string representation of the <see cref="Datadog.Trace.Configuration.IntegrationId"/> value.
Expand Down Expand Up @@ -109,6 +109,7 @@ public static string ToStringFast(this Datadog.Trace.Configuration.IntegrationId
Datadog.Trace.Configuration.IntegrationId.DatadogTraceVersionConflict => nameof(Datadog.Trace.Configuration.IntegrationId.DatadogTraceVersionConflict),
Datadog.Trace.Configuration.IntegrationId.Hangfire => nameof(Datadog.Trace.Configuration.IntegrationId.Hangfire),
Datadog.Trace.Configuration.IntegrationId.OpenFeature => nameof(Datadog.Trace.Configuration.IntegrationId.OpenFeature),
Datadog.Trace.Configuration.IntegrationId.ServerlessCompat => nameof(Datadog.Trace.Configuration.IntegrationId.ServerlessCompat),
_ => value.ToString(),
};

Expand Down Expand Up @@ -201,6 +202,7 @@ public static Datadog.Trace.Configuration.IntegrationId[] GetValues()
Datadog.Trace.Configuration.IntegrationId.DatadogTraceVersionConflict,
Datadog.Trace.Configuration.IntegrationId.Hangfire,
Datadog.Trace.Configuration.IntegrationId.OpenFeature,
Datadog.Trace.Configuration.IntegrationId.ServerlessCompat,
};

/// <summary>
Expand Down Expand Up @@ -293,5 +295,6 @@ public static string[] GetNames()
nameof(Datadog.Trace.Configuration.IntegrationId.DatadogTraceVersionConflict),
nameof(Datadog.Trace.Configuration.IntegrationId.Hangfire),
nameof(Datadog.Trace.Configuration.IntegrationId.OpenFeature),
nameof(Datadog.Trace.Configuration.IntegrationId.ServerlessCompat),
};
}
Loading
Loading