From 61a18642b3d530c91eb42c1cbc41cda10e4214ff Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Mon, 29 Jul 2024 11:55:52 +0200 Subject: [PATCH 01/26] Implement additional osdetector attributes --- ...Telemetry.Resources.OperatingSystem.csproj | 1 + .../OperatingSystemDetector.cs | 252 +++++++++++++++++- .../OperatingSystemResourcesEventSource.cs | 37 +++ .../OperatingSystemSemanticConventions.cs | 4 + .../OperatingSystemDetectorTests.cs | 24 +- 5 files changed, 309 insertions(+), 9 deletions(-) create mode 100644 src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OpenTelemetry.Resources.OperatingSystem.csproj b/src/OpenTelemetry.Resources.OperatingSystem/OpenTelemetry.Resources.OperatingSystem.csproj index de79c48fe9..95c55dec8b 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OpenTelemetry.Resources.OperatingSystem.csproj +++ b/src/OpenTelemetry.Resources.OperatingSystem/OpenTelemetry.Resources.OperatingSystem.csproj @@ -19,6 +19,7 @@ + diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 4bcce208ba..9c10676991 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -2,7 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 #if !NETFRAMEWORK +using System.Diagnostics; using System.Runtime.InteropServices; + #endif using static OpenTelemetry.Resources.OperatingSystem.OperatingSystemSemanticConventions; @@ -19,17 +21,50 @@ internal sealed class OperatingSystemDetector : IResourceDetector /// Resource with key-value pairs of resource attributes. public Resource Detect() { + var attributes = new List>(); var osType = GetOSType(); - if (osType == null) { return Resource.Empty; } - return new Resource( - [ - new(AttributeOperatingSystemType, osType), - ]); + attributes.Add(new KeyValuePair(AttributeOperatingSystemType, osType)); + + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemDescription, GetOSDescription()); + + switch (osType) + { + case OperatingSystemsValues.Windows: + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, GetWindowsBuildId()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "Windows"); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, GetWindowsVersion()); + break; +#if !NETFRAMEWORK + case OperatingSystemsValues.Linux: + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, GetLinuxBuildId()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, GetLinuxDistributionName()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, GetLinuxVersion()); + break; + case OperatingSystemsValues.Darwin: + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, GetMacOSBuildId()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "MacOS"); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, GetMacOSVersion()); + break; +#endif + } + + return new Resource(attributes); + } + + private static void AddAttributeIfNotNullOrEmpty(List> attributes, string key, object? value) + { + if (value is string strValue && string.IsNullOrEmpty(strValue)) + { + OperatingSystemResourcesEventSource.Log.FailedToValidateValue("The provided value string is null or empty."); + return; + } + + attributes.Add(new KeyValuePair(key, value!)); } private static string? GetOSType() @@ -55,4 +90,211 @@ public Resource Detect() } #endif } + + private static string? GetWindowsBuildId() + { + var registryKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; + var buildLabValue = "BuildLab"; + var buildLabExValue = "BuildLabEx"; + + try + { + using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(registryKey)) + { + if (key != null) + { + var buildLab = key.GetValue(buildLabValue)?.ToString(); + var buildLabEx = key.GetValue(buildLabExValue)?.ToString(); + return !string.IsNullOrEmpty(buildLabEx) ? buildLabEx : buildLab; + } + } + } + catch (Exception ex) + { + OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get Windows build ID", ex); + } + + return null; + } + +#if !NETFRAMEWORK + private static string? GetLinuxBuildId() + { + try + { + var osReleaseContent = File.ReadAllText("/etc/os-release"); + foreach (var line in osReleaseContent.Split('\n')) + { + if (line.StartsWith("BUILD_ID=", StringComparison.Ordinal)) + { + return line.Substring("BUILD_ID=".Length).Trim('"'); + } + } + } + catch (Exception ex) + { + OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get Linux build ID", ex); + + } + + return null; + } + + private static string? GetMacOSBuildId() + { + try + { + var psi = new ProcessStartInfo + { + FileName = "sw_vers", + Arguments = "-buildVersion", + RedirectStandardOutput = true, + }; + using (var process = Process.Start(psi)) + { + if (process != null) + { + var output = process.StandardOutput.ReadToEnd().Trim(); + return output; + } + } + } + catch (Exception ex) + { + OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get MacOS build ID", ex); + } + + return null; + } +#endif + + private static string GetOSDescription() + { +#if !NETFRAMEWORK + return RuntimeInformation.OSDescription; +#else + return Environment.OSVersion.ToString(); +#endif + } + + private static string GetLinuxDistributionName() + { + try + { + var osReleaseFile = "/etc/os-release"; + if (File.Exists(osReleaseFile)) + { + var lines = File.ReadAllLines(osReleaseFile); + foreach (var line in lines) + { + if (line.StartsWith("NAME=", StringComparison.Ordinal)) + { + return line.Substring("NAME=".Length).Trim('"'); + } + } + } + } + catch (Exception ex) + { + OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get Linux distribution name", ex); + } + + return "Linux"; + } + + private static string? GetWindowsVersion() + { + try + { + var registryKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; + using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(registryKey)) + { + if (key != null) + { + var currentVersion = key.GetValue("CurrentVersion")?.ToString(); + var currentBuild = key.GetValue("CurrentBuild")?.ToString(); + var ubr = key.GetValue("UBR")?.ToString(); + return $"{currentVersion}.{currentBuild}.{ubr}"; + } + } + } + catch (Exception ex) + { + OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get Windows version", ex); + } + + return null; + } + +#if !NETFRAMEWORK + private static string? GetMacOSVersion() + { + try + { + var psi = new ProcessStartInfo + { + FileName = "sw_vers", + Arguments = "-productVersion", + RedirectStandardOutput = true, + }; + using (var process = Process.Start(psi)) + { + if (process != null) + { + var output = process.StandardOutput.ReadToEnd().Trim(); + return output; + } + } + } + catch (Exception ex) + { + OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get MacOS version", ex); + } + + return null; + } + + private static string? GetLinuxVersion() + { + try + { + var osReleaseFile = "/etc/os-release"; + if (File.Exists(osReleaseFile)) + { + var lines = File.ReadAllLines(osReleaseFile); + foreach (var line in lines) + { + if (line.StartsWith("VERSION_ID=", StringComparison.Ordinal)) + { + return line.Substring("VERSION_ID=".Length).Trim('"'); + } + } + } + + using (var process = new Process()) + { + process.StartInfo = new ProcessStartInfo + { + FileName = "uname", + Arguments = "-r", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + + process.Start(); + var output = process.StandardOutput.ReadToEnd().Trim(); + process.WaitForExit(); + return output; + } + } + catch (Exception ex) + { + OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get Linux version", ex); + } + + return null; + } + +#endif } diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs new file mode 100644 index 0000000000..24ae7ec6b5 --- /dev/null +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs @@ -0,0 +1,37 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics.Tracing; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Resources.OperatingSystem; + +[EventSource(Name = "OpenTelemetry-Resources-OperatingSystem")] +internal sealed class OperatingSystemResourcesEventSource : EventSource +{ + public static OperatingSystemResourcesEventSource Log = new(); + + private const int EventIdFailedToExtractAttributes = 1; + private const int EventIdFailedToValidateValue = 2; + + [NonEvent] + public void ResourceAttributesExtractException(string format, Exception ex) + { + if (this.IsEnabled(EventLevel.Warning, (EventKeywords)(-1))) + { + this.FailedToExtractResourceAttributes(format, ex.ToInvariantString()); + } + } + + [Event(EventIdFailedToExtractAttributes, Message = "Failed to extract resource attributes in '{0}'.", Level = EventLevel.Warning)] + public void FailedToExtractResourceAttributes(string format, string exception) + { + this.WriteEvent(EventIdFailedToExtractAttributes, format, exception); + } + + [Event(EventIdFailedToValidateValue, Message = "Failed to validate value. Details: '{0}'", Level = EventLevel.Warning)] + public void FailedToValidateValue(string error) + { + this.WriteEvent(EventIdFailedToValidateValue, error); + } +} diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemSemanticConventions.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemSemanticConventions.cs index 356ad734da..87d8c54081 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemSemanticConventions.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemSemanticConventions.cs @@ -6,6 +6,10 @@ namespace OpenTelemetry.Resources.OperatingSystem; internal static class OperatingSystemSemanticConventions { public const string AttributeOperatingSystemType = "os.type"; + public const string AttributeOperatingSystemBuildId = "os.build_id"; + public const string AttributeOperatingSystemDescription = "os.description"; + public const string AttributeOperatingSystemName = "os.name"; + public const string AttributeOperatingSystemVersion = "os.version"; public static class OperatingSystemsValues { diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs index dbcf1bb113..7670746ba2 100644 --- a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs @@ -12,32 +12,48 @@ public class OperatingSystemDetectorTests public void TestOperatingSystemAttributes() { var resource = ResourceBuilder.CreateEmpty().AddOperatingSystemDetector().Build(); - var resourceAttributes = resource.Attributes.ToDictionary(x => x.Key, x => (string)x.Value); string expectedPlatform; + string expectedDescription; + string expectedName; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { expectedPlatform = OperatingSystemSemanticConventions.OperatingSystemsValues.Windows; + expectedDescription = "Windows"; + expectedName = "Windows"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { expectedPlatform = OperatingSystemSemanticConventions.OperatingSystemsValues.Linux; + expectedDescription = "Linux"; + expectedName = "Linux"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { expectedPlatform = OperatingSystemSemanticConventions.OperatingSystemsValues.Darwin; + expectedDescription = "Darwin"; + expectedName = "MacOS"; } else { throw new PlatformNotSupportedException("Unknown platform"); } - Assert.Single(resourceAttributes); - + Assert.Equal(5, resourceAttributes.Count); + Assert.True(resourceAttributes.ContainsKey(OperatingSystemSemanticConventions.AttributeOperatingSystemDescription)); + Assert.True(resourceAttributes.ContainsKey(OperatingSystemSemanticConventions.AttributeOperatingSystemName)); Assert.True(resourceAttributes.ContainsKey(OperatingSystemSemanticConventions.AttributeOperatingSystemType)); + Assert.True(resourceAttributes.ContainsKey(OperatingSystemSemanticConventions.AttributeOperatingSystemBuildId)); + Assert.True(resourceAttributes.ContainsKey(OperatingSystemSemanticConventions.AttributeOperatingSystemVersion)); + + if (expectedName != "Linux") + { + Assert.Contains(expectedDescription, resourceAttributes[OperatingSystemSemanticConventions.AttributeOperatingSystemDescription]); + Assert.Equal(expectedName, resourceAttributes[OperatingSystemSemanticConventions.AttributeOperatingSystemName]); + } - Assert.Equal(resourceAttributes[OperatingSystemSemanticConventions.AttributeOperatingSystemType], expectedPlatform); + Assert.Equal(expectedPlatform, resourceAttributes[OperatingSystemSemanticConventions.AttributeOperatingSystemType]); } } From 057925182867303d33673ca97d692260c040f6a8 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Wed, 31 Jul 2024 12:55:07 +0200 Subject: [PATCH 02/26] Update CHANGELOG.md --- src/OpenTelemetry.Resources.OperatingSystem/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/CHANGELOG.md b/src/OpenTelemetry.Resources.OperatingSystem/CHANGELOG.md index 24e7e0ba47..8f867eee34 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/CHANGELOG.md +++ b/src/OpenTelemetry.Resources.OperatingSystem/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +* Implement additional attributes in + `OpenTelemetry.ResourceDetectors.OperatingSystem`. + ([#1983](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1983)) + ## 0.1.0-alpha.2 Released 2024-Jul-22 From 631ddfd431e609f1f544f8180cbf74852ca7b2f4 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Thu, 1 Aug 2024 09:18:32 +0200 Subject: [PATCH 03/26] Remove warnings --- .../OperatingSystemDetector.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 9c10676991..51e934fb35 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -91,6 +91,7 @@ private static void AddAttributeIfNotNullOrEmpty(List Date: Thu, 1 Aug 2024 12:52:36 +0200 Subject: [PATCH 04/26] Update CHANGELOG.md --- src/OpenTelemetry.Resources.OperatingSystem/CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/CHANGELOG.md b/src/OpenTelemetry.Resources.OperatingSystem/CHANGELOG.md index 8f867eee34..be9faebeed 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/CHANGELOG.md +++ b/src/OpenTelemetry.Resources.OperatingSystem/CHANGELOG.md @@ -2,7 +2,11 @@ ## Unreleased -* Implement additional attributes in +* Implement + `os.build_id`, + `os.description`, + `os.name`, + `os.version` attributes in `OpenTelemetry.ResourceDetectors.OperatingSystem`. ([#1983](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1983)) From 9589d6b6935945d585eff6164d2c243dd1d7e6a2 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Thu, 1 Aug 2024 12:52:57 +0200 Subject: [PATCH 05/26] Update README.md --- src/OpenTelemetry.Resources.OperatingSystem/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/README.md b/src/OpenTelemetry.Resources.OperatingSystem/README.md index d65014af41..3ccb63c9b6 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/README.md +++ b/src/OpenTelemetry.Resources.OperatingSystem/README.md @@ -49,7 +49,8 @@ using var loggerFactory = LoggerFactory.Create(builder => The resource detectors will record the following metadata based on where your application is running: -- **OperatingSystemDetector**: `os.type`. +- **OperatingSystemDetector**: `os.type`, `os.build_id`, `os.description`, + `os.name`, `os.version`. ## References From d0d2cb5f104c943f128e5649c846c7043f9bcc4a Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Thu, 1 Aug 2024 13:36:54 +0200 Subject: [PATCH 06/26] fix lint format --- .../OperatingSystemDetector.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 51e934fb35..73f85c1af7 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -35,21 +35,21 @@ public Resource Detect() switch (osType) { case OperatingSystemsValues.Windows: - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, GetWindowsBuildId()); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "Windows"); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, GetWindowsVersion()); - break; + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, GetWindowsBuildId()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "Windows"); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, GetWindowsVersion()); + break; #if !NETFRAMEWORK case OperatingSystemsValues.Linux: - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, GetLinuxBuildId()); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, GetLinuxDistributionName()); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, GetLinuxVersion()); - break; + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, GetLinuxBuildId()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, GetLinuxDistributionName()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, GetLinuxVersion()); + break; case OperatingSystemsValues.Darwin: - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, GetMacOSBuildId()); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "MacOS"); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, GetMacOSVersion()); - break; + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, GetMacOSBuildId()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "MacOS"); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, GetMacOSVersion()); + break; #endif } From 6c0340f3f9d35d57b6c103fddefd03db88783d2e Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Mon, 5 Aug 2024 11:01:15 +0200 Subject: [PATCH 07/26] Add null check to AddAttributeIfNotNullOrEmpty --- .../OperatingSystemDetector.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 73f85c1af7..608635d37d 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -58,9 +58,15 @@ public Resource Detect() private static void AddAttributeIfNotNullOrEmpty(List> attributes, string key, object? value) { + if (value == null) + { + OperatingSystemResourcesEventSource.Log.FailedToValidateValue("The provided value is null"); + return; + } + if (value is string strValue && string.IsNullOrEmpty(strValue)) { - OperatingSystemResourcesEventSource.Log.FailedToValidateValue("The provided value string is null or empty."); + OperatingSystemResourcesEventSource.Log.FailedToValidateValue("The provided value string is empty."); return; } From 9916df694025f04afb7244e7bcb5d42cae261069 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Mon, 5 Aug 2024 15:06:45 +0200 Subject: [PATCH 08/26] Change build id for Windows, move uname for linux --- .../OperatingSystemDetector.cs | 59 ++++++++----------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 608635d37d..51fffb49a2 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -#if !NETFRAMEWORK +#if NET using System.Diagnostics; using System.Runtime.InteropServices; @@ -39,7 +39,7 @@ public Resource Detect() AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "Windows"); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, GetWindowsVersion()); break; -#if !NETFRAMEWORK +#if NET case OperatingSystemsValues.Linux: AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, GetLinuxBuildId()); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, GetLinuxDistributionName()); @@ -100,19 +100,15 @@ private static void AddAttributeIfNotNullOrEmpty(List Date: Tue, 6 Aug 2024 09:45:17 +0200 Subject: [PATCH 09/26] Refactor: reduce process invocations --- .../OperatingSystemDetector.cs | 194 +++++------------- 1 file changed, 52 insertions(+), 142 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 51fffb49a2..6e28db301d 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -4,8 +4,8 @@ #if NET using System.Diagnostics; using System.Runtime.InteropServices; - #endif + using static OpenTelemetry.Resources.OperatingSystem.OperatingSystemSemanticConventions; namespace OpenTelemetry.Resources.OperatingSystem; @@ -15,10 +15,13 @@ namespace OpenTelemetry.Resources.OperatingSystem; /// internal sealed class OperatingSystemDetector : IResourceDetector { + internal static readonly char[] Separator = new[] { '\n', '\r' }; + /// /// Detects the resource attributes from the operating system. /// /// Resource with key-value pairs of resource attributes. + /// public Resource Detect() { var attributes = new List>(); @@ -35,20 +38,14 @@ public Resource Detect() switch (osType) { case OperatingSystemsValues.Windows: - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, GetWindowsBuildId()); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "Windows"); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, GetWindowsVersion()); + AddWindowsAttributes(attributes); break; #if NET case OperatingSystemsValues.Linux: - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, GetLinuxBuildId()); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, GetLinuxDistributionName()); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, GetLinuxVersion()); + AddLinuxAttributes(attributes); break; case OperatingSystemsValues.Darwin: - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, GetMacOSBuildId()); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "MacOS"); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, GetMacOSVersion()); + AddMacOSAttributes(attributes); break; #endif } @@ -98,46 +95,55 @@ private static void AddAttributeIfNotNullOrEmpty(List> attributes) { try { var registryKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; - using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(registryKey)) { if (key != null) { - return key.GetValue("CurrentBuild")?.ToString(); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, key.GetValue("CurrentBuild")?.ToString()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "Windows"); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, key.GetValue("CurrentVersion")?.ToString()); } } } catch (Exception ex) { - OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get Windows build ID", ex); + OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get Windows attributes", ex); } - - return null; } #pragma warning restore CA1416 #if NET - private static string? GetLinuxBuildId() + private static void AddLinuxAttributes(List> attributes) { try { var osReleaseContent = File.ReadAllText("/etc/os-release"); + string? buildId = null, name = null, version = null; + foreach (var line in osReleaseContent.Split('\n')) { if (line.StartsWith("BUILD_ID=", StringComparison.Ordinal)) { - return line.Substring("BUILD_ID=".Length).Trim('"'); + buildId = line.Substring("BUILD_ID=".Length).Trim('"'); + } + else if (line.StartsWith("NAME=", StringComparison.Ordinal)) + { + name = line.Substring("NAME=".Length).Trim('"'); + } + else if (line.StartsWith("VERSION_ID=", StringComparison.Ordinal)) + { + version = line.Substring("VERSION_ID=".Length).Trim('"'); } } - using (var process = new Process()) + if (string.IsNullOrEmpty(buildId)) { - process.StartInfo = new ProcessStartInfo + var psi = new ProcessStartInfo { FileName = "uname", Arguments = "-r", @@ -146,46 +152,53 @@ private static void AddAttributeIfNotNullOrEmpty(List> attributes) { try { var psi = new ProcessStartInfo { - FileName = "sw_vers", - Arguments = "-buildVersion", + FileName = "sh", + Arguments = "-c \"sw_vers -productVersion && sw_vers -buildVersion\"", RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, }; - using (var process = Process.Start(psi)) + using var process = Process.Start(psi); + if (process != null) { - if (process != null) - { - var output = process.StandardOutput.ReadToEnd().Trim(); - return output; - } + string[] lines = process.StandardOutput.ReadToEnd().Trim().Split(Separator, StringSplitOptions.RemoveEmptyEntries); + var version = lines[0]; + var buildId = lines[1]; + + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "MacOS"); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, version); } } catch (Exception ex) { - OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get MacOS build ID", ex); + OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to add MacOS attributes", ex); } - - return null; } + #endif private static string GetOSDescription() @@ -196,107 +209,4 @@ private static string GetOSDescription() return Environment.OSVersion.ToString(); #endif } - - private static string GetLinuxDistributionName() - { - try - { - var osReleaseFile = "/etc/os-release"; - if (File.Exists(osReleaseFile)) - { - var lines = File.ReadAllLines(osReleaseFile); - foreach (var line in lines) - { - if (line.StartsWith("NAME=", StringComparison.Ordinal)) - { - return line.Substring("NAME=".Length).Trim('"'); - } - } - } - } - catch (Exception ex) - { - OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get Linux distribution name", ex); - } - - return "Linux"; - } - -#pragma warning disable CA1416 - private static string? GetWindowsVersion() - { - try - { - var registryKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; - using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(registryKey)) - { - if (key != null) - { - return key.GetValue("CurrentVersion")?.ToString(); - } - } - } - catch (Exception ex) - { - OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get Windows version", ex); - } - - return null; - } -#pragma warning restore CA1416 - -#if NET - private static string? GetMacOSVersion() - { - try - { - var psi = new ProcessStartInfo - { - FileName = "sw_vers", - Arguments = "-productVersion", - RedirectStandardOutput = true, - }; - using (var process = Process.Start(psi)) - { - if (process != null) - { - var output = process.StandardOutput.ReadToEnd().Trim(); - return output; - } - } - } - catch (Exception ex) - { - OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get MacOS version", ex); - } - - return null; - } - - private static string? GetLinuxVersion() - { - try - { - var osReleaseFile = "/etc/os-release"; - if (File.Exists(osReleaseFile)) - { - var lines = File.ReadAllLines(osReleaseFile); - foreach (var line in lines) - { - if (line.StartsWith("VERSION_ID=", StringComparison.Ordinal)) - { - return line.Substring("VERSION_ID=".Length).Trim('"'); - } - } - } - } - catch (Exception ex) - { - OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get Linux version", ex); - } - - return null; - } - -#endif } From 6d54ee2ff6a5b2f9114782defd490ef6ded67300 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Mon, 12 Aug 2024 12:14:02 +0200 Subject: [PATCH 10/26] wp --- .../OperatingSystemDetector.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 6e28db301d..4804b23305 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -15,8 +15,6 @@ namespace OpenTelemetry.Resources.OperatingSystem; /// internal sealed class OperatingSystemDetector : IResourceDetector { - internal static readonly char[] Separator = new[] { '\n', '\r' }; - /// /// Detects the resource attributes from the operating system. /// @@ -176,7 +174,7 @@ private static void AddMacOSAttributes(List> attrib var psi = new ProcessStartInfo { FileName = "sh", - Arguments = "-c \"sw_vers -productVersion && sw_vers -buildVersion\"", + Arguments = "-c \"sw_vers\"", RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true, @@ -184,9 +182,11 @@ private static void AddMacOSAttributes(List> attrib using var process = Process.Start(psi); if (process != null) { - string[] lines = process.StandardOutput.ReadToEnd().Trim().Split(Separator, StringSplitOptions.RemoveEmptyEntries); - var version = lines[0]; - var buildId = lines[1]; + string output = process.StandardOutput.ReadToEnd().Trim(); + string[] lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries); + + string? version = lines.FirstOrDefault(line => line.StartsWith("ProductVersion:", StringComparison.Ordinal))?.Split(':').Last().Trim(); + string? buildId = lines.FirstOrDefault(line => line.StartsWith("BuildVersion:", StringComparison.Ordinal))?.Split(':').Last().Trim(); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "MacOS"); @@ -195,7 +195,7 @@ private static void AddMacOSAttributes(List> attrib } catch (Exception ex) { - OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to add MacOS attributes", ex); + OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get MacOS attributes", ex); } } From 3393bb22fe03fafb0f75bcafa227d54d55e42131 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Tue, 13 Aug 2024 09:25:28 +0200 Subject: [PATCH 11/26] update os.name to get from product name --- .../OperatingSystemDetector.cs | 5 +++-- .../OperatingSystemDetectorTests.cs | 7 +------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 4804b23305..1296e8c515 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -103,7 +103,7 @@ private static void AddWindowsAttributes(List> attr if (key != null) { AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, key.GetValue("CurrentBuild")?.ToString()); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "Windows"); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, key.GetValue("ProductName")?.ToString()); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, key.GetValue("CurrentVersion")?.ToString()); } } @@ -186,10 +186,11 @@ private static void AddMacOSAttributes(List> attrib string[] lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries); string? version = lines.FirstOrDefault(line => line.StartsWith("ProductVersion:", StringComparison.Ordinal))?.Split(':').Last().Trim(); + string? name = lines.FirstOrDefault(line => line.StartsWith("ProductName:", StringComparison.Ordinal))?.Split(':').Last().Trim(); string? buildId = lines.FirstOrDefault(line => line.StartsWith("BuildVersion:", StringComparison.Ordinal))?.Split(':').Last().Trim(); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, "MacOS"); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, name); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, version); } } diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs index 7670746ba2..32ebfeb46f 100644 --- a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs @@ -16,25 +16,21 @@ public void TestOperatingSystemAttributes() string expectedPlatform; string expectedDescription; - string expectedName; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { expectedPlatform = OperatingSystemSemanticConventions.OperatingSystemsValues.Windows; expectedDescription = "Windows"; - expectedName = "Windows"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { expectedPlatform = OperatingSystemSemanticConventions.OperatingSystemsValues.Linux; expectedDescription = "Linux"; - expectedName = "Linux"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { expectedPlatform = OperatingSystemSemanticConventions.OperatingSystemsValues.Darwin; expectedDescription = "Darwin"; - expectedName = "MacOS"; } else { @@ -48,10 +44,9 @@ public void TestOperatingSystemAttributes() Assert.True(resourceAttributes.ContainsKey(OperatingSystemSemanticConventions.AttributeOperatingSystemBuildId)); Assert.True(resourceAttributes.ContainsKey(OperatingSystemSemanticConventions.AttributeOperatingSystemVersion)); - if (expectedName != "Linux") + if (expectedDescription != "Linux") { Assert.Contains(expectedDescription, resourceAttributes[OperatingSystemSemanticConventions.AttributeOperatingSystemDescription]); - Assert.Equal(expectedName, resourceAttributes[OperatingSystemSemanticConventions.AttributeOperatingSystemName]); } Assert.Equal(expectedPlatform, resourceAttributes[OperatingSystemSemanticConventions.AttributeOperatingSystemType]); From a243e48c4291321c21d5494bfcee7eadac28dc1f Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Tue, 20 Aug 2024 12:34:30 +0200 Subject: [PATCH 12/26] Update test --- .../OperatingSystemDetectorTests.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs index 32ebfeb46f..284efbb529 100644 --- a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs @@ -38,12 +38,13 @@ public void TestOperatingSystemAttributes() } Assert.Equal(5, resourceAttributes.Count); - Assert.True(resourceAttributes.ContainsKey(OperatingSystemSemanticConventions.AttributeOperatingSystemDescription)); - Assert.True(resourceAttributes.ContainsKey(OperatingSystemSemanticConventions.AttributeOperatingSystemName)); - Assert.True(resourceAttributes.ContainsKey(OperatingSystemSemanticConventions.AttributeOperatingSystemType)); - Assert.True(resourceAttributes.ContainsKey(OperatingSystemSemanticConventions.AttributeOperatingSystemBuildId)); - Assert.True(resourceAttributes.ContainsKey(OperatingSystemSemanticConventions.AttributeOperatingSystemVersion)); + Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemDescription, resourceAttributes.Keys); + Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemName, resourceAttributes.Keys); + Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemType, resourceAttributes.Keys); + Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemBuildId, resourceAttributes.Keys); + Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemVersion, resourceAttributes.Keys); + // Not checking on Linux because the description may vary depending on the distribution. if (expectedDescription != "Linux") { Assert.Contains(expectedDescription, resourceAttributes[OperatingSystemSemanticConventions.AttributeOperatingSystemDescription]); From 461b19077cc1712ba4f7388521a06c1255636ca9 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Tue, 20 Aug 2024 13:22:57 +0200 Subject: [PATCH 13/26] add WaitForExit --- .../OperatingSystemDetector.cs | 14 +++++++------- .../OperatingSystemResourcesEventSource.cs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 1296e8c515..452151eeb2 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -98,14 +98,12 @@ private static void AddWindowsAttributes(List> attr try { var registryKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; - using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(registryKey)) + using var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(registryKey); + if (key != null) { - if (key != null) - { - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, key.GetValue("CurrentBuild")?.ToString()); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, key.GetValue("ProductName")?.ToString()); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, key.GetValue("CurrentVersion")?.ToString()); - } + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, key.GetValue("CurrentBuild")?.ToString()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, key.GetValue("ProductName")?.ToString()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, key.GetValue("CurrentVersion")?.ToString()); } } catch (Exception ex) @@ -151,6 +149,7 @@ private static void AddLinuxAttributes(List> attrib }; using var process = Process.Start(psi); + process?.WaitForExit(5000); if (process != null) { buildId = process.StandardOutput.ReadToEnd().Trim(); @@ -180,6 +179,7 @@ private static void AddMacOSAttributes(List> attrib CreateNoWindow = true, }; using var process = Process.Start(psi); + process?.WaitForExit(5000); if (process != null) { string output = process.StandardOutput.ReadToEnd().Trim(); diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs index 24ae7ec6b5..657c2df060 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs @@ -17,7 +17,7 @@ internal sealed class OperatingSystemResourcesEventSource : EventSource [NonEvent] public void ResourceAttributesExtractException(string format, Exception ex) { - if (this.IsEnabled(EventLevel.Warning, (EventKeywords)(-1))) + if (this.IsEnabled(EventLevel.Warning, EventKeywords.All)) { this.FailedToExtractResourceAttributes(format, ex.ToInvariantString()); } From 0594c723fbed74def1b2a510d70a3769269fc908 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Tue, 20 Aug 2024 14:04:03 +0200 Subject: [PATCH 14/26] log error when process timeout --- .../OperatingSystemDetector.cs | 36 +++++++++++++------ .../OperatingSystemResourcesEventSource.cs | 7 ++++ 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 452151eeb2..2086fb941f 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -149,10 +149,17 @@ private static void AddLinuxAttributes(List> attrib }; using var process = Process.Start(psi); - process?.WaitForExit(5000); if (process != null) { - buildId = process.StandardOutput.ReadToEnd().Trim(); + var isExited = process.WaitForExit(5000); + if (isExited) + { + buildId = process.StandardOutput.ReadToEnd().Trim(); + } + else + { + OperatingSystemResourcesEventSource.Log.ProcessTimeout("Process did not exit within the given timeout."); + } } } @@ -179,19 +186,26 @@ private static void AddMacOSAttributes(List> attrib CreateNoWindow = true, }; using var process = Process.Start(psi); - process?.WaitForExit(5000); if (process != null) { - string output = process.StandardOutput.ReadToEnd().Trim(); - string[] lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries); + var isExited = process.WaitForExit(5000); + if (isExited) + { + string output = process.StandardOutput.ReadToEnd().Trim(); + string[] lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries); - string? version = lines.FirstOrDefault(line => line.StartsWith("ProductVersion:", StringComparison.Ordinal))?.Split(':').Last().Trim(); - string? name = lines.FirstOrDefault(line => line.StartsWith("ProductName:", StringComparison.Ordinal))?.Split(':').Last().Trim(); - string? buildId = lines.FirstOrDefault(line => line.StartsWith("BuildVersion:", StringComparison.Ordinal))?.Split(':').Last().Trim(); + string? version = lines.FirstOrDefault(line => line.StartsWith("ProductVersion:", StringComparison.Ordinal))?.Split(':').Last().Trim(); + string? name = lines.FirstOrDefault(line => line.StartsWith("ProductName:", StringComparison.Ordinal))?.Split(':').Last().Trim(); + string? buildId = lines.FirstOrDefault(line => line.StartsWith("BuildVersion:", StringComparison.Ordinal))?.Split(':').Last().Trim(); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, name); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, version); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, name); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, version); + } + else + { + OperatingSystemResourcesEventSource.Log.ProcessTimeout("Process did not exit within the given timeout."); + } } } catch (Exception ex) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs index 657c2df060..c333cdfb2f 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs @@ -13,6 +13,7 @@ internal sealed class OperatingSystemResourcesEventSource : EventSource private const int EventIdFailedToExtractAttributes = 1; private const int EventIdFailedToValidateValue = 2; + private const int EventIdProcessTimeout = 3; [NonEvent] public void ResourceAttributesExtractException(string format, Exception ex) @@ -34,4 +35,10 @@ public void FailedToValidateValue(string error) { this.WriteEvent(EventIdFailedToValidateValue, error); } + + [Event(EventIdProcessTimeout, Message = "Process timeout occurred: '{0}'", Level = EventLevel.Warning)] + public void ProcessTimeout(string error) + { + this.WriteEvent(EventIdProcessTimeout, error); + } } From 5ded1abe55a7668a7eec9aba1c9903d15d7444a5 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Wed, 21 Aug 2024 09:42:23 +0200 Subject: [PATCH 15/26] remove processes /for mac read info from file --- .../OperatingSystemDetector.cs | 80 ++++++++----------- .../OperatingSystemResourcesEventSource.cs | 8 +- .../OperatingSystemDetectorTests.cs | 2 +- 3 files changed, 37 insertions(+), 53 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 2086fb941f..05ee3b7383 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 #if NET -using System.Diagnostics; using System.Runtime.InteropServices; +using System.Xml.Linq; #endif using static OpenTelemetry.Resources.OperatingSystem.OperatingSystemSemanticConventions; @@ -137,31 +137,7 @@ private static void AddLinuxAttributes(List> attrib } } - if (string.IsNullOrEmpty(buildId)) - { - var psi = new ProcessStartInfo - { - FileName = "uname", - Arguments = "-r", - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true, - }; - - using var process = Process.Start(psi); - if (process != null) - { - var isExited = process.WaitForExit(5000); - if (isExited) - { - buildId = process.StandardOutput.ReadToEnd().Trim(); - } - else - { - OperatingSystemResourcesEventSource.Log.ProcessTimeout("Process did not exit within the given timeout."); - } - } - } + // TODO: fallback for buildId AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, name ?? "Linux"); @@ -177,36 +153,44 @@ private static void AddMacOSAttributes(List> attrib { try { - var psi = new ProcessStartInfo + var possibleFiles = new[] { - FileName = "sh", - Arguments = "-c \"sw_vers\"", - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true, + "/System/Library/CoreServices/SystemVersion.plist", + "/System/Library/CoreServices/ServerVersion.plist", }; - using var process = Process.Start(psi); - if (process != null) + + string? plistFilePath = possibleFiles.FirstOrDefault(File.Exists); + if (string.IsNullOrEmpty(plistFilePath)) { - var isExited = process.WaitForExit(5000); - if (isExited) - { - string output = process.StandardOutput.ReadToEnd().Trim(); - string[] lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries); + OperatingSystemResourcesEventSource.Log.FailedToFindFile("No suitable plist file found"); + return; + } - string? version = lines.FirstOrDefault(line => line.StartsWith("ProductVersion:", StringComparison.Ordinal))?.Split(':').Last().Trim(); - string? name = lines.FirstOrDefault(line => line.StartsWith("ProductName:", StringComparison.Ordinal))?.Split(':').Last().Trim(); - string? buildId = lines.FirstOrDefault(line => line.StartsWith("BuildVersion:", StringComparison.Ordinal))?.Split(':').Last().Trim(); + var properties = new Dictionary(); + XDocument doc = XDocument.Load(plistFilePath); + var dict = doc.Root?.Element("dict"); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, name); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, version); - } - else + if (dict != null) + { + var keys = dict.Elements("key").ToList(); + var values = dict.Elements("string").ToList(); + + if (keys.Count == values.Count) { - OperatingSystemResourcesEventSource.Log.ProcessTimeout("Process did not exit within the given timeout."); + for (int i = 0; i < keys.Count; i++) + { + properties[keys[i].Value] = values[i].Value; + } } } + + string? name = properties.GetValueOrDefault("ProductName"); + string? version = properties.GetValueOrDefault("ProductVersion"); + string? buildId = properties.GetValueOrDefault("ProductBuildVersion"); + + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, name); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, version); } catch (Exception ex) { diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs index c333cdfb2f..12b82ec7aa 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemResourcesEventSource.cs @@ -13,7 +13,7 @@ internal sealed class OperatingSystemResourcesEventSource : EventSource private const int EventIdFailedToExtractAttributes = 1; private const int EventIdFailedToValidateValue = 2; - private const int EventIdProcessTimeout = 3; + private const int EventIdFailedToFindFile = 3; [NonEvent] public void ResourceAttributesExtractException(string format, Exception ex) @@ -36,9 +36,9 @@ public void FailedToValidateValue(string error) this.WriteEvent(EventIdFailedToValidateValue, error); } - [Event(EventIdProcessTimeout, Message = "Process timeout occurred: '{0}'", Level = EventLevel.Warning)] - public void ProcessTimeout(string error) + [Event(EventIdFailedToFindFile, Message = "Process timeout occurred: '{0}'", Level = EventLevel.Warning)] + public void FailedToFindFile(string error) { - this.WriteEvent(EventIdProcessTimeout, error); + this.WriteEvent(EventIdFailedToFindFile, error); } } diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs index 284efbb529..f07931fb21 100644 --- a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs @@ -38,7 +38,6 @@ public void TestOperatingSystemAttributes() } Assert.Equal(5, resourceAttributes.Count); - Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemDescription, resourceAttributes.Keys); Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemName, resourceAttributes.Keys); Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemType, resourceAttributes.Keys); Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemBuildId, resourceAttributes.Keys); @@ -47,6 +46,7 @@ public void TestOperatingSystemAttributes() // Not checking on Linux because the description may vary depending on the distribution. if (expectedDescription != "Linux") { + Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemDescription, resourceAttributes.Keys); Assert.Contains(expectedDescription, resourceAttributes[OperatingSystemSemanticConventions.AttributeOperatingSystemDescription]); } From 968863fc32b630c4144061685a52584210cb1a7a Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Wed, 21 Aug 2024 10:37:59 +0200 Subject: [PATCH 16/26] fix test --- .../OperatingSystemDetectorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs index f07931fb21..441d262275 100644 --- a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs @@ -37,7 +37,6 @@ public void TestOperatingSystemAttributes() throw new PlatformNotSupportedException("Unknown platform"); } - Assert.Equal(5, resourceAttributes.Count); Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemName, resourceAttributes.Keys); Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemType, resourceAttributes.Keys); Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemBuildId, resourceAttributes.Keys); @@ -48,6 +47,7 @@ public void TestOperatingSystemAttributes() { Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemDescription, resourceAttributes.Keys); Assert.Contains(expectedDescription, resourceAttributes[OperatingSystemSemanticConventions.AttributeOperatingSystemDescription]); + Assert.Equal(5, resourceAttributes.Count); } Assert.Equal(expectedPlatform, resourceAttributes[OperatingSystemSemanticConventions.AttributeOperatingSystemType]); From b4ae4c1a3f88e388234d4f19e734ea12c2a5eb1a Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Wed, 21 Aug 2024 10:50:13 +0200 Subject: [PATCH 17/26] fix tests --- .../OperatingSystemDetectorTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs index 441d262275..d098cc4f57 100644 --- a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs @@ -37,15 +37,15 @@ public void TestOperatingSystemAttributes() throw new PlatformNotSupportedException("Unknown platform"); } + Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemDescription, resourceAttributes.Keys); Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemName, resourceAttributes.Keys); Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemType, resourceAttributes.Keys); - Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemBuildId, resourceAttributes.Keys); Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemVersion, resourceAttributes.Keys); // Not checking on Linux because the description may vary depending on the distribution. if (expectedDescription != "Linux") { - Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemDescription, resourceAttributes.Keys); + Assert.Contains(OperatingSystemSemanticConventions.AttributeOperatingSystemBuildId, resourceAttributes.Keys); Assert.Contains(expectedDescription, resourceAttributes[OperatingSystemSemanticConventions.AttributeOperatingSystemDescription]); Assert.Equal(5, resourceAttributes.Count); } From c3f6a7ae2da62964a4d3d67205752d10701bdfea Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Wed, 21 Aug 2024 11:07:47 +0200 Subject: [PATCH 18/26] ReadAllLines --- .../OperatingSystemDetector.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 05ee3b7383..699a9038ec 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -118,10 +118,10 @@ private static void AddLinuxAttributes(List> attrib { try { - var osReleaseContent = File.ReadAllText("/etc/os-release"); + var osReleaseContent = File.ReadAllLines("/etc/os-release"); string? buildId = null, name = null, version = null; - foreach (var line in osReleaseContent.Split('\n')) + foreach (var line in osReleaseContent) { if (line.StartsWith("BUILD_ID=", StringComparison.Ordinal)) { From 45febc8f07ca93b5515552cf8278cbf80a3a2f2f Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Thu, 22 Aug 2024 10:02:09 +0200 Subject: [PATCH 19/26] refactor --- .../OperatingSystemDetector.cs | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 699a9038ec..2c9508e10a 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -22,7 +22,7 @@ internal sealed class OperatingSystemDetector : IResourceDetector /// public Resource Detect() { - var attributes = new List>(); + var attributes = new List>(5); var osType = GetOSType(); if (osType == null) { @@ -101,7 +101,7 @@ private static void AddWindowsAttributes(List> attr using var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(registryKey); if (key != null) { - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, key.GetValue("CurrentBuild")?.ToString()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, key.GetValue("CurrentBuildNumber")?.ToString()); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, key.GetValue("ProductName")?.ToString()); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, key.GetValue("CurrentVersion")?.ToString()); } @@ -119,6 +119,12 @@ private static void AddLinuxAttributes(List> attrib try { var osReleaseContent = File.ReadAllLines("/etc/os-release"); + if (osReleaseContent == null) + { + OperatingSystemResourcesEventSource.Log.FailedToFindFile("No suitable plist file found"); + return; + } + string? buildId = null, name = null, version = null; foreach (var line in osReleaseContent) @@ -166,28 +172,40 @@ private static void AddMacOSAttributes(List> attrib return; } - var properties = new Dictionary(); XDocument doc = XDocument.Load(plistFilePath); var dict = doc.Root?.Element("dict"); + string? buildId = null, name = null, version = null; if (dict != null) { - var keys = dict.Elements("key").ToList(); - var values = dict.Elements("string").ToList(); + string? currentKey = null; - if (keys.Count == values.Count) + foreach (var element in dict.Elements()) { - for (int i = 0; i < keys.Count; i++) + if (element.Name == "key") + { + currentKey = element.Value; + } + else if (element.Name == "string" && currentKey != null) { - properties[keys[i].Value] = values[i].Value; + if (currentKey == "ProductName") + { + name = element.Value; + } + else if (currentKey == "ProductVersion") + { + version = element.Value; + } + else if (currentKey == "ProductBuildVersion") + { + buildId = element.Value; + } + + currentKey = null; } } } - string? name = properties.GetValueOrDefault("ProductName"); - string? version = properties.GetValueOrDefault("ProductVersion"); - string? buildId = properties.GetValueOrDefault("ProductBuildVersion"); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, name); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, version); From 0387c53b1f2327925c2d27d00684fa9dbba14bbc Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Fri, 23 Aug 2024 11:34:17 +0200 Subject: [PATCH 20/26] Add parse methods and corresponding tests --- .../OperatingSystemDetector.cs | 99 ++++++++++--------- .../OperatingSystemDetectorTests.cs | 69 +++++++++++++ 2 files changed, 119 insertions(+), 49 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 2c9508e10a..71050d6a0d 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -51,6 +51,43 @@ public Resource Detect() return new Resource(attributes); } +#if NET + internal static string? GetOsReleaseValue(string[] osReleaseContent, string prefix) + { + foreach (var line in osReleaseContent) + { + if (line.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + { + var fieldValue = line.Substring(prefix.Length); + + // Remove enclosing quotes. + if (fieldValue.Length >= 2 && + (fieldValue[0] == '"' || fieldValue[0] == '\'') && + fieldValue[0] == fieldValue[^1]) + { + fieldValue = fieldValue[1..^1]; + } + + return fieldValue; + } + } + + return null; + } + + internal static string? GetPlistValue(XElement dict, string key) + { + var keyElement = dict.Elements("key").FirstOrDefault(e => e.Value == key); + if (keyElement != null) + { + var valueElement = keyElement.ElementsAfterSelf("string").FirstOrDefault(); + return valueElement?.Value; + } + + return null; + } +#endif + private static void AddAttributeIfNotNullOrEmpty(List> attributes, string key, object? value) { if (value == null) @@ -119,34 +156,20 @@ private static void AddLinuxAttributes(List> attrib try { var osReleaseContent = File.ReadAllLines("/etc/os-release"); - if (osReleaseContent == null) + if (osReleaseContent == null || osReleaseContent.Length == 0) { - OperatingSystemResourcesEventSource.Log.FailedToFindFile("No suitable plist file found"); + OperatingSystemResourcesEventSource.Log.FailedToFindFile("Failed to find or read the os-release file"); return; } - string? buildId = null, name = null, version = null; - - foreach (var line in osReleaseContent) - { - if (line.StartsWith("BUILD_ID=", StringComparison.Ordinal)) - { - buildId = line.Substring("BUILD_ID=".Length).Trim('"'); - } - else if (line.StartsWith("NAME=", StringComparison.Ordinal)) - { - name = line.Substring("NAME=".Length).Trim('"'); - } - else if (line.StartsWith("VERSION_ID=", StringComparison.Ordinal)) - { - version = line.Substring("VERSION_ID=".Length).Trim('"'); - } - } + string? name = GetOsReleaseValue(osReleaseContent, "NAME=") ?? "Linux"; + string? version = GetOsReleaseValue(osReleaseContent, "VERSION_ID="); + string? buildId = GetOsReleaseValue(osReleaseContent, "BUILD_ID="); // TODO: fallback for buildId AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, name ?? "Linux"); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, name); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, version); } catch (Exception ex) @@ -174,38 +197,17 @@ private static void AddMacOSAttributes(List> attrib XDocument doc = XDocument.Load(plistFilePath); var dict = doc.Root?.Element("dict"); - string? buildId = null, name = null, version = null; - if (dict != null) + if (dict == null) { - string? currentKey = null; - - foreach (var element in dict.Elements()) - { - if (element.Name == "key") - { - currentKey = element.Value; - } - else if (element.Name == "string" && currentKey != null) - { - if (currentKey == "ProductName") - { - name = element.Value; - } - else if (currentKey == "ProductVersion") - { - version = element.Value; - } - else if (currentKey == "ProductBuildVersion") - { - buildId = element.Value; - } - - currentKey = null; - } - } + OperatingSystemResourcesEventSource.Log.FailedToFindFile("No element found in plist file"); + return; } + string? name = GetPlistValue(dict, "ProductName"); + string? version = GetPlistValue(dict, "ProductVersion"); + string? buildId = GetPlistValue(dict, "ProductBuildVersion"); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, name); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, version); @@ -215,7 +217,6 @@ private static void AddMacOSAttributes(List> attrib OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get MacOS attributes", ex); } } - #endif private static string GetOSDescription() diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs index d098cc4f57..a0c6cb3014 100644 --- a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs @@ -1,6 +1,9 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#if NET +using System.Xml.Linq; +#endif using System.Runtime.InteropServices; using Xunit; @@ -8,6 +11,37 @@ namespace OpenTelemetry.Resources.OperatingSystem.Test; public class OperatingSystemDetectorTests { +#if NET + public const string MacOSPlist = @" + + + + ProductBuildVersion + 10K549 + ProductCopyright + 1983-2011 Apple Inc. + ProductName + Mac OS X + ProductUserVisibleVersion + 10.6.8 + ProductVersion + 10.6.8 + + "; + + public const string LinuxOsRelease = @"NAME=Ubuntu + VERSION=""22.04 LTS (Jammy Jellyfish)"" + VERSION_ID=""22.04"" + VERSION_CODENAME=jammy + ID=ubuntu + HOME_URL=https://www.ubuntu.com/ + SUPPORT_URL=https://help.ubuntu.com/ + BUG_REPORT_URL=https://bugs.launchpad.net/ubuntu + PRIVACY_POLICY_URL=https://www.ubuntu.com/legal/terms-and-policies/privacy-policy + UBUNTU_CODENAME=jammy + BUILD_ID=20240823"; +#endif + [Fact] public void TestOperatingSystemAttributes() { @@ -52,4 +86,39 @@ public void TestOperatingSystemAttributes() Assert.Equal(expectedPlatform, resourceAttributes[OperatingSystemSemanticConventions.AttributeOperatingSystemType]); } + +#if NET + [Fact] + public void TestParseMacOSPlist() + { + XElement dict = XElement.Parse(MacOSPlist).Element("dict")!; + + string? name = OperatingSystemDetector.GetPlistValue(dict, "ProductName"); + string? version = OperatingSystemDetector.GetPlistValue(dict, "ProductVersion"); + string? buildId = OperatingSystemDetector.GetPlistValue(dict, "ProductBuildVersion"); + + Assert.Equal("Mac OS X", name); + Assert.Equal("10.6.8", version); + Assert.Equal("10K549", buildId); + } + + [Fact] + public void TestParseLinuxOsRelease() + { + string[] osReleaseContent = LinuxOsRelease + .Split('\n') + .Select(line => line.Trim()) + .Where(line => !string.IsNullOrWhiteSpace(line)) + .ToArray(); + + string? name = OperatingSystemDetector.GetOsReleaseValue(osReleaseContent, "NAME=") ?? "Linux"; + string? version = OperatingSystemDetector.GetOsReleaseValue(osReleaseContent, "VERSION_ID="); + string? buildId = OperatingSystemDetector.GetOsReleaseValue(osReleaseContent, "BUILD_ID="); + + Assert.Equal("Ubuntu", name); + Assert.Equal("22.04", version); + Assert.Equal("20240823", buildId); + } + +#endif } From dcda373d5b2162164c9aab91be4f85cd240480cd Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Fri, 23 Aug 2024 17:34:57 +0200 Subject: [PATCH 21/26] refactor/add samples --- .../OperatingSystemDetector.cs | 166 ++++++++++-------- .../OperatingSystemDetectorTests.cs | 77 +++----- .../Samples/SystemVersion.plist | 16 ++ .../Samples/os-release | 11 ++ 4 files changed, 135 insertions(+), 135 deletions(-) create mode 100644 test/OpenTelemetry.Resources.OperatingSystem.Tests/Samples/SystemVersion.plist create mode 100644 test/OpenTelemetry.Resources.OperatingSystem.Tests/Samples/os-release diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 71050d6a0d..d618720abb 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -20,6 +20,10 @@ internal sealed class OperatingSystemDetector : IResourceDetector /// /// Resource with key-value pairs of resource attributes. /// +#if NET + private const string EtcOsReleasePath = "/etc/os-release"; +#endif + public Resource Detect() { var attributes = new List>(5); @@ -51,61 +55,7 @@ public Resource Detect() return new Resource(attributes); } -#if NET - internal static string? GetOsReleaseValue(string[] osReleaseContent, string prefix) - { - foreach (var line in osReleaseContent) - { - if (line.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - var fieldValue = line.Substring(prefix.Length); - - // Remove enclosing quotes. - if (fieldValue.Length >= 2 && - (fieldValue[0] == '"' || fieldValue[0] == '\'') && - fieldValue[0] == fieldValue[^1]) - { - fieldValue = fieldValue[1..^1]; - } - - return fieldValue; - } - } - - return null; - } - - internal static string? GetPlistValue(XElement dict, string key) - { - var keyElement = dict.Elements("key").FirstOrDefault(e => e.Value == key); - if (keyElement != null) - { - var valueElement = keyElement.ElementsAfterSelf("string").FirstOrDefault(); - return valueElement?.Value; - } - - return null; - } -#endif - - private static void AddAttributeIfNotNullOrEmpty(List> attributes, string key, object? value) - { - if (value == null) - { - OperatingSystemResourcesEventSource.Log.FailedToValidateValue("The provided value is null"); - return; - } - - if (value is string strValue && string.IsNullOrEmpty(strValue)) - { - OperatingSystemResourcesEventSource.Log.FailedToValidateValue("The provided value string is empty."); - return; - } - - attributes.Add(new KeyValuePair(key, value!)); - } - - private static string? GetOSType() + internal static string? GetOSType() { #if NETFRAMEWORK return OperatingSystemsValues.Windows; @@ -130,7 +80,7 @@ private static void AddAttributeIfNotNullOrEmpty(List> attributes) + internal static void AddWindowsAttributes(List> attributes) { try { @@ -151,44 +101,72 @@ private static void AddWindowsAttributes(List> attr #pragma warning restore CA1416 #if NET - private static void AddLinuxAttributes(List> attributes) + internal static void AddLinuxAttributes(List> attributes, string etcOsReleasePath = EtcOsReleasePath) { try { - var osReleaseContent = File.ReadAllLines("/etc/os-release"); - if (osReleaseContent == null || osReleaseContent.Length == 0) + if (!File.Exists(etcOsReleasePath)) { OperatingSystemResourcesEventSource.Log.FailedToFindFile("Failed to find or read the os-release file"); return; } - string? name = GetOsReleaseValue(osReleaseContent, "NAME=") ?? "Linux"; - string? version = GetOsReleaseValue(osReleaseContent, "VERSION_ID="); - string? buildId = GetOsReleaseValue(osReleaseContent, "BUILD_ID="); + var osReleaseContent = File.ReadAllLines(etcOsReleasePath); + ReadOnlySpan buildId = default, name = default, version = default; + + foreach (var line in osReleaseContent) + { + ReadOnlySpan lineSpan = line.AsSpan(); + + _ = TryGetFieldValue(lineSpan, "BUILD_ID=", ref buildId) || + TryGetFieldValue(lineSpan, "NAME=", ref name) || + TryGetFieldValue(lineSpan, "VERSION_ID=", ref version); + } // TODO: fallback for buildId - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, name); - AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, version); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId.IsEmpty ? null : buildId.ToString()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, name.IsEmpty ? "Linux" : name.ToString()); + AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemVersion, version.IsEmpty ? null : version.ToString()); } catch (Exception ex) { OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get Linux attributes", ex); } + + static bool TryGetFieldValue(ReadOnlySpan line, ReadOnlySpan prefix, ref ReadOnlySpan value) + { + if (!line.StartsWith(prefix)) + { + return false; + } + + ReadOnlySpan fieldValue = line.Slice(prefix.Length); + + // Remove enclosing quotes if present. + if (fieldValue.Length >= 2 && + (fieldValue[0] == '"' || fieldValue[0] == '\'') && + fieldValue[0] == fieldValue[^1]) + { + fieldValue = fieldValue[1..^1]; + } + + value = fieldValue; + return true; + } } - private static void AddMacOSAttributes(List> attributes) + internal static void AddMacOSAttributes(List> attributes, string[]? plistFilePaths = null) { try { - var possibleFiles = new[] + plistFilePaths ??= new[] { - "/System/Library/CoreServices/SystemVersion.plist", - "/System/Library/CoreServices/ServerVersion.plist", + "/System/Library/CoreServices/SystemVersion.plist", + "/System/Library/CoreServices/ServerVersion.plist", }; - string? plistFilePath = possibleFiles.FirstOrDefault(File.Exists); + string? plistFilePath = plistFilePaths.FirstOrDefault(File.Exists); if (string.IsNullOrEmpty(plistFilePath)) { OperatingSystemResourcesEventSource.Log.FailedToFindFile("No suitable plist file found"); @@ -198,15 +176,29 @@ private static void AddMacOSAttributes(List> attrib XDocument doc = XDocument.Load(plistFilePath); var dict = doc.Root?.Element("dict"); - if (dict == null) + string? buildId = null, name = null, version = null; + + if (dict != null) { - OperatingSystemResourcesEventSource.Log.FailedToFindFile("No element found in plist file"); - return; - } + var keys = dict.Elements("key").ToList(); + var values = dict.Elements("string").ToList(); - string? name = GetPlistValue(dict, "ProductName"); - string? version = GetPlistValue(dict, "ProductVersion"); - string? buildId = GetPlistValue(dict, "ProductBuildVersion"); + for (int i = 0; i < keys.Count; i++) + { + switch (keys[i].Value) + { + case "ProductBuildVersion": + buildId = values[i].Value; + break; + case "ProductName": + name = values[i].Value; + break; + case "ProductVersion": + version = values[i].Value; + break; + } + } + } AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, buildId); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemName, name); @@ -217,9 +209,10 @@ private static void AddMacOSAttributes(List> attrib OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get MacOS attributes", ex); } } + #endif - private static string GetOSDescription() + internal static string GetOSDescription() { #if NET return RuntimeInformation.OSDescription; @@ -227,4 +220,21 @@ private static string GetOSDescription() return Environment.OSVersion.ToString(); #endif } + + private static void AddAttributeIfNotNullOrEmpty(List> attributes, string key, object? value) + { + if (value == null) + { + OperatingSystemResourcesEventSource.Log.FailedToValidateValue("The provided value is null"); + return; + } + + if (value is string strValue && string.IsNullOrEmpty(strValue)) + { + OperatingSystemResourcesEventSource.Log.FailedToValidateValue("The provided value string is empty."); + return; + } + + attributes.Add(new KeyValuePair(key, value!)); + } } diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs index a0c6cb3014..c77dad5e8b 100644 --- a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs @@ -1,9 +1,6 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -#if NET -using System.Xml.Linq; -#endif using System.Runtime.InteropServices; using Xunit; @@ -11,37 +8,6 @@ namespace OpenTelemetry.Resources.OperatingSystem.Test; public class OperatingSystemDetectorTests { -#if NET - public const string MacOSPlist = @" - - - - ProductBuildVersion - 10K549 - ProductCopyright - 1983-2011 Apple Inc. - ProductName - Mac OS X - ProductUserVisibleVersion - 10.6.8 - ProductVersion - 10.6.8 - - "; - - public const string LinuxOsRelease = @"NAME=Ubuntu - VERSION=""22.04 LTS (Jammy Jellyfish)"" - VERSION_ID=""22.04"" - VERSION_CODENAME=jammy - ID=ubuntu - HOME_URL=https://www.ubuntu.com/ - SUPPORT_URL=https://help.ubuntu.com/ - BUG_REPORT_URL=https://bugs.launchpad.net/ubuntu - PRIVACY_POLICY_URL=https://www.ubuntu.com/legal/terms-and-policies/privacy-policy - UBUNTU_CODENAME=jammy - BUILD_ID=20240823"; -#endif - [Fact] public void TestOperatingSystemAttributes() { @@ -91,34 +57,31 @@ public void TestOperatingSystemAttributes() [Fact] public void TestParseMacOSPlist() { - XElement dict = XElement.Parse(MacOSPlist).Element("dict")!; - - string? name = OperatingSystemDetector.GetPlistValue(dict, "ProductName"); - string? version = OperatingSystemDetector.GetPlistValue(dict, "ProductVersion"); - string? buildId = OperatingSystemDetector.GetPlistValue(dict, "ProductBuildVersion"); - - Assert.Equal("Mac OS X", name); - Assert.Equal("10.6.8", version); - Assert.Equal("10K549", buildId); + List> resourceAttributes = new(); + string baseDirectory = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\")); + string path = Path.Combine(baseDirectory, "Samples", "SystemVersion.plist"); + OperatingSystemDetector.AddMacOSAttributes( + resourceAttributes, + [path]); + var attributes = resourceAttributes.ToDictionary(x => x.Key, x => (string)x.Value); + + Assert.Equal("Mac OS X", attributes[OperatingSystemSemanticConventions.AttributeOperatingSystemName]); + Assert.Equal("10.6.8", attributes[OperatingSystemSemanticConventions.AttributeOperatingSystemVersion]); + Assert.Equal("10K549", attributes[OperatingSystemSemanticConventions.AttributeOperatingSystemBuildId]); } [Fact] public void TestParseLinuxOsRelease() { - string[] osReleaseContent = LinuxOsRelease - .Split('\n') - .Select(line => line.Trim()) - .Where(line => !string.IsNullOrWhiteSpace(line)) - .ToArray(); - - string? name = OperatingSystemDetector.GetOsReleaseValue(osReleaseContent, "NAME=") ?? "Linux"; - string? version = OperatingSystemDetector.GetOsReleaseValue(osReleaseContent, "VERSION_ID="); - string? buildId = OperatingSystemDetector.GetOsReleaseValue(osReleaseContent, "BUILD_ID="); - - Assert.Equal("Ubuntu", name); - Assert.Equal("22.04", version); - Assert.Equal("20240823", buildId); + List> resourceAttributes = new(); + string baseDirectory = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\")); + string path = Path.Combine(baseDirectory, "Samples", "os-release"); + OperatingSystemDetector.AddLinuxAttributes(resourceAttributes, path); + var attributes = resourceAttributes.ToDictionary(x => x.Key, x => (string)x.Value); + + Assert.Equal("Ubuntu", attributes[OperatingSystemSemanticConventions.AttributeOperatingSystemName]); + Assert.Equal("22.04", attributes[OperatingSystemSemanticConventions.AttributeOperatingSystemVersion]); + Assert.Equal("20240823", attributes[OperatingSystemSemanticConventions.AttributeOperatingSystemBuildId]); } - #endif } diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/Samples/SystemVersion.plist b/test/OpenTelemetry.Resources.OperatingSystem.Tests/Samples/SystemVersion.plist new file mode 100644 index 0000000000..ef45504dcd --- /dev/null +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/Samples/SystemVersion.plist @@ -0,0 +1,16 @@ + + + + +ProductBuildVersion +10K549 +ProductCopyright +1983-2011 Apple Inc. +ProductName +Mac OS X +ProductUserVisibleVersion +10.6.8 +ProductVersion +10.6.8 + + diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/Samples/os-release b/test/OpenTelemetry.Resources.OperatingSystem.Tests/Samples/os-release new file mode 100644 index 0000000000..7cc37681e4 --- /dev/null +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/Samples/os-release @@ -0,0 +1,11 @@ +NAME=Ubuntu +VERSION="22.04 LTS (Jammy Jellyfish)" +VERSION_ID="22.04" +VERSION_CODENAME=jammy +ID=ubuntu +HOME_URL=https://www.ubuntu.com/ +SUPPORT_URL=https://help.ubuntu.com/ +BUG_REPORT_URL=https://bugs.launchpad.net/ubuntu +PRIVACY_POLICY_URL=https://www.ubuntu.com/legal/terms-and-policies/privacy-policy +UBUNTU_CODENAME=jammy +BUILD_ID=20240823 From 4e0ccf9b3d2419f63b7dbf303c980b85edf7ecfa Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Mon, 26 Aug 2024 09:41:49 +0200 Subject: [PATCH 22/26] fix tests/ refactor --- .../OperatingSystemDetector.cs | 18 +++++++++++------- ...etry.Resources.OperatingSystem.Tests.csproj | 9 +++++++++ .../OperatingSystemDetectorTests.cs | 6 ++---- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index d618720abb..84fc3fed8e 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -22,6 +22,11 @@ internal sealed class OperatingSystemDetector : IResourceDetector /// #if NET private const string EtcOsReleasePath = "/etc/os-release"; + private static readonly string[] DefaultPlistFilePaths = + [ + "/System/Library/CoreServices/SystemVersion.plist", + "/System/Library/CoreServices/ServerVersion.plist", + ]; #endif public Resource Detect() @@ -101,6 +106,8 @@ internal static void AddWindowsAttributes(List> att #pragma warning restore CA1416 #if NET + // based on: + // https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/Interop/Linux/os-release/Interop.OSReleaseFile.cs internal static void AddLinuxAttributes(List> attributes, string etcOsReleasePath = EtcOsReleasePath) { try @@ -156,16 +163,13 @@ static bool TryGetFieldValue(ReadOnlySpan line, ReadOnlySpan prefix, } } - internal static void AddMacOSAttributes(List> attributes, string[]? plistFilePaths = null) + internal static void AddMacOSAttributes( + List> attributes, + string[]? plistFilePaths = null) { try { - plistFilePaths ??= new[] - { - "/System/Library/CoreServices/SystemVersion.plist", - "/System/Library/CoreServices/ServerVersion.plist", - }; - + plistFilePaths ??= DefaultPlistFilePaths; string? plistFilePath = plistFilePaths.FirstOrDefault(File.Exists); if (string.IsNullOrEmpty(plistFilePath)) { diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OpenTelemetry.Resources.OperatingSystem.Tests.csproj b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OpenTelemetry.Resources.OperatingSystem.Tests.csproj index c5b25f22ea..b8c188d1d8 100644 --- a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OpenTelemetry.Resources.OperatingSystem.Tests.csproj +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OpenTelemetry.Resources.OperatingSystem.Tests.csproj @@ -11,4 +11,13 @@ + + + PreserveNewest + + + PreserveNewest + + + diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs index c77dad5e8b..5ed548f25c 100644 --- a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs @@ -58,8 +58,7 @@ public void TestOperatingSystemAttributes() public void TestParseMacOSPlist() { List> resourceAttributes = new(); - string baseDirectory = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\")); - string path = Path.Combine(baseDirectory, "Samples", "SystemVersion.plist"); + string path = "Samples/SystemVersion.plist"; OperatingSystemDetector.AddMacOSAttributes( resourceAttributes, [path]); @@ -74,8 +73,7 @@ public void TestParseMacOSPlist() public void TestParseLinuxOsRelease() { List> resourceAttributes = new(); - string baseDirectory = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\")); - string path = Path.Combine(baseDirectory, "Samples", "os-release"); + string path = "Samples/os-release"; OperatingSystemDetector.AddLinuxAttributes(resourceAttributes, path); var attributes = resourceAttributes.ToDictionary(x => x.Key, x => (string)x.Value); From d85d57e27f39c6ad349ae96301e7ad97878bb23d Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Tue, 27 Aug 2024 10:28:28 +0200 Subject: [PATCH 23/26] add ctor for testing --- .../OperatingSystemDetector.cs | 76 +++++++++++-------- .../OperatingSystemDetectorTests.cs | 18 +++-- 2 files changed, 57 insertions(+), 37 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 84fc3fed8e..0d1772a4a9 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -4,6 +4,7 @@ #if NET using System.Runtime.InteropServices; using System.Xml.Linq; + #endif using static OpenTelemetry.Resources.OperatingSystem.OperatingSystemSemanticConventions; @@ -13,46 +14,65 @@ namespace OpenTelemetry.Resources.OperatingSystem; /// /// Operating system detector. /// -internal sealed class OperatingSystemDetector : IResourceDetector +internal sealed class OperatingSystemDetector() : IResourceDetector { /// /// Detects the resource attributes from the operating system. /// /// Resource with key-value pairs of resource attributes. /// + private readonly string? registryKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; + private readonly string? osType = GetOSType(); #if NET - private const string EtcOsReleasePath = "/etc/os-release"; - private static readonly string[] DefaultPlistFilePaths = + private readonly string? etcOsReleasePath = "/etc/os-release"; + private readonly string[]? defaultPlistFilePaths = [ "/System/Library/CoreServices/SystemVersion.plist", "/System/Library/CoreServices/ServerVersion.plist", ]; #endif +#if NET + /// + /// Initializes a new instance of the class for testing. + /// + /// The target platform identifier, specifying the operating system type from SemanticConventions. + /// The string path in the Windows Registry to retrieve specific Windows attributes. + /// The string path to the file used to obtain Linux attributes. + /// An array of file paths used to retrieve MacOS attributes from plist files. + internal OperatingSystemDetector(string? osType, string? registryKey, string? etcOsReleasePath, string[]? defaultPlistFilePaths) + : this() + { + this.osType = osType; + this.registryKey = registryKey; + this.etcOsReleasePath = etcOsReleasePath; + this.defaultPlistFilePaths = defaultPlistFilePaths; + } +#endif + public Resource Detect() { var attributes = new List>(5); - var osType = GetOSType(); - if (osType == null) + if (this.osType == null) { return Resource.Empty; } - attributes.Add(new KeyValuePair(AttributeOperatingSystemType, osType)); + attributes.Add(new KeyValuePair(AttributeOperatingSystemType, this.osType)); AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemDescription, GetOSDescription()); - switch (osType) + switch (this.osType) { case OperatingSystemsValues.Windows: - AddWindowsAttributes(attributes); + this.AddWindowsAttributes(attributes); break; #if NET case OperatingSystemsValues.Linux: - AddLinuxAttributes(attributes); + this.AddLinuxAttributes(attributes); break; case OperatingSystemsValues.Darwin: - AddMacOSAttributes(attributes); + this.AddMacOSAttributes(attributes); break; #endif } @@ -84,13 +104,21 @@ public Resource Detect() #endif } + internal static string GetOSDescription() + { +#if NET + return RuntimeInformation.OSDescription; +#else + return Environment.OSVersion.ToString(); +#endif + } + #pragma warning disable CA1416 - internal static void AddWindowsAttributes(List> attributes) + internal void AddWindowsAttributes(List> attributes) { try { - var registryKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; - using var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(registryKey); + using var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(this.registryKey!); if (key != null) { AddAttributeIfNotNullOrEmpty(attributes, AttributeOperatingSystemBuildId, key.GetValue("CurrentBuildNumber")?.ToString()); @@ -108,17 +136,17 @@ internal static void AddWindowsAttributes(List> att #if NET // based on: // https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/Interop/Linux/os-release/Interop.OSReleaseFile.cs - internal static void AddLinuxAttributes(List> attributes, string etcOsReleasePath = EtcOsReleasePath) + internal void AddLinuxAttributes(List> attributes) { try { - if (!File.Exists(etcOsReleasePath)) + if (!File.Exists(this.etcOsReleasePath)) { OperatingSystemResourcesEventSource.Log.FailedToFindFile("Failed to find or read the os-release file"); return; } - var osReleaseContent = File.ReadAllLines(etcOsReleasePath); + var osReleaseContent = File.ReadAllLines(this.etcOsReleasePath); ReadOnlySpan buildId = default, name = default, version = default; foreach (var line in osReleaseContent) @@ -163,14 +191,11 @@ static bool TryGetFieldValue(ReadOnlySpan line, ReadOnlySpan prefix, } } - internal static void AddMacOSAttributes( - List> attributes, - string[]? plistFilePaths = null) + internal void AddMacOSAttributes(List> attributes) { try { - plistFilePaths ??= DefaultPlistFilePaths; - string? plistFilePath = plistFilePaths.FirstOrDefault(File.Exists); + string? plistFilePath = this.defaultPlistFilePaths!.FirstOrDefault(File.Exists); if (string.IsNullOrEmpty(plistFilePath)) { OperatingSystemResourcesEventSource.Log.FailedToFindFile("No suitable plist file found"); @@ -216,15 +241,6 @@ internal static void AddMacOSAttributes( #endif - internal static string GetOSDescription() - { -#if NET - return RuntimeInformation.OSDescription; -#else - return Environment.OSVersion.ToString(); -#endif - } - private static void AddAttributeIfNotNullOrEmpty(List> attributes, string key, object? value) { if (value == null) diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs index 5ed548f25c..3fb0c2a87f 100644 --- a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs @@ -57,12 +57,13 @@ public void TestOperatingSystemAttributes() [Fact] public void TestParseMacOSPlist() { - List> resourceAttributes = new(); string path = "Samples/SystemVersion.plist"; - OperatingSystemDetector.AddMacOSAttributes( - resourceAttributes, + var osDetector = new OperatingSystemDetector( + OperatingSystemSemanticConventions.OperatingSystemsValues.Darwin, + null, + null, [path]); - var attributes = resourceAttributes.ToDictionary(x => x.Key, x => (string)x.Value); + var attributes = osDetector.Detect().Attributes.ToDictionary(x => x.Key, x => (string)x.Value); Assert.Equal("Mac OS X", attributes[OperatingSystemSemanticConventions.AttributeOperatingSystemName]); Assert.Equal("10.6.8", attributes[OperatingSystemSemanticConventions.AttributeOperatingSystemVersion]); @@ -72,10 +73,13 @@ public void TestParseMacOSPlist() [Fact] public void TestParseLinuxOsRelease() { - List> resourceAttributes = new(); string path = "Samples/os-release"; - OperatingSystemDetector.AddLinuxAttributes(resourceAttributes, path); - var attributes = resourceAttributes.ToDictionary(x => x.Key, x => (string)x.Value); + var osDetector = new OperatingSystemDetector( + OperatingSystemSemanticConventions.OperatingSystemsValues.Linux, + null, + path, + null); + var attributes = osDetector.Detect().Attributes.ToDictionary(x => x.Key, x => (string)x.Value); Assert.Equal("Ubuntu", attributes[OperatingSystemSemanticConventions.AttributeOperatingSystemName]); Assert.Equal("22.04", attributes[OperatingSystemSemanticConventions.AttributeOperatingSystemVersion]); From 958b3a7720415fdf3279d988f0355432097bd662 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Wed, 28 Aug 2024 13:16:15 +0200 Subject: [PATCH 24/26] refactor --- .../OperatingSystemDetector.cs | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 0d1772a4a9..eb186434a3 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -4,7 +4,6 @@ #if NET using System.Runtime.InteropServices; using System.Xml.Linq; - #endif using static OpenTelemetry.Resources.OperatingSystem.OperatingSystemSemanticConventions; @@ -14,42 +13,50 @@ namespace OpenTelemetry.Resources.OperatingSystem; /// /// Operating system detector. /// -internal sealed class OperatingSystemDetector() : IResourceDetector +internal sealed class OperatingSystemDetector : IResourceDetector { - /// - /// Detects the resource attributes from the operating system. - /// - /// Resource with key-value pairs of resource attributes. - /// - private readonly string? registryKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; - private readonly string? osType = GetOSType(); -#if NET - private readonly string? etcOsReleasePath = "/etc/os-release"; - private readonly string[]? defaultPlistFilePaths = - [ - "/System/Library/CoreServices/SystemVersion.plist", - "/System/Library/CoreServices/ServerVersion.plist", - ]; -#endif + private const string RegistryKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; + private const string EtcOsReleasePath = "/etc/os-release"; + private static readonly string[] DefaultPlistFilePaths = + [ + "/System/Library/CoreServices/SystemVersion.plist", + "/System/Library/CoreServices/ServerVersion.plist" + ]; + + private readonly string? osType; + private readonly string? registryKey; + private readonly string? etcOsReleasePath; + private readonly string[]? plistFilePaths; + + internal OperatingSystemDetector() + : this( + GetOSType(), + RegistryKey, + EtcOsReleasePath, + DefaultPlistFilePaths) + { + } -#if NET /// /// Initializes a new instance of the class for testing. /// /// The target platform identifier, specifying the operating system type from SemanticConventions. /// The string path in the Windows Registry to retrieve specific Windows attributes. /// The string path to the file used to obtain Linux attributes. - /// An array of file paths used to retrieve MacOS attributes from plist files. - internal OperatingSystemDetector(string? osType, string? registryKey, string? etcOsReleasePath, string[]? defaultPlistFilePaths) - : this() + /// An array of file paths used to retrieve MacOS attributes from plist files. + internal OperatingSystemDetector(string? osType, string? registryKey, string? etcOsReleasePath, string[]? plistFilePaths) { this.osType = osType; this.registryKey = registryKey; this.etcOsReleasePath = etcOsReleasePath; - this.defaultPlistFilePaths = defaultPlistFilePaths; + this.plistFilePaths = plistFilePaths; } -#endif + /// + /// Detects the resource attributes from the operating system. + /// + /// Resource with key-value pairs of resource attributes. + /// public Resource Detect() { var attributes = new List>(5); @@ -195,7 +202,7 @@ internal void AddMacOSAttributes(List> attributes) { try { - string? plistFilePath = this.defaultPlistFilePaths!.FirstOrDefault(File.Exists); + string? plistFilePath = this.plistFilePaths!.FirstOrDefault(File.Exists); if (string.IsNullOrEmpty(plistFilePath)) { OperatingSystemResourcesEventSource.Log.FailedToFindFile("No suitable plist file found"); From 9f20fd86aa50652b928f349decd8cccfeed81de2 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Thu, 29 Aug 2024 12:28:43 +0200 Subject: [PATCH 25/26] add key-value count check in plist, log warning if mismatch/add fallback in resource detector/remove non-existent build_id for Ubuntu --- .../OperatingSystemDetector.cs | 71 +++++++++++-------- .../OperatingSystemDetectorTests.cs | 3 +- .../Samples/os-release | 1 - 3 files changed, 42 insertions(+), 33 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index eb186434a3..9a594ee96f 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -16,7 +16,12 @@ namespace OpenTelemetry.Resources.OperatingSystem; internal sealed class OperatingSystemDetector : IResourceDetector { private const string RegistryKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; - private const string EtcOsReleasePath = "/etc/os-release"; + private static readonly string[] DefaultEtcOsReleasePath = + [ + "/etc/os-release", + "/usr/lib/os-release" + ]; + private static readonly string[] DefaultPlistFilePaths = [ "/System/Library/CoreServices/SystemVersion.plist", @@ -25,14 +30,14 @@ internal sealed class OperatingSystemDetector : IResourceDetector private readonly string? osType; private readonly string? registryKey; - private readonly string? etcOsReleasePath; + private readonly string[]? etcOsReleasePath; private readonly string[]? plistFilePaths; internal OperatingSystemDetector() : this( GetOSType(), RegistryKey, - EtcOsReleasePath, + DefaultEtcOsReleasePath, DefaultPlistFilePaths) { } @@ -44,7 +49,7 @@ internal OperatingSystemDetector() /// The string path in the Windows Registry to retrieve specific Windows attributes. /// The string path to the file used to obtain Linux attributes. /// An array of file paths used to retrieve MacOS attributes from plist files. - internal OperatingSystemDetector(string? osType, string? registryKey, string? etcOsReleasePath, string[]? plistFilePaths) + internal OperatingSystemDetector(string? osType, string? registryKey, string[]? etcOsReleasePath, string[]? plistFilePaths) { this.osType = osType; this.registryKey = registryKey; @@ -87,7 +92,24 @@ public Resource Detect() return new Resource(attributes); } - internal static string? GetOSType() + private static void AddAttributeIfNotNullOrEmpty(List> attributes, string key, object? value) + { + if (value == null) + { + OperatingSystemResourcesEventSource.Log.FailedToValidateValue("The provided value is null"); + return; + } + + if (value is string strValue && string.IsNullOrEmpty(strValue)) + { + OperatingSystemResourcesEventSource.Log.FailedToValidateValue("The provided value string is empty."); + return; + } + + attributes.Add(new KeyValuePair(key, value!)); + } + + private static string? GetOSType() { #if NETFRAMEWORK return OperatingSystemsValues.Windows; @@ -111,7 +133,7 @@ public Resource Detect() #endif } - internal static string GetOSDescription() + private static string GetOSDescription() { #if NET return RuntimeInformation.OSDescription; @@ -121,7 +143,7 @@ internal static string GetOSDescription() } #pragma warning disable CA1416 - internal void AddWindowsAttributes(List> attributes) + private void AddWindowsAttributes(List> attributes) { try { @@ -143,17 +165,18 @@ internal void AddWindowsAttributes(List> attributes #if NET // based on: // https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/Interop/Linux/os-release/Interop.OSReleaseFile.cs - internal void AddLinuxAttributes(List> attributes) + private void AddLinuxAttributes(List> attributes) { try { - if (!File.Exists(this.etcOsReleasePath)) + string? etcOsReleasePath = this.etcOsReleasePath!.FirstOrDefault(File.Exists); + if (string.IsNullOrEmpty(etcOsReleasePath)) { - OperatingSystemResourcesEventSource.Log.FailedToFindFile("Failed to find or read the os-release file"); + OperatingSystemResourcesEventSource.Log.FailedToFindFile("Failed to find the os-release file"); return; } - var osReleaseContent = File.ReadAllLines(this.etcOsReleasePath); + var osReleaseContent = File.ReadAllLines(etcOsReleasePath); ReadOnlySpan buildId = default, name = default, version = default; foreach (var line in osReleaseContent) @@ -198,7 +221,7 @@ static bool TryGetFieldValue(ReadOnlySpan line, ReadOnlySpan prefix, } } - internal void AddMacOSAttributes(List> attributes) + private void AddMacOSAttributes(List> attributes) { try { @@ -219,6 +242,12 @@ internal void AddMacOSAttributes(List> attributes) var keys = dict.Elements("key").ToList(); var values = dict.Elements("string").ToList(); + if (keys.Count != values.Count) + { + OperatingSystemResourcesEventSource.Log.FailedToValidateValue($"Failed to get MacOS attributes: The number of keys does not match the number of values. Keys count: {keys.Count}, Values count: {values.Count}"); + return; + } + for (int i = 0; i < keys.Count; i++) { switch (keys[i].Value) @@ -245,23 +274,5 @@ internal void AddMacOSAttributes(List> attributes) OperatingSystemResourcesEventSource.Log.ResourceAttributesExtractException("Failed to get MacOS attributes", ex); } } - #endif - - private static void AddAttributeIfNotNullOrEmpty(List> attributes, string key, object? value) - { - if (value == null) - { - OperatingSystemResourcesEventSource.Log.FailedToValidateValue("The provided value is null"); - return; - } - - if (value is string strValue && string.IsNullOrEmpty(strValue)) - { - OperatingSystemResourcesEventSource.Log.FailedToValidateValue("The provided value string is empty."); - return; - } - - attributes.Add(new KeyValuePair(key, value!)); - } } diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs index 3fb0c2a87f..68ae79e3c5 100644 --- a/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/OperatingSystemDetectorTests.cs @@ -77,13 +77,12 @@ public void TestParseLinuxOsRelease() var osDetector = new OperatingSystemDetector( OperatingSystemSemanticConventions.OperatingSystemsValues.Linux, null, - path, + [path], null); var attributes = osDetector.Detect().Attributes.ToDictionary(x => x.Key, x => (string)x.Value); Assert.Equal("Ubuntu", attributes[OperatingSystemSemanticConventions.AttributeOperatingSystemName]); Assert.Equal("22.04", attributes[OperatingSystemSemanticConventions.AttributeOperatingSystemVersion]); - Assert.Equal("20240823", attributes[OperatingSystemSemanticConventions.AttributeOperatingSystemBuildId]); } #endif } diff --git a/test/OpenTelemetry.Resources.OperatingSystem.Tests/Samples/os-release b/test/OpenTelemetry.Resources.OperatingSystem.Tests/Samples/os-release index 7cc37681e4..af5f81109a 100644 --- a/test/OpenTelemetry.Resources.OperatingSystem.Tests/Samples/os-release +++ b/test/OpenTelemetry.Resources.OperatingSystem.Tests/Samples/os-release @@ -8,4 +8,3 @@ SUPPORT_URL=https://help.ubuntu.com/ BUG_REPORT_URL=https://bugs.launchpad.net/ubuntu PRIVACY_POLICY_URL=https://www.ubuntu.com/legal/terms-and-policies/privacy-policy UBUNTU_CODENAME=jammy -BUILD_ID=20240823 From bf115534ce576c1117c841ef2215e1a3deb1cd08 Mon Sep 17 00:00:00 2001 From: Yevhenii Solomchenko Date: Thu, 29 Aug 2024 12:44:05 +0200 Subject: [PATCH 26/26] rename variables --- .../OperatingSystemDetector.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs index 9a594ee96f..976307fba8 100644 --- a/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs +++ b/src/OpenTelemetry.Resources.OperatingSystem/OperatingSystemDetector.cs @@ -16,7 +16,7 @@ namespace OpenTelemetry.Resources.OperatingSystem; internal sealed class OperatingSystemDetector : IResourceDetector { private const string RegistryKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; - private static readonly string[] DefaultEtcOsReleasePath = + private static readonly string[] DefaultEtcOsReleasePaths = [ "/etc/os-release", "/usr/lib/os-release" @@ -30,14 +30,14 @@ internal sealed class OperatingSystemDetector : IResourceDetector private readonly string? osType; private readonly string? registryKey; - private readonly string[]? etcOsReleasePath; + private readonly string[]? etcOsReleasePaths; private readonly string[]? plistFilePaths; internal OperatingSystemDetector() : this( GetOSType(), RegistryKey, - DefaultEtcOsReleasePath, + DefaultEtcOsReleasePaths, DefaultPlistFilePaths) { } @@ -53,7 +53,7 @@ internal OperatingSystemDetector(string? osType, string? registryKey, string[]? { this.osType = osType; this.registryKey = registryKey; - this.etcOsReleasePath = etcOsReleasePath; + this.etcOsReleasePaths = etcOsReleasePath; this.plistFilePaths = plistFilePaths; } @@ -169,7 +169,7 @@ private void AddLinuxAttributes(List> attributes) { try { - string? etcOsReleasePath = this.etcOsReleasePath!.FirstOrDefault(File.Exists); + string? etcOsReleasePath = this.etcOsReleasePaths!.FirstOrDefault(File.Exists); if (string.IsNullOrEmpty(etcOsReleasePath)) { OperatingSystemResourcesEventSource.Log.FailedToFindFile("Failed to find the os-release file");