Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extensibility prototype #129

Merged
merged 16 commits into from
May 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions Datadog.Trace.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
8 changes: 6 additions & 2 deletions src/Datadog.Trace.ClrProfiler.Managed/Instrumentation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not from your change but unless the logger can't work at this time it will be good to at least log the exception.

Expand Down
5 changes: 5 additions & 0 deletions src/Datadog.Trace/Configuration/ConfigurationKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ public static class ConfigurationKeys
/// </summary>
public const string ConfigurationFileName = "OTEL_TRACE_CONFIG_FILE";

/// <summary>
/// Configuration key for the path to the plugins configuration file.
/// </summary>
public const string PluginConfigurationFileName = "OTEL_DOTNET_TRACER_PLUGINS_FILE";

/// <summary>
/// Configuration key for the application's environment. Sets the "env" tag on every <see cref="Span"/>.
/// </summary>
Expand Down
35 changes: 29 additions & 6 deletions src/Datadog.Trace/Configuration/GlobalSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ internal GlobalSettings(IConfigurationSource source)
DiagnosticSourceEnabled = source?.GetBool(ConfigurationKeys.DiagnosticSourceEnabled) ??
// default value
true;

if (TryLoadPluginJsonConfigurationFile(source, out JsonConfigurationSource jsonConfigurationSource))
{
PluginsConfiguration = jsonConfigurationSource;
}
}

/// <summary>
Expand All @@ -55,6 +60,11 @@ internal GlobalSettings(IConfigurationSource source)
/// </summary>
internal bool DiagnosticSourceEnabled { get; }

/// <summary>
/// Gets the plugins configuration.
/// </summary>
internal JsonConfigurationSource PluginsConfiguration { get; }

/// <summary>
/// Set whether debug mode is enabled.
/// Affects the level of logs written to file.
Expand Down Expand Up @@ -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");
Copy link
Member

@pellared pellared Apr 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we want to use Path.Combine(GetCurrentDirectory(), "plugins.json") if nothing is provided.
I always fear such stuff because it is a great place that someone may use for ACE (Arbitrary Code Execution).

The bad-guy must only have a possibility to create some file in the same directory (it can be created by a different user!). The user may be not aware of the risk. I would prefer to require explicitly enabling via ENV VAR. Non-admin user is unable to set env var for different users.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially almost the same logic as for json configuration file, just tries to load different file (see TryLoadJsonConfigurationFile). Just removing plugins.json defaults wouldn't give much benefits, as the ACE could be executed through autoloading datadog.json which in turn can direct to plugin by overloading unspecified plugin path.

var configurationFileName = configurationSource.GetString(ConfigurationKeys.ConfigurationFileName) ??
      configurationSource.GetString("OTEL_DOTNET_TRACER_CONFIG_FILE") ??
      Path.Combine(GetCurrentDirectory(), "datadog.json");

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Then I would say the same mechanism be removed for datadog.json 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable. Should we refactor json configuration loading in different PR? - as this addresses completely different logic than current one. + the parsing issue #129 (comment).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you not mean #129 (comment) ?

For me, it can be a separate PR + GH issue (just to not forget about it)

Copy link
Contributor Author

@RassK RassK May 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had the issue with IIS hosting multiple .NET Framework web apps, that could not be env scoped - although this issue was solved with web.config / app.config.
I'm not 100% sure, that everything could be solved with env scoping or app.config / web.config.

Maybe just add another var to enable config load? - so by default it will be off.

Copy link
Member

@pellared pellared May 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure, that everything could be solved with env scoping or app.config / web.config.
[...] Maybe just add another var to enable config load? - so by default it will be off.

I would rather add a var to enable config load if we are 100% sure that we cannot solve it any other way. Less is more 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would agree if it's about adding functionality. But more concerned when we are reducing publicly available functionality. Anyway we can discuss it under a new issue when it's open and more detailed.

Copy link
Member

@pellared pellared May 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am ok with moving it to an issue and discussing it during SIG.

See #129 (comment) 😉

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plugins.json unlike the one used in the config should come with the install and be global for the whole machine. So it should follow logic similar to loading the integrations.json - if the plugins have configuration options per application those should be handled config (including the per-app json).

