diff --git a/Datadog.Trace.sln.DotSettings b/Datadog.Trace.sln.DotSettings
index def04568a..725bd2cf4 100644
--- a/Datadog.Trace.sln.DotSettings
+++ b/Datadog.Trace.sln.DotSettings
@@ -35,7 +35,7 @@
CHOP_IF_LONG
CHOP_IF_LONG
False
- CHOP_ALWAYS
+
False
True
False
@@ -43,6 +43,7 @@
True
True
True
+ OS
True
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
diff --git a/src/Datadog.Trace.ClrProfiler.Managed/NativeMethods.cs b/src/Datadog.Trace.ClrProfiler.Managed/NativeMethods.cs
index 366f25cc9..9ab64e6f1 100644
--- a/src/Datadog.Trace.ClrProfiler.Managed/NativeMethods.cs
+++ b/src/Datadog.Trace.ClrProfiler.Managed/NativeMethods.cs
@@ -1,3 +1,4 @@
+using System;
using System.Runtime.InteropServices;
// ReSharper disable MemberHidesStaticFromOuterClass
@@ -5,9 +6,11 @@ namespace Datadog.Trace.ClrProfiler
{
internal static class NativeMethods
{
+ private static readonly bool IsWindows = string.Equals(FrameworkDescription.Create().OSPlatform, "Windows", StringComparison.OrdinalIgnoreCase);
+
public static bool IsProfilerAttached()
{
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ if (IsWindows)
{
return Windows.IsProfilerAttached();
}
diff --git a/src/Datadog.Trace/Agent/Api.cs b/src/Datadog.Trace/Agent/Api.cs
index 0f081da53..b76650791 100644
--- a/src/Datadog.Trace/Agent/Api.cs
+++ b/src/Datadog.Trace/Agent/Api.cs
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Reflection;
-using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Datadog.Trace.Containers;
using Datadog.Trace.Logging;
@@ -15,7 +14,7 @@ namespace Datadog.Trace.Agent
{
private const string TracesPath = "/v0.4/traces";
- private static readonly ILog Log = LogProvider.For();
+ private static readonly ILog Log = LogProvider.GetCurrentClassLogger();
private static readonly SerializationContext SerializationContext = new SerializationContext();
private static readonly SpanMessagePackSerializer Serializer = new SpanMessagePackSerializer(SerializationContext);
@@ -41,20 +40,31 @@ namespace Datadog.Trace.Agent
_tracesEndpoint = new Uri(baseEndpoint, TracesPath);
- GetFrameworkDescription(out string frameworkName, out string frameworkVersion);
- var tracerVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
-
- var containerId = ContainerInfo.GetContainerId();
+ _client.DefaultRequestHeaders.Add(AgentHttpHeaderNames.Language, ".NET");
// report runtime details
- _client.DefaultRequestHeaders.Add(AgentHttpHeaderNames.Language, ".NET");
- _client.DefaultRequestHeaders.Add(AgentHttpHeaderNames.LanguageInterpreter, frameworkName);
- _client.DefaultRequestHeaders.Add(AgentHttpHeaderNames.LanguageVersion, frameworkVersion);
+ try
+ {
+ var frameworkDescription = FrameworkDescription.Create();
+
+ if (frameworkDescription != null)
+ {
+ _client.DefaultRequestHeaders.Add(AgentHttpHeaderNames.LanguageInterpreter, frameworkDescription.Name);
+ _client.DefaultRequestHeaders.Add(AgentHttpHeaderNames.LanguageVersion, frameworkDescription.ProductVersion);
+ }
+ }
+ catch (Exception e)
+ {
+ Log.ErrorException("Error getting framework description", e);
+ }
// report Tracer version
+ var tracerVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
_client.DefaultRequestHeaders.Add(AgentHttpHeaderNames.TracerVersion, tracerVersion);
// report container id (only Linux containers supported for now)
+ var containerId = ContainerInfo.GetContainerId();
+
if (containerId != null)
{
_client.DefaultRequestHeaders.Add(AgentHttpHeaderNames.ContainerId, containerId);
@@ -145,20 +155,6 @@ namespace Datadog.Trace.Agent
return uniqueTraceIds;
}
- private static void GetFrameworkDescription(out string name, out string version)
- {
- // RuntimeInformation.FrameworkDescription returns string like ".NET Framework 4.7.2" or ".NET Core 2.1",
- // we want to split the runtime from the version so we can report them as separate values
- string frameworkDescription = RuntimeInformation.FrameworkDescription;
- int index = RuntimeInformation.FrameworkDescription.LastIndexOf(' ');
-
- // everything before the last space
- name = frameworkDescription.Substring(0, index).Trim();
-
- // everything after the last space
- version = frameworkDescription.Substring(index).Trim();
- }
-
internal class ApiResponse
{
[JsonProperty("rate_by_service")]
diff --git a/src/Datadog.Trace/AgentHttpHeaderNames.cs b/src/Datadog.Trace/AgentHttpHeaderNames.cs
index b84dae8e5..4d8dea176 100644
--- a/src/Datadog.Trace/AgentHttpHeaderNames.cs
+++ b/src/Datadog.Trace/AgentHttpHeaderNames.cs
@@ -15,13 +15,11 @@ namespace Datadog.Trace
///
/// The interpreter for the given language, e.g. ".NET Framework" or ".NET Core".
- /// The value of .
///
public const string LanguageInterpreter = "Datadog-Meta-Lang-Interpreter";
///
/// The interpreter version for the given language, e.g. "4.7.2" for .NET Framework or "2.1" for .NET Core.
- /// The value of .
///
public const string LanguageVersion = "Datadog-Meta-Lang-Version";
diff --git a/src/Datadog.Trace/Containers/ContainerInfo.cs b/src/Datadog.Trace/Containers/ContainerInfo.cs
index 3de0e09ac..08e44d7cb 100644
--- a/src/Datadog.Trace/Containers/ContainerInfo.cs
+++ b/src/Datadog.Trace/Containers/ContainerInfo.cs
@@ -59,21 +59,12 @@ namespace Datadog.Trace.Containers
private static string GetContainerIdInternal()
{
- bool isLinux;
-
try
{
- isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
- }
- catch (Exception ex)
- {
- Log.WarnException("Unable to determine OS. Will not report container id.", ex);
- return null;
- }
+ var isLinux = string.Equals(FrameworkDescription.Create().OSPlatform, "Linux", StringComparison.OrdinalIgnoreCase);
- try
- {
- if (isLinux && File.Exists(ControlGroupsFilePath))
+ if (isLinux &&
+ File.Exists(ControlGroupsFilePath))
{
var lines = File.ReadLines(ControlGroupsFilePath);
return ParseCgroupLines(lines);
@@ -82,7 +73,6 @@ namespace Datadog.Trace.Containers
catch (Exception ex)
{
Log.WarnException("Error reading cgroup file. Will not report container id.", ex);
- return null;
}
return null;
diff --git a/src/Datadog.Trace/Datadog.Trace.csproj b/src/Datadog.Trace/Datadog.Trace.csproj
index 3b55d1c62..1cd088bd6 100644
--- a/src/Datadog.Trace/Datadog.Trace.csproj
+++ b/src/Datadog.Trace/Datadog.Trace.csproj
@@ -1,4 +1,4 @@
-
+
@@ -13,10 +13,16 @@
+
+
+
+
+
-
- all
-
+
@@ -24,4 +30,4 @@
-
+
\ No newline at end of file
diff --git a/src/Datadog.Trace/FrameworkDescription.cs b/src/Datadog.Trace/FrameworkDescription.cs
new file mode 100644
index 000000000..6329aef79
--- /dev/null
+++ b/src/Datadog.Trace/FrameworkDescription.cs
@@ -0,0 +1,243 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
+using Datadog.Trace.Logging;
+using Microsoft.Win32;
+
+namespace Datadog.Trace
+{
+ internal class FrameworkDescription
+ {
+ private static readonly ILog Log = LogProvider.GetCurrentClassLogger();
+
+ private static readonly Assembly RootAssembly = typeof(object).Assembly;
+
+ private static readonly Tuple[] DotNetFrameworkVersionMapping =
+ {
+ // highest known value is 528049
+ Tuple.Create(528050, "4.8+"),
+
+ // known min value for each framework version
+ Tuple.Create(528040, "4.8"),
+ Tuple.Create(461808, "4.7.2"),
+ Tuple.Create(461308, "4.7.1"),
+ Tuple.Create(460798, "4.7"),
+ Tuple.Create(394802, "4.6.2"),
+ Tuple.Create(394254, "4.6.1"),
+ Tuple.Create(393295, "4.6"),
+ Tuple.Create(379893, "4.5.2"),
+ Tuple.Create(378675, "4.5.1"),
+ Tuple.Create(378389, "4.5"),
+ };
+
+ private FrameworkDescription(
+ string name,
+ string productVersion,
+ string osPlatform,
+ string osArchitecture,
+ string processArchitecture)
+ {
+ Name = name;
+ ProductVersion = productVersion;
+ OSPlatform = osPlatform;
+ OSArchitecture = osArchitecture;
+ ProcessArchitecture = processArchitecture;
+ }
+
+ public string Name { get; }
+
+ public string ProductVersion { get; }
+
+ public string OSPlatform { get; }
+
+ public string OSArchitecture { get; }
+
+ public string ProcessArchitecture { get; }
+
+ public static FrameworkDescription Create()
+ {
+ var assemblyName = RootAssembly.GetName();
+
+ if (string.Equals(assemblyName.Name, "mscorlib", StringComparison.OrdinalIgnoreCase))
+ {
+ // .NET Framework
+ return new FrameworkDescription(
+ ".NET Framework",
+ GetNetFrameworkVersion() ?? "unknown",
+ "Windows",
+ Environment.Is64BitOperatingSystem ? "x64" : "x86",
+ Environment.Is64BitProcess ? "x64" : "x86");
+ }
+
+ // .NET Core
+ return CreateFromRuntimeInformation();
+ }
+
+ public override string ToString()
+ {
+ // examples:
+ // .NET Framework 4.8 x86 on Windows x64
+ // .NET Core 3.0.0 x64 on Linux x64
+ return $"{Name} {ProductVersion} {ProcessArchitecture} on {OSPlatform} {OSArchitecture}";
+ }
+
+ private static FrameworkDescription CreateFromRuntimeInformation()
+ {
+ string frameworkName = null;
+ string osPlatform = null;
+
+ try
+ {
+ // RuntimeInformation.FrameworkDescription returns a string like ".NET Framework 4.7.2" or ".NET Core 2.1",
+ // we want to return everything before the last space
+ string frameworkDescription = RuntimeInformation.FrameworkDescription;
+ int index = frameworkDescription.LastIndexOf(' ');
+ frameworkName = frameworkDescription.Substring(0, index).Trim();
+ }
+ catch (Exception e)
+ {
+ Log.ErrorException("Error getting framework name from RuntimeInformation", e);
+ }
+
+ if (RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
+ {
+ osPlatform = "Windows";
+ }
+ else if (RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux))
+ {
+ osPlatform = "Linux";
+ }
+ else if (RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX))
+ {
+ osPlatform = "MacOS";
+ }
+
+ return new FrameworkDescription(
+ frameworkName ?? "unknown",
+ GetNetCoreVersion() ?? "unknown",
+ osPlatform ?? "unknown",
+ RuntimeInformation.OSArchitecture.ToString().ToLowerInvariant(),
+ RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant());
+ }
+
+ private static string GetNetFrameworkVersion()
+ {
+ string productVersion = null;
+
+ try
+ {
+ object registryValue;
+
+ using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default))
+ using (var subKey = baseKey.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\"))
+ {
+ registryValue = subKey?.GetValue("Release");
+ }
+
+ if (registryValue is int release)
+ {
+ // find the known version on the list with the largest release number
+ // that is lower than or equal to the release number in the Windows Registry
+ productVersion = DotNetFrameworkVersionMapping.FirstOrDefault(t => release >= t.Item1)?.Item2;
+ }
+ }
+ catch (Exception e)
+ {
+ Log.ErrorException("Error getting .NET Framework version from Windows Registry", e);
+ }
+
+ if (productVersion == null)
+ {
+ // if we fail to extract version from assembly path,
+ // fall back to the [AssemblyInformationalVersion] or [AssemblyFileVersion]
+ productVersion = GetVersionFromAssemblyAttributes();
+ }
+
+ return productVersion;
+ }
+
+ private static string GetNetCoreVersion()
+ {
+ string productVersion = null;
+
+ if (Environment.Version.Major == 3 || Environment.Version.Major >= 5)
+ {
+ // Environment.Version returns "4.x" in .NET Core 2.x,
+ // but it is correct since .NET Core 3.0.0
+ productVersion = Environment.Version.ToString();
+ }
+
+ if (productVersion == null)
+ {
+ try
+ {
+ // try to get product version from assembly path
+ Match match = Regex.Match(
+ RootAssembly.CodeBase,
+ @"/[^/]*microsoft\.netcore\.app/(\d+\.\d+\.\d+[^/]*)/",
+ RegexOptions.IgnoreCase);
+
+ if (match.Success && match.Groups.Count > 0 && match.Groups[1].Success)
+ {
+ productVersion = match.Groups[1].Value;
+ }
+ }
+ catch (Exception e)
+ {
+ Log.ErrorException("Error getting .NET Core version from assembly path", e);
+ }
+ }
+
+ if (productVersion == null)
+ {
+ // if we fail to extract version from assembly path,
+ // fall back to the [AssemblyInformationalVersion] or [AssemblyFileVersion]
+ productVersion = GetVersionFromAssemblyAttributes();
+ }
+
+ if (productVersion == null)
+ {
+ // at this point, everything else has failed (this is probably the same as [AssemblyFileVersion] above)
+ productVersion = Environment.Version.ToString();
+ }
+
+ return productVersion;
+ }
+
+ private static string GetVersionFromAssemblyAttributes()
+ {
+ string productVersion = null;
+
+ try
+ {
+ // if we fail to extract version from assembly path, fall back to the [AssemblyInformationalVersion],
+ var informationalVersionAttribute = (AssemblyInformationalVersionAttribute)RootAssembly.GetCustomAttribute(typeof(AssemblyInformationalVersionAttribute));
+
+ // split remove the commit hash from pre-release versions
+ productVersion = informationalVersionAttribute?.InformationalVersion?.Split('+')[0];
+ }
+ catch (Exception e)
+ {
+ Log.ErrorException("Error getting framework version from [AssemblyInformationalVersion]", e);
+ }
+
+ if (productVersion == null)
+ {
+ try
+ {
+ // and if that fails, try [AssemblyFileVersion]
+ var fileVersionAttribute = (AssemblyFileVersionAttribute)RootAssembly.GetCustomAttribute(typeof(AssemblyFileVersionAttribute));
+ productVersion = fileVersionAttribute?.Version;
+ }
+ catch (Exception e)
+ {
+ Log.ErrorException("Error getting framework version from [AssemblyFileVersion]", e);
+ }
+ }
+
+ return productVersion;
+ }
+ }
+}