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..baa7b13094 100644
--- a/src/Datadog.Trace.ClrProfiler.Managed/Instrumentation.cs
+++ b/src/Datadog.Trace.ClrProfiler.Managed/Instrumentation.cs
@@ -4,6 +4,7 @@
using Datadog.Trace.Configuration;
using Datadog.Trace.DiagnosticListeners;
using Datadog.Trace.Logging;
+using Datadog.Trace.Plugins;
using Datadog.Trace.ServiceFabric;
namespace Datadog.Trace.ClrProfiler
@@ -59,8 +60,11 @@ public static void Initialize()
try
{
- // ensure global instance is created if it's not already
- _ = Tracer.Instance;
+ // Creates GlobalSettings instance and loads plugins
+ var plugins = PluginManager.TryLoadPlugins(GlobalSettings.Source.PluginsConfiguration);
+
+ // First call to create Tracer instace
+ Tracer.Instance = new Tracer(plugins);
}
catch
{
diff --git a/src/Datadog.Trace/Configuration/ConfigurationKeys.cs b/src/Datadog.Trace/Configuration/ConfigurationKeys.cs
index 23c7646b35..b63e2dd626 100644
--- a/src/Datadog.Trace/Configuration/ConfigurationKeys.cs
+++ b/src/Datadog.Trace/Configuration/ConfigurationKeys.cs
@@ -12,6 +12,11 @@ public static class ConfigurationKeys
///
public const string ConfigurationFileName = "OTEL_TRACE_CONFIG_FILE";
+ ///
+ /// Configuration key for the path to the plugins configuration file.
+ ///
+ public const string PluginConfigurationFileName = "OTEL_DOTNET_TRACER_PLUGINS_FILE";
+
///
/// Configuration key for the application's environment. Sets the "env" tag on every .
///
diff --git a/src/Datadog.Trace/Configuration/GlobalSettings.cs b/src/Datadog.Trace/Configuration/GlobalSettings.cs
index 77b1450a3a..4427a1cca0 100644
--- a/src/Datadog.Trace/Configuration/GlobalSettings.cs
+++ b/src/Datadog.Trace/Configuration/GlobalSettings.cs
@@ -32,6 +32,11 @@ internal GlobalSettings(IConfigurationSource source)
DiagnosticSourceEnabled = source?.GetBool(ConfigurationKeys.DiagnosticSourceEnabled) ??
// default value
true;
+
+ if (TryLoadPluginJsonConfigurationFile(source, out JsonConfigurationSource jsonConfigurationSource))
+ {
+ PluginsConfiguration = jsonConfigurationSource;
+ }
}
///
@@ -55,6 +60,11 @@ internal GlobalSettings(IConfigurationSource source)
///
internal bool DiagnosticSourceEnabled { get; }
+ ///
+ /// Gets the plugins configuration.
+ ///
+ internal JsonConfigurationSource PluginsConfiguration { get; }
+
///
/// Set whether debug mode is enabled.
/// Affects the level of logs written to file.
@@ -121,15 +131,28 @@ internal static CompositeConfigurationSource CreateDefaultConfigurationSource()
return configurationSource;
}
- private static bool TryLoadJsonConfigurationFile(IConfigurationSource configurationSource, out IConfigurationSource jsonConfigurationSource)
+ private static bool TryLoadPluginJsonConfigurationFile(IConfigurationSource configurationSource, out JsonConfigurationSource jsonConfigurationSource)
+ {
+ var configurationFileName = configurationSource?.GetString(ConfigurationKeys.PluginConfigurationFileName) ??
+ Path.Combine(GetCurrentDirectory(), "plugins.json");
+
+ return TryLoadJsonConfigurationFile(configurationFileName, out jsonConfigurationSource);
+ }
+
+ private static bool TryLoadJsonConfigurationFile(IConfigurationSource configurationSource, out JsonConfigurationSource jsonConfigurationSource)
+ {
+ // if environment variable is not set, look for default file name in the current directory
+ var configurationFileName = configurationSource.GetString(ConfigurationKeys.ConfigurationFileName) ??
+ configurationSource.GetString("OTEL_DOTNET_TRACER_CONFIG_FILE") ??
+ Path.Combine(GetCurrentDirectory(), "datadog.json");
+
+ return TryLoadJsonConfigurationFile(configurationFileName, out jsonConfigurationSource);
+ }
+
+ private static bool TryLoadJsonConfigurationFile(string configurationFileName, out JsonConfigurationSource jsonConfigurationSource)
{
try
{
- // if environment variable is not set, look for default file name in the current directory
- var configurationFileName = configurationSource.GetString(ConfigurationKeys.ConfigurationFileName) ??
- configurationSource.GetString("OTEL_DOTNET_TRACER_CONFIG_FILE") ??
- Path.Combine(GetCurrentDirectory(), "datadog.json");
-
if (string.Equals(Path.GetExtension(configurationFileName), ".JSON", StringComparison.OrdinalIgnoreCase) &&
File.Exists(configurationFileName))
{
diff --git a/src/Datadog.Trace/Configuration/JsonConfigurationSource.cs b/src/Datadog.Trace/Configuration/JsonConfigurationSource.cs
index f19215a520..3a22e8728f 100644
--- a/src/Datadog.Trace/Configuration/JsonConfigurationSource.cs
+++ b/src/Datadog.Trace/Configuration/JsonConfigurationSource.cs
@@ -145,6 +145,15 @@ public IDictionary GetDictionary(string key, bool allowOptionalM
return GetDictionaryInternal(key, allowOptionalMappings);
}
+ ///
+ /// Gets the string representation of json config.
+ ///
+ /// String format.
+ public override string ToString()
+ {
+ return _configuration.ToString();
+ }
+
private IDictionary GetDictionaryInternal(string key, bool allowOptionalMappings)
{
var token = _configuration.SelectToken(key, errorWhenNoMatch: false);
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/FrameworkDescription.cs b/src/Datadog.Trace/FrameworkDescription.cs
index 803e31d89a..856d6e65f7 100644
--- a/src/Datadog.Trace/FrameworkDescription.cs
+++ b/src/Datadog.Trace/FrameworkDescription.cs
@@ -1,10 +1,8 @@
using System;
-using System.Linq;
+using System.Collections.Generic;
using System.Reflection;
-using System.Runtime.InteropServices;
-using System.Text.RegularExpressions;
+using System.Runtime.Versioning;
using Datadog.Trace.Logging;
-using Microsoft.Win32;
namespace Datadog.Trace
{
@@ -29,6 +27,15 @@ internal partial class FrameworkDescription
Tuple.Create(378389, "4.5"),
};
+ private static readonly IReadOnlyDictionary TargetFrameworkMapping = new Dictionary()
+ {
+ { ".NETFramework,Version=v4.5", "net45" },
+ { ".NETFramework,Version=v4.6.1", "net461" },
+ { ".NETStandard,Version=v2.0", "netstandard2.0" },
+ { ".NETCoreApp,Version=v3.1", "netcoreapp3.1" },
+ { ".NETCoreApp,Version=v5.0", "net50" }
+ };
+
private FrameworkDescription(
string name,
string productVersion,
@@ -41,6 +48,7 @@ private FrameworkDescription(
OSPlatform = osPlatform;
OSArchitecture = osArchitecture;
ProcessArchitecture = processArchitecture;
+ TargetFramework = GetTargetFramework();
}
public string Name { get; }
@@ -53,6 +61,8 @@ private FrameworkDescription(
public string ProcessArchitecture { get; }
+ public string TargetFramework { get; }
+
public override string ToString()
{
// examples:
@@ -61,6 +71,20 @@ public override string ToString()
return $"{Name} {ProductVersion} {ProcessArchitecture} on {OSPlatform} {OSArchitecture}";
}
+ private static string GetTargetFramework()
+ {
+ var framework = typeof(FrameworkDescription).Assembly
+ .GetCustomAttribute()?
+ .FrameworkName;
+
+ if (!TargetFrameworkMapping.TryGetValue(framework, out string targetFramework))
+ {
+ throw new InvalidOperationException($"Target framework mapping is not defined for '{framework}'");
+ }
+
+ return targetFramework;
+ }
+
private static string GetVersionFromAssemblyAttributes()
{
string productVersion = null;
diff --git a/src/Datadog.Trace/Plugins/IOTelExtension.cs b/src/Datadog.Trace/Plugins/IOTelExtension.cs
new file mode 100644
index 0000000000..d97eb2c55e
--- /dev/null
+++ b/src/Datadog.Trace/Plugins/IOTelExtension.cs
@@ -0,0 +1,9 @@
+namespace Datadog.Trace.Plugins
+{
+ ///
+ /// Base marker interface for extendability points.
+ ///
+ public interface IOTelExtension
+ {
+ }
+}
diff --git a/src/Datadog.Trace/Plugins/PluginManager.cs b/src/Datadog.Trace/Plugins/PluginManager.cs
new file mode 100644
index 0000000000..5864fffec2
--- /dev/null
+++ b/src/Datadog.Trace/Plugins/PluginManager.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Datadog.Trace.Configuration;
+using Datadog.Trace.Logging;
+using Datadog.Trace.Util;
+using Datadog.Trace.Vendors.Newtonsoft.Json.Linq;
+
+namespace Datadog.Trace.Plugins
+{
+ internal static class PluginManager
+ {
+ private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(PluginManager));
+
+ internal static IReadOnlyCollection TryLoadPlugins(JsonConfigurationSource pluginsConfig)
+ {
+ if (pluginsConfig == null)
+ {
+ return ArrayHelper.Empty();
+ }
+
+ var targetFramework = FrameworkDescription.Instance.TargetFramework;
+
+ Log.Debug("Executing plugins configuration: {0}", pluginsConfig);
+ Log.Information("Trying to load plugins with target framework '{0}'.", targetFramework);
+
+ string[] pluginFiles;
+
+ try
+ {
+ // TODO: Here additional metadata could be loaded (eg: for security and integrity)
+ // instead of just string path
+ pluginFiles = pluginsConfig.GetValue($"['{targetFramework}']").ToObject();
+ }
+ catch (ArgumentException ex)
+ {
+ Log.Warning(ex, "Could not parse list of plugin paths. Invalid plugin configuration provided.");
+
+ return ArrayHelper.Empty();
+ }
+
+ if (pluginFiles == null || !pluginFiles.Any())
+ {
+ Log.Information("Skipping plugins load. Could not find any plugins with target framework '{0}'.", targetFramework);
+
+ return ArrayHelper.Empty();
+ }
+
+ var loadedPlugins = TryLoadPlugins(pluginFiles);
+
+ Log.Information("Successfully loaded '{0}' plugin(s).", property: loadedPlugins.Count);
+
+ return loadedPlugins;
+ }
+
+ private static IReadOnlyCollection TryLoadPlugins(string[] pluginFiles)
+ {
+ var loaded = new List();
+
+ foreach (string file in pluginFiles)
+ {
+ string fullPath = Path.GetFullPath(file);
+
+ if (File.Exists(fullPath))
+ {
+ try
+ {
+ Assembly pluginAssembly = Assembly.LoadFrom(fullPath);
+ ICollection extensions = GetExtensions(pluginAssembly);
+
+ if (extensions != null && extensions.Any())
+ {
+ loaded.AddRange(extensions);
+
+ Log.Information("Plugin assembly loaded '{0}'.", pluginAssembly.FullName);
+ }
+ else
+ {
+ Log.Warning("Could not load {0} from '{1}'.", nameof(IOTelExtension), pluginAssembly.FullName);
+ }
+ }
+ catch (Exception ex) when (
+ ExceptionUtil.IsAssemblyLoadException(ex) ||
+ ExceptionUtil.IsDynamicInvocationException(ex))
+ {
+ Log.Warning(ex, "Plugin assembly could not be loaded. Skipping vendor plugin load.");
+ }
+ }
+ else
+ {
+ Log.Warning("Plugin path is defined but could not find the path '{0}'.", fullPath);
+ }
+ }
+
+ return loaded;
+ }
+
+ private static ICollection GetExtensions(Assembly assembly)
+ {
+ var extensionType = typeof(IOTelExtension);
+
+ return assembly
+ .GetTypes()
+ .Where(p => extensionType.IsAssignableFrom(p))
+ .Select(p => (IOTelExtension)Activator.CreateInstance(p))
+ .ToList();
+ }
+ }
+}
diff --git a/src/Datadog.Trace/Propagation/B3SpanContextPropagator.cs b/src/Datadog.Trace/Propagation/B3SpanContextPropagator.cs
index 44353acb05..d0e124f203 100644
--- a/src/Datadog.Trace/Propagation/B3SpanContextPropagator.cs
+++ b/src/Datadog.Trace/Propagation/B3SpanContextPropagator.cs
@@ -10,7 +10,7 @@ namespace Datadog.Trace.Propagation
///
/// Class that handles B3 style context propagation.
///
- internal class B3SpanContextPropagator : IPropagator
+ public class B3SpanContextPropagator : IPropagator
{
private const NumberStyles NumberStyle = NumberStyles.HexNumber;
@@ -20,12 +20,17 @@ internal class B3SpanContextPropagator : IPropagator
private readonly ITraceIdConvention _traceIdConvention;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Trace id convention
public B3SpanContextPropagator(ITraceIdConvention traceIdConvention)
{
_traceIdConvention = traceIdConvention;
}
- public void Inject(SpanContext context, T carrier, Action setter)
+ ///
+ public virtual void Inject(SpanContext context, T carrier, Action setter)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
@@ -51,7 +56,8 @@ public void Inject(SpanContext context, T carrier, Action
}
}
- public SpanContext Extract(T carrier, Func> getter)
+ ///
+ public virtual SpanContext Extract(T carrier, Func> getter)
{
if (carrier == null) { throw new ArgumentNullException(nameof(carrier)); }
diff --git a/src/Datadog.Trace/Propagation/CompositePropagatorsProvider.cs b/src/Datadog.Trace/Propagation/CompositePropagatorsProvider.cs
new file mode 100644
index 0000000000..88d76ffdae
--- /dev/null
+++ b/src/Datadog.Trace/Propagation/CompositePropagatorsProvider.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Datadog.Trace.Conventions;
+using Datadog.Trace.Logging;
+
+namespace Datadog.Trace.Propagation
+{
+ internal class CompositePropagatorsProvider
+ {
+ private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor();
+
+ private readonly ICollection _providers;
+
+ public CompositePropagatorsProvider()
+ {
+ _providers = new List();
+ }
+
+ public void RegisterProvider(IPropagatorsProvider provider)
+ {
+ _providers.Add(provider);
+ }
+
+ public IEnumerable GetPropagators(IEnumerable propagatorIds, ITraceIdConvention traceIdConvention)
+ {
+ return propagatorIds.Select(type => GetPropagator(type, traceIdConvention)).ToList();
+ }
+
+ private IPropagator GetPropagator(string propagatorId, ITraceIdConvention traceIdConvention)
+ {
+ var propagator = _providers
+ .Where(x => x.CanProvide(propagatorId, traceIdConvention))
+ .Select(x => x.GetPropagator(propagatorId, traceIdConvention))
+ .FirstOrDefault();
+
+ if (propagator == null)
+ {
+ string msg = $"There is no propagator registered for type '{propagatorId}'.";
+
+ Log.Error(msg);
+
+ throw new InvalidOperationException(msg);
+ }
+
+ return propagator;
+ }
+ }
+}
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/Propagation/IPropagatorsProvider.cs b/src/Datadog.Trace/Propagation/IPropagatorsProvider.cs
new file mode 100644
index 0000000000..930a1319d1
--- /dev/null
+++ b/src/Datadog.Trace/Propagation/IPropagatorsProvider.cs
@@ -0,0 +1,28 @@
+using Datadog.Trace.Conventions;
+using Datadog.Trace.Plugins;
+
+namespace Datadog.Trace.Propagation
+{
+ ///
+ /// Factory interface for propagators.
+ /// Implementation must have a default (parameterless) constructor.
+ ///
+ public interface IPropagatorsProvider : IOTelExtension
+ {
+ ///
+ /// Gets propagator for propagator id.
+ ///
+ /// Propagator id.
+ /// Trace id convention.
+ /// Context propagator.
+ IPropagator GetPropagator(string propagatorId, ITraceIdConvention traceIdConvention);
+
+ ///
+ /// Gets if provider can provide propagator for required spec.
+ ///
+ /// Propagator id.
+ /// Trace id convention.
+ /// Is providable.
+ bool CanProvide(string propagatorId, ITraceIdConvention traceIdConvention);
+ }
+}
diff --git a/src/Datadog.Trace/Propagation/OTelPropagatorsProvider.cs b/src/Datadog.Trace/Propagation/OTelPropagatorsProvider.cs
new file mode 100644
index 0000000000..f39bbf92d0
--- /dev/null
+++ b/src/Datadog.Trace/Propagation/OTelPropagatorsProvider.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using Datadog.Trace.Configuration;
+using Datadog.Trace.Configuration.Types;
+using Datadog.Trace.Conventions;
+
+namespace Datadog.Trace.Propagation
+{
+ internal class OTelPropagatorsProvider : IPropagatorsProvider
+ {
+ 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) },
+ };
+
+ public bool CanProvide(string propagatorId, ITraceIdConvention traceIdConvention)
+ {
+ if (propagatorId == PropagatorTypes.W3C && traceIdConvention is not OtelTraceIdConvention)
+ {
+ return false;
+ }
+
+ return PropagatorSelector.ContainsKey(propagatorId);
+ }
+
+ ///
+ /// Builds the propagator with given spec.
+ ///
+ /// Propagator id.
+ /// Trace id convention.
+ /// Context propagator.
+ public 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}'.");
+ }
+ }
+}
diff --git a/src/Datadog.Trace/Propagation/W3CSpanContextPropagator.cs b/src/Datadog.Trace/Propagation/W3CSpanContextPropagator.cs
index 28299028eb..9eaf87f715 100644
--- a/src/Datadog.Trace/Propagation/W3CSpanContextPropagator.cs
+++ b/src/Datadog.Trace/Propagation/W3CSpanContextPropagator.cs
@@ -9,7 +9,10 @@
namespace Datadog.Trace.Propagation
{
- internal class W3CSpanContextPropagator : IPropagator
+ ///
+ /// Class that handles W3C style context propagation.
+ ///
+ public class W3CSpanContextPropagator : IPropagator
{
private const string TraceParentFormat = "00-{0}-{1}-01";
@@ -27,12 +30,17 @@ internal class W3CSpanContextPropagator : IPropagator
private readonly ITraceIdConvention _traceIdConvention;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Trace id convention
public W3CSpanContextPropagator(ITraceIdConvention traceIdConvention)
{
_traceIdConvention = traceIdConvention;
}
- public void Inject(SpanContext context, T carrier, Action setter)
+ ///
+ public virtual void Inject(SpanContext context, T carrier, Action setter)
{
// lock sampling priority when span propagates.
context.TraceContext?.LockSamplingPriority();
@@ -44,7 +52,8 @@ public void Inject(SpanContext context, T carrier, Action
}
}
- public SpanContext Extract(T carrier, Func> getter)
+ ///
+ public virtual SpanContext Extract(T carrier, Func> getter)
{
var traceParentCollection = getter(carrier, W3CHeaderNames.TraceParent).ToList();
if (traceParentCollection.Count != 1)
diff --git a/src/Datadog.Trace/Tracer.cs b/src/Datadog.Trace/Tracer.cs
index 86beafffc0..20023f5c2a 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;
@@ -14,6 +15,7 @@
using Datadog.Trace.DogStatsd;
using Datadog.Trace.Logging;
using Datadog.Trace.PlatformHelpers;
+using Datadog.Trace.Plugins;
using Datadog.Trace.Propagation;
using Datadog.Trace.RuntimeMetrics;
using Datadog.Trace.Sampling;
@@ -67,7 +69,17 @@ static Tracer()
/// Initializes a new instance of the class with default settings.
///
public Tracer()
- : this(settings: null, traceWriter: null, sampler: null, scopeManager: null, statsd: null)
+ : this(settings: null, plugins: null, traceWriter: null, sampler: null, scopeManager: null, statsd: null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class and extends
+ /// implementation with plugins
+ ///
+ /// Plugins to extend with
+ public Tracer(IReadOnlyCollection plugins)
+ : this(settings: null, plugins: plugins, traceWriter: null, sampler: null, scopeManager: null, statsd: null)
{
}
@@ -80,11 +92,11 @@ public Tracer()
/// or null to use the default configuration sources.
///
public Tracer(TracerSettings settings)
- : this(settings, traceWriter: null, sampler: null, scopeManager: null, statsd: null)
+ : this(settings, plugins: null, traceWriter: null, sampler: null, scopeManager: null, statsd: null)
{
}
- internal Tracer(TracerSettings settings, ITraceWriter traceWriter, ISampler sampler, IScopeManager scopeManager, IDogStatsd statsd)
+ internal Tracer(TracerSettings settings, IReadOnlyCollection plugins, ITraceWriter traceWriter, ISampler sampler, IScopeManager scopeManager, IDogStatsd statsd)
{
// update the count of Tracer instances
Interlocked.Increment(ref _liveTracerCount);
@@ -119,8 +131,7 @@ 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);
- _propagator = new CompositeTextMapPropagator(propagators);
+ _propagator = CreateCompositePropagator(Settings, TraceIdConvention, plugins ?? ArrayHelper.Empty());
if (!string.IsNullOrWhiteSpace(Settings.CustomSamplingRules))
{
@@ -745,6 +756,26 @@ private static ITraceWriter CreateTraceWriter(TracerSettings settings, IDogStats
}
}
+ private static CompositeTextMapPropagator CreateCompositePropagator(TracerSettings settings, ITraceIdConvention traceIdConvention, IReadOnlyCollection extensions)
+ {
+ var compositeProvider = new CompositePropagatorsProvider();
+ compositeProvider.RegisterProvider(new OTelPropagatorsProvider());
+
+ foreach (var extension in extensions)
+ {
+ if (extension is IPropagatorsProvider provider)
+ {
+ compositeProvider.RegisterProvider(provider);
+ }
+ }
+
+ var propagators = compositeProvider
+ .GetPropagators(settings.Propagators, traceIdConvention)
+ .ToList();
+
+ return new CompositeTextMapPropagator(propagators);
+ }
+
private void InitializeLibLogScopeEventSubscriber(IScopeManager scopeManager, string defaultServiceName, string version, string env)
{
new LibLogScopeEventSubscriber(scopeManager, defaultServiceName, version ?? string.Empty, env ?? string.Empty);
diff --git a/src/Datadog.Trace/Util/ExceptionUtil.cs b/src/Datadog.Trace/Util/ExceptionUtil.cs
new file mode 100644
index 0000000000..200bd5253a
--- /dev/null
+++ b/src/Datadog.Trace/Util/ExceptionUtil.cs
@@ -0,0 +1,37 @@
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace Datadog.Trace.Util
+{
+ internal static class ExceptionUtil
+ {
+ public static bool IsAssemblyLoadException(Exception ex)
+ {
+ return ex is ArgumentNullException
+ or FileNotFoundException
+ or FileLoadException
+ or BadImageFormatException
+ or SecurityException
+ or ArgumentException
+ or PathTooLongException;
+ }
+
+ public static bool IsDynamicInvocationException(Exception ex)
+ {
+ return ex is ReflectionTypeLoadException
+ or ArgumentNullException
+ or ArgumentException
+ or NotSupportedException
+ or TargetInvocationException
+ or MethodAccessException
+ or MemberAccessException
+ or InvalidComObjectException
+ or MissingMethodException
+ or COMException
+ or TypeLoadException;
+ }
+ }
+}
diff --git a/test/Datadog.Trace.ClrProfiler.Managed.Tests/OtelScopeFactoryTests.cs b/test/Datadog.Trace.ClrProfiler.Managed.Tests/OtelScopeFactoryTests.cs
index 4a6f044240..e2da904278 100644
--- a/test/Datadog.Trace.ClrProfiler.Managed.Tests/OtelScopeFactoryTests.cs
+++ b/test/Datadog.Trace.ClrProfiler.Managed.Tests/OtelScopeFactoryTests.cs
@@ -16,7 +16,7 @@ public void OutboundHttp(Input input, Result expected)
{
var settings = new TracerSettings();
settings.Convention = ConventionType.OpenTelemetry;
- var tracer = new Tracer(settings, Mock.Of(), Mock.Of(), scopeManager: null, statsd: null);
+ var tracer = new Tracer(settings, plugins: null, Mock.Of(), Mock.Of(), scopeManager: null, statsd: null);
using (var scope = ScopeFactory.CreateOutboundHttpScope(tracer, input.Method, new Uri(input.Uri), new IntegrationInfo((int)IntegrationIds.HttpMessageHandler), out var tags))
{
diff --git a/test/Datadog.Trace.ClrProfiler.Managed.Tests/ScopeFactoryTests.cs b/test/Datadog.Trace.ClrProfiler.Managed.Tests/ScopeFactoryTests.cs
index 79156b3004..078dfe24ff 100644
--- a/test/Datadog.Trace.ClrProfiler.Managed.Tests/ScopeFactoryTests.cs
+++ b/test/Datadog.Trace.ClrProfiler.Managed.Tests/ScopeFactoryTests.cs
@@ -68,7 +68,7 @@ public void CleanUri_ResourceName(string uri, string method, string expected)
var settings = new TracerSettings();
var writerMock = new Mock();
var samplerMock = new Mock();
- var tracer = new Tracer(settings, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
+ var tracer = new Tracer(settings, plugins: null, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
using (var automaticScope = ScopeFactory.CreateOutboundHttpScope(tracer, method, new Uri(uri), new IntegrationInfo((int)IntegrationIds.HttpMessageHandler), out _))
{
@@ -93,7 +93,7 @@ public void CleanUri_HttpUrlTag(string uri, string expected)
var settings = new TracerSettings();
var writerMock = new Mock();
var samplerMock = new Mock();
- var tracer = new Tracer(settings, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
+ var tracer = new Tracer(settings, plugins: null, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
const string method = "GET";
@@ -113,7 +113,7 @@ public void CreateOutboundHttpScope_AlwaysCreatesOneAutomaticInstrumentationScop
var settings = new TracerSettings();
var writerMock = new Mock();
var samplerMock = new Mock();
- var tracer = new Tracer(settings, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
+ var tracer = new Tracer(settings, plugins: null, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
const string method = "GET";
const string url = "http://www.contoso.com";
diff --git a/test/Datadog.Trace.IntegrationTests/DiagnosticListeners/AspNetCoreDiagnosticObserverTests.cs b/test/Datadog.Trace.IntegrationTests/DiagnosticListeners/AspNetCoreDiagnosticObserverTests.cs
index 51f856d1d9..253404553b 100644
--- a/test/Datadog.Trace.IntegrationTests/DiagnosticListeners/AspNetCoreDiagnosticObserverTests.cs
+++ b/test/Datadog.Trace.IntegrationTests/DiagnosticListeners/AspNetCoreDiagnosticObserverTests.cs
@@ -261,7 +261,7 @@ private static Tracer GetTracer(ITraceWriter writer = null, IConfigurationSource
var agentWriter = writer ?? new Mock().Object;
var samplerMock = new Mock();
- return new Tracer(settings, agentWriter, samplerMock.Object, scopeManager: null, statsd: null);
+ return new Tracer(settings, plugins: null, agentWriter, samplerMock.Object, scopeManager: null, statsd: null);
}
private class AgentWriterStub : ITraceWriter
diff --git a/test/Datadog.Trace.IntegrationTests/SendTracesToAgent.cs b/test/Datadog.Trace.IntegrationTests/SendTracesToAgent.cs
index a72b28e9a9..aa80f8fb07 100644
--- a/test/Datadog.Trace.IntegrationTests/SendTracesToAgent.cs
+++ b/test/Datadog.Trace.IntegrationTests/SendTracesToAgent.cs
@@ -24,7 +24,7 @@ public SendTracesToAgent()
var api = new Api(endpoint, apiRequestFactory: null, statsd: null);
var agentWriter = new AgentWriter(api, new NullMetrics());
- _tracer = new Tracer(settings, agentWriter, sampler: null, scopeManager: null, statsd: null);
+ _tracer = new Tracer(settings, plugins: null, agentWriter, sampler: null, scopeManager: null, statsd: null);
}
[Fact(Skip = "Run manually")]
diff --git a/test/Datadog.Trace.IntegrationTests/SendTracesToZipkinCollector.cs b/test/Datadog.Trace.IntegrationTests/SendTracesToZipkinCollector.cs
index b3b4f0303d..b166c832a8 100644
--- a/test/Datadog.Trace.IntegrationTests/SendTracesToZipkinCollector.cs
+++ b/test/Datadog.Trace.IntegrationTests/SendTracesToZipkinCollector.cs
@@ -18,7 +18,7 @@ public SendTracesToZipkinCollector()
var agentUri = new Uri($"http://localhost:{collectorPort}/api/v2/spans");
var exporter = new ZipkinExporter(agentUri);
var exporterWriter = new ExporterWriter(exporter, new NullMetrics());
- _tracer = new Tracer(new TracerSettings(), exporterWriter, sampler: null, scopeManager: null, statsd: null);
+ _tracer = new Tracer(new TracerSettings(), plugins: null, exporterWriter, sampler: null, scopeManager: null, statsd: null);
_zipkinCollector = new MockZipkinCollector(collectorPort);
}
diff --git a/test/Datadog.Trace.OpenTracing.IntegrationTests/OpenTracingSendTracesToAgent.cs b/test/Datadog.Trace.OpenTracing.IntegrationTests/OpenTracingSendTracesToAgent.cs
index 2d6116629d..2e76e26be1 100644
--- a/test/Datadog.Trace.OpenTracing.IntegrationTests/OpenTracingSendTracesToAgent.cs
+++ b/test/Datadog.Trace.OpenTracing.IntegrationTests/OpenTracingSendTracesToAgent.cs
@@ -23,7 +23,7 @@ public OpenTracingSendTracesToAgent()
var api = new Api(endpoint, apiRequestFactory: null, statsd: null);
var agentWriter = new AgentWriter(api, new NullMetrics());
- var tracer = new Tracer(settings, agentWriter, sampler: null, scopeManager: null, statsd: null);
+ var tracer = new Tracer(settings, plugins: null, agentWriter, sampler: null, scopeManager: null, statsd: null);
_tracer = new OpenTracingTracer(tracer);
}
diff --git a/test/Datadog.Trace.OpenTracing.Tests/OpenTracingSpanBuilderTests.cs b/test/Datadog.Trace.OpenTracing.Tests/OpenTracingSpanBuilderTests.cs
index 91c037428e..60d1e7d2ef 100644
--- a/test/Datadog.Trace.OpenTracing.Tests/OpenTracingSpanBuilderTests.cs
+++ b/test/Datadog.Trace.OpenTracing.Tests/OpenTracingSpanBuilderTests.cs
@@ -24,7 +24,7 @@ public OpenTracingSpanBuilderTests()
var writerMock = new Mock(MockBehavior.Strict);
var samplerMock = new Mock();
- var datadogTracer = new Tracer(settings, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
+ var datadogTracer = new Tracer(settings, plugins: null, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
_tracer = new OpenTracingTracer(datadogTracer);
}
diff --git a/test/Datadog.Trace.OpenTracing.Tests/OpenTracingSpanTests.cs b/test/Datadog.Trace.OpenTracing.Tests/OpenTracingSpanTests.cs
index 373280eab2..8e118107f5 100644
--- a/test/Datadog.Trace.OpenTracing.Tests/OpenTracingSpanTests.cs
+++ b/test/Datadog.Trace.OpenTracing.Tests/OpenTracingSpanTests.cs
@@ -18,7 +18,7 @@ public OpenTracingSpanTests()
var writerMock = new Mock();
var samplerMock = new Mock();
- var datadogTracer = new Tracer(settings, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
+ var datadogTracer = new Tracer(settings, plugins: null, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
_tracer = new OpenTracingTracer(datadogTracer);
}
diff --git a/test/Datadog.Trace.OpenTracing.Tests/OpenTracingTracerTests.cs b/test/Datadog.Trace.OpenTracing.Tests/OpenTracingTracerTests.cs
index 74c6d3ae36..010b9d640e 100644
--- a/test/Datadog.Trace.OpenTracing.Tests/OpenTracingTracerTests.cs
+++ b/test/Datadog.Trace.OpenTracing.Tests/OpenTracingTracerTests.cs
@@ -23,7 +23,7 @@ public OpenTracingTracerTests()
var writerMock = new Mock();
var samplerMock = new Mock();
- var datadogTracer = new Tracer(settings, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
+ var datadogTracer = new Tracer(settings, plugins: null, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
_tracer = new OpenTracingTracer(datadogTracer);
}
diff --git a/test/Datadog.Trace.Tests/CorrelationIdentifierTests.cs b/test/Datadog.Trace.Tests/CorrelationIdentifierTests.cs
index ff716fc922..82b6c4612a 100644
--- a/test/Datadog.Trace.Tests/CorrelationIdentifierTests.cs
+++ b/test/Datadog.Trace.Tests/CorrelationIdentifierTests.cs
@@ -1,10 +1,6 @@
using System;
using System.Reflection;
-using Datadog.Trace.Agent;
using Datadog.Trace.Configuration;
-using Datadog.Trace.Sampling;
-using Datadog.Trace.TestHelpers;
-using Moq;
using Xunit;
using Xunit.Sdk;
diff --git a/test/Datadog.Trace.Tests/DiagnosticListeners/AspNetCoreDiagnosticObserverTests.cs b/test/Datadog.Trace.Tests/DiagnosticListeners/AspNetCoreDiagnosticObserverTests.cs
index 5a7323c20c..a229e3d3bb 100644
--- a/test/Datadog.Trace.Tests/DiagnosticListeners/AspNetCoreDiagnosticObserverTests.cs
+++ b/test/Datadog.Trace.Tests/DiagnosticListeners/AspNetCoreDiagnosticObserverTests.cs
@@ -89,7 +89,7 @@ private static Tracer GetTracer()
var writerMock = new Mock();
var samplerMock = new Mock();
- return new Tracer(settings, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
+ return new Tracer(settings, plugins: null, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
}
private static HttpContext GetHttpContext()
diff --git a/test/Datadog.Trace.Tests/DogStatsDTests.cs b/test/Datadog.Trace.Tests/DogStatsDTests.cs
index d248d1cf33..6ec20e49b8 100644
--- a/test/Datadog.Trace.Tests/DogStatsDTests.cs
+++ b/test/Datadog.Trace.Tests/DogStatsDTests.cs
@@ -107,7 +107,7 @@ public void Send_metrics_when_enabled()
StartupDiagnosticLogEnabled = false,
};
- var tracer = new Tracer(settings, traceWriter: null, sampler: null, scopeManager: null, statsd);
+ var tracer = new Tracer(settings, plugins: null, traceWriter: null, sampler: null, scopeManager: null, statsd);
using (var scope = tracer.StartActive("root"))
{
diff --git a/test/Datadog.Trace.Tests/Logging/LoggingProviderTestHelpers.cs b/test/Datadog.Trace.Tests/Logging/LoggingProviderTestHelpers.cs
index 2d0b917bcd..d35a259d37 100644
--- a/test/Datadog.Trace.Tests/Logging/LoggingProviderTestHelpers.cs
+++ b/test/Datadog.Trace.Tests/Logging/LoggingProviderTestHelpers.cs
@@ -1,14 +1,9 @@
using System;
-using System.Globalization;
-using System.IO;
-using System.Text;
using Datadog.Trace.Agent;
using Datadog.Trace.Configuration;
using Datadog.Trace.Logging;
using Datadog.Trace.Sampling;
using Moq;
-using Serilog.Formatting.Display;
-using Xunit;
namespace Datadog.Trace.Tests.Logging
{
@@ -28,7 +23,7 @@ internal static Tracer InitializeTracer(bool enableLogsInjection)
settings.ServiceVersion = "custom-version";
settings.Environment = "custom-env";
- return new Tracer(settings, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
+ return new Tracer(settings, plugins: null, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
}
internal static void LogInSpanWithServiceName(Tracer tracer, ILog logger, Func openMappedContext, string service, out Scope scope)
diff --git a/test/Datadog.Trace.Tests/Propagators/CompositePropagatorsProviderTests.cs b/test/Datadog.Trace.Tests/Propagators/CompositePropagatorsProviderTests.cs
new file mode 100644
index 0000000000..b7b7eddaea
--- /dev/null
+++ b/test/Datadog.Trace.Tests/Propagators/CompositePropagatorsProviderTests.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Datadog.Trace.Conventions;
+using Datadog.Trace.Propagation;
+using Xunit;
+
+namespace Datadog.Trace.Tests.Propagators
+{
+ public class CompositePropagatorsProviderTests
+ {
+ private const string Propagator1 = "Propagator_1";
+ private const string Propagator2 = "Propagator_2";
+ private const string Propagator3 = "Propagator_3";
+
+ [Fact]
+ public void CompositePropagatorsProvider_GetPropagators()
+ {
+ var provider = new CompositePropagatorsProvider();
+ provider.RegisterProvider(new ProviderStub(Propagator1));
+ provider.RegisterProvider(new ProviderStub(Propagator3));
+
+ var propagators = provider
+ .GetPropagators(new[] { Propagator1, Propagator3 }, null)
+ .Cast()
+ .ToList();
+
+ Assert.Equal(2, propagators.Count());
+ Assert.Equal(Propagator1, propagators[0].Id);
+ Assert.Equal(Propagator3, propagators[1].Id);
+ }
+
+ [Fact]
+ public void CompositePropagatorsProvider_GetPropagators_When_NoProvider()
+ {
+ var provider = new CompositePropagatorsProvider();
+ provider.RegisterProvider(new ProviderStub(Propagator1));
+
+ Assert.Throws(() => provider
+ .GetPropagators(new[] { Propagator2 }, null)
+ .Cast()
+ .ToList());
+ }
+
+ private class ProviderStub : IPropagatorsProvider
+ {
+ private readonly string _provides;
+
+ public ProviderStub(string provides)
+ {
+ _provides = provides;
+ }
+
+ public bool CanProvide(string propagatorId, ITraceIdConvention traceIdConvention)
+ {
+ return _provides == propagatorId;
+ }
+
+ public IPropagator GetPropagator(string propagatorId, ITraceIdConvention traceIdConvention)
+ {
+ return new PropagatorStub(propagatorId);
+ }
+ }
+
+ private class PropagatorStub : IPropagator
+ {
+ public PropagatorStub(string id)
+ {
+ Id = id;
+ }
+
+ public string Id { get; }
+
+ public SpanContext Extract(T carrier, Func> getter)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Inject(SpanContext context, T carrier, Action setter)
+ {
+ throw new NotImplementedException();
+ }
+ }
+ }
+}
diff --git a/test/Datadog.Trace.Tests/SpanTests.cs b/test/Datadog.Trace.Tests/SpanTests.cs
index 5a45d94c63..2914b45141 100644
--- a/test/Datadog.Trace.Tests/SpanTests.cs
+++ b/test/Datadog.Trace.Tests/SpanTests.cs
@@ -27,7 +27,7 @@ public SpanTests(ITestOutputHelper output)
_writerMock = new Mock();
var samplerMock = new Mock();
- _tracer = new Tracer(settings, _writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
+ _tracer = new Tracer(settings, plugins: null, _writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
}
[Fact]
diff --git a/test/Datadog.Trace.Tests/TracerSettingsTests.cs b/test/Datadog.Trace.Tests/TracerSettingsTests.cs
index 81f5139b28..375e2eac4c 100644
--- a/test/Datadog.Trace.Tests/TracerSettingsTests.cs
+++ b/test/Datadog.Trace.Tests/TracerSettingsTests.cs
@@ -33,7 +33,7 @@ public void ConfiguredTracerSettings_DefaultTagsSetFromEnvironmentVariable(strin
IConfigurationSource source = new NameValueConfigurationSource(collection);
var settings = new TracerSettings(source);
- var tracer = new Tracer(settings, _writerMock.Object, _samplerMock.Object, scopeManager: null, statsd: null);
+ var tracer = new Tracer(settings, plugins: null, _writerMock.Object, _samplerMock.Object, scopeManager: null, statsd: null);
var span = tracer.StartSpan("Operation");
Assert.Equal(span.GetTag(tagKey), value);
@@ -52,7 +52,7 @@ public void DDVarTakesPrecedenceOverDDTags(string envKey, string tagKey)
var settings = new TracerSettings(source);
Assert.True(settings.GlobalTags.Any());
- var tracer = new Tracer(settings, _writerMock.Object, _samplerMock.Object, scopeManager: null, statsd: null);
+ var tracer = new Tracer(settings, plugins: null, _writerMock.Object, _samplerMock.Object, scopeManager: null, statsd: null);
var span = tracer.StartSpan("Operation");
Assert.Equal(span.GetTag(tagKey), envValue);
@@ -75,7 +75,7 @@ public void TraceEnabled(string value, bool areTracesEnabled)
_writerMock.Invocations.Clear();
- var tracer = new Tracer(tracerSettings, _writerMock.Object, _samplerMock.Object, scopeManager: null, statsd: null);
+ var tracer = new Tracer(tracerSettings, plugins: null, _writerMock.Object, _samplerMock.Object, scopeManager: null, statsd: null);
var span = tracer.StartSpan("TestTracerDisabled");
span.Dispose();
diff --git a/test/Datadog.Trace.Tests/TracerTests.cs b/test/Datadog.Trace.Tests/TracerTests.cs
index 63daa6ecfc..0a889ee815 100644
--- a/test/Datadog.Trace.Tests/TracerTests.cs
+++ b/test/Datadog.Trace.Tests/TracerTests.cs
@@ -44,7 +44,7 @@ public TracerTests()
var writerMock = new Mock();
var samplerMock = new Mock();
- _tracer = new Tracer(settings, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
+ _tracer = new Tracer(settings, plugins: null, writerMock.Object, samplerMock.Object, scopeManager: null, statsd: null);
}
[Fact]
diff --git a/test/benchmarks/Benchmarks.Trace/AspNetCoreBenchmark.cs b/test/benchmarks/Benchmarks.Trace/AspNetCoreBenchmark.cs
index 56dc43ae08..b08b432e18 100644
--- a/test/benchmarks/Benchmarks.Trace/AspNetCoreBenchmark.cs
+++ b/test/benchmarks/Benchmarks.Trace/AspNetCoreBenchmark.cs
@@ -31,7 +31,7 @@ static AspNetCoreBenchmark()
StartupDiagnosticLogEnabled = false,
};
- Tracer.Instance = new Tracer(settings, new DummyAgentWriter(), null, null, null);
+ Tracer.Instance = new Tracer(settings, null, new DummyAgentWriter(), null, null, null);
var builder = new WebHostBuilder()
.UseStartup();
diff --git a/test/benchmarks/Benchmarks.Trace/DbCommandBenchmark.cs b/test/benchmarks/Benchmarks.Trace/DbCommandBenchmark.cs
index 6929c94f80..b68eae9b86 100644
--- a/test/benchmarks/Benchmarks.Trace/DbCommandBenchmark.cs
+++ b/test/benchmarks/Benchmarks.Trace/DbCommandBenchmark.cs
@@ -25,7 +25,7 @@ static DbCommandBenchmark()
StartupDiagnosticLogEnabled = false
};
- Tracer.Instance = new Tracer(settings, new DummyAgentWriter(), null, null, null);
+ Tracer.Instance = new Tracer(settings, null, new DummyAgentWriter(), null, null, null);
var methodInfo = typeof(IDbCommand).GetMethod("ExecuteNonQuery", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
diff --git a/test/benchmarks/Benchmarks.Trace/ElasticsearchBenchmark.cs b/test/benchmarks/Benchmarks.Trace/ElasticsearchBenchmark.cs
index 29388fb4c1..9de7dd41c0 100644
--- a/test/benchmarks/Benchmarks.Trace/ElasticsearchBenchmark.cs
+++ b/test/benchmarks/Benchmarks.Trace/ElasticsearchBenchmark.cs
@@ -33,7 +33,7 @@ static ElasticsearchBenchmark()
StartupDiagnosticLogEnabled = false
};
- Tracer.Instance = new Tracer(settings, new DummyAgentWriter(), null, null, null);
+ Tracer.Instance = new Tracer(settings, null, new DummyAgentWriter(), null, null, null);
var methodInfo = typeof(RequestPipeline).GetMethod("CallElasticsearchAsync", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
diff --git a/test/benchmarks/Benchmarks.Trace/GraphQLBenchmark.cs b/test/benchmarks/Benchmarks.Trace/GraphQLBenchmark.cs
index 48b92398e5..db9f6ca46c 100644
--- a/test/benchmarks/Benchmarks.Trace/GraphQLBenchmark.cs
+++ b/test/benchmarks/Benchmarks.Trace/GraphQLBenchmark.cs
@@ -28,7 +28,7 @@ static GraphQLBenchmark()
StartupDiagnosticLogEnabled = false
};
- Tracer.Instance = new Tracer(settings, new DummyAgentWriter(), null, null, null);
+ Tracer.Instance = new Tracer(settings, null, new DummyAgentWriter(), null, null, null);
var methodInfo = typeof(GraphQLClient).GetMethod("ExecuteAsync", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
diff --git a/test/benchmarks/Benchmarks.Trace/HttpClientBenchmark.cs b/test/benchmarks/Benchmarks.Trace/HttpClientBenchmark.cs
index 89248638b4..7a381bf185 100644
--- a/test/benchmarks/Benchmarks.Trace/HttpClientBenchmark.cs
+++ b/test/benchmarks/Benchmarks.Trace/HttpClientBenchmark.cs
@@ -31,7 +31,7 @@ static HttpClientBenchmark()
StartupDiagnosticLogEnabled = false
};
- Tracer.Instance = new Tracer(settings, new DummyAgentWriter(), null, null, null);
+ Tracer.Instance = new Tracer(settings, null, new DummyAgentWriter(), null, null, null);
var methodInfo = typeof(HttpMessageHandler).GetMethod("SendAsync", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
diff --git a/test/benchmarks/Benchmarks.Trace/RedisBenchmark.cs b/test/benchmarks/Benchmarks.Trace/RedisBenchmark.cs
index aec01299d1..3911c5914f 100644
--- a/test/benchmarks/Benchmarks.Trace/RedisBenchmark.cs
+++ b/test/benchmarks/Benchmarks.Trace/RedisBenchmark.cs
@@ -30,7 +30,7 @@ static RedisBenchmark()
StartupDiagnosticLogEnabled = false
};
- Tracer.Instance = new Tracer(settings, new DummyAgentWriter(), null, null, null);
+ Tracer.Instance = new Tracer(settings, null, new DummyAgentWriter(), null, null, null);
var methodInfo = typeof(RedisNativeClient).GetMethod("SendReceive", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
diff --git a/test/benchmarks/Benchmarks.Trace/SerilogBenchmark.cs b/test/benchmarks/Benchmarks.Trace/SerilogBenchmark.cs
index 1669eaf5d4..5b3aca7cdd 100644
--- a/test/benchmarks/Benchmarks.Trace/SerilogBenchmark.cs
+++ b/test/benchmarks/Benchmarks.Trace/SerilogBenchmark.cs
@@ -26,7 +26,7 @@ static SerilogBenchmark()
ServiceVersion = "version"
};
- LogInjectionTracer = new Tracer(logInjectionSettings, new DummyAgentWriter(), null, null, null);
+ LogInjectionTracer = new Tracer(logInjectionSettings, null, new DummyAgentWriter(), null, null, null);
Tracer.Instance = LogInjectionTracer;
var baselineSettings = new TracerSettings
@@ -37,7 +37,7 @@ static SerilogBenchmark()
ServiceVersion = "version"
};
- BaselineTracer = new Tracer(baselineSettings, new DummyAgentWriter(), null, null, null);
+ BaselineTracer = new Tracer(baselineSettings, null, new DummyAgentWriter(), null, null, null);
EnrichedLogger = new LoggerConfiguration()
// Add Enrich.FromLogContext to emit Datadog properties
diff --git a/test/benchmarks/Benchmarks.Trace/SpanBenchmark.cs b/test/benchmarks/Benchmarks.Trace/SpanBenchmark.cs
index 1924e0c727..356c1380dc 100644
--- a/test/benchmarks/Benchmarks.Trace/SpanBenchmark.cs
+++ b/test/benchmarks/Benchmarks.Trace/SpanBenchmark.cs
@@ -23,7 +23,7 @@ static SpanBenchmark()
StartupDiagnosticLogEnabled = false
};
- Tracer = new Tracer(settings, new DummyAgentWriter(), null, null, null);
+ Tracer = new Tracer(settings, null, new DummyAgentWriter(), null, null, null);
}
///
diff --git a/test/test-applications/integrations/Samples.Vendoring/Propagation/VendorPropagatorsProvider.cs b/test/test-applications/integrations/Samples.Vendoring/Propagation/VendorPropagatorsProvider.cs
new file mode 100644
index 0000000000..060a0fcb1a
--- /dev/null
+++ b/test/test-applications/integrations/Samples.Vendoring/Propagation/VendorPropagatorsProvider.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using Datadog.Trace.Conventions;
+using Datadog.Trace.Propagation;
+using Samples.Vendoring.Types;
+
+namespace Samples.Vendoring.Propagation
+{
+ public class VendorPropagatorsProvider : IPropagatorsProvider
+ {
+ private static readonly IReadOnlyDictionary> PropagatorSelector =
+ new Dictionary>(StringComparer.InvariantCultureIgnoreCase)
+ {
+ { VendorPropagatorTypes.VendorPropagator, convention => new VendorSpanContextPropagator(convention) }
+ };
+
+ public bool CanProvide(string propagatorId, ITraceIdConvention traceIdConvention)
+ {
+ return PropagatorSelector.ContainsKey(propagatorId);
+ }
+
+ public IPropagator GetPropagator(string propagatorId, ITraceIdConvention traceIdConvention)
+ {
+ if (PropagatorSelector.TryGetValue(propagatorId, out Func getter))
+ {
+ return getter(traceIdConvention);
+ }
+
+ throw new InvalidOperationException($"There is no propagator registered for type '{propagatorId}'.");
+ }
+ }
+}
diff --git a/test/test-applications/integrations/Samples.Vendoring/Propagation/VendorSpanContextPropagator.cs b/test/test-applications/integrations/Samples.Vendoring/Propagation/VendorSpanContextPropagator.cs
new file mode 100644
index 0000000000..fd0492a7dc
--- /dev/null
+++ b/test/test-applications/integrations/Samples.Vendoring/Propagation/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.Propagation
+{
+ 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..bf4bedaaa7
--- /dev/null
+++ b/test/test-applications/integrations/Samples.Vendoring/Samples.Vendoring.csproj
@@ -0,0 +1,12 @@
+
+
+
+ true
+ 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/plugins.json b/test/test-applications/integrations/Samples.Vendoring/plugins.json
new file mode 100644
index 0000000000..94fdd379af
--- /dev/null
+++ b/test/test-applications/integrations/Samples.Vendoring/plugins.json
@@ -0,0 +1,12 @@
+/*
+ Example of plugin configuration file
+*/
+
+{
+ "net45": [
+ "plugins/Samples.Vendoring.dll"
+ ],
+ "netcoreapp3.1": [
+ "plugins/Samples.Vendoring.dll"
+ ]
+}