diff --git a/tracer/src/Datadog.Trace.Tools.Runner/Utils.cs b/tracer/src/Datadog.Trace.Tools.Runner/Utils.cs index bc0c9eb22780..9b5b3b82b16d 100644 --- a/tracer/src/Datadog.Trace.Tools.Runner/Utils.cs +++ b/tracer/src/Datadog.Trace.Tools.Runner/Utils.cs @@ -422,6 +422,7 @@ public static async Task CheckAgentConnectionAsync(string ag var discoveryService = DiscoveryService.CreateUnmanaged( settings.Manager.InitialExporterSettings, ContainerMetadata.Instance, + new ServiceRemappingHash(null), tcpTimeout: TimeSpan.FromSeconds(5), initialRetryDelayMs: 200, maxRetryDelayMs: 1000, diff --git a/tracer/src/Datadog.Trace/Agent/DiscoveryService/DiscoveryService.cs b/tracer/src/Datadog.Trace/Agent/DiscoveryService/DiscoveryService.cs index d038016642ff..084492b8d5df 100644 --- a/tracer/src/Datadog.Trace/Agent/DiscoveryService/DiscoveryService.cs +++ b/tracer/src/Datadog.Trace/Agent/DiscoveryService/DiscoveryService.cs @@ -47,7 +47,7 @@ internal sealed class DiscoveryService : IDiscoveryService private readonly object _lock = new(); private readonly Task _discoveryTask; private readonly IDisposable? _settingSubscription; - private readonly ContainerMetadata _containerMetadata; + private readonly ServiceRemappingHash _serviceRemappingHash; private IApiRequestFactory _apiRequestFactory; private AgentConfiguration? _configuration; private string? _configurationHash; @@ -57,11 +57,12 @@ internal sealed class DiscoveryService : IDiscoveryService public DiscoveryService( TracerSettings.SettingsManager settings, ContainerMetadata containerMetadata, + ServiceRemappingHash serviceRemappingHash, TimeSpan tcpTimeout, int initialRetryDelayMs, int maxRetryDelayMs, int recheckIntervalMs) - : this(CreateApiRequestFactory(settings.InitialExporterSettings, containerMetadata.ContainerId, tcpTimeout), containerMetadata, initialRetryDelayMs, maxRetryDelayMs, recheckIntervalMs) + : this(CreateApiRequestFactory(settings.InitialExporterSettings, containerMetadata.ContainerId, tcpTimeout), serviceRemappingHash, initialRetryDelayMs, maxRetryDelayMs, recheckIntervalMs) { // Create as a "managed" service that can update the request factory _settingSubscription = settings.SubscribeToChanges(changes => @@ -80,13 +81,13 @@ public DiscoveryService( /// public DiscoveryService( IApiRequestFactory apiRequestFactory, - ContainerMetadata containerMetadata, + ServiceRemappingHash serviceRemappingHash, int initialRetryDelayMs, int maxRetryDelayMs, int recheckIntervalMs) { _apiRequestFactory = apiRequestFactory; - _containerMetadata = containerMetadata; + _serviceRemappingHash = serviceRemappingHash; _initialRetryDelayMs = initialRetryDelayMs; _maxRetryDelayMs = maxRetryDelayMs; _recheckIntervalMs = recheckIntervalMs; @@ -119,10 +120,11 @@ public DiscoveryService( /// /// Create a instance that responds to runtime changes in settings /// - public static DiscoveryService CreateManaged(TracerSettings settings, ContainerMetadata containerMetadata) + public static DiscoveryService CreateManaged(TracerSettings settings, ContainerMetadata containerMetadata, ServiceRemappingHash serviceRemappingHash) => new( settings.Manager, containerMetadata, + serviceRemappingHash, tcpTimeout: TimeSpan.FromSeconds(15), initialRetryDelayMs: 500, maxRetryDelayMs: 5_000, @@ -131,10 +133,11 @@ public static DiscoveryService CreateManaged(TracerSettings settings, ContainerM /// /// Create a instance that does _not_ respond to runtime changes in settings /// - public static DiscoveryService CreateUnmanaged(ExporterSettings exporterSettings, ContainerMetadata containerMetadata) + public static DiscoveryService CreateUnmanaged(ExporterSettings exporterSettings, ContainerMetadata containerMetadata, ServiceRemappingHash serviceRemappingHash) => CreateUnmanaged( exporterSettings, containerMetadata, + serviceRemappingHash, tcpTimeout: TimeSpan.FromSeconds(15), initialRetryDelayMs: 500, maxRetryDelayMs: 5_000, @@ -146,13 +149,14 @@ public static DiscoveryService CreateUnmanaged(ExporterSettings exporterSettings public static DiscoveryService CreateUnmanaged( ExporterSettings exporterSettings, ContainerMetadata containerMetadata, + ServiceRemappingHash serviceRemappingHash, TimeSpan tcpTimeout, int initialRetryDelayMs, int maxRetryDelayMs, int recheckIntervalMs) => new( CreateApiRequestFactory(exporterSettings, containerMetadata.ContainerId, tcpTimeout), - containerMetadata, + serviceRemappingHash, initialRetryDelayMs, maxRetryDelayMs, recheckIntervalMs); @@ -310,7 +314,7 @@ private async Task ProcessDiscoveryResponse(IApiResponse response) var containerTagsHash = response.GetHeader(AgentHttpHeaderNames.ContainerTagsHash); if (containerTagsHash != null) { - _containerMetadata.ContainerTagsHash = containerTagsHash; + _serviceRemappingHash.UpdateContainerTagsHash(containerTagsHash); } // Grab the original stream @@ -429,7 +433,7 @@ private async Task ProcessDiscoveryResponse(IApiResponse response) eventPlatformProxyEndpoint: eventPlatformProxyEndpoint, telemetryProxyEndpoint: telemetryProxyEndpoint, tracerFlareEndpoint: tracerFlareEndpoint, - containerTagsHash: _containerMetadata.ContainerTagsHash, // either the value just received, or the one we stored before (prevents overriding with null) + containerTagsHash: _serviceRemappingHash.ContainerTagsHash, // either the value just received, or the one we stored before (prevents overriding with null) clientDropP0: clientDropP0, spanMetaStructs: spanMetaStructs, spanEvents: spanEvents); diff --git a/tracer/src/Datadog.Trace/Ci/TestOptimization.cs b/tracer/src/Datadog.Trace/Ci/TestOptimization.cs index 54097fd30715..6e3a26c986b0 100644 --- a/tracer/src/Datadog.Trace/Ci/TestOptimization.cs +++ b/tracer/src/Datadog.Trace/Ci/TestOptimization.cs @@ -250,6 +250,7 @@ public void Initialize() getDiscoveryServiceFunc: static s => DiscoveryService.CreateUnmanaged( s.TracerSettings.Manager.InitialExporterSettings, ContainerMetadata.Instance, + new ServiceRemappingHash(null), tcpTimeout: TimeSpan.FromSeconds(5), initialRetryDelayMs: 100, maxRetryDelayMs: 1000, diff --git a/tracer/src/Datadog.Trace/Ci/TestOptimizationTracerManager.cs b/tracer/src/Datadog.Trace/Ci/TestOptimizationTracerManager.cs index 9c1ec01908ad..e4ea3e645860 100644 --- a/tracer/src/Datadog.Trace/Ci/TestOptimizationTracerManager.cs +++ b/tracer/src/Datadog.Trace/Ci/TestOptimizationTracerManager.cs @@ -47,7 +47,8 @@ public TestOptimizationTracerManager( IDynamicConfigurationManager dynamicConfigurationManager, ITracerFlareManager tracerFlareManager, ISpanEventsManager spanEventsManager, - FeatureFlagsModule featureFlags) + FeatureFlagsModule featureFlags, + ServiceRemappingHash serviceRemappingHash) : base( settings, agentWriter, @@ -66,6 +67,7 @@ public TestOptimizationTracerManager( tracerFlareManager, spanEventsManager, featureFlags, + serviceRemappingHash, GetProcessors(settings.PartialFlushEnabled, agentWriter is CIVisibilityProtocolWriter)) { } @@ -165,7 +167,8 @@ public LockedManager( IDynamicConfigurationManager dynamicConfigurationManager, ITracerFlareManager tracerFlareManager, ISpanEventsManager spanEventsManager, - FeatureFlagsModule featureFlags) + FeatureFlagsModule featureFlags, + ServiceRemappingHash serviceRemappingHash) : base( settings, agentWriter, @@ -183,7 +186,8 @@ public LockedManager( dynamicConfigurationManager, tracerFlareManager, spanEventsManager, - featureFlags) + featureFlags, + serviceRemappingHash) { } } diff --git a/tracer/src/Datadog.Trace/Ci/TestOptimizationTracerManagerFactory.cs b/tracer/src/Datadog.Trace/Ci/TestOptimizationTracerManagerFactory.cs index 9051664063f5..db3cca315b20 100644 --- a/tracer/src/Datadog.Trace/Ci/TestOptimizationTracerManagerFactory.cs +++ b/tracer/src/Datadog.Trace/Ci/TestOptimizationTracerManagerFactory.cs @@ -56,15 +56,16 @@ protected override TracerManager CreateTracerManagerFrom( IDynamicConfigurationManager dynamicConfigurationManager, ITracerFlareManager tracerFlareManager, ISpanEventsManager spanEventsManager, - FeatureFlagsModule featureFlags) + FeatureFlagsModule featureFlags, + ServiceRemappingHash serviceRemappingHash) { telemetry.RecordTestOptimizationSettings(_settings); if (_testOptimizationTracerManagement.UseLockedTracerManager) { - return new TestOptimizationTracerManager.LockedManager(settings, agentWriter, scopeManager, statsd, runtimeMetrics, logSubmissionManager, telemetry, discoveryService, dataStreamsManager, gitMetadataTagsProvider, traceSampler, spanSampler, remoteConfigurationManager, dynamicConfigurationManager, tracerFlareManager, spanEventsManager, featureFlags); + return new TestOptimizationTracerManager.LockedManager(settings, agentWriter, scopeManager, statsd, runtimeMetrics, logSubmissionManager, telemetry, discoveryService, dataStreamsManager, gitMetadataTagsProvider, traceSampler, spanSampler, remoteConfigurationManager, dynamicConfigurationManager, tracerFlareManager, spanEventsManager, featureFlags, serviceRemappingHash); } - return new TestOptimizationTracerManager(settings, agentWriter, scopeManager, statsd, runtimeMetrics, logSubmissionManager, telemetry, discoveryService, dataStreamsManager, gitMetadataTagsProvider, traceSampler, spanSampler, remoteConfigurationManager, dynamicConfigurationManager, tracerFlareManager, spanEventsManager, featureFlags); + return new TestOptimizationTracerManager(settings, agentWriter, scopeManager, statsd, runtimeMetrics, logSubmissionManager, telemetry, discoveryService, dataStreamsManager, gitMetadataTagsProvider, traceSampler, spanSampler, remoteConfigurationManager, dynamicConfigurationManager, tracerFlareManager, spanEventsManager, featureFlags, serviceRemappingHash); } protected override TelemetrySettings CreateTelemetrySettings(TracerSettings settings) @@ -116,7 +117,7 @@ protected override IAgentWriter GetAgentWriter(TracerSettings settings, IStatsdM return new ApmAgentWriter(settings, updateSampleRates, updateConfigHash, discoveryService, traceBufferSize); } - internal override IDiscoveryService GetDiscoveryService(TracerSettings settings) + internal override IDiscoveryService GetDiscoveryService(TracerSettings settings, ServiceRemappingHash serviceRemappingHash) => _testOptimizationTracerManagement.DiscoveryService; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AdoNet/DbScopeFactory.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AdoNet/DbScopeFactory.cs index 6a69ff390e0f..c71f23260a93 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AdoNet/DbScopeFactory.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AdoNet/DbScopeFactory.cs @@ -25,7 +25,7 @@ internal static class DbScopeFactory private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(DbScopeFactory)); private static bool _dbCommandCachingLogged; - private static Scope? CreateDbCommandScope(Tracer tracer, IDbCommand command, IntegrationId integrationId, string dbType, string operationName, string serviceName, string? serviceNameSource, ref DbCommandCache.TagsCacheItem tagsFromConnectionString) + private static Scope? CreateDbCommandScope(Tracer tracer, IDbCommand command, IntegrationId integrationId, string dbType, string operationName, string serviceName, string? serviceNameSource, string? baseHash, ref DbCommandCache.TagsCacheItem tagsFromConnectionString) { var perTraceSettings = tracer.CurrentTraceSettings; if (!perTraceSettings.Settings.IsIntegrationEnabled(integrationId) || !perTraceSettings.Settings.IsIntegrationEnabled(IntegrationId.AdoNet)) @@ -103,9 +103,15 @@ internal static class DbScopeFactory } else { - // PropagateDataViaComment (service) - this injects varius trace information as a comment in the query + if (baseHash != null) + { + // note: it's ok that we don't write the basehash in the span if "alreadyInjected" because we only need one span to get the tag values + tags.BaseHash = baseHash; + } + + // PropagateDataViaComment (service) - this injects various trace information as a comment in the query // PropagateDataViaContext (full) - this makes a special set context_info for Microsoft SQL Server (nothing else supported) - var traceParentInjectedInComment = DatabaseMonitoringPropagator.PropagateDataViaComment(tracer.Settings.DbmPropagationMode, integrationId, command, tracer.DefaultServiceName, tagsFromConnectionString.DbName, tagsFromConnectionString.OutHost, scope.Span, tracer.Settings.InjectContextIntoStoredProceduresEnabled); + var traceParentInjectedInComment = DatabaseMonitoringPropagator.PropagateDataViaComment(tracer.Settings.DbmPropagationMode, integrationId, command, tracer.DefaultServiceName, tagsFromConnectionString.DbName, tagsFromConnectionString.OutHost, scope.Span, tracer.Settings.InjectContextIntoStoredProceduresEnabled, baseHash); // try context injection only after comment injection, so that if it fails, we still have service level propagation var traceParentInjectedInContext = DatabaseMonitoringPropagator.PropagateDataViaContext(tracer.Settings.DbmPropagationMode, integrationId, command, scope.Span); @@ -259,6 +265,9 @@ static Cache() public static Scope? CreateDbCommandScope(Tracer tracer, IDbCommand command) { var commandType = command.GetType(); + var baseHash = tracer.Settings.PropagateProcessTags && tracer.Settings.DbmInjectSqlBasehash + ? tracer.TracerManager.ServiceRemappingHash?.Base64Value + : null; // null if disabled if (commandType == CommandType && DbTypeName is not null && OperationName is not null) { @@ -274,6 +283,7 @@ static Cache() operationName: OperationName, serviceName: cachedServiceName, serviceNameSource: cachedServiceNameSource, + baseHash: baseHash, tagsFromConnectionString: ref tagsFromConnectionString); } @@ -292,6 +302,7 @@ static Cache() operationName: operationName, serviceName: resolvedServiceName, serviceNameSource: resolvedServiceNameSource, + baseHash: baseHash, tagsFromConnectionString: ref tagsFromConnectionString); } diff --git a/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs b/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs index 9d8e86c0edec..cd500ce9ab7a 100644 --- a/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs +++ b/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs @@ -629,6 +629,10 @@ not null when string.Equals(value, "otlp", StringComparison.OrdinalIgnoreCase) = converter: x => ToDbmPropagationInput(x) ?? ParsingResult.Failure(), validator: null); + DbmInjectSqlBasehash = config + .WithKeys(ConfigurationKeys.DbmInjectSqlBasehash) + .AsBool(false); + RemoteConfigurationEnabled = config.WithKeys(ConfigurationKeys.Rcm.RemoteConfigurationEnabled).AsBool(true); TraceId128BitGenerationEnabled = config @@ -1243,6 +1247,12 @@ not null when string.Equals(value, "otlp", StringComparison.OrdinalIgnoreCase) = /// internal DbmPropagationLevel DbmPropagationMode { get; } + /// + /// Gets a value indicating whether the tracer should inject Base Hash in SQL Comments. + /// Default value is false (disabled). + /// + internal bool DbmInjectSqlBasehash { get; } + /// /// Gets a value indicating whether the tracer will generate 128-bit trace ids /// instead of 64-bits trace ids. diff --git a/tracer/src/Datadog.Trace/Configuration/supported-configurations.yaml b/tracer/src/Datadog.Trace/Configuration/supported-configurations.yaml index 2e28bea6e6c2..0b6058c9c964 100644 --- a/tracer/src/Datadog.Trace/Configuration/supported-configurations.yaml +++ b/tracer/src/Datadog.Trace/Configuration/supported-configurations.yaml @@ -533,6 +533,15 @@ supportedConfigurations: Configuration key for setting DBM propagation mode Default value is disabled, expected values are either: disabled, service or full + DD_DBM_INJECT_SQL_BASEHASH: + - implementation: A + type: boolean + default: 'false' + const_name: DbmInjectSqlBasehash + documentation: |- + Configuration key for enabling or disabling the injection of Base Hash in SQL Comments. + Default value is false (disabled). + DD_DIAGNOSTIC_SOURCE_ENABLED: - implementation: A type: boolean diff --git a/tracer/src/Datadog.Trace/DatabaseMonitoring/DatabaseMonitoringPropagator.cs b/tracer/src/Datadog.Trace/DatabaseMonitoring/DatabaseMonitoringPropagator.cs index 5bd365712aff..1ba7b092278f 100644 --- a/tracer/src/Datadog.Trace/DatabaseMonitoring/DatabaseMonitoringPropagator.cs +++ b/tracer/src/Datadog.Trace/DatabaseMonitoring/DatabaseMonitoringPropagator.cs @@ -10,6 +10,7 @@ using System.Threading; using Datadog.Trace.Configuration; using Datadog.Trace.Logging; +using Datadog.Trace.PlatformHelpers; using Datadog.Trace.Propagators; using Datadog.Trace.Tagging; using Datadog.Trace.Util; @@ -28,6 +29,7 @@ internal static class DatabaseMonitoringPropagator private const string SqlCommentOuthost = "ddh"; private const string SqlCommentVersion = "ddpv"; private const string SqlCommentEnv = "dde"; + private const string SqlCommentBaseHash = "ddsh"; internal const string DbmPrefix = $"/*{SqlCommentSpanService}='"; private const string ContextInfoParameterName = "@dd_trace_context"; internal const string SetContextCommand = $"set context_info {ContextInfoParameterName}"; @@ -41,7 +43,8 @@ internal static class DatabaseMonitoringPropagator private static int _remainingDirectionErrorLogs = 100; private static int _remainingQuoteErrorLogs = 100; - internal static bool PropagateDataViaComment(DbmPropagationLevel propagationLevel, IntegrationId integrationId, IDbCommand command, string configuredServiceName, string? dbName, string? outhost, Span span, bool injectStoredProcedure) + // baseHash should be null if hash injection is disabled, config is not checked in this method + internal static bool PropagateDataViaComment(DbmPropagationLevel propagationLevel, IntegrationId integrationId, IDbCommand command, string configuredServiceName, string? dbName, string? outhost, Span span, bool injectStoredProcedure, string? baseHash) { if (integrationId is not (IntegrationId.MySql or IntegrationId.Npgsql or IntegrationId.SqlClient or IntegrationId.Oracle) || propagationLevel is not (DbmPropagationLevel.Service or DbmPropagationLevel.Full)) @@ -103,6 +106,11 @@ propagationLevel is not (DbmPropagationLevel.Service or DbmPropagationLevel.Full propagatorStringBuilder.Append(',').Append(SqlCommentVersion).Append("='").Append(Uri.EscapeDataString(versionTag)).Append('\''); } + if (!string.IsNullOrEmpty(baseHash)) + { + propagatorStringBuilder.Append(',').Append(SqlCommentBaseHash).Append("='").Append(Uri.EscapeDataString(baseHash)).Append('\''); + } + var traceParentInjected = false; // For SqlServer & Oracle we don't inject the traceparent to avoid affecting performance, since those DBs generate a new plan for any query changes if (propagationLevel == DbmPropagationLevel.Full diff --git a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs index 1926eaef1500..5ac4ed1e7782 100644 --- a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs @@ -71,6 +71,13 @@ internal static partial class ConfigurationKeys /// public const string ApplicationMonitoringConfigFileEnabled = "DD_APPLICATION_MONITORING_CONFIG_FILE_ENABLED"; + /// + /// Configuration key for enabling or disabling the injection of Base Hash in SQL Comments. + /// Default value is false (disabled). + /// + /// + public const string DbmInjectSqlBasehash = "DD_DBM_INJECT_SQL_BASEHASH"; + /// /// Configuration key for setting DBM propagation mode /// Default value is disabled, expected values are either: disabled, service or full diff --git a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs index 4d4314090873..db1cce10b854 100644 --- a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs @@ -56,6 +56,12 @@ partial class SqlTags #else private static readonly byte[] DbmTraceInjectedBytes = new byte[] { 182, 95, 100, 100, 46, 100, 98, 109, 95, 116, 114, 97, 99, 101, 95, 105, 110, 106, 101, 99, 116, 101, 100 }; #endif + // BaseHashBytes = MessagePack.Serialize("_dd.propagated_hash"); +#if NETCOREAPP + private static ReadOnlySpan BaseHashBytes => new byte[] { 179, 95, 100, 100, 46, 112, 114, 111, 112, 97, 103, 97, 116, 101, 100, 95, 104, 97, 115, 104 }; +#else + private static readonly byte[] BaseHashBytes = new byte[] { 179, 95, 100, 100, 46, 112, 114, 111, 112, 97, 103, 97, 116, 101, 100, 95, 104, 97, 115, 104 }; +#endif public override string? GetTag(string key) { @@ -68,6 +74,7 @@ partial class SqlTags "db.user" => DbUser, "out.host" => OutHost, "_dd.dbm_trace_injected" => DbmTraceInjected, + "_dd.propagated_hash" => BaseHash, _ => base.GetTag(key), }; } @@ -94,6 +101,9 @@ public override void SetTag(string key, string? value) case "_dd.dbm_trace_injected": DbmTraceInjected = value; break; + case "_dd.propagated_hash": + BaseHash = value; + break; case "span.kind": Logger.Value.Warning("Attempted to set readonly tag {TagName} on {TagType}. Ignoring.", key, nameof(SqlTags)); break; @@ -140,6 +150,11 @@ public override void EnumerateTags(ref TProcessor processor) processor.Process(new TagItem("_dd.dbm_trace_injected", DbmTraceInjected, DbmTraceInjectedBytes)); } + if (BaseHash is not null) + { + processor.Process(new TagItem("_dd.propagated_hash", BaseHash, BaseHashBytes)); + } + base.EnumerateTags(ref processor); } @@ -194,6 +209,13 @@ protected override void WriteAdditionalTags(System.Text.StringBuilder sb) .Append(','); } + if (BaseHash is not null) + { + sb.Append("_dd.propagated_hash (tag):") + .Append(BaseHash) + .Append(','); + } + base.WriteAdditionalTags(sb); } } diff --git a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs index 1926eaef1500..5ac4ed1e7782 100644 --- a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs @@ -71,6 +71,13 @@ internal static partial class ConfigurationKeys /// public const string ApplicationMonitoringConfigFileEnabled = "DD_APPLICATION_MONITORING_CONFIG_FILE_ENABLED"; + /// + /// Configuration key for enabling or disabling the injection of Base Hash in SQL Comments. + /// Default value is false (disabled). + /// + /// + public const string DbmInjectSqlBasehash = "DD_DBM_INJECT_SQL_BASEHASH"; + /// /// Configuration key for setting DBM propagation mode /// Default value is disabled, expected values are either: disabled, service or full diff --git a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs index 4d4314090873..db1cce10b854 100644 --- a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs @@ -56,6 +56,12 @@ partial class SqlTags #else private static readonly byte[] DbmTraceInjectedBytes = new byte[] { 182, 95, 100, 100, 46, 100, 98, 109, 95, 116, 114, 97, 99, 101, 95, 105, 110, 106, 101, 99, 116, 101, 100 }; #endif + // BaseHashBytes = MessagePack.Serialize("_dd.propagated_hash"); +#if NETCOREAPP + private static ReadOnlySpan BaseHashBytes => new byte[] { 179, 95, 100, 100, 46, 112, 114, 111, 112, 97, 103, 97, 116, 101, 100, 95, 104, 97, 115, 104 }; +#else + private static readonly byte[] BaseHashBytes = new byte[] { 179, 95, 100, 100, 46, 112, 114, 111, 112, 97, 103, 97, 116, 101, 100, 95, 104, 97, 115, 104 }; +#endif public override string? GetTag(string key) { @@ -68,6 +74,7 @@ partial class SqlTags "db.user" => DbUser, "out.host" => OutHost, "_dd.dbm_trace_injected" => DbmTraceInjected, + "_dd.propagated_hash" => BaseHash, _ => base.GetTag(key), }; } @@ -94,6 +101,9 @@ public override void SetTag(string key, string? value) case "_dd.dbm_trace_injected": DbmTraceInjected = value; break; + case "_dd.propagated_hash": + BaseHash = value; + break; case "span.kind": Logger.Value.Warning("Attempted to set readonly tag {TagName} on {TagType}. Ignoring.", key, nameof(SqlTags)); break; @@ -140,6 +150,11 @@ public override void EnumerateTags(ref TProcessor processor) processor.Process(new TagItem("_dd.dbm_trace_injected", DbmTraceInjected, DbmTraceInjectedBytes)); } + if (BaseHash is not null) + { + processor.Process(new TagItem("_dd.propagated_hash", BaseHash, BaseHashBytes)); + } + base.EnumerateTags(ref processor); } @@ -194,6 +209,13 @@ protected override void WriteAdditionalTags(System.Text.StringBuilder sb) .Append(','); } + if (BaseHash is not null) + { + sb.Append("_dd.propagated_hash (tag):") + .Append(BaseHash) + .Append(','); + } + base.WriteAdditionalTags(sb); } } diff --git a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs index 1926eaef1500..5ac4ed1e7782 100644 --- a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs @@ -71,6 +71,13 @@ internal static partial class ConfigurationKeys /// public const string ApplicationMonitoringConfigFileEnabled = "DD_APPLICATION_MONITORING_CONFIG_FILE_ENABLED"; + /// + /// Configuration key for enabling or disabling the injection of Base Hash in SQL Comments. + /// Default value is false (disabled). + /// + /// + public const string DbmInjectSqlBasehash = "DD_DBM_INJECT_SQL_BASEHASH"; + /// /// Configuration key for setting DBM propagation mode /// Default value is disabled, expected values are either: disabled, service or full diff --git a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs index 4d4314090873..db1cce10b854 100644 --- a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs @@ -56,6 +56,12 @@ partial class SqlTags #else private static readonly byte[] DbmTraceInjectedBytes = new byte[] { 182, 95, 100, 100, 46, 100, 98, 109, 95, 116, 114, 97, 99, 101, 95, 105, 110, 106, 101, 99, 116, 101, 100 }; #endif + // BaseHashBytes = MessagePack.Serialize("_dd.propagated_hash"); +#if NETCOREAPP + private static ReadOnlySpan BaseHashBytes => new byte[] { 179, 95, 100, 100, 46, 112, 114, 111, 112, 97, 103, 97, 116, 101, 100, 95, 104, 97, 115, 104 }; +#else + private static readonly byte[] BaseHashBytes = new byte[] { 179, 95, 100, 100, 46, 112, 114, 111, 112, 97, 103, 97, 116, 101, 100, 95, 104, 97, 115, 104 }; +#endif public override string? GetTag(string key) { @@ -68,6 +74,7 @@ partial class SqlTags "db.user" => DbUser, "out.host" => OutHost, "_dd.dbm_trace_injected" => DbmTraceInjected, + "_dd.propagated_hash" => BaseHash, _ => base.GetTag(key), }; } @@ -94,6 +101,9 @@ public override void SetTag(string key, string? value) case "_dd.dbm_trace_injected": DbmTraceInjected = value; break; + case "_dd.propagated_hash": + BaseHash = value; + break; case "span.kind": Logger.Value.Warning("Attempted to set readonly tag {TagName} on {TagType}. Ignoring.", key, nameof(SqlTags)); break; @@ -140,6 +150,11 @@ public override void EnumerateTags(ref TProcessor processor) processor.Process(new TagItem("_dd.dbm_trace_injected", DbmTraceInjected, DbmTraceInjectedBytes)); } + if (BaseHash is not null) + { + processor.Process(new TagItem("_dd.propagated_hash", BaseHash, BaseHashBytes)); + } + base.EnumerateTags(ref processor); } @@ -194,6 +209,13 @@ protected override void WriteAdditionalTags(System.Text.StringBuilder sb) .Append(','); } + if (BaseHash is not null) + { + sb.Append("_dd.propagated_hash (tag):") + .Append(BaseHash) + .Append(','); + } + base.WriteAdditionalTags(sb); } } diff --git a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs index 1926eaef1500..5ac4ed1e7782 100644 --- a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/ConfigurationKeys.g.cs @@ -71,6 +71,13 @@ internal static partial class ConfigurationKeys /// public const string ApplicationMonitoringConfigFileEnabled = "DD_APPLICATION_MONITORING_CONFIG_FILE_ENABLED"; + /// + /// Configuration key for enabling or disabling the injection of Base Hash in SQL Comments. + /// Default value is false (disabled). + /// + /// + public const string DbmInjectSqlBasehash = "DD_DBM_INJECT_SQL_BASEHASH"; + /// /// Configuration key for setting DBM propagation mode /// Default value is disabled, expected values are either: disabled, service or full diff --git a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs index 4d4314090873..db1cce10b854 100644 --- a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TagListGenerator/SqlTags.g.cs @@ -56,6 +56,12 @@ partial class SqlTags #else private static readonly byte[] DbmTraceInjectedBytes = new byte[] { 182, 95, 100, 100, 46, 100, 98, 109, 95, 116, 114, 97, 99, 101, 95, 105, 110, 106, 101, 99, 116, 101, 100 }; #endif + // BaseHashBytes = MessagePack.Serialize("_dd.propagated_hash"); +#if NETCOREAPP + private static ReadOnlySpan BaseHashBytes => new byte[] { 179, 95, 100, 100, 46, 112, 114, 111, 112, 97, 103, 97, 116, 101, 100, 95, 104, 97, 115, 104 }; +#else + private static readonly byte[] BaseHashBytes = new byte[] { 179, 95, 100, 100, 46, 112, 114, 111, 112, 97, 103, 97, 116, 101, 100, 95, 104, 97, 115, 104 }; +#endif public override string? GetTag(string key) { @@ -68,6 +74,7 @@ partial class SqlTags "db.user" => DbUser, "out.host" => OutHost, "_dd.dbm_trace_injected" => DbmTraceInjected, + "_dd.propagated_hash" => BaseHash, _ => base.GetTag(key), }; } @@ -94,6 +101,9 @@ public override void SetTag(string key, string? value) case "_dd.dbm_trace_injected": DbmTraceInjected = value; break; + case "_dd.propagated_hash": + BaseHash = value; + break; case "span.kind": Logger.Value.Warning("Attempted to set readonly tag {TagName} on {TagType}. Ignoring.", key, nameof(SqlTags)); break; @@ -140,6 +150,11 @@ public override void EnumerateTags(ref TProcessor processor) processor.Process(new TagItem("_dd.dbm_trace_injected", DbmTraceInjected, DbmTraceInjectedBytes)); } + if (BaseHash is not null) + { + processor.Process(new TagItem("_dd.propagated_hash", BaseHash, BaseHashBytes)); + } + base.EnumerateTags(ref processor); } @@ -194,6 +209,13 @@ protected override void WriteAdditionalTags(System.Text.StringBuilder sb) .Append(','); } + if (BaseHash is not null) + { + sb.Append("_dd.propagated_hash (tag):") + .Append(BaseHash) + .Append(','); + } + base.WriteAdditionalTags(sb); } } diff --git a/tracer/src/Datadog.Trace/PlatformHelpers/ContainerMetadata.NetFramework.cs b/tracer/src/Datadog.Trace/PlatformHelpers/ContainerMetadata.NetFramework.cs index 088b42089d3a..2d60a728da92 100644 --- a/tracer/src/Datadog.Trace/PlatformHelpers/ContainerMetadata.NetFramework.cs +++ b/tracer/src/Datadog.Trace/PlatformHelpers/ContainerMetadata.NetFramework.cs @@ -17,9 +17,6 @@ namespace Datadog.Trace.PlatformHelpers; /// internal sealed class ContainerMetadata { - private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(ContainerMetadata)); - private static bool _warnedOnSet; // to log only once - public static readonly ContainerMetadata Instance = new(); private ContainerMetadata() @@ -32,20 +29,6 @@ public ContainerMetadata(string containerId, string entityId) // nothing to do, just to match the other version } - // always null in this implementation - public string? ContainerTagsHash - { - get => null; - set - { - if (!_warnedOnSet) - { - _warnedOnSet = true; - Log.Error("The code is trying to set the value '{Value}' to {Prop}, but this has no effect in .NET Framework.", value, nameof(ContainerTagsHash)); - } - } - } - /// /// Gets the id of the container executing the code. /// Return null if code is not executing inside a supported container. diff --git a/tracer/src/Datadog.Trace/PlatformHelpers/ContainerMetadata.cs b/tracer/src/Datadog.Trace/PlatformHelpers/ContainerMetadata.cs index bfe215f50e9c..135fb0e1af39 100644 --- a/tracer/src/Datadog.Trace/PlatformHelpers/ContainerMetadata.cs +++ b/tracer/src/Datadog.Trace/PlatformHelpers/ContainerMetadata.cs @@ -61,16 +61,6 @@ public ContainerMetadata(string? containerId, string? entityId) _entityId = new Lazy(() => entityId); } - /// - /// Gets or sets the container tags hash received from the agent, used by DBM/DSM - /// This is set when we receive a value for it in an http response from the agent - /// - public string? ContainerTagsHash - { - get => Volatile.Read(ref field); - set => Volatile.Write(ref field, value); - } - /// /// Gets the id of the container executing the code. /// Return null if code is not executing inside a supported container. diff --git a/tracer/src/Datadog.Trace/ProcessTags.cs b/tracer/src/Datadog.Trace/ProcessTags.cs index 369c7dd89089..480cca12fea4 100644 --- a/tracer/src/Datadog.Trace/ProcessTags.cs +++ b/tracer/src/Datadog.Trace/ProcessTags.cs @@ -38,7 +38,10 @@ public ProcessTags(bool serviceNameUserDefined, string autoServiceName) // two views on the same data public List TagsList => field ??= GetTagsList(_serviceNameUserDefined, _autoServiceName); - public string SerializedTags => field ??= string.Join(",", TagsList); + public string SerializedTags + { + get => field ??= string.Join(",", TagsList); // don't forget to refresh the hash in ServiceRemappingHash on write if this value becomes mutable + } private static List GetTagsList(bool serviceNameUserDefined, string autoServiceName) { diff --git a/tracer/src/Datadog.Trace/ServiceRemappingHash.cs b/tracer/src/Datadog.Trace/ServiceRemappingHash.cs new file mode 100644 index 000000000000..1a59133a30e2 --- /dev/null +++ b/tracer/src/Datadog.Trace/ServiceRemappingHash.cs @@ -0,0 +1,107 @@ +// +// 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. +// + +#nullable enable + +using System; +using System.Text; +using System.Threading; +using Datadog.Trace.Util; + +namespace Datadog.Trace; + +/// +/// This class is a container for the "base hash", a hash of process tags and container tags. +/// Used by DBM to retrieve all the tag values from the spans, from just a single parameter (this hash), +/// Used by DSM in the pathway to identify different sources that could have been service-remapped +/// +internal sealed class ServiceRemappingHash +{ + private readonly string? _serializedProcessTags; + + public ServiceRemappingHash(string? serializedProcessTags) + { + _serializedProcessTags = serializedProcessTags; + if (serializedProcessTags != null) + { + // containers tags hash is always null at creation, because we discover it later (if any) + Base64Value = Compute(serializedProcessTags, containerTagsHash: null); + } + } + + /// + /// Gets the container tags hash received from the agent, used by DBM/DSM + /// This is set when we receive a value for it in an http response from the agent + /// + public string? ContainerTagsHash + { + get => Volatile.Read(ref field); + private set => Volatile.Write(ref field, value); + } + + /// + /// Gets the base64 representation of the hash + /// + public string? Base64Value + { + get => Volatile.Read(ref field); + private set => Volatile.Write(ref field, value); + } + + public void UpdateContainerTagsHash(string containerTagsHash) + { + ContainerTagsHash = containerTagsHash; + + if (_serializedProcessTags != null) + { + Base64Value = Compute(_serializedProcessTags, containerTagsHash); + } + } + + private static string Compute(string processTags, string? containerTagsHash) + { + var hash = FnvHash64.GenerateHash(processTags, FnvHash64.Version.V1); + if (containerTagsHash != null) + { + hash = FnvHash64.GenerateHash(containerTagsHash, FnvHash64.Version.V1, hash); + } + + // ulong/UInt64 is 8 bytes + // Encoding to Base64 expands the number of bytes by 4 * ceil(n / 3) = 12 bytes +#if NETCOREAPP3_1_OR_GREATER + Span buf = stackalloc byte[12]; +#else + // can't stackalloc into the vendored Span + var buf = new byte[12]; +#endif + BinaryPrimitives.WriteUInt64LittleEndian(buf, hash); // write 8 bytes into a 12-byte buffer + Base64.EncodeToUtf8InPlace(buf, dataLength: 8, out var bytesWritten); + + // no padding + // Base64 always pads input to multiples of 3, so we know that we will + // always have 11 bytes of "real" data, and one of padding `=`. + bytesWritten--; + + // use url-safe characters (for the SQL comment) + for (var i = 0; i < bytesWritten; i++) + { + if (buf[i] == (byte)'+') + { + buf[i] = (byte)'-'; + } + else if (buf[i] == (byte)'/') + { + buf[i] = (byte)'_'; + } + } + +#if NETCOREAPP3_1_OR_GREATER + return Encoding.ASCII.GetString(buf.Slice(0, bytesWritten)); +#else + // can't use Range + return Encoding.ASCII.GetString(buf, index: 0, bytesWritten); +#endif + } +} diff --git a/tracer/src/Datadog.Trace/Tagging/SqlTags.cs b/tracer/src/Datadog.Trace/Tagging/SqlTags.cs index 79b673523c5f..fa79e7654f45 100644 --- a/tracer/src/Datadog.Trace/Tagging/SqlTags.cs +++ b/tracer/src/Datadog.Trace/Tagging/SqlTags.cs @@ -3,7 +3,6 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // -using Datadog.Trace.Configuration; using Datadog.Trace.SourceGenerators; #pragma warning disable SA1402 // File must contain single type @@ -14,23 +13,26 @@ internal partial class SqlTags : InstrumentationTags [Tag(Trace.Tags.SpanKind)] public override string SpanKind => SpanKinds.Client; - [Tag(Trace.Tags.DbType)] + [Tag(Tags.DbType)] public string DbType { get; set; } - [Tag(Trace.Tags.InstrumentationName)] + [Tag(Tags.InstrumentationName)] public string InstrumentationName { get; set; } - [Tag(Trace.Tags.DbName)] + [Tag(Tags.DbName)] public string DbName { get; set; } - [Tag(Trace.Tags.DbUser)] + [Tag(Tags.DbUser)] public string DbUser { get; set; } - [Tag(Trace.Tags.OutHost)] + [Tag(Tags.OutHost)] public string OutHost { get; set; } - [Tag(Trace.Tags.DbmTraceInjected)] + [Tag(Tags.DbmTraceInjected)] public string DbmTraceInjected { get; set; } + + [Tag(Tags.BaseHash)] + public string BaseHash { get; set; } } internal sealed partial class SqlV1Tags : SqlTags @@ -42,14 +44,14 @@ internal sealed partial class SqlV1Tags : SqlTags // value from predefined precursor attributes. // However, this can still be set from ITags.SetTag so the user can // customize the value if they wish. - [Tag(Trace.Tags.PeerService)] + [Tag(Tags.PeerService)] public string PeerService { get => _peerServiceOverride ?? DbName ?? OutHost; private set => _peerServiceOverride = value; } - [Tag(Trace.Tags.PeerServiceSource)] + [Tag(Tags.PeerServiceSource)] public string PeerServiceSource { get diff --git a/tracer/src/Datadog.Trace/Tags.cs b/tracer/src/Datadog.Trace/Tags.cs index 5eff6f23aa60..bff80aac495f 100644 --- a/tracer/src/Datadog.Trace/Tags.cs +++ b/tracer/src/Datadog.Trace/Tags.cs @@ -796,6 +796,9 @@ public static partial class Tags /// internal const string DbmTraceInjected = "_dd.dbm_trace_injected"; + /// contains a hash of container tags (for now), to be matched with the hash injected in queries, so that we can retrieve the corresponding values from the span + internal const string BaseHash = "_dd.propagated_hash"; + // Data Streams Monitoring internal const string SchemaDefinition = "schema.definition"; internal const string SchemaWeight = "schema.weight"; diff --git a/tracer/src/Datadog.Trace/Tracer.cs b/tracer/src/Datadog.Trace/Tracer.cs index 242eb8eda564..13e679be07d9 100644 --- a/tracer/src/Datadog.Trace/Tracer.cs +++ b/tracer/src/Datadog.Trace/Tracer.cs @@ -55,8 +55,8 @@ public class Tracer : IDatadogTracer, IDatadogOpenTracingTracer /// Note that this API does NOT replace the global Tracer instance. /// The created will be scoped specifically to this instance. /// - internal Tracer(TracerSettings settings, IAgentWriter agentWriter, ITraceSampler sampler, IScopeManager scopeManager, IStatsdManager statsd, ITelemetryController telemetry = null, IDiscoveryService discoveryService = null) - : this(TracerManagerFactory.Instance.CreateTracerManager(settings, agentWriter, sampler, scopeManager, statsd, runtimeMetrics: null, logSubmissionManager: null, telemetry: telemetry ?? NullTelemetryController.Instance, discoveryService ?? NullDiscoveryService.Instance, dataStreamsManager: null, remoteConfigurationManager: null, dynamicConfigurationManager: null, tracerFlareManager: null, spanEventsManager: null, featureFlags: null)) + internal Tracer(TracerSettings settings, IAgentWriter agentWriter, ITraceSampler sampler, IScopeManager scopeManager, IStatsdManager statsd, ITelemetryController telemetry = null, IDiscoveryService discoveryService = null, ServiceRemappingHash serviceRemappingHash = null) + : this(TracerManagerFactory.Instance.CreateTracerManager(settings, agentWriter, sampler, scopeManager, statsd, runtimeMetrics: null, logSubmissionManager: null, telemetry: telemetry ?? NullTelemetryController.Instance, discoveryService ?? NullDiscoveryService.Instance, dataStreamsManager: null, remoteConfigurationManager: null, dynamicConfigurationManager: null, tracerFlareManager: null, spanEventsManager: null, featureFlags: null, serviceRemappingHash: serviceRemappingHash)) { } diff --git a/tracer/src/Datadog.Trace/TracerManager.cs b/tracer/src/Datadog.Trace/TracerManager.cs index 12a07ad9aacb..7965ce96ee7d 100644 --- a/tracer/src/Datadog.Trace/TracerManager.cs +++ b/tracer/src/Datadog.Trace/TracerManager.cs @@ -24,6 +24,7 @@ using Datadog.Trace.Logging; using Datadog.Trace.Logging.DirectSubmission; using Datadog.Trace.Logging.TracerFlare; +using Datadog.Trace.PlatformHelpers; using Datadog.Trace.Processors; using Datadog.Trace.Propagators; using Datadog.Trace.RemoteConfigurationManagement; @@ -77,6 +78,7 @@ public TracerManager( ITracerFlareManager tracerFlareManager, ISpanEventsManager spanEventsManager, FeatureFlagsModule featureFlagsModule, + ServiceRemappingHash serviceRemappingHash, ITraceProcessor[] traceProcessors = null) { Settings = settings; @@ -108,6 +110,8 @@ public TracerManager( SpanEventsManager = spanEventsManager; FeatureFlags = featureFlagsModule; + ServiceRemappingHash = serviceRemappingHash; + SpanContextPropagator = SpanContextPropagatorFactory.GetSpanContextPropagator(settings.PropagationStyleInject, settings.PropagationStyleExtract, settings.PropagationExtractFirstOnly, settings.PropagationBehaviorExtract); UpdatePerTraceSettings(settings.Manager.InitialMutableSettings); _settingSubscription = settings.Manager.SubscribeToChanges(changes => @@ -187,6 +191,8 @@ public static TracerManager Instance public SpanContextPropagator SpanContextPropagator { get; } + public ServiceRemappingHash ServiceRemappingHash { get; } + /// /// Replaces the global settings. This affects all instances /// which use the global diff --git a/tracer/src/Datadog.Trace/TracerManagerFactory.cs b/tracer/src/Datadog.Trace/TracerManagerFactory.cs index 4ed14d44212e..b58e10866626 100644 --- a/tracer/src/Datadog.Trace/TracerManagerFactory.cs +++ b/tracer/src/Datadog.Trace/TracerManagerFactory.cs @@ -91,7 +91,7 @@ internal TracerManager CreateTracerManager(TracerSettings settings, TracerManage /// /// Internal for use in tests that create "standalone" by - /// + /// /// internal TracerManager CreateTracerManager( TracerSettings settings, @@ -108,7 +108,8 @@ internal TracerManager CreateTracerManager( IDynamicConfigurationManager dynamicConfigurationManager, ITracerFlareManager tracerFlareManager, ISpanEventsManager spanEventsManager, - FeatureFlagsModule featureFlags) + FeatureFlagsModule featureFlags, + ServiceRemappingHash serviceRemappingHash = null) { settings ??= TracerSettings.FromDefaultSourcesInternal(); var result = GlobalConfigurationSource.CreationResult; @@ -123,7 +124,8 @@ internal TracerManager CreateTracerManager( Log.Warning(libdatadogAvailaibility.Exception, "An exception occurred while checking if libdatadog is available"); } - discoveryService ??= GetDiscoveryService(settings); + serviceRemappingHash ??= new ServiceRemappingHash(settings.Manager.InitialMutableSettings.ProcessTags?.SerializedTags); + discoveryService ??= GetDiscoveryService(settings, serviceRemappingHash); var telemetrySettings = CreateTelemetrySettings(settings); telemetry ??= CreateTelemetryController(settings, discoveryService, telemetrySettings); @@ -214,7 +216,8 @@ internal TracerManager CreateTracerManager( dynamicConfigurationManager, tracerFlareManager, spanEventsManager, - featureFlags); + featureFlags, + serviceRemappingHash); } protected virtual TelemetrySettings CreateTelemetrySettings(TracerSettings settings) => @@ -255,8 +258,11 @@ protected virtual TracerManager CreateTracerManagerFrom( IDynamicConfigurationManager dynamicConfigurationManager, ITracerFlareManager tracerFlareManager, ISpanEventsManager spanEventsManager, - FeatureFlagsModule featureFlagsModule) - => new TracerManager(settings, agentWriter, scopeManager, statsd, runtimeMetrics, logSubmissionManager, telemetry, discoveryService, dataStreamsManager, gitMetadataTagsProvider, traceSampler, spanSampler, remoteConfigurationManager, dynamicConfigurationManager, tracerFlareManager, spanEventsManager, featureFlagsModule); + FeatureFlagsModule featureFlagsModule, + ServiceRemappingHash serviceRemappingHash) + { + return new TracerManager(settings, agentWriter, scopeManager, statsd, runtimeMetrics, logSubmissionManager, telemetry, discoveryService, dataStreamsManager, gitMetadataTagsProvider, traceSampler, spanSampler, remoteConfigurationManager, dynamicConfigurationManager, tracerFlareManager, spanEventsManager, featureFlagsModule, serviceRemappingHash); + } protected virtual ITraceSampler GetSampler(TracerSettings settings) { @@ -297,9 +303,11 @@ protected virtual IAgentWriter GetAgentWriter(TracerSettings settings, IStatsdMa return new AgentWriter(api, statsAggregator, statsd, settings); } - internal virtual IDiscoveryService GetDiscoveryService(TracerSettings settings) - => settings.AgentFeaturePollingEnabled ? DiscoveryService.CreateManaged(settings, ContainerMetadata.Instance) - : - NullDiscoveryService.Instance; + internal virtual IDiscoveryService GetDiscoveryService(TracerSettings settings, ServiceRemappingHash serviceRemappingHash) + { + return settings.AgentFeaturePollingEnabled + ? DiscoveryService.CreateManaged(settings, ContainerMetadata.Instance, serviceRemappingHash) + : NullDiscoveryService.Instance; + } } } diff --git a/tracer/test/Datadog.Trace.ClrProfiler.Managed.Tests/DbScopeFactoryTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.Managed.Tests/DbScopeFactoryTests.cs index 4b661147cca9..e0e0568c8adb 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.Managed.Tests/DbScopeFactoryTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.Managed.Tests/DbScopeFactoryTests.cs @@ -61,8 +61,8 @@ public static TheoryData GetDbmCommands() public static IEnumerable GetEnabledDbmData() => from command in (IEnumerable)GetDbmCommands() from dbm in new[] { "service", "full" } - from storedProcInject in new[] { false, true } - select new[] { command[0], dbm, storedProcInject }; + from enabled in new[] { false, true } + select new[] { command[0], dbm, enabled }; [Theory] [MemberData(nameof(GetDbCommands))] @@ -151,6 +151,38 @@ public async Task CreateDbCommandScope_IgnoresReplacementServiceNameWhenNotProvi Assert.NotEqual("my-custom-type", scope.Span.ServiceName); } + [Theory] + [MemberData(nameof(GetEnabledDbmData))] + public async Task CreateDbCommandScope_HasBaseHashWhenConfigured(Type commandType, string dbmMode, bool hashPropagationEnabled) + { + var command = (IDbCommand)Activator.CreateInstance(commandType)!; + command.CommandText = DbmCommandText; + + var tracerSettings = TracerSettings.Create(new Dictionary + { + // hash propagation requires both those settings to be true + { ConfigurationKeys.PropagateProcessTags, hashPropagationEnabled.ToString() }, + { ConfigurationKeys.DbmInjectSqlBasehash, hashPropagationEnabled.ToString() }, + { ConfigurationKeys.DbmPropagationMode, dbmMode } + }); + var serviceRemappingHash = new ServiceRemappingHash("process:tag,service:service"); + await using var tracer = TracerHelper.Create(tracerSettings, serviceRemappingHash: serviceRemappingHash); + + using var scope = CreateDbCommandScope(tracer, command); + + scope.Should().NotBeNull(); + if (hashPropagationEnabled) + { + scope.Span.GetTag(Tags.BaseHash).Should().Be(serviceRemappingHash.Base64Value); + command.CommandText.Should().Contain($"ddsh='{serviceRemappingHash.Base64Value}'"); + } + else + { + scope.Span.GetTag(Tags.BaseHash).Should().BeNull(); + command.CommandText.Should().NotContain("ddsh="); + } + } + [Theory] [MemberData(nameof(GetEnabledDbmData))] public async Task CreateDbCommandScope_InjectsDbmWhenEnabled(Type commandType, string dbmMode, bool storedProcInject) diff --git a/tracer/test/Datadog.Trace.IntegrationTests/LibDatadog/TraceExporterTests.cs b/tracer/test/Datadog.Trace.IntegrationTests/LibDatadog/TraceExporterTests.cs index 7286d658e777..d9c3c9c1ca21 100644 --- a/tracer/test/Datadog.Trace.IntegrationTests/LibDatadog/TraceExporterTests.cs +++ b/tracer/test/Datadog.Trace.IntegrationTests/LibDatadog/TraceExporterTests.cs @@ -77,7 +77,7 @@ public async Task SendsTracesUsingDataPipeline(TestTransports transport) var sampleRateResponses = new ConcurrentQueue>(); - var discovery = DiscoveryService.CreateUnmanaged(tracerSettings.Manager.InitialExporterSettings, new ContainerMetadata(containerId: null, entityId: null)); + var discovery = DiscoveryService.CreateUnmanaged(tracerSettings.Manager.InitialExporterSettings, new ContainerMetadata(containerId: null, entityId: null), new ServiceRemappingHash(null)); var statsd = new NoOpStatsd(); // We have to replace the agent writer so that we can intercept the sample rate responses diff --git a/tracer/test/Datadog.Trace.IntegrationTests/StatsTests.cs b/tracer/test/Datadog.Trace.IntegrationTests/StatsTests.cs index 317223ed6595..0bedc73e4d11 100644 --- a/tracer/test/Datadog.Trace.IntegrationTests/StatsTests.cs +++ b/tracer/test/Datadog.Trace.IntegrationTests/StatsTests.cs @@ -58,7 +58,7 @@ public async Task SendsStatsWithProcessing_Normalizer() { ConfigurationKeys.TraceDataPipelineEnabled, "false" }, }); - var discovery = DiscoveryService.CreateUnmanaged(settings.Manager.InitialExporterSettings, new ContainerMetadata(null, null)); + var discovery = DiscoveryService.CreateUnmanaged(settings.Manager.InitialExporterSettings, new ContainerMetadata(containerId: null, entityId: null), new ServiceRemappingHash(null)); // Note: we are explicitly _not_ using a using here, as we dispose it ourselves manually at a specific point // and this was easiest to retrofit without changing the test structure too much. var tracer = TracerHelper.Create(settings, agentWriter: null, sampler: null, scopeManager: null, statsd: null, discoveryService: discovery); @@ -205,7 +205,7 @@ public async Task SendsStatsWithProcessing_Obfuscator() { ConfigurationKeys.TraceDataPipelineEnabled, "false" }, }); - var discovery = DiscoveryService.CreateUnmanaged(settings.Manager.InitialExporterSettings, new ContainerMetadata(null, null)); + var discovery = DiscoveryService.CreateUnmanaged(settings.Manager.InitialExporterSettings, new ContainerMetadata(containerId: null, entityId: null), new ServiceRemappingHash(null)); // Note: we are explicitly _not_ using a using here, as we dispose it ourselves manually at a specific point // and this was easiest to retrofit without changing the test structure too much. var tracer = TracerHelper.Create(settings, agentWriter: null, sampler: null, scopeManager: null, statsd: null, discoveryService: discovery); @@ -366,7 +366,7 @@ private async Task SendStatsHelper(bool statsComputationEnabled, bool expectStat { ConfigurationKeys.TraceDataPipelineEnabled, "false" }, })); - var discovery = DiscoveryService.CreateUnmanaged(settings.Manager.InitialExporterSettings, new ContainerMetadata(null, null)); + var discovery = DiscoveryService.CreateUnmanaged(settings.Manager.InitialExporterSettings, new ContainerMetadata(containerId: null, entityId: null), new ServiceRemappingHash(null)); // Note: we are explicitly _not_ using a using here, as we dispose it ourselves manually at a specific point // and this was easiest to retrofit without changing the test structure too much. var tracer = TracerHelper.Create(settings, agentWriter: null, sampler: null, scopeManager: null, statsd: null, discoveryService: discovery); diff --git a/tracer/test/Datadog.Trace.TestHelpers/TestTracer/ScopedTracer.cs b/tracer/test/Datadog.Trace.TestHelpers/TestTracer/ScopedTracer.cs index 77da03d833a7..a46f7975e441 100644 --- a/tracer/test/Datadog.Trace.TestHelpers/TestTracer/ScopedTracer.cs +++ b/tracer/test/Datadog.Trace.TestHelpers/TestTracer/ScopedTracer.cs @@ -25,8 +25,9 @@ public ScopedTracer( IScopeManager scopeManager = null, IDogStatsd statsd = null, ITelemetryController telemetryController = null, - IDiscoveryService discoveryService = null) - : this(settings, agentWriter, sampler, scopeManager, statsd is null ? null : new TestStatsdManager(statsd), telemetryController, discoveryService) + IDiscoveryService discoveryService = null, + ServiceRemappingHash serviceRemappingHash = null) + : this(settings, agentWriter, sampler, scopeManager, statsd is null ? null : new TestStatsdManager(statsd), telemetryController, discoveryService, serviceRemappingHash) { } @@ -37,8 +38,9 @@ public ScopedTracer( IScopeManager scopeManager, IStatsdManager statsdManager, ITelemetryController telemetryController = null, - IDiscoveryService discoveryService = null) - : base(settings, agentWriter, sampler, scopeManager, statsdManager, telemetry: telemetryController, discoveryService: discoveryService) + IDiscoveryService discoveryService = null, + ServiceRemappingHash serviceRemappingHash = null) + : base(settings, agentWriter, sampler, scopeManager, statsdManager, telemetry: telemetryController, discoveryService: discoveryService, serviceRemappingHash: serviceRemappingHash) { } diff --git a/tracer/test/Datadog.Trace.TestHelpers/TestTracer/TracerHelper.cs b/tracer/test/Datadog.Trace.TestHelpers/TestTracer/TracerHelper.cs index 5be51cb2394b..d214bc91964c 100644 --- a/tracer/test/Datadog.Trace.TestHelpers/TestTracer/TracerHelper.cs +++ b/tracer/test/Datadog.Trace.TestHelpers/TestTracer/TracerHelper.cs @@ -25,8 +25,9 @@ public static ScopedTracer Create( IScopeManager scopeManager = null, IDogStatsd statsd = null, ITelemetryController telemetryController = null, - IDiscoveryService discoveryService = null) => - new(settings, agentWriter, sampler, scopeManager, statsd, discoveryService: discoveryService, telemetryController: telemetryController); + IDiscoveryService discoveryService = null, + ServiceRemappingHash serviceRemappingHash = null) => + new(settings, agentWriter, sampler, scopeManager, statsd, discoveryService: discoveryService, telemetryController: telemetryController, serviceRemappingHash: serviceRemappingHash); /// /// Create a test instance of the Tracer, that doesn't use any shared instances diff --git a/tracer/test/Datadog.Trace.Tests/Agent/DiscoveryServiceTests.cs b/tracer/test/Datadog.Trace.Tests/Agent/DiscoveryServiceTests.cs index 97227a2d6f6e..b1597353dcba 100644 --- a/tracer/test/Datadog.Trace.Tests/Agent/DiscoveryServiceTests.cs +++ b/tracer/test/Datadog.Trace.Tests/Agent/DiscoveryServiceTests.cs @@ -27,7 +27,7 @@ public class DiscoveryServiceTests private const int MaxRetryDelayMs = 50; private const int RecheckIntervalMs = 300_000; - private static readonly ContainerMetadata NullContainerMetadata = new(containerId: null, entityId: null); + private static readonly ServiceRemappingHash DisabledServiceRemappingHash = new(null); [Fact] public async Task HandlesFlakyConfiguration() @@ -37,7 +37,7 @@ public async Task HandlesFlakyConfiguration() x => new FaultyApiRequest(x), x => new TestApiRequest(x)); - var ds = new DiscoveryService(factory, NullContainerMetadata, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); + var ds = new DiscoveryService(factory, DisabledServiceRemappingHash, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); ds.SubscribeToChanges(x => mutex.Set()); mutex.Wait(30_000).Should().BeTrue("Should raise subscription changes"); @@ -56,7 +56,7 @@ public async Task ReturnsDeserializedConfig() var factory = new TestRequestFactory( x => new TestApiRequest(x, responseContent: GetConfig(clientDropP0s, version))); - var ds = new DiscoveryService(factory, NullContainerMetadata, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); + var ds = new DiscoveryService(factory, DisabledServiceRemappingHash, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); ds.SubscribeToChanges( x => { @@ -89,7 +89,7 @@ public async Task CalculatesConfigStateHash() var factory = new TestRequestFactory( x => new TestApiRequest(x, responseContent: serializedConfig)); - await using var ds = new DiscoveryService(factory, NullContainerMetadata, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); + await using var ds = new DiscoveryService(factory, DisabledServiceRemappingHash, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); ds.SubscribeToChanges( x => { @@ -123,7 +123,7 @@ public async Task DoesNotFireInitialCallbackIfInitialConfigNotFetched() return new TestApiRequest(x, responseContent: GetConfig()); }); - var ds = new DiscoveryService(factory, NullContainerMetadata, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); + var ds = new DiscoveryService(factory, DisabledServiceRemappingHash, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); ds.SubscribeToChanges(x => notificationFired = true); await Task.Delay(5_000); // should recheck 5 times in this duration @@ -145,7 +145,7 @@ public async Task FiresInitialCallbackIfInitialConfigAlreadyFetched() }, y => throw new Exception("Should not make a second request")); - var ds = new DiscoveryService(factory, NullContainerMetadata, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); + var ds = new DiscoveryService(factory, DisabledServiceRemappingHash, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); // make sure we have config ds.SubscribeToChanges(x => mutex.Set()); mutex.Wait(30_000).Should().BeTrue("Should make request to api"); @@ -176,7 +176,7 @@ public async Task DoesNotFireCallbackOnRecheckIfNoChangesToConfig() return new TestApiRequest(x, responseContent: GetConfig()); }); - var ds = new DiscoveryService(factory, NullContainerMetadata, InitialRetryDelayMs, MaxRetryDelayMs, recheckIntervalMs); + var ds = new DiscoveryService(factory, DisabledServiceRemappingHash, InitialRetryDelayMs, MaxRetryDelayMs, recheckIntervalMs); ds.SubscribeToChanges(x => Interlocked.Increment(ref notificationCount)); // fire first request mutex1.Set(); @@ -208,7 +208,7 @@ public async Task FiresCallbackOnRecheckIfHasChangesToConfig() return new TestApiRequest(x, responseContent: GetConfig(dropP0: false)); }); - var ds = new DiscoveryService(factory, NullContainerMetadata, InitialRetryDelayMs, MaxRetryDelayMs, recheckIntervalMs); + var ds = new DiscoveryService(factory, DisabledServiceRemappingHash, InitialRetryDelayMs, MaxRetryDelayMs, recheckIntervalMs); ds.SubscribeToChanges(x => Interlocked.Increment(ref notificationCount)); // fire first request mutex1.Set(); @@ -241,7 +241,7 @@ public async Task DoesNotFireAfterUnsubscribing() return new TestApiRequest(x, responseContent: GetConfig(dropP0: false)); }); - var ds = new DiscoveryService(factory, NullContainerMetadata, InitialRetryDelayMs, MaxRetryDelayMs, recheckIntervalMs); + var ds = new DiscoveryService(factory, DisabledServiceRemappingHash, InitialRetryDelayMs, MaxRetryDelayMs, recheckIntervalMs); ds.SubscribeToChanges(Callback); @@ -274,7 +274,7 @@ public async Task DisposesInATimelyManner() }, x => new TestApiRequest(x, responseContent: GetConfig(dropP0: false))); - var ds = new DiscoveryService(factory, NullContainerMetadata, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); + var ds = new DiscoveryService(factory, DisabledServiceRemappingHash, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); // should be inside recheck loop mutex.Wait(30_000).Should().BeTrue("Should make request to api"); @@ -357,7 +357,7 @@ public async Task RequireRefresh(string originalHash, string agentHash, int time { var recheckIntervalMs = 30_000; var factory = new TestRequestFactory(); - await using var ds = new DiscoveryService(factory, NullContainerMetadata, InitialRetryDelayMs, MaxRetryDelayMs, recheckIntervalMs); + await using var ds = new DiscoveryService(factory, DisabledServiceRemappingHash, InitialRetryDelayMs, MaxRetryDelayMs, recheckIntervalMs); var now = DateTimeOffset.UtcNow; ds.SetCurrentConfigStateHash(agentHash); @@ -382,7 +382,7 @@ public async Task HandlesFailuresInApiWithBackoff() // These are the default values in the other constructor // but setting them explicitly here as it's the behaviour we're testing // not the exact values we choose later - var ds = new DiscoveryService(factory, NullContainerMetadata, initialRetryDelayMs: 500, maxRetryDelayMs: 5_000, recheckIntervalMs: 30_000); + var ds = new DiscoveryService(factory, DisabledServiceRemappingHash, initialRetryDelayMs: 500, maxRetryDelayMs: 5_000, recheckIntervalMs: 30_000); ds.SubscribeToChanges(_ => mutex.Set()); // wait for 0 + 500 + 1000 + 2000 + 4000 + 5000 ms (+ 2500 buffer). @@ -407,15 +407,15 @@ public async Task ExtractsContainerTagsHashFromResponseHeader() responseContent: GetConfig(), responseHeaders: new Dictionary { { AgentHttpHeaderNames.ContainerTagsHash, expectedTagsHash } })); - var containerMetadata = new ContainerMetadata(containerId: null, entityId: null); + var serviceRemappingHash = new ServiceRemappingHash("process:tag,service:service-name"); - var ds = new DiscoveryService(factory, containerMetadata, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); + var ds = new DiscoveryService(factory, serviceRemappingHash, InitialRetryDelayMs, MaxRetryDelayMs, RecheckIntervalMs); ds.SubscribeToChanges(x => mutex.Set()); mutex.Wait(30_000).Should().BeTrue("Should raise subscription changes"); // Verify the container tags hash was extracted and stored - containerMetadata.ContainerTagsHash.Should().Be(expectedTagsHash); + serviceRemappingHash.ContainerTagsHash.Should().Be(expectedTagsHash); await ds.DisposeAsync(); } diff --git a/tracer/test/Datadog.Trace.Tests/Configuration/ServiceNameTests.cs b/tracer/test/Datadog.Trace.Tests/Configuration/ServiceNameTests.cs index 918c7c1f7b88..b05e4d14701b 100644 --- a/tracer/test/Datadog.Trace.Tests/Configuration/ServiceNameTests.cs +++ b/tracer/test/Datadog.Trace.Tests/Configuration/ServiceNameTests.cs @@ -118,7 +118,7 @@ internal LockedTracer(TracerSettings tracerSettings) private class LockedTracerManager : TracerManager, ILockedTracer { public LockedTracerManager(TracerSettings tracerSettings) - : base(tracerSettings, null, Mock.Of(), null, null, null, null, null, null, null, null, null, null, null, null, null, null) + : base(tracerSettings, null, Mock.Of(), null, null, null, null, null, null, null, null, null, null, null, null, null, null, null) { } } diff --git a/tracer/test/Datadog.Trace.Tests/DatabaseMonitoring/DatabaseMonitoringPropagatorTests.cs b/tracer/test/Datadog.Trace.Tests/DatabaseMonitoring/DatabaseMonitoringPropagatorTests.cs index da0c4eaefb55..303d1c77b5e0 100644 --- a/tracer/test/Datadog.Trace.Tests/DatabaseMonitoring/DatabaseMonitoringPropagatorTests.cs +++ b/tracer/test/Datadog.Trace.Tests/DatabaseMonitoring/DatabaseMonitoringPropagatorTests.cs @@ -23,14 +23,14 @@ namespace Datadog.Trace.Tests.DatabaseMonitoring public class DatabaseMonitoringPropagatorTests { [Theory] - [InlineData("string100", SamplingPriorityValues.UserKeep, "npgsql", "", "", false)] - [InlineData("full", SamplingPriorityValues.UserKeep, "sqlite", "", "", false)] - [InlineData("disabled", SamplingPriorityValues.UserKeep, "sqlclient", "", "", false)] - [InlineData("Service", SamplingPriorityValues.AutoReject, "npgsql", "Test.Service-postgres", "/*dddbs='Test.Service-postgres',ddps='Test.Service',dddb='MyDatabase',ddh='MyHost'*/", false)] - [InlineData("full", SamplingPriorityValues.UserReject, "sqlclient", "Test.Service-sql-server", "/*dddbs='Test.Service-sql-server',ddps='Test.Service',dddb='MyDatabase',ddh='MyHost'*/", false)] - [InlineData("full", SamplingPriorityValues.UserReject, "oracle", "Test.Service-oracle", "/*dddbs='Test.Service-oracle',ddps='Test.Service',dddb='MyDatabase',ddh='MyHost'*/", false)] - [InlineData("fUlL", SamplingPriorityValues.AutoKeep, "mysql", "Test.Service-mysql", "/*dddbs='Test.Service-mysql',ddps='Test.Service',dddb='MyDatabase',ddh='MyHost',traceparent='00-00000000000000006172c1c9a829c71c-05a5f7b5320d6e4d-01'*/", true)] - public async Task ExpectedCommentInjected(string propagationMode, int? samplingPriority, string integration, string dbServiceName, string expectedComment, bool traceParentInjected) + [InlineData("string100", SamplingPriorityValues.UserKeep, "npgsql", "", null, "", false)] + [InlineData("full", SamplingPriorityValues.UserKeep, "sqlite", "", null, "", false)] + [InlineData("disabled", SamplingPriorityValues.UserKeep, "sqlclient", "", null, "", false)] + [InlineData("Service", SamplingPriorityValues.AutoReject, "npgsql", "Test.Service-postgres", null, "/*dddbs='Test.Service-postgres',ddps='Test.Service',dddb='MyDatabase',ddh='MyHost'*/", false)] + [InlineData("full", SamplingPriorityValues.UserReject, "sqlclient", "Test.Service-sql-server", "12345", "/*dddbs='Test.Service-sql-server',ddps='Test.Service',dddb='MyDatabase',ddh='MyHost',ddsh='12345'*/", false)] + [InlineData("full", SamplingPriorityValues.UserReject, "oracle", "Test.Service-oracle", null, "/*dddbs='Test.Service-oracle',ddps='Test.Service',dddb='MyDatabase',ddh='MyHost'*/", false)] + [InlineData("fUlL", SamplingPriorityValues.AutoKeep, "mysql", "Test.Service-mysql", "12345", "/*dddbs='Test.Service-mysql',ddps='Test.Service',dddb='MyDatabase',ddh='MyHost',ddsh='12345',traceparent='00-00000000000000006172c1c9a829c71c-05a5f7b5320d6e4d-01'*/", true)] + public async Task ExpectedCommentInjected(string propagationMode, int? samplingPriority, string integration, string dbServiceName, string baseHash, string expectedComment, bool traceParentInjected) { await using var v0Tracer = GetV0Tracer(); DbmPropagationLevel dbmPropagationLevel; @@ -45,7 +45,7 @@ public async Task ExpectedCommentInjected(string propagationMode, int? samplingP var initialCommandText = "select * from table"; var command = CreateCommand(initialCommandText); - var traceParentInjectedValue = DatabaseMonitoringPropagator.PropagateDataViaComment(dbmPropagationLevel, integrationId, command, "Test.Service", "MyDatabase", "MyHost", span, injectStoredProcedure: true); + var traceParentInjectedValue = DatabaseMonitoringPropagator.PropagateDataViaComment(dbmPropagationLevel, integrationId, command, "Test.Service", "MyDatabase", "MyHost", span, injectStoredProcedure: true, baseHash); traceParentInjectedValue.Should().Be(traceParentInjected); command.CommandText.Should().StartWith(expectedComment); @@ -68,7 +68,7 @@ public async Task ExpectedTagsInjected(string expectedComment, string env = null var initialCommandText = "select * from table"; var command = CreateCommand(initialCommandText); - var traceParentInjected = DatabaseMonitoringPropagator.PropagateDataViaComment(DbmPropagationLevel.Service, IntegrationId.MySql, command, "Test.Service", "MyDatabase", "MyHost", span, injectStoredProcedure: true); + var traceParentInjected = DatabaseMonitoringPropagator.PropagateDataViaComment(DbmPropagationLevel.Service, IntegrationId.MySql, command, "Test.Service", "MyDatabase", "MyHost", span, injectStoredProcedure: true, baseHash: null); // Always false since this test never runs for full mode traceParentInjected.Should().Be(false); @@ -92,7 +92,7 @@ public async Task ExpectedTagsEncoded(string expectedComment, string service, st var initialCommandText = "select * from table"; var command = CreateCommand(initialCommandText); - var traceParentInjected = DatabaseMonitoringPropagator.PropagateDataViaComment(DbmPropagationLevel.Service, IntegrationId.MySql, command, service, dbName, host, span, injectStoredProcedure: true); + var traceParentInjected = DatabaseMonitoringPropagator.PropagateDataViaComment(DbmPropagationLevel.Service, IntegrationId.MySql, command, service, dbName, host, span, injectStoredProcedure: true, baseHash: null); // Always false since this test never runs for full mode traceParentInjected.Should().Be(false); @@ -118,7 +118,7 @@ public async Task ExpectedCommentInjectedV1() var span = v1Tracer.StartSpan(tags: new SqlV1Tags() { DbName = dbName }, operationName: "db.query", parent: SpanContext.None, serviceName: dbServiceName, traceId: (TraceId)7021887840877922076, spanId: 407003698947780173); span.SetTraceSamplingPriority(samplingPriority); - var traceParentInjectedValue = DatabaseMonitoringPropagator.PropagateDataViaComment(dbmPropagationLevel, integrationId, command, "Test.Service", "MyDatabase", "MyHost", span, injectStoredProcedure: true); + var traceParentInjectedValue = DatabaseMonitoringPropagator.PropagateDataViaComment(dbmPropagationLevel, integrationId, command, "Test.Service", "MyDatabase", "MyHost", span, injectStoredProcedure: true, baseHash: null); traceParentInjectedValue.Should().Be(traceParentInjected); command.CommandText.Should().StartWith(expectedComment); @@ -143,7 +143,7 @@ public async Task ExpectedCommentAppendedV1() var span = v1Tracer.StartSpan(tags: new SqlV1Tags() { DbName = dbName }, operationName: "db.query", parent: SpanContext.None, serviceName: dbServiceName, traceId: (TraceId)7021887840877922076, spanId: 407003698947780173); span.SetTraceSamplingPriority(samplingPriority); - var traceParentInjectedValue = DatabaseMonitoringPropagator.PropagateDataViaComment(dbmPropagationLevel, integrationId, command, "Test.Service", "MyDatabase", "MyHost", span, injectStoredProcedure: true); + var traceParentInjectedValue = DatabaseMonitoringPropagator.PropagateDataViaComment(dbmPropagationLevel, integrationId, command, "Test.Service", "MyDatabase", "MyHost", span, injectStoredProcedure: true, baseHash: null); traceParentInjectedValue.Should().Be(traceParentInjected); command.CommandText.Should().EndWith(expectedComment); @@ -168,7 +168,7 @@ internal async Task PeerServiceInjected(SchemaVersion version) await using var tracer = version == SchemaVersion.V1 ? GetV1Tracer() : GetV0Tracer(); var span = tracer.StartSpan("db.query", sqlTags, serviceName: "myServiceName"); - var traceParentInjectedValue = DatabaseMonitoringPropagator.PropagateDataViaComment(dbmPropagationLevel, IntegrationId.Npgsql, command, "Test.Service", "MyDatabase", "MyHost", span, injectStoredProcedure: true); + var traceParentInjectedValue = DatabaseMonitoringPropagator.PropagateDataViaComment(dbmPropagationLevel, IntegrationId.Npgsql, command, "Test.Service", "MyDatabase", "MyHost", span, injectStoredProcedure: true, baseHash: null); traceParentInjectedValue.Should().Be(traceParentInjected); command.CommandText.Should().StartWith("/*dddbs='myServiceName',ddprs='myPeerService',ddps='Test.Service',dddb='MyDatabase',ddh='MyHost'*/"); diff --git a/tracer/test/Datadog.Trace.Tests/ServiceRemappingHashTests.cs b/tracer/test/Datadog.Trace.Tests/ServiceRemappingHashTests.cs new file mode 100644 index 000000000000..9cdd966b0794 --- /dev/null +++ b/tracer/test/Datadog.Trace.Tests/ServiceRemappingHashTests.cs @@ -0,0 +1,68 @@ +// +// 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. +// + +using System.Reflection; +using FluentAssertions; +using Xunit; + +namespace Datadog.Trace.Tests; + +public class ServiceRemappingHashTests +{ + [Fact] + public void Constructor_WithProcessTags_ComputesInitialBase64Value() + { + var hash = new ServiceRemappingHash("process:tag,hello:world"); + + hash.ContainerTagsHash.Should().BeNull(); + hash.Base64Value.Should().NotBeNullOrEmpty(); + } + + [Fact] + public void Constructor_WithoutProcessTags_LeavesBase64ValueNull() + { + var hash = new ServiceRemappingHash(null); + + hash.ContainerTagsHash.Should().BeNull(); + hash.Base64Value.Should().BeNull(); + } + + [Fact] + public void UpdateContainerTagsHash_WithProcessTags_UpdatesContainerHashAndBase64Value() + { + var hash = new ServiceRemappingHash("process:tag,hello:world"); + var initialBase64Value = hash.Base64Value; + + hash.UpdateContainerTagsHash("container0"); + + hash.ContainerTagsHash.Should().Be("container0"); + hash.Base64Value.Should().NotBe(initialBase64Value); + } + + [Fact] + public void UpdateContainerTagsHash_WithoutProcessTags_UpdatesOnlyContainerHash() + { + var hash = new ServiceRemappingHash(null); + + hash.UpdateContainerTagsHash("container0"); + + hash.ContainerTagsHash.Should().Be("container0"); + hash.Base64Value.Should().BeNull(); + } + + [Theory] + [InlineData("svc.auto:service", null, "X2HKTA63-84")] + [InlineData("svc.auto:service", "container0", "ANVkaLuv_RQ")] + public void Compute_ReturnsExpectedUrlSafeBase64WithoutPadding(string processTags, string containerTagsHash, string expected) + { + var hash = new ServiceRemappingHash(processTags); + hash.UpdateContainerTagsHash(containerTagsHash); + + hash.Base64Value.Should().Be(expected); + hash.Base64Value.Should().NotContain("+"); + hash.Base64Value.Should().NotContain("/"); + hash.Base64Value.Should().NotContain("="); + } +} diff --git a/tracer/test/Datadog.Trace.Tests/Telemetry/config_norm_rules.json b/tracer/test/Datadog.Trace.Tests/Telemetry/config_norm_rules.json index 928ecc5275f7..76bb7bcd7468 100644 --- a/tracer/test/Datadog.Trace.Tests/Telemetry/config_norm_rules.json +++ b/tracer/test/Datadog.Trace.Tests/Telemetry/config_norm_rules.json @@ -372,6 +372,7 @@ "DD_HTTP_SERVER_TAG_QUERY_STRING_SIZE": "trace_http_server_tag_query_string_size", "DD_HTTP_SERVER_TAG_QUERY_STRING": "trace_http_server_tag_query_string_enabled", "DD_DBM_PROPAGATION_MODE": "dbm_propagation_mode", + "DD_DBM_INJECT_SQL_BASEHASH": "dbm_inject_sql_basehash", "DD_TRACE_SPAN_ATTRIBUTE_SCHEMA": "trace_span_attribute_schema", "DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED": "trace_peer_service_defaults_enabled", "DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED": "trace_remove_integration_service_names_enabled", diff --git a/tracer/test/Datadog.Trace.Tests/TracerInstanceTest.cs b/tracer/test/Datadog.Trace.Tests/TracerInstanceTest.cs index 20cf703090f5..65064d566c7b 100644 --- a/tracer/test/Datadog.Trace.Tests/TracerInstanceTest.cs +++ b/tracer/test/Datadog.Trace.Tests/TracerInstanceTest.cs @@ -115,7 +115,7 @@ internal LockedTracer() private class LockedTracerManager : TracerManager, ILockedTracer { public LockedTracerManager() - : base(new TracerSettings(), null, null, null, null, null, null, null, null, null, null, null, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), null) + : base(new TracerSettings(), null, null, null, null, null, null, null, null, null, null, null, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), null, null) { } } diff --git a/tracer/test/Datadog.Trace.Tests/TracerManagerFactoryTests.cs b/tracer/test/Datadog.Trace.Tests/TracerManagerFactoryTests.cs index 5fd5d9925aaa..a194da4fcd92 100644 --- a/tracer/test/Datadog.Trace.Tests/TracerManagerFactoryTests.cs +++ b/tracer/test/Datadog.Trace.Tests/TracerManagerFactoryTests.cs @@ -116,7 +116,7 @@ public void DiscoveryServiceCanBeDisabled(bool enabled) settings.AgentFeaturePollingEnabled.Should().Be(enabled); var factory = new TracerManagerFactory(); - var discoveryService = factory.GetDiscoveryService(settings); + var discoveryService = factory.GetDiscoveryService(settings, new ServiceRemappingHash(null)); if (enabled) {