That said let's handle that in a separate PR.


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))
{
Expand Down
9 changes: 9 additions & 0 deletions src/Datadog.Trace/Configuration/JsonConfigurationSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@ public IDictionary<string, string> GetDictionary(string key, bool allowOptionalM
return GetDictionaryInternal(key, allowOptionalMappings);
}

/// <summary>
/// Gets the string representation of json config.
/// </summary>
/// <returns>String format.</returns>
public override string ToString()
{
return _configuration.ToString();
}

private IDictionary<string, string> GetDictionaryInternal(string key, bool allowOptionalMappings)
{
var token = _configuration.SelectToken(key, errorWhenNoMatch: false);
Expand Down
29 changes: 0 additions & 29 deletions src/Datadog.Trace/Configuration/PropagatorType.cs

This file was deleted.

11 changes: 6 additions & 5 deletions src/Datadog.Trace/Configuration/TracerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -369,7 +370,7 @@ public TracerSettings(IConfigurationSource source)
/// Default is <c>Datadog</c>
/// <seealso cref="ConfigurationKeys.Propagators"/>
/// </summary>
public HashSet<PropagatorType> Propagators { get; set; }
public HashSet<string> Propagators { get; set; }
pellared marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Gets or sets a value indicating whether runtime metrics
Expand Down Expand Up @@ -640,18 +641,18 @@ internal string GetServiceName(Tracer tracer, string serviceName)
return ServiceNameMappings.GetServiceName(tracer.DefaultServiceName, serviceName);
}

private static HashSet<PropagatorType> GetPropagators(IConfigurationSource source)
private static HashSet<string> GetPropagators(IConfigurationSource source)
{
var propagators = source.GetTypedValues<PropagatorType>(ConfigurationKeys.Propagators);
var propagators = source.GetStrings(ConfigurationKeys.Propagators);

if (!propagators.Any())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will cause multiple enumeration (at .Any() and then at HashSet creation). I know there will always be an array underneath but I would add .ToArray() in that case just for the sake of it. We won't risk it breaking if the implementation changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally it was intentional due perf and allocation benefits (Enumerate first + enumerate all vs allocate new list / array). @pellared any ideas which way is more preferable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not worry much about the perf for such cases since it is executed once per application lifetime. Of course, I'm not saying to ignore performance and to not do easy/obvious savings but the impact here will be very minimal anyway.

{
// 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>() { PropagatorType.Datadog };
return new HashSet<string>() { PropagatorTypes.Datadog };
}

return new HashSet<PropagatorType>(propagators);
return new HashSet<string>(propagators);
}
}
}
23 changes: 23 additions & 0 deletions src/Datadog.Trace/Configuration/Types/PropagatorTypes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Datadog.Trace.Configuration.Types
{
/// <summary>
/// Contains default available propagator types.
/// </summary>
public static class PropagatorTypes
{
/// <summary>
/// The Datadog propagator.
/// </summary>
public const string Datadog = "Datadog";

/// <summary>
/// The B3 propagator
/// </summary>
public const string B3 = "B3";

/// <summary>
/// The W3C propagator
/// </summary>
public const string W3C = "W3C";
}
}
11 changes: 10 additions & 1 deletion src/Datadog.Trace/Conventions/ITraceIdConvention.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,19 @@ namespace Datadog.Trace.Conventions
/// <summary>
/// Convention used when defining format of TraceId.
/// </summary>
internal interface ITraceIdConvention
public interface ITraceIdConvention
{
/// <summary>
/// Generates new unique trace id based on convention.
/// </summary>
/// <returns>Trace id.</returns>
TraceId GenerateNewTraceId();

/// <summary>
/// Creates new trace id based on given string.
/// </summary>
/// <param name="id">String of id.</param>
/// <returns>Trace id.</returns>
TraceId CreateFromString(string id);
}
}
32 changes: 28 additions & 4 deletions src/Datadog.Trace/FrameworkDescription.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -29,6 +27,15 @@ internal partial class FrameworkDescription
Tuple.Create(378389, "4.5"),
};

private static readonly IReadOnlyDictionary<string, string> TargetFrameworkMapping = new Dictionary<string, string>()
{
{ ".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,
Expand All @@ -41,6 +48,7 @@ private FrameworkDescription(
OSPlatform = osPlatform;
OSArchitecture = osArchitecture;
ProcessArchitecture = processArchitecture;
TargetFramework = GetTargetFramework();
}

public string Name { get; }
Expand All @@ -53,6 +61,8 @@ private FrameworkDescription(

public string ProcessArchitecture { get; }

public string TargetFramework { get; }

public override string ToString()
{
// examples:
Expand All @@ -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<TargetFrameworkAttribute>()?
.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;
Expand Down
9 changes: 9 additions & 0 deletions src/Datadog.Trace/Plugins/IOTelExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Datadog.Trace.Plugins
{
/// <summary>
/// Base marker interface for extendability points.
/// </summary>
public interface IOTelExtension
{
}
}
Loading