From ea62865d88f81fede1e2ca7d24d9e819fbb77842 Mon Sep 17 00:00:00 2001 From: Rasmus Kuusmann Date: Thu, 15 Apr 2021 21:46:58 +0300 Subject: [PATCH] First prototype --- Datadog.Trace.sln | 21 ++++-- .../Instrumentation.cs | 36 ++++++++++ .../Configuration/ConfigurationKeys.cs | 5 ++ .../DefaultFactoryConfigurator.cs | 22 +++++++ .../Factories/IPropagatorFactory.cs | 28 ++++++++ .../Configuration/GlobalSettings.cs | 55 ++++++++++++++++ .../Configuration/IFactoryConfigurator.cs | 16 +++++ .../Configuration/PropagatorType.cs | 29 -------- .../Configuration/TracerSettings.cs | 11 ++-- .../Configuration/Types/PropagatorTypes.cs | 23 +++++++ .../Conventions/ITraceIdConvention.cs | 11 +++- .../Factories/DefaultPropagatorFactory.cs | 58 ++++++++++++++++ .../Propagation/ContextPropagatorFactory.cs | 41 ------------ src/Datadog.Trace/Propagation/IPropagator.cs | 7 +- src/Datadog.Trace/Tracer.cs | 7 +- .../Factories/VendorPropagatorFactory.cs | 28 ++++++++ .../VendorSpanContextPropagator.cs | 66 +++++++++++++++++++ .../Samples.Vendoring.csproj | 9 +++ .../Types/VendorPropagatorTypes.cs | 7 ++ .../VendorFactoryConfigurator.cs | 16 +++++ 20 files changed, 413 insertions(+), 83 deletions(-) create mode 100644 src/Datadog.Trace/Configuration/DefaultFactoryConfigurator.cs create mode 100644 src/Datadog.Trace/Configuration/Factories/IPropagatorFactory.cs create mode 100644 src/Datadog.Trace/Configuration/IFactoryConfigurator.cs delete mode 100644 src/Datadog.Trace/Configuration/PropagatorType.cs create mode 100644 src/Datadog.Trace/Configuration/Types/PropagatorTypes.cs create mode 100644 src/Datadog.Trace/Factories/DefaultPropagatorFactory.cs delete mode 100644 src/Datadog.Trace/Propagation/ContextPropagatorFactory.cs create mode 100644 test/test-applications/integrations/Samples.Vendoring/Factories/VendorPropagatorFactory.cs create mode 100644 test/test-applications/integrations/Samples.Vendoring/Propagators/VendorSpanContextPropagator.cs create mode 100644 test/test-applications/integrations/Samples.Vendoring/Samples.Vendoring.csproj create mode 100644 test/test-applications/integrations/Samples.Vendoring/Types/VendorPropagatorTypes.cs create mode 100644 test/test-applications/integrations/Samples.Vendoring/VendorFactoryConfigurator.cs diff --git a/Datadog.Trace.sln b/Datadog.Trace.sln index b521f4de89..4a1643e926 100644 --- a/Datadog.Trace.sln +++ b/Datadog.Trace.sln @@ -33,13 +33,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .gitattributes = .gitattributes .gitignore = .gitignore Datadog.Trace.proj = Datadog.Trace.proj - OpenTelemetry.AutoInstrumentation.snk = OpenTelemetry.AutoInstrumentation.snk Directory.Build.props = Directory.Build.props docker-compose.yml = docker-compose.yml GlobalSuppressions.cs = GlobalSuppressions.cs integrations.json = integrations.json LICENSE = LICENSE LICENSE-3rdparty.csv = LICENSE-3rdparty.csv + OpenTelemetry.AutoInstrumentation.snk = OpenTelemetry.AutoInstrumentation.snk docs\README.md = docs\README.md stylecop.json = stylecop.json EndProjectSection @@ -428,7 +428,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "crank", "crank", "{E3FB283A EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DuplicateTypeProxy", "test\test-applications\regression\DuplicateTypeProxy\DuplicateTypeProxy.csproj", "{34B67004-7249-4EF1-8E12-6E6DA37EA6BE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.AspNetCoreRazorPages", "test\test-applications\integrations\Samples.AspNetCoreRazorPages\Samples.AspNetCoreRazorPages.csproj", "{1B9E6BF4-9D48-4988-9945-248096119E46}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.AspNetCoreRazorPages", "test\test-applications\integrations\Samples.AspNetCoreRazorPages\Samples.AspNetCoreRazorPages.csproj", "{1B9E6BF4-9D48-4988-9945-248096119E46}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.Vendoring", "test\test-applications\integrations\Samples.Vendoring\Samples.Vendoring.csproj", "{0D4ACA4A-44DC-4603-8992-926B5BED5D76}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution @@ -1590,18 +1592,28 @@ Global {34B67004-7249-4EF1-8E12-6E6DA37EA6BE}.Release|x64.Build.0 = Release|x64 {34B67004-7249-4EF1-8E12-6E6DA37EA6BE}.Release|x86.ActiveCfg = Release|x86 {34B67004-7249-4EF1-8E12-6E6DA37EA6BE}.Release|x86.Build.0 = Release|x86 - {1B9E6BF4-9D48-4988-9945-248096119E46}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1B9E6BF4-9D48-4988-9945-248096119E46}.Release|Any CPU.Build.0 = Release|Any CPU {1B9E6BF4-9D48-4988-9945-248096119E46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1B9E6BF4-9D48-4988-9945-248096119E46}.Debug|Any CPU.Build.0 = Debug|Any CPU {1B9E6BF4-9D48-4988-9945-248096119E46}.Debug|x64.ActiveCfg = Debug|Any CPU {1B9E6BF4-9D48-4988-9945-248096119E46}.Debug|x64.Build.0 = Debug|Any CPU {1B9E6BF4-9D48-4988-9945-248096119E46}.Debug|x86.ActiveCfg = Debug|Any CPU {1B9E6BF4-9D48-4988-9945-248096119E46}.Debug|x86.Build.0 = Debug|Any CPU + {1B9E6BF4-9D48-4988-9945-248096119E46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B9E6BF4-9D48-4988-9945-248096119E46}.Release|Any CPU.Build.0 = Release|Any CPU {1B9E6BF4-9D48-4988-9945-248096119E46}.Release|x64.ActiveCfg = Release|Any CPU {1B9E6BF4-9D48-4988-9945-248096119E46}.Release|x64.Build.0 = Release|Any CPU {1B9E6BF4-9D48-4988-9945-248096119E46}.Release|x86.ActiveCfg = Release|Any CPU {1B9E6BF4-9D48-4988-9945-248096119E46}.Release|x86.Build.0 = Release|Any CPU + {0D4ACA4A-44DC-4603-8992-926B5BED5D76}.Debug|Any CPU.ActiveCfg = Debug|x86 + {0D4ACA4A-44DC-4603-8992-926B5BED5D76}.Debug|x64.ActiveCfg = Debug|x64 + {0D4ACA4A-44DC-4603-8992-926B5BED5D76}.Debug|x64.Build.0 = Debug|x64 + {0D4ACA4A-44DC-4603-8992-926B5BED5D76}.Debug|x86.ActiveCfg = Debug|x86 + {0D4ACA4A-44DC-4603-8992-926B5BED5D76}.Debug|x86.Build.0 = Debug|x86 + {0D4ACA4A-44DC-4603-8992-926B5BED5D76}.Release|Any CPU.ActiveCfg = Release|x86 + {0D4ACA4A-44DC-4603-8992-926B5BED5D76}.Release|x64.ActiveCfg = Release|x64 + {0D4ACA4A-44DC-4603-8992-926B5BED5D76}.Release|x64.Build.0 = Release|x64 + {0D4ACA4A-44DC-4603-8992-926B5BED5D76}.Release|x86.ActiveCfg = Release|x86 + {0D4ACA4A-44DC-4603-8992-926B5BED5D76}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1727,6 +1739,7 @@ Global {E3FB283A-B766-4887-95E1-329667671921} = {A0C5FBBB-CFB2-4FB9-B8F0-55676E9DCF06} {34B67004-7249-4EF1-8E12-6E6DA37EA6BE} = {498A300E-D036-49B7-A43D-821D1CAF11A5} {1B9E6BF4-9D48-4988-9945-248096119E46} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A} + {0D4ACA4A-44DC-4603-8992-926B5BED5D76} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {160A1D00-1F5B-40F8-A155-621B4459D78F} diff --git a/src/Datadog.Trace.ClrProfiler.Managed/Instrumentation.cs b/src/Datadog.Trace.ClrProfiler.Managed/Instrumentation.cs index 74fdb4193c..dc02d04bd6 100644 --- a/src/Datadog.Trace.ClrProfiler.Managed/Instrumentation.cs +++ b/src/Datadog.Trace.ClrProfiler.Managed/Instrumentation.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Reflection; using System.Threading; using Datadog.Trace.Configuration; using Datadog.Trace.DiagnosticListeners; @@ -59,6 +61,8 @@ public static void Initialize() try { + TryLoadVendorPlugin(); + // ensure global instance is created if it's not already _ = Tracer.Instance; } @@ -126,5 +130,37 @@ private static void StartDiagnosticManager() DiagnosticManager.Instance = diagnosticManager; } #endif + + private static void TryLoadVendorPlugin() + { + string pluginName = GlobalSettings.Source.VendorPluginName; + if (!string.IsNullOrWhiteSpace(pluginName)) + { + string pluginPath = Path.Combine( + Path.GetDirectoryName(typeof(Instrumentation).Assembly.Location), + pluginName); + + if (File.Exists(pluginPath)) + { + try + { + Assembly pluginAssembly = Assembly.LoadFrom(pluginPath); + + GlobalSettings.SetVendorPlugin(pluginAssembly); + + Log.Information("Vendor plugin assembly loaded '{0}'.", pluginAssembly.FullName); + } + catch (Exception ex) + { + Log.Warning(ex, "Plugin assembly could not be loaded."); + Log.Information("Skipping vendor plugin load"); + } + } + else + { + Log.Warning("Plugin path is defined but could not find the path '{0}'.", pluginPath); + } + } + } } } diff --git a/src/Datadog.Trace/Configuration/ConfigurationKeys.cs b/src/Datadog.Trace/Configuration/ConfigurationKeys.cs index bee715a047..b3c700351e 100644 --- a/src/Datadog.Trace/Configuration/ConfigurationKeys.cs +++ b/src/Datadog.Trace/Configuration/ConfigurationKeys.cs @@ -348,6 +348,11 @@ public static class ConfigurationKeys /// public const string PartialFlushMinSpans = "OTEL_TRACE_PARTIAL_FLUSH_MIN_SPANS"; + /// + /// Configuration key for specifing vendor plugin path + /// + public const string VendorPluginName = "OTEL_VENDOR_PLUGIN"; + /// /// String format patterns used to match integration-specific configuration keys. /// diff --git a/src/Datadog.Trace/Configuration/DefaultFactoryConfigurator.cs b/src/Datadog.Trace/Configuration/DefaultFactoryConfigurator.cs new file mode 100644 index 0000000000..dd5bedf167 --- /dev/null +++ b/src/Datadog.Trace/Configuration/DefaultFactoryConfigurator.cs @@ -0,0 +1,22 @@ +using Datadog.Trace.Configuration.Factories; +using Datadog.Trace.Factories; + +namespace Datadog.Trace.Configuration +{ + /// + /// Default implementation for factories. + /// + public class DefaultFactoryConfigurator : IFactoryConfigurator + { + private IPropagatorFactory _propagatorFactory; + + /// + /// Gets the default propagator factory. + /// + /// Default propagator factory. + public virtual IPropagatorFactory GetPropagatorFactory() + { + return _propagatorFactory ??= new DefaultPropagatorFactory(); + } + } +} diff --git a/src/Datadog.Trace/Configuration/Factories/IPropagatorFactory.cs b/src/Datadog.Trace/Configuration/Factories/IPropagatorFactory.cs new file mode 100644 index 0000000000..2eabb1f6cc --- /dev/null +++ b/src/Datadog.Trace/Configuration/Factories/IPropagatorFactory.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Datadog.Trace.Conventions; +using Datadog.Trace.Propagation; + +namespace Datadog.Trace.Configuration.Factories +{ + /// + /// Factory interface for propagators. + /// + public interface IPropagatorFactory + { + /// + /// Gets multiple span context propagators. + /// + /// List of propagator ids. + /// Trace id convention. + /// Context propagators. + IEnumerable GetPropagators(IEnumerable propagatorIds, ITraceIdConvention traceIdConvention); + + /// + /// Gets propagator for propagator id. + /// + /// Propagator id. + /// Trace id convention. + /// Context propagator. + IPropagator GetPropagator(string propagatorId, ITraceIdConvention traceIdConvention); + } +} diff --git a/src/Datadog.Trace/Configuration/GlobalSettings.cs b/src/Datadog.Trace/Configuration/GlobalSettings.cs index 77b1450a3a..cb68a05f61 100644 --- a/src/Datadog.Trace/Configuration/GlobalSettings.cs +++ b/src/Datadog.Trace/Configuration/GlobalSettings.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Linq; +using System.Reflection; using Datadog.Trace.Logging; using Datadog.Trace.Vendors.Serilog.Events; @@ -32,6 +34,11 @@ internal GlobalSettings(IConfigurationSource source) DiagnosticSourceEnabled = source?.GetBool(ConfigurationKeys.DiagnosticSourceEnabled) ?? // default value true; + + VendorPluginName = source?.GetString(ConfigurationKeys.VendorPluginName); + + // Sets initial / fallback configuration + FactoryConfigurator = new DefaultFactoryConfigurator(); } /// @@ -42,6 +49,21 @@ internal GlobalSettings(IConfigurationSource source) /// public bool DebugEnabled { get; private set; } + /// + /// Gets the vendor plugin path. + /// + public string VendorPluginName { get; private set; } + + /// + /// Gets a value indicating whether vendor plugin is loaded. + /// + public bool VendorPluginLoaded { get; private set; } + + /// + /// Gets the vendor plugin assembly. + /// + public Assembly VendorPlugin { get; private set; } + /// /// Gets or sets the global settings instance. /// @@ -55,6 +77,11 @@ internal GlobalSettings(IConfigurationSource source) /// internal bool DiagnosticSourceEnabled { get; } + /// + /// Gets the factory configurator. + /// + internal IFactoryConfigurator FactoryConfigurator { get; private set; } + /// /// Set whether debug mode is enabled. /// Affects the level of logs written to file. @@ -74,6 +101,18 @@ public static void SetDebugEnabled(bool enabled) } } + /// + /// Sets a vendor plugin on successful load. + /// + /// Loaded plugin assembly + public static void SetVendorPlugin(Assembly assembly) + { + Source.VendorPluginLoaded = true; + Source.VendorPlugin = assembly; + + LoadVendorFactoryConfigurator(); + } + /// /// Used to refresh global settings when environment variables or config sources change. /// This is not necessary if changes are set via code, only environment. @@ -184,5 +223,21 @@ private static bool TryLoadHostingEnvironmentPath(out string hostingPath) hostingPath = default; return false; } + + private static void LoadVendorFactoryConfigurator() + { + if (Source.VendorPluginLoaded) + { + var factoryInterface = typeof(IFactoryConfigurator); + var factoryType = Source.VendorPlugin + .GetTypes() + .FirstOrDefault(x => factoryInterface.IsAssignableFrom(x)); + + if (factoryType != null) + { + Source.FactoryConfigurator = (IFactoryConfigurator)Activator.CreateInstance(factoryType); + } + } + } } } diff --git a/src/Datadog.Trace/Configuration/IFactoryConfigurator.cs b/src/Datadog.Trace/Configuration/IFactoryConfigurator.cs new file mode 100644 index 0000000000..fef3a0d25c --- /dev/null +++ b/src/Datadog.Trace/Configuration/IFactoryConfigurator.cs @@ -0,0 +1,16 @@ +using Datadog.Trace.Configuration.Factories; + +namespace Datadog.Trace.Configuration +{ + /// + /// Provides configuration for factories + /// + public interface IFactoryConfigurator + { + /// + /// Get the propagator factory. + /// + /// Propagator factory. + IPropagatorFactory GetPropagatorFactory(); + } +} diff --git a/src/Datadog.Trace/Configuration/PropagatorType.cs b/src/Datadog.Trace/Configuration/PropagatorType.cs deleted file mode 100644 index 1ba5a5de9d..0000000000 --- a/src/Datadog.Trace/Configuration/PropagatorType.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Datadog.Trace.Configuration -{ - /// - /// Enumeration for the available propagator types. - /// - public enum PropagatorType - { - /// - /// The default propagator. - /// Default is Datadog. - /// - Default, - - /// - /// The Datadog propagator. - /// - Datadog, - - /// - /// The B3 propagator - /// - B3, - - /// - /// The W3C propagator - /// - W3C - } -} diff --git a/src/Datadog.Trace/Configuration/TracerSettings.cs b/src/Datadog.Trace/Configuration/TracerSettings.cs index 5d550dce05..e6b223996f 100644 --- a/src/Datadog.Trace/Configuration/TracerSettings.cs +++ b/src/Datadog.Trace/Configuration/TracerSettings.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using Datadog.Trace.Configuration.Types; using Datadog.Trace.ExtensionMethods; using Datadog.Trace.PlatformHelpers; using Datadog.Trace.Util; @@ -369,7 +370,7 @@ public TracerSettings(IConfigurationSource source) /// Default is Datadog /// /// - public HashSet Propagators { get; set; } + public HashSet Propagators { get; set; } /// /// Gets or sets a value indicating whether runtime metrics @@ -640,18 +641,18 @@ internal string GetServiceName(Tracer tracer, string serviceName) return ServiceNameMappings.GetServiceName(tracer.DefaultServiceName, serviceName); } - private static HashSet GetPropagators(IConfigurationSource source) + private static HashSet GetPropagators(IConfigurationSource source) { - var propagators = source.GetTypedValues(ConfigurationKeys.Propagators); + var propagators = source.GetStrings(ConfigurationKeys.Propagators); if (!propagators.Any()) { // TODO: Default to W3C (be aware of integration tests) // see more: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/api-propagators.md#global-propagators - return new HashSet() { PropagatorType.Datadog }; + return new HashSet() { PropagatorTypes.Datadog }; } - return new HashSet(propagators); + return new HashSet(propagators); } } } diff --git a/src/Datadog.Trace/Configuration/Types/PropagatorTypes.cs b/src/Datadog.Trace/Configuration/Types/PropagatorTypes.cs new file mode 100644 index 0000000000..5b1f9cfc5e --- /dev/null +++ b/src/Datadog.Trace/Configuration/Types/PropagatorTypes.cs @@ -0,0 +1,23 @@ +namespace Datadog.Trace.Configuration.Types +{ + /// + /// Contains default available propagator types. + /// + public static class PropagatorTypes + { + /// + /// The Datadog propagator. + /// + public const string Datadog = "Datadog"; + + /// + /// The B3 propagator + /// + public const string B3 = "B3"; + + /// + /// The W3C propagator + /// + public const string W3C = "W3C"; + } +} diff --git a/src/Datadog.Trace/Conventions/ITraceIdConvention.cs b/src/Datadog.Trace/Conventions/ITraceIdConvention.cs index c9c6638547..770b764e3a 100644 --- a/src/Datadog.Trace/Conventions/ITraceIdConvention.cs +++ b/src/Datadog.Trace/Conventions/ITraceIdConvention.cs @@ -3,10 +3,19 @@ namespace Datadog.Trace.Conventions /// /// Convention used when defining format of TraceId. /// - internal interface ITraceIdConvention + public interface ITraceIdConvention { + /// + /// Generates new unique trace id based on convention. + /// + /// Trace id. TraceId GenerateNewTraceId(); + /// + /// Creates new trace id based on given string. + /// + /// String of id. + /// Trace id. TraceId CreateFromString(string id); } } diff --git a/src/Datadog.Trace/Factories/DefaultPropagatorFactory.cs b/src/Datadog.Trace/Factories/DefaultPropagatorFactory.cs new file mode 100644 index 0000000000..c745886cad --- /dev/null +++ b/src/Datadog.Trace/Factories/DefaultPropagatorFactory.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Datadog.Trace.Configuration; +using Datadog.Trace.Configuration.Factories; +using Datadog.Trace.Configuration.Types; +using Datadog.Trace.Conventions; +using Datadog.Trace.Propagation; + +namespace Datadog.Trace.Factories +{ + /// + /// Default propagator factory. + /// + public class DefaultPropagatorFactory : IPropagatorFactory + { + private static readonly IReadOnlyDictionary> PropagatorSelector = + new Dictionary>(StringComparer.InvariantCultureIgnoreCase) + { + { PropagatorTypes.W3C, convention => new W3CSpanContextPropagator(convention) }, + { PropagatorTypes.B3, convention => new B3SpanContextPropagator(convention) }, + { PropagatorTypes.Datadog, convention => new DDSpanContextPropagator(convention) }, + }; + + /// + /// Builds the propagator with given spec. + /// + /// Propagator id. + /// Trace id convention. + /// Context propagator. + public virtual IPropagator GetPropagator(string propagatorId, ITraceIdConvention traceIdConvention) + { + if (PropagatorSelector.TryGetValue(propagatorId, out Func getter)) + { + // W3C propagator requires Otel TraceId convention as it's specification clearly states lengths of traceId and spanId values in the header. + if (propagatorId == PropagatorTypes.W3C && traceIdConvention is not OtelTraceIdConvention) + { + throw new NotSupportedException($"'{PropagatorTypes.W3C}' propagator requires '{ConventionType.OpenTelemetry}' convention to be set"); + } + + return getter(traceIdConvention); + } + + throw new InvalidOperationException($"There is no propagator registered for type '{propagatorId}'."); + } + + /// + /// Builds the propagator enumeration with given specs. + /// + /// Propagator ids. + /// Trace id convention. + /// Enumeration of context propagators. + public virtual IEnumerable GetPropagators(IEnumerable propagatorIds, ITraceIdConvention traceIdConvention) + { + return propagatorIds.Select(type => GetPropagator(type, traceIdConvention)).ToList(); + } + } +} diff --git a/src/Datadog.Trace/Propagation/ContextPropagatorFactory.cs b/src/Datadog.Trace/Propagation/ContextPropagatorFactory.cs deleted file mode 100644 index 7869be072c..0000000000 --- a/src/Datadog.Trace/Propagation/ContextPropagatorFactory.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Datadog.Trace.Configuration; -using Datadog.Trace.Conventions; - -namespace Datadog.Trace.Propagation -{ - internal static class ContextPropagatorFactory - { - private static readonly IReadOnlyDictionary> PropagatorSelector = - new Dictionary>() - { - { PropagatorType.W3C, convention => new W3CSpanContextPropagator(convention) }, - { PropagatorType.B3, convention => new B3SpanContextPropagator(convention) }, - { PropagatorType.Datadog, convention => new DDSpanContextPropagator(convention) }, - { PropagatorType.Default, convention => new DDSpanContextPropagator(convention) }, - }; - - public static ICollection BuildPropagators(IEnumerable propagatorTypes, ITraceIdConvention traceIdConvention) - { - return propagatorTypes.Select(type => BuildPropagator(type, traceIdConvention)).ToList(); - } - - private static IPropagator BuildPropagator(PropagatorType propagatorType, ITraceIdConvention traceIdConvention) - { - if (PropagatorSelector.TryGetValue(propagatorType, out Func getter)) - { - // W3C propagator requires Otel TraceId convention as it's specification clearly states lengths of traceId and spanId values in the header. - if (propagatorType == PropagatorType.W3C && traceIdConvention is not OtelTraceIdConvention) - { - throw new NotSupportedException($"'{PropagatorType.W3C}' propagator requires '{ConventionType.OpenTelemetry}' convention to be set"); - } - - return getter(traceIdConvention); - } - - throw new InvalidOperationException($"There is no propagator registered for type '{propagatorType}'."); - } - } -} diff --git a/src/Datadog.Trace/Propagation/IPropagator.cs b/src/Datadog.Trace/Propagation/IPropagator.cs index 405fe1d547..aa3a712e22 100644 --- a/src/Datadog.Trace/Propagation/IPropagator.cs +++ b/src/Datadog.Trace/Propagation/IPropagator.cs @@ -3,10 +3,13 @@ namespace Datadog.Trace.Propagation { - internal interface IPropagator + /// + /// Specifies interface for span context propagator. + /// + public interface IPropagator { /// - /// Propagates the specified context by adding new headers to a carrier + /// Propagates the specified context by adding new headers to a carrier. /// This locks the sampling priority for . /// /// A value that will be propagated into instance. diff --git a/src/Datadog.Trace/Tracer.cs b/src/Datadog.Trace/Tracer.cs index 86beafffc0..237c31d713 100644 --- a/src/Datadog.Trace/Tracer.cs +++ b/src/Datadog.Trace/Tracer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -119,7 +120,11 @@ internal Tracer(TracerSettings settings, ITraceWriter traceWriter, ISampler samp _scopeManager = scopeManager ?? new AsyncLocalScopeManager(); Sampler = sampler ?? new RuleBasedSampler(new RateLimiter(Settings.MaxTracesSubmittedPerSecond)); - var propagators = ContextPropagatorFactory.BuildPropagators(Settings.Propagators, TraceIdConvention); + var propagators = GlobalSettings.Source.FactoryConfigurator + .GetPropagatorFactory() + .GetPropagators(Settings.Propagators, TraceIdConvention) + .ToList(); + _propagator = new CompositeTextMapPropagator(propagators); if (!string.IsNullOrWhiteSpace(Settings.CustomSamplingRules)) diff --git a/test/test-applications/integrations/Samples.Vendoring/Factories/VendorPropagatorFactory.cs b/test/test-applications/integrations/Samples.Vendoring/Factories/VendorPropagatorFactory.cs new file mode 100644 index 0000000000..ef13ffaaa9 --- /dev/null +++ b/test/test-applications/integrations/Samples.Vendoring/Factories/VendorPropagatorFactory.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using Datadog.Trace.Conventions; +using Datadog.Trace.Factories; +using Datadog.Trace.Propagation; +using Samples.Vendoring.Propagators; +using Samples.Vendoring.Types; + +namespace Samples.Vendoring.Factories +{ + public class VendorPropagatorFactory : DefaultPropagatorFactory + { + public override IEnumerable GetPropagators(IEnumerable propagatorIds, ITraceIdConvention traceIdConvention) + { + foreach (string id in propagatorIds) + { + if (id.Equals(VendorPropagatorTypes.VendorPropagator, StringComparison.InvariantCultureIgnoreCase)) + { + yield return new VendorSpanContextPropagator(traceIdConvention); + } + else + { + yield return base.GetPropagator(id, traceIdConvention); + } + } + } + } +} diff --git a/test/test-applications/integrations/Samples.Vendoring/Propagators/VendorSpanContextPropagator.cs b/test/test-applications/integrations/Samples.Vendoring/Propagators/VendorSpanContextPropagator.cs new file mode 100644 index 0000000000..af3d4fe14c --- /dev/null +++ b/test/test-applications/integrations/Samples.Vendoring/Propagators/VendorSpanContextPropagator.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Datadog.Trace; +using Datadog.Trace.Conventions; +using Datadog.Trace.Propagation; + +namespace Samples.Vendoring.Propagators +{ + internal class VendorSpanContextPropagator : IPropagator + { + private const string TraceIdHeader = "vendor-trace-id"; + private const string SpanIdHeader = "vendor-span-id"; + + private readonly ITraceIdConvention _traceIdConvention; + + public VendorSpanContextPropagator(ITraceIdConvention traceIdConvention) + { + _traceIdConvention = traceIdConvention; + } + + public SpanContext Extract(T carrier, Func> getter) + { + var traceId = ParseTraceId(carrier, getter); + + if (traceId == TraceId.Zero) + { + // a valid traceId is required to use distributed tracing + return null; + } + + var spanId = ParseSpanId(carrier, getter); + + return new SpanContext(traceId, spanId); + } + + public void Inject(SpanContext context, T carrier, Action setter) + { + setter(carrier, TraceIdHeader, context.TraceId.ToString()); + setter(carrier, SpanIdHeader, context.SpanId.ToString()); + } + + private TraceId ParseTraceId(T carrier, Func> getter) + { + var headerValue = getter(carrier, TraceIdHeader).FirstOrDefault(); + if (headerValue == null) + { + return TraceId.Zero; + } + + return _traceIdConvention.CreateFromString(headerValue); + } + + private ulong ParseSpanId(T carrier, Func> getter) + { + var headerValue = getter(carrier, SpanIdHeader).FirstOrDefault(); + if (headerValue == null) + { + return default; + } + + ulong.TryParse(headerValue, out ulong spanId); + return spanId; + } + } +} diff --git a/test/test-applications/integrations/Samples.Vendoring/Samples.Vendoring.csproj b/test/test-applications/integrations/Samples.Vendoring/Samples.Vendoring.csproj new file mode 100644 index 0000000000..c8349370ce --- /dev/null +++ b/test/test-applications/integrations/Samples.Vendoring/Samples.Vendoring.csproj @@ -0,0 +1,9 @@ + + + + net45;net461;netcoreapp2.0;netcoreapp3.1 + netcoreapp2.0;netcoreapp3.1 + Library + + + diff --git a/test/test-applications/integrations/Samples.Vendoring/Types/VendorPropagatorTypes.cs b/test/test-applications/integrations/Samples.Vendoring/Types/VendorPropagatorTypes.cs new file mode 100644 index 0000000000..004e674fb4 --- /dev/null +++ b/test/test-applications/integrations/Samples.Vendoring/Types/VendorPropagatorTypes.cs @@ -0,0 +1,7 @@ +namespace Samples.Vendoring.Types +{ + internal static class VendorPropagatorTypes + { + public const string VendorPropagator = "Vendor"; + } +} diff --git a/test/test-applications/integrations/Samples.Vendoring/VendorFactoryConfigurator.cs b/test/test-applications/integrations/Samples.Vendoring/VendorFactoryConfigurator.cs new file mode 100644 index 0000000000..f685b9cac4 --- /dev/null +++ b/test/test-applications/integrations/Samples.Vendoring/VendorFactoryConfigurator.cs @@ -0,0 +1,16 @@ +using Datadog.Trace.Configuration; +using Datadog.Trace.Configuration.Factories; +using Samples.Vendoring.Factories; + +namespace Samples.Vendoring +{ + public class VendorFactoryConfigurator : DefaultFactoryConfigurator + { + private VendorPropagatorFactory _vendorPropagatorFactory; + + public override IPropagatorFactory GetPropagatorFactory() + { + return _vendorPropagatorFactory ??= new VendorPropagatorFactory(); + } + } +}