From ef431afa5abffbbcd63424f0e93ac69e1055e02d Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Wed, 4 Dec 2024 18:44:21 +0100 Subject: [PATCH 01/15] WIP telemetry for sharing --- Directory.Build.props | 2 +- NuGet.config | 35 +++- eng/Packages.props | 7 + .../BackEnd/BuildManager/BuildManager.cs | 20 +- .../Microsoft.Build.Framework.csproj | 14 ++ src/Framework/Telemetry/FrameworkTelemetry.cs | 174 ++++++++++++++++++ src/MSBuild/app.config | 8 + 7 files changed, 257 insertions(+), 3 deletions(-) create mode 100644 src/Framework/Telemetry/FrameworkTelemetry.cs diff --git a/Directory.Build.props b/Directory.Build.props index b0454aa3564..d9e3ec92d8d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -57,7 +57,7 @@ RS0016 & RS0017: Roslyn analyzers seem to be bugged, claiming that API's that exist don't and vise-versa: https://github.com/dotnet/msbuild/issues/7903 --> - $(NoWarn);NU1507;NU1603;NU5105;1701;1702;SYSLIB0011;SYSLIB0037;SYSLIB0044;RS0016;RS0017; + $(NoWarn);NU1507;NU1603;NU1605;NU5105;NU5100;NU1701;1702;SYSLIB0011;SYSLIB0037;SYSLIB0044;RS0016;RS0017; $(NoWarn);SYSLIB0057; diff --git a/NuGet.config b/NuGet.config index 107cd4542dc..620a031ac09 100644 --- a/NuGet.config +++ b/NuGet.config @@ -1,4 +1,4 @@ - + @@ -13,6 +13,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eng/Packages.props b/eng/Packages.props index 87cf3b78909..60e5a74f9fc 100644 --- a/eng/Packages.props +++ b/eng/Packages.props @@ -33,5 +33,12 @@ + + + + + + + diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index 4ee166bb5e2..4d069ee41c8 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -40,6 +40,11 @@ using ForwardingLoggerRecord = Microsoft.Build.Logging.ForwardingLoggerRecord; using LoggerDescription = Microsoft.Build.Logging.LoggerDescription; +#if NETFRAMEWORK +using static Microsoft.Extensions.Logging.LoggerExtensions; +using ExtILogger = Microsoft.Extensions.Logging.ILogger; +#endif + namespace Microsoft.Build.Execution { /// @@ -492,6 +497,13 @@ public void BeginBuild(BuildParameters parameters) parameters.DetailedSummary = true; parameters.LogTaskInputs = true; } +#if NETFRAMEWORK + Environment.SetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317"); + Environment.SetEnvironmentVariable("OTEL_SERVICE_NAME", "MSBuild"); + FrameworkTelemetry.Enable(); + var a = TelemetryHelpers.StartActivity("BeginBuild", new Dictionary { { "feature_flag.IsBuildCheckEnabled", parameters.IsBuildCheckEnabled } }); + a.Dispose(); +#endif lock (_syncLock) { @@ -1029,6 +1041,7 @@ public void EndBuild() { try { + object TelemetryService = new(); ILoggingService? loggingService = ((IBuildComponentHost)this).LoggingService; if (loggingService != null) @@ -1069,8 +1082,13 @@ public void EndBuild() var sacState = NativeMethodsShared.GetSACState(); // The Enforcement would lead to build crash - but let's have the check for completeness sake. _buildTelemetry.SACEnabled = sacState == NativeMethodsShared.SAC_State.Evaluation || sacState == NativeMethodsShared.SAC_State.Enforcement; - + Debugger.Launch(); loggingService.LogTelemetry(buildEventContext: null, _buildTelemetry.EventName, _buildTelemetry.GetProperties()); +#if NETFRAMEWORK + // var telemetryActivity = TelemetryHelpers.StartActivity("endbuild"); + FrameworkTelemetry.EndOfBuildTelemetry(_buildTelemetry); + // telemetryActivity.Dispose(); +#endif // Clean telemetry to make it ready for next build submission. _buildTelemetry = null; } diff --git a/src/Framework/Microsoft.Build.Framework.csproj b/src/Framework/Microsoft.Build.Framework.csproj index 736cccac2f1..7afa0e527f9 100644 --- a/src/Framework/Microsoft.Build.Framework.csproj +++ b/src/Framework/Microsoft.Build.Framework.csproj @@ -43,6 +43,20 @@ Shared\IMSBuildElementLocation.cs + + + + + + + + + + + + + + diff --git a/src/Framework/Telemetry/FrameworkTelemetry.cs b/src/Framework/Telemetry/FrameworkTelemetry.cs new file mode 100644 index 00000000000..e0157fba200 --- /dev/null +++ b/src/Framework/Telemetry/FrameworkTelemetry.cs @@ -0,0 +1,174 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#if NETFRAMEWORK +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.OpenTelemetry.ClientExtensions; +using Microsoft.VisualStudio.OpenTelemetry.ClientExtensions.Exporters; +using Microsoft.VisualStudio.OpenTelemetry.Collector.Interfaces; +using Microsoft.VisualStudio.OpenTelemetry.Collector.Settings; + +using OpenTelemetry; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Build.Framework.Telemetry +{ + /// + /// A static class to instrument telemetry via OpenTelemetry. + /// + public static class FrameworkTelemetry + { + + private const string OTelNamespace = "Microsoft.VisualStudio.OpenTelemetry.MSBuild"; + private const string vsMajorVersion = "17.0"; + private static IOpenTelemetryCollector? collector; + private static TracerProvider? tracerProvider; + private static MeterProvider? meterProvider; + + private static bool isInitialized; + // private static ILoggerFactory? loggerFactory; + public static Microsoft.Extensions.Logging.ILogger? logger; + + + /// + /// Gets an that is configured to create objects + /// that can get reported as a VS telemetry event when disposed. + /// + internal static MSBuildActivitySourceWrapper DefaultTelemetrySource { get; } = new(); + + /// + /// Configures the to send telemetry through the Open Telemetry pipeline. + /// + /// + /// This should get called once at the start of the process. Subsequent calls are no-ops. + /// If this is not called, then objects created from will always be . + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Enable() + { + // this relies on single thread being here + if (isInitialized) + { + return; + } + + isInitialized = true; + + IOpenTelemetryExporterSettings defaultExporterSettings = OpenTelemetryExporterSettingsBuilder + .CreateVSDefault(vsMajorVersion) + .Build(); + IOpenTelemetryCollectorSettings collectorSettings = OpenTelemetryCollectorSettingsBuilder + .CreateVSDefault(vsMajorVersion) + .Build(); + + using ILoggerFactory factory = LoggerFactory.Create(builder => { builder.AddOpenTelemetry(logging => { logging.AddVisualStudioDefaultLogExporter(defaultExporterSettings); logging.AddOtlpExporter(); }); }); + + tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddVisualStudioDefaultTraceExporter(defaultExporterSettings) + .AddOtlpExporter() + .Build(); + logger = factory.CreateLogger(OTelNamespace); + + meterProvider = Sdk.CreateMeterProviderBuilder() + .AddVisualStudioDefaultMetricExporter(defaultExporterSettings) + .Build(); + + collector = OpenTelemetryCollectorProvider.CreateCollector(collectorSettings); + collector.StartAsync(); + } + + private const string SamplePropertyPrefix = "VS.MSBuild.Event."; + internal static void EndOfBuildTelemetry(BuildTelemetry buildTelemetry) + { + Enable(); +#pragma warning disable CS8604 // Possible null reference argument. + using var telemetryActivity = TelemetryHelpers.StartActivity("build", initialProperties: new + Dictionary + { + { "StartAt", buildTelemetry.StartAt?.ToString() }, + { "InnerStartAt", buildTelemetry.InnerStartAt?.ToString() }, + { "FinishedAt", buildTelemetry.FinishedAt?.ToString() }, + { "Success", buildTelemetry.Success }, + { "Target", buildTelemetry.Target }, + { "ServerFallbackReason", buildTelemetry.ServerFallbackReason }, + { "Version", buildTelemetry.Version?.ToString() }, + { "DisplayVersion", buildTelemetry.DisplayVersion }, + { "SAC", buildTelemetry.SACEnabled }, + { "BuildCheckEnabled", buildTelemetry.BuildCheckEnabled }, + }); +#pragma warning restore CS8604 // Possible null reference argument. + telemetryActivity.AddBaggage("baggage", "hey"); + telemetryActivity.AddEvent(new ActivityEvent("hey2")); + telemetryActivity.AddEvent(new ActivityEvent(OTelNamespace + "hey3")); + telemetryActivity.SetStartTime(buildTelemetry.StartAt ?? DateTime.UtcNow); + telemetryActivity.Stop(); + telemetryActivity.SetEndTime(buildTelemetry.FinishedAt ?? DateTime.UtcNow); + telemetryActivity.SetCustomProperty(SamplePropertyPrefix + "hey", "hello"); + telemetryActivity.Dispose(); + } + + internal class MSBuildActivitySourceWrapper + { + private const string OTelNamespace = "Microsoft.VisualStudio.OpenTelemetry.MSBuild"; + internal MSBuildActivitySourceWrapper() + { + Source = new ActivitySource(OTelNamespace, vsMajorVersion); + } + public ActivitySource Source { get; } + + public string Name => Source.Name; + + public string? Version => Source.Version; + + + public Activity StartActivity(string name = "", ActivityKind kind = ActivityKind.Internal) + { + // If the current activity has a remote parent, then we should start a child activity with the same parent ID. + Activity? activity = Activity.Current?.HasRemoteParent is true + ? Source.StartActivity(name, kind, parentId: Activity.Current.ParentId) + : Source.StartActivity(name); + + if (activity is null) + { + activity = new Activity(name); + activity.Start(); + } + + return activity; + } + } + } + public static class TelemetryHelpers + { + + private const string EventPrefix = "VS/MSBuild/Event/"; + public static Activity StartActivity(string name, IDictionary initialProperties) + { + return FrameworkTelemetry.DefaultTelemetrySource + .StartActivity(EventPrefix + name, ActivityKind.Internal) + .WithTags(initialProperties); + } + public static Activity WithTags(this Activity activity, IDictionary tags) + { + if (tags is null) + { + return activity; + } + + foreach (KeyValuePair tag in tags) + { + activity.SetTag(tag.Key, tag.Value); + } + + return activity; + } + } +} +#endif diff --git a/src/MSBuild/app.config b/src/MSBuild/app.config index 9bc9a4c595c..063d1f715c5 100644 --- a/src/MSBuild/app.config +++ b/src/MSBuild/app.config @@ -60,6 +60,14 @@ + + + + + + + + From 7173dcbacbf5fbca7462055a200990b09e76b356 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Thu, 5 Dec 2024 19:11:03 +0100 Subject: [PATCH 02/15] remove nuget.org dependency --- NuGet.config | 7 +----- eng/Packages.props | 9 ++++---- .../BackEnd/BuildManager/BuildManager.cs | 12 +++++----- .../NodeProviderOutOfProcBase.cs | 7 ++++++ .../Microsoft.Build.Framework.csproj | 4 ++-- ...meworkTelemetry.cs => NewOpenTelemetry.cs} | 23 +++++++++---------- src/MSBuild/app.amd64.config | 4 ++-- src/MSBuild/app.config | 6 ++--- 8 files changed, 37 insertions(+), 35 deletions(-) rename src/Framework/Telemetry/{FrameworkTelemetry.cs => NewOpenTelemetry.cs} (89%) diff --git a/NuGet.config b/NuGet.config index 620a031ac09..c9bda385cf9 100644 --- a/NuGet.config +++ b/NuGet.config @@ -14,7 +14,6 @@ - @@ -39,11 +38,7 @@ - - - - - + diff --git a/eng/Packages.props b/eng/Packages.props index 60e5a74f9fc..8463cb362fe 100644 --- a/eng/Packages.props +++ b/eng/Packages.props @@ -35,10 +35,11 @@ - - - - + + + + + diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index 4d069ee41c8..7c935b8525e 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -498,10 +498,10 @@ public void BeginBuild(BuildParameters parameters) parameters.LogTaskInputs = true; } #if NETFRAMEWORK - Environment.SetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317"); - Environment.SetEnvironmentVariable("OTEL_SERVICE_NAME", "MSBuild"); - FrameworkTelemetry.Enable(); - var a = TelemetryHelpers.StartActivity("BeginBuild", new Dictionary { { "feature_flag.IsBuildCheckEnabled", parameters.IsBuildCheckEnabled } }); + // Environment.SetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317"); + // Environment.SetEnvironmentVariable("OTEL_SERVICE_NAME", "MSBuild"); + NewOpenTelemetry.Enable(); + var a = TelemetryHelpers.StartActivity("BeginBuild", new Dictionary { { "IsBuildCheckEnabled", parameters.IsBuildCheckEnabled } }); a.Dispose(); #endif @@ -1082,11 +1082,11 @@ public void EndBuild() var sacState = NativeMethodsShared.GetSACState(); // The Enforcement would lead to build crash - but let's have the check for completeness sake. _buildTelemetry.SACEnabled = sacState == NativeMethodsShared.SAC_State.Evaluation || sacState == NativeMethodsShared.SAC_State.Enforcement; - Debugger.Launch(); + // Debugger.Launch(); loggingService.LogTelemetry(buildEventContext: null, _buildTelemetry.EventName, _buildTelemetry.GetProperties()); #if NETFRAMEWORK // var telemetryActivity = TelemetryHelpers.StartActivity("endbuild"); - FrameworkTelemetry.EndOfBuildTelemetry(_buildTelemetry); + NewOpenTelemetry.EndOfBuildTelemetry(_buildTelemetry); // telemetryActivity.Dispose(); #endif // Clean telemetry to make it ready for next build submission. diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index 2395b09f44d..20d9d92f7bb 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -25,6 +25,7 @@ using Task = System.Threading.Tasks.Task; using Microsoft.Build.Framework; using Microsoft.Build.BackEnd.Logging; +using Microsoft.Build.Framework.Telemetry; #nullable disable @@ -333,6 +334,9 @@ bool StartNewNode(int nodeId) #endif // Create the node process INodeLauncher nodeLauncher = (INodeLauncher)_componentHost.GetComponent(BuildComponentType.NodeLauncher); +#if NETFRAMEWORK + var activity = TelemetryHelpers.StartActivity("NodeLaunching", new Dictionary() { }); +#endif Process msbuildProcess = nodeLauncher.Start(msbuildLocation, commandLineArgs, nodeId); _processesToIgnore.TryAdd(GetProcessesToIgnoreKey(hostHandshake, msbuildProcess.Id), default); @@ -342,6 +346,9 @@ bool StartNewNode(int nodeId) // Now try to connect to it. Stream nodeStream = TryConnectToProcess(msbuildProcess.Id, TimeoutForNewNodeCreation, hostHandshake); +#if NETFRAMEWORK + activity.Dispose(); +#endif if (nodeStream != null) { // Connection successful, use this node. diff --git a/src/Framework/Microsoft.Build.Framework.csproj b/src/Framework/Microsoft.Build.Framework.csproj index 7afa0e527f9..7083062f85f 100644 --- a/src/Framework/Microsoft.Build.Framework.csproj +++ b/src/Framework/Microsoft.Build.Framework.csproj @@ -45,7 +45,7 @@ - + @@ -55,7 +55,7 @@ - + diff --git a/src/Framework/Telemetry/FrameworkTelemetry.cs b/src/Framework/Telemetry/NewOpenTelemetry.cs similarity index 89% rename from src/Framework/Telemetry/FrameworkTelemetry.cs rename to src/Framework/Telemetry/NewOpenTelemetry.cs index e0157fba200..600a9ec742d 100644 --- a/src/Framework/Telemetry/FrameworkTelemetry.cs +++ b/src/Framework/Telemetry/NewOpenTelemetry.cs @@ -23,7 +23,7 @@ namespace Microsoft.Build.Framework.Telemetry /// /// A static class to instrument telemetry via OpenTelemetry. /// - public static class FrameworkTelemetry + public static class NewOpenTelemetry { private const string OTelNamespace = "Microsoft.VisualStudio.OpenTelemetry.MSBuild"; @@ -72,7 +72,7 @@ public static void Enable() tracerProvider = Sdk.CreateTracerProviderBuilder() .AddVisualStudioDefaultTraceExporter(defaultExporterSettings) - .AddOtlpExporter() + .AddOtlpExporter() // see if this looks at any env vars .Build(); logger = factory.CreateLogger(OTelNamespace); @@ -80,16 +80,16 @@ public static void Enable() .AddVisualStudioDefaultMetricExporter(defaultExporterSettings) .Build(); + // this should not happen in VS collector = OpenTelemetryCollectorProvider.CreateCollector(collectorSettings); collector.StartAsync(); } - private const string SamplePropertyPrefix = "VS.MSBuild.Event."; internal static void EndOfBuildTelemetry(BuildTelemetry buildTelemetry) { Enable(); #pragma warning disable CS8604 // Possible null reference argument. - using var telemetryActivity = TelemetryHelpers.StartActivity("build", initialProperties: new + using var telemetryActivity = TelemetryHelpers.StartActivity("Build", initialProperties: new Dictionary { { "StartAt", buildTelemetry.StartAt?.ToString() }, @@ -97,20 +97,16 @@ internal static void EndOfBuildTelemetry(BuildTelemetry buildTelemetry) { "FinishedAt", buildTelemetry.FinishedAt?.ToString() }, { "Success", buildTelemetry.Success }, { "Target", buildTelemetry.Target }, - { "ServerFallbackReason", buildTelemetry.ServerFallbackReason }, { "Version", buildTelemetry.Version?.ToString() }, { "DisplayVersion", buildTelemetry.DisplayVersion }, { "SAC", buildTelemetry.SACEnabled }, { "BuildCheckEnabled", buildTelemetry.BuildCheckEnabled }, + { "Host", buildTelemetry.Host }, }); #pragma warning restore CS8604 // Possible null reference argument. - telemetryActivity.AddBaggage("baggage", "hey"); - telemetryActivity.AddEvent(new ActivityEvent("hey2")); - telemetryActivity.AddEvent(new ActivityEvent(OTelNamespace + "hey3")); telemetryActivity.SetStartTime(buildTelemetry.StartAt ?? DateTime.UtcNow); telemetryActivity.Stop(); telemetryActivity.SetEndTime(buildTelemetry.FinishedAt ?? DateTime.UtcNow); - telemetryActivity.SetCustomProperty(SamplePropertyPrefix + "hey", "hello"); telemetryActivity.Dispose(); } @@ -148,10 +144,13 @@ public Activity StartActivity(string name = "", ActivityKind kind = ActivityKind public static class TelemetryHelpers { - private const string EventPrefix = "VS/MSBuild/Event/"; + private const string EventPrefix = "MSBuild/"; + private const string PropertyPrefix = "MSBuild."; + // private const string PropertyPrefix = ""; + public static Activity StartActivity(string name, IDictionary initialProperties) { - return FrameworkTelemetry.DefaultTelemetrySource + return NewOpenTelemetry.DefaultTelemetrySource .StartActivity(EventPrefix + name, ActivityKind.Internal) .WithTags(initialProperties); } @@ -164,7 +163,7 @@ public static Activity WithTags(this Activity activity, IDictionary tag in tags) { - activity.SetTag(tag.Key, tag.Value); + activity.SetTag(PropertyPrefix + tag.Key, tag.Value); } return activity; diff --git a/src/MSBuild/app.amd64.config b/src/MSBuild/app.amd64.config index 339dfe620bf..b2ff35a1606 100644 --- a/src/MSBuild/app.amd64.config +++ b/src/MSBuild/app.amd64.config @@ -62,8 +62,8 @@ - - + + diff --git a/src/MSBuild/app.config b/src/MSBuild/app.config index 063d1f715c5..71ca93c8605 100644 --- a/src/MSBuild/app.config +++ b/src/MSBuild/app.config @@ -33,7 +33,7 @@ - + @@ -62,11 +62,11 @@ - + - + From ff4c704f18c236df2a401ceb6dc4ef6b94f67758 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Fri, 6 Dec 2024 09:47:24 +0100 Subject: [PATCH 03/15] prefix --- src/Framework/Telemetry/NewOpenTelemetry.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Framework/Telemetry/NewOpenTelemetry.cs b/src/Framework/Telemetry/NewOpenTelemetry.cs index 600a9ec742d..77bd4d5db86 100644 --- a/src/Framework/Telemetry/NewOpenTelemetry.cs +++ b/src/Framework/Telemetry/NewOpenTelemetry.cs @@ -144,8 +144,8 @@ public Activity StartActivity(string name = "", ActivityKind kind = ActivityKind public static class TelemetryHelpers { - private const string EventPrefix = "MSBuild/"; - private const string PropertyPrefix = "MSBuild."; + private const string EventPrefix = "VS/MSBuild/"; + private const string PropertyPrefix = "VS.MSBuild."; // private const string PropertyPrefix = ""; public static Activity StartActivity(string name, IDictionary initialProperties) From 6ac19009696ae6456bfba1f6703037085d04193d Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Fri, 6 Dec 2024 13:42:45 +0100 Subject: [PATCH 04/15] remove warning exclusions --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index d9e3ec92d8d..889acdc0e67 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -57,7 +57,7 @@ RS0016 & RS0017: Roslyn analyzers seem to be bugged, claiming that API's that exist don't and vise-versa: https://github.com/dotnet/msbuild/issues/7903 --> - $(NoWarn);NU1507;NU1603;NU1605;NU5105;NU5100;NU1701;1702;SYSLIB0011;SYSLIB0037;SYSLIB0044;RS0016;RS0017; + $(NoWarn);NU1507;NU1603;NU5105;1702;SYSLIB0011;SYSLIB0037;SYSLIB0044;RS0016;RS0017; $(NoWarn);SYSLIB0057; From f69a86d8739fa0e00e2d23d7dc46fdda5a63cb6c Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Tue, 10 Dec 2024 14:29:10 +0100 Subject: [PATCH 05/15] refactor corresponding more to the abstractions we want to have --- .../BackEnd/BuildManager/BuildManager.cs | 27 +- .../NodeProviderOutOfProcBase.cs | 6 +- .../Microsoft.Build.Framework.csproj | 9 +- src/Framework/Telemetry/NewOpenTelemetry.cs | 355 ++++++++++++------ 4 files changed, 254 insertions(+), 143 deletions(-) diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index 7c935b8525e..b366bc78b39 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -40,11 +40,6 @@ using ForwardingLoggerRecord = Microsoft.Build.Logging.ForwardingLoggerRecord; using LoggerDescription = Microsoft.Build.Logging.LoggerDescription; -#if NETFRAMEWORK -using static Microsoft.Extensions.Logging.LoggerExtensions; -using ExtILogger = Microsoft.Extensions.Logging.ILogger; -#endif - namespace Microsoft.Build.Execution { /// @@ -497,13 +492,7 @@ public void BeginBuild(BuildParameters parameters) parameters.DetailedSummary = true; parameters.LogTaskInputs = true; } -#if NETFRAMEWORK - // Environment.SetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317"); - // Environment.SetEnvironmentVariable("OTEL_SERVICE_NAME", "MSBuild"); - NewOpenTelemetry.Enable(); - var a = TelemetryHelpers.StartActivity("BeginBuild", new Dictionary { { "IsBuildCheckEnabled", parameters.IsBuildCheckEnabled } }); - a.Dispose(); -#endif + BuildTelemetryManager.Initialize(false); lock (_syncLock) { @@ -1084,11 +1073,15 @@ public void EndBuild() _buildTelemetry.SACEnabled = sacState == NativeMethodsShared.SAC_State.Evaluation || sacState == NativeMethodsShared.SAC_State.Enforcement; // Debugger.Launch(); loggingService.LogTelemetry(buildEventContext: null, _buildTelemetry.EventName, _buildTelemetry.GetProperties()); -#if NETFRAMEWORK - // var telemetryActivity = TelemetryHelpers.StartActivity("endbuild"); - NewOpenTelemetry.EndOfBuildTelemetry(_buildTelemetry); - // telemetryActivity.Dispose(); -#endif + var endOfBuildTelemetry = BuildTelemetryManager.StartActivity( + "Build", + new Dictionary { + { "IsBuildCheckEnabled", _buildTelemetry.BuildCheckEnabled }, + { "Target", _buildTelemetry.Target ?? "" } + }); + endOfBuildTelemetry?.Dispose(); + BuildTelemetryManager.Shutdown(); + // Clean telemetry to make it ready for next build submission. _buildTelemetry = null; } diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index 20d9d92f7bb..465ff552079 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -334,9 +334,7 @@ bool StartNewNode(int nodeId) #endif // Create the node process INodeLauncher nodeLauncher = (INodeLauncher)_componentHost.GetComponent(BuildComponentType.NodeLauncher); -#if NETFRAMEWORK - var activity = TelemetryHelpers.StartActivity("NodeLaunching", new Dictionary() { }); -#endif + var activity = BuildTelemetryManager.StartActivity("NodeLaunching", new Dictionary() { }); Process msbuildProcess = nodeLauncher.Start(msbuildLocation, commandLineArgs, nodeId); _processesToIgnore.TryAdd(GetProcessesToIgnoreKey(hostHandshake, msbuildProcess.Id), default); @@ -346,9 +344,7 @@ bool StartNewNode(int nodeId) // Now try to connect to it. Stream nodeStream = TryConnectToProcess(msbuildProcess.Id, TimeoutForNewNodeCreation, hostHandshake); -#if NETFRAMEWORK activity.Dispose(); -#endif if (nodeStream != null) { // Connection successful, use this node. diff --git a/src/Framework/Microsoft.Build.Framework.csproj b/src/Framework/Microsoft.Build.Framework.csproj index 7083062f85f..7d4d4f22165 100644 --- a/src/Framework/Microsoft.Build.Framework.csproj +++ b/src/Framework/Microsoft.Build.Framework.csproj @@ -44,18 +44,17 @@ - - - - + + + - + diff --git a/src/Framework/Telemetry/NewOpenTelemetry.cs b/src/Framework/Telemetry/NewOpenTelemetry.cs index 77bd4d5db86..50f0cb5f8e4 100644 --- a/src/Framework/Telemetry/NewOpenTelemetry.cs +++ b/src/Framework/Telemetry/NewOpenTelemetry.cs @@ -1,173 +1,296 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#if NETFRAMEWORK using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; +#if NETFRAMEWORK using Microsoft.VisualStudio.OpenTelemetry.ClientExtensions; using Microsoft.VisualStudio.OpenTelemetry.ClientExtensions.Exporters; using Microsoft.VisualStudio.OpenTelemetry.Collector.Interfaces; using Microsoft.VisualStudio.OpenTelemetry.Collector.Settings; - +#endif using OpenTelemetry; -using OpenTelemetry.Logs; using OpenTelemetry.Metrics; using OpenTelemetry.Trace; namespace Microsoft.Build.Framework.Telemetry { - /// - /// A static class to instrument telemetry via OpenTelemetry. - /// - public static class NewOpenTelemetry + + public static class TelemetryConstants + { + public const string VSNamespace = "Microsoft.VisualStudio.OpenTelemetry.MSBuild"; + public const string MSBuildSourceName = "Microsoft.Build"; + public const string EventPrefix = "VS/MSBuild/"; + public const string PropertyPrefix = "VS.MSBuild."; + public const string Version = "1.0.0"; + } + + public class TelemetryConfiguration { + private static readonly Lazy _instance = + new(() => new TelemetryConfiguration()); - private const string OTelNamespace = "Microsoft.VisualStudio.OpenTelemetry.MSBuild"; - private const string vsMajorVersion = "17.0"; - private static IOpenTelemetryCollector? collector; - private static TracerProvider? tracerProvider; - private static MeterProvider? meterProvider; - - private static bool isInitialized; - // private static ILoggerFactory? loggerFactory; - public static Microsoft.Extensions.Logging.ILogger? logger; - - - /// - /// Gets an that is configured to create objects - /// that can get reported as a VS telemetry event when disposed. - /// - internal static MSBuildActivitySourceWrapper DefaultTelemetrySource { get; } = new(); - - /// - /// Configures the to send telemetry through the Open Telemetry pipeline. - /// - /// - /// This should get called once at the start of the process. Subsequent calls are no-ops. - /// If this is not called, then objects created from will always be . - /// - [MethodImpl(MethodImplOptions.NoInlining)] - public static void Enable() - { - // this relies on single thread being here - if (isInitialized) + public static TelemetryConfiguration Instance => _instance.Value; + + // Will be populated with actual env vars later + public const string OptOutEnvVar = "PLACEHOLDER_OPTOUT"; + public const string VSTelemetryOptOutEnvVar = "PLACEHOLDER_VS_OPTOUT"; + public const string OTLPExportEnvVar = "PLACEHOLDER_OTLP_ENABLE"; + public const string NoCollectorsEnvVar = "PLACEHOLDER_NO_COLLECTORS"; + + private TelemetryConfiguration() + { + RefreshConfiguration(); + } + + public bool IsEnabled { get; private set; } + public bool IsVSTelemetryEnabled { get; private set; } + public bool IsOTLPExportEnabled { get; private set; } + public bool ShouldInitializeCollectors { get; private set; } + + public void RefreshConfiguration() + { + IsEnabled = !IsEnvVarEnabled(OptOutEnvVar); + IsVSTelemetryEnabled = IsEnabled && !IsEnvVarEnabled(VSTelemetryOptOutEnvVar); + // IsOTLPExportEnabled = IsEnabled && IsEnvVarEnabled(OTLPExportEnvVar); +#if DEBUG + IsOTLPExportEnabled = true; +#endif + ShouldInitializeCollectors = IsEnabled && !IsEnvVarEnabled(NoCollectorsEnvVar); + } + + private static bool IsEnvVarEnabled(string name) => + !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(name)); + } + + public static class BuildTelemetryManager + { + private static ITelemetrySession? _currentSession; + + public static void Initialize(bool isVisualStudioBuild, string? hostName = null) + { + if (_currentSession != null) { - return; + throw new InvalidOperationException("Telemetry session already initialized"); } - isInitialized = true; + _currentSession = TelemetrySessionFactory.Create(isVisualStudioBuild, hostName); + } - IOpenTelemetryExporterSettings defaultExporterSettings = OpenTelemetryExporterSettingsBuilder - .CreateVSDefault(vsMajorVersion) - .Build(); - IOpenTelemetryCollectorSettings collectorSettings = OpenTelemetryCollectorSettingsBuilder - .CreateVSDefault(vsMajorVersion) - .Build(); + public static void Shutdown() + { + if (_currentSession != null) + { + _currentSession.Dispose(); + _currentSession = null; + } + } - using ILoggerFactory factory = LoggerFactory.Create(builder => { builder.AddOpenTelemetry(logging => { logging.AddVisualStudioDefaultLogExporter(defaultExporterSettings); logging.AddOtlpExporter(); }); }); + public static Activity? StartActivity(string name, IDictionary? tags = null) + { + return _currentSession?.StartActivity( + $"{TelemetryConstants.EventPrefix}{name}", + tags?.ToDictionary( + kvp => $"{TelemetryConstants.PropertyPrefix}{kvp.Key}", + kvp => kvp.Value)); + } + } - tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddVisualStudioDefaultTraceExporter(defaultExporterSettings) - .AddOtlpExporter() // see if this looks at any env vars - .Build(); - logger = factory.CreateLogger(OTelNamespace); + // This would be internal in reality, shown here for completeness + internal interface ITelemetrySession : IDisposable + { + Activity? StartActivity(string name, IDictionary? tags = null); + } + internal static class TelemetrySessionFactory + { + public static ITelemetrySession Create(bool isVisualStudioBuild, string? hostName) + { + var session = new TelemetrySession(isVisualStudioBuild, hostName); + session.Initialize(); + return session; + } + } - meterProvider = Sdk.CreateMeterProviderBuilder() - .AddVisualStudioDefaultMetricExporter(defaultExporterSettings) - .Build(); + internal class TelemetrySession : ITelemetrySession + { + private readonly bool _isVisualStudioBuild; + private readonly string? _hostName; + private readonly MSBuildActivitySource _activitySource; + private readonly List _collectors; + private bool _isDisposed; - // this should not happen in VS - collector = OpenTelemetryCollectorProvider.CreateCollector(collectorSettings); - collector.StartAsync(); + public TelemetrySession(bool isVisualStudioBuild, string? hostName) + { + _isVisualStudioBuild = isVisualStudioBuild; + _hostName = hostName; + _activitySource = new MSBuildActivitySource(); + _collectors = new(); } - internal static void EndOfBuildTelemetry(BuildTelemetry buildTelemetry) + public void Initialize() { - Enable(); -#pragma warning disable CS8604 // Possible null reference argument. - using var telemetryActivity = TelemetryHelpers.StartActivity("Build", initialProperties: new - Dictionary - { - { "StartAt", buildTelemetry.StartAt?.ToString() }, - { "InnerStartAt", buildTelemetry.InnerStartAt?.ToString() }, - { "FinishedAt", buildTelemetry.FinishedAt?.ToString() }, - { "Success", buildTelemetry.Success }, - { "Target", buildTelemetry.Target }, - { "Version", buildTelemetry.Version?.ToString() }, - { "DisplayVersion", buildTelemetry.DisplayVersion }, - { "SAC", buildTelemetry.SACEnabled }, - { "BuildCheckEnabled", buildTelemetry.BuildCheckEnabled }, - { "Host", buildTelemetry.Host }, - }); -#pragma warning restore CS8604 // Possible null reference argument. - telemetryActivity.SetStartTime(buildTelemetry.StartAt ?? DateTime.UtcNow); - telemetryActivity.Stop(); - telemetryActivity.SetEndTime(buildTelemetry.FinishedAt ?? DateTime.UtcNow); - telemetryActivity.Dispose(); - } - - internal class MSBuildActivitySourceWrapper - { - private const string OTelNamespace = "Microsoft.VisualStudio.OpenTelemetry.MSBuild"; - internal MSBuildActivitySourceWrapper() + var config = TelemetryConfiguration.Instance; + + if (config.IsOTLPExportEnabled) { - Source = new ActivitySource(OTelNamespace, vsMajorVersion); + _collectors.Add(new OTLPCollector(_activitySource).Initialize()); } - public ActivitySource Source { get; } - public string Name => Source.Name; +#if NETFRAMEWORK + if (_isVisualStudioBuild && config.IsVSTelemetryEnabled) + { + _collectors.Add(new VSCollector(_activitySource).Initialize()); + } +#endif + } - public string? Version => Source.Version; + public Activity? StartActivity(string name, IDictionary? tags = null) + { + if (_isDisposed) + { + return null; + } + return _activitySource.StartActivity(name, tags); + } - public Activity StartActivity(string name = "", ActivityKind kind = ActivityKind.Internal) + public void Dispose() + { + if (_isDisposed) { - // If the current activity has a remote parent, then we should start a child activity with the same parent ID. - Activity? activity = Activity.Current?.HasRemoteParent is true - ? Source.StartActivity(name, kind, parentId: Activity.Current.ParentId) - : Source.StartActivity(name); + return; + } - if (activity is null) - { - activity = new Activity(name); - activity.Start(); - } + _isDisposed = true; - return activity; + foreach (var collector in _collectors) + { + collector.Dispose(); } + + _collectors.Clear(); } } - public static class TelemetryHelpers - { - private const string EventPrefix = "VS/MSBuild/"; - private const string PropertyPrefix = "VS.MSBuild."; - // private const string PropertyPrefix = ""; + internal class MSBuildActivitySource + { + private readonly ActivitySource _source; - public static Activity StartActivity(string name, IDictionary initialProperties) + public MSBuildActivitySource() { - return NewOpenTelemetry.DefaultTelemetrySource - .StartActivity(EventPrefix + name, ActivityKind.Internal) - .WithTags(initialProperties); + _source = new ActivitySource( + TelemetryConstants.MSBuildSourceName, + TelemetryConstants.Version); } - public static Activity WithTags(this Activity activity, IDictionary tags) + + public Activity? StartActivity(string name, IDictionary? tags) { - if (tags is null) - { - return activity; - } + var activity = Activity.Current?.HasRemoteParent == true + ? _source.StartActivity(name, ActivityKind.Internal, parentId: Activity.Current.ParentId) + : _source.StartActivity(name); - foreach (KeyValuePair tag in tags) + if (activity != null && tags != null) { - activity.SetTag(PropertyPrefix + tag.Key, tag.Value); + foreach (var tag in tags) + { + activity.SetTag(tag.Key, tag.Value); + } } return activity; } } -} + + internal class OTLPCollector : IDisposable + { + private readonly MSBuildActivitySource _activitySource; + private TracerProvider? _tracerProvider; + private MeterProvider? _meterProvider; + + public OTLPCollector(MSBuildActivitySource activitySource) + { + _activitySource = activitySource; + } + + public OTLPCollector Initialize() + { + _tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddSource(TelemetryConstants.MSBuildSourceName) + .AddOtlpExporter() + .Build(); + + _meterProvider = Sdk.CreateMeterProviderBuilder() + .AddMeter(TelemetryConstants.MSBuildSourceName) + .Build(); + + return this; + } + + public void Dispose() + { + _tracerProvider?.Dispose(); + _meterProvider?.Dispose(); + } + } + +#if NETFRAMEWORK + internal class VSCollector : IDisposable + { + private const string VsMajorVersion = "17.0"; + + private readonly MSBuildActivitySource _activitySource; + private IOpenTelemetryCollector? _collector; + private TracerProvider? _tracerProvider; + private MeterProvider? _meterProvider; + + public VSCollector(MSBuildActivitySource activitySource) + { + _activitySource = activitySource; + } + + public VSCollector Initialize() + { + var exporterSettings = OpenTelemetryExporterSettingsBuilder + .CreateVSDefault(VsMajorVersion) + .Build(); + + var collectorSettings = OpenTelemetryCollectorSettingsBuilder + .CreateVSDefault(VsMajorVersion) + .Build(); + + _tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddVisualStudioDefaultTraceExporter(exporterSettings) + .AddSource(TelemetryConstants.MSBuildSourceName) + .Build(); + + _meterProvider = Sdk.CreateMeterProviderBuilder() + .AddVisualStudioDefaultMetricExporter(exporterSettings) + .AddMeter(TelemetryConstants.MSBuildSourceName) + .Build(); + + _collector = OpenTelemetryCollectorProvider.CreateCollector(collectorSettings); + + _collector.StartAsync(); + + return this; + } + + public void Dispose() + { + if (_collector != null) + { + _collector.Dispose(); + } + _tracerProvider?.Dispose(); + _meterProvider?.Dispose(); + } + } #endif + +} + From c50cbb38930c8f28de5bae09637f118e6e976284 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Wed, 18 Dec 2024 14:40:04 +0100 Subject: [PATCH 06/15] refactor, simplify --- Directory.Build.props | 2 +- eng/Packages.props | 1 - .../BackEnd/BuildManager/BuildManager.cs | 18 +- .../NodeProviderOutOfProcBase.cs | 4 +- .../Microsoft.Build.Framework.csproj | 22 +- .../IActivityTelemetryDataHolder.cs | 18 ++ src/Framework/Telemetry/BuildTelemetry.cs | 48 ++- src/Framework/Telemetry/NewOpenTelemetry.cs | 296 ------------------ .../Telemetry/OpenTelemetryManager.cs | 210 +++++++++++++ src/Framework/Telemetry/TelemetryConstants.cs | 135 ++++++++ src/MSBuild/XMake.cs | 2 + 11 files changed, 433 insertions(+), 323 deletions(-) create mode 100644 src/Framework/Telemetry/ActivityInstrumentation/IActivityTelemetryDataHolder.cs delete mode 100644 src/Framework/Telemetry/NewOpenTelemetry.cs create mode 100644 src/Framework/Telemetry/OpenTelemetryManager.cs create mode 100644 src/Framework/Telemetry/TelemetryConstants.cs diff --git a/Directory.Build.props b/Directory.Build.props index 889acdc0e67..b0454aa3564 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -57,7 +57,7 @@ RS0016 & RS0017: Roslyn analyzers seem to be bugged, claiming that API's that exist don't and vise-versa: https://github.com/dotnet/msbuild/issues/7903 --> - $(NoWarn);NU1507;NU1603;NU5105;1702;SYSLIB0011;SYSLIB0037;SYSLIB0044;RS0016;RS0017; + $(NoWarn);NU1507;NU1603;NU5105;1701;1702;SYSLIB0011;SYSLIB0037;SYSLIB0044;RS0016;RS0017; $(NoWarn);SYSLIB0057; diff --git a/eng/Packages.props b/eng/Packages.props index 8463cb362fe..28dcd31dbe1 100644 --- a/eng/Packages.props +++ b/eng/Packages.props @@ -36,7 +36,6 @@ - diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index b366bc78b39..4240dc2e9fd 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -492,7 +492,7 @@ public void BeginBuild(BuildParameters parameters) parameters.DetailedSummary = true; parameters.LogTaskInputs = true; } - BuildTelemetryManager.Initialize(false); + OpenTelemetryManager.Initialize(false); lock (_syncLock) { @@ -1030,7 +1030,6 @@ public void EndBuild() { try { - object TelemetryService = new(); ILoggingService? loggingService = ((IBuildComponentHost)this).LoggingService; if (loggingService != null) @@ -1071,16 +1070,15 @@ public void EndBuild() var sacState = NativeMethodsShared.GetSACState(); // The Enforcement would lead to build crash - but let's have the check for completeness sake. _buildTelemetry.SACEnabled = sacState == NativeMethodsShared.SAC_State.Evaluation || sacState == NativeMethodsShared.SAC_State.Enforcement; - // Debugger.Launch(); + loggingService.LogTelemetry(buildEventContext: null, _buildTelemetry.EventName, _buildTelemetry.GetProperties()); - var endOfBuildTelemetry = BuildTelemetryManager.StartActivity( - "Build", - new Dictionary { - { "IsBuildCheckEnabled", _buildTelemetry.BuildCheckEnabled }, - { "Target", _buildTelemetry.Target ?? "" } - }); + Activity? endOfBuildTelemetry = OpenTelemetryManager.DefaultActivitySource? + .StartActivity("Build")? + .WithTags(_buildTelemetry) + .WithStartTime(_buildTelemetry.InnerStartAt); + endOfBuildTelemetry?.Dispose(); - BuildTelemetryManager.Shutdown(); + OpenTelemetryManager.ForceFlush(); // Clean telemetry to make it ready for next build submission. _buildTelemetry = null; diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index 465ff552079..25ac326c562 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -334,7 +334,7 @@ bool StartNewNode(int nodeId) #endif // Create the node process INodeLauncher nodeLauncher = (INodeLauncher)_componentHost.GetComponent(BuildComponentType.NodeLauncher); - var activity = BuildTelemetryManager.StartActivity("NodeLaunching", new Dictionary() { }); + var activity = OpenTelemetryManager.DefaultActivitySource.StartActivity("NodeLaunching"); Process msbuildProcess = nodeLauncher.Start(msbuildLocation, commandLineArgs, nodeId); _processesToIgnore.TryAdd(GetProcessesToIgnoreKey(hostHandshake, msbuildProcess.Id), default); @@ -344,7 +344,7 @@ bool StartNewNode(int nodeId) // Now try to connect to it. Stream nodeStream = TryConnectToProcess(msbuildProcess.Id, TimeoutForNewNodeCreation, hostHandshake); - activity.Dispose(); + activity?.Dispose(); if (nodeStream != null) { // Connection successful, use this node. diff --git a/src/Framework/Microsoft.Build.Framework.csproj b/src/Framework/Microsoft.Build.Framework.csproj index 7d4d4f22165..6a0b9e476e3 100644 --- a/src/Framework/Microsoft.Build.Framework.csproj +++ b/src/Framework/Microsoft.Build.Framework.csproj @@ -15,6 +15,7 @@ + @@ -22,6 +23,15 @@ + + + + + + + + + @@ -44,18 +54,6 @@ - - - - - - - - - - - - diff --git a/src/Framework/Telemetry/ActivityInstrumentation/IActivityTelemetryDataHolder.cs b/src/Framework/Telemetry/ActivityInstrumentation/IActivityTelemetryDataHolder.cs new file mode 100644 index 00000000000..ec29dbd0d72 --- /dev/null +++ b/src/Framework/Telemetry/ActivityInstrumentation/IActivityTelemetryDataHolder.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.Build.Framework.Telemetry; + +internal record TelemetryItem(string Name, object Value, bool Hashed); + +/// +/// +/// +internal interface IActivityTelemetryDataHolder +{ + IList GetActivityProperties(); +} \ No newline at end of file diff --git a/src/Framework/Telemetry/BuildTelemetry.cs b/src/Framework/Telemetry/BuildTelemetry.cs index c23d9269c9b..f877c6bcf6e 100644 --- a/src/Framework/Telemetry/BuildTelemetry.cs +++ b/src/Framework/Telemetry/BuildTelemetry.cs @@ -10,7 +10,7 @@ namespace Microsoft.Build.Framework.Telemetry /// /// Telemetry of build. /// - internal class BuildTelemetry : TelemetryBase + internal class BuildTelemetry : TelemetryBase, IActivityTelemetryDataHolder { public override string EventName => "build"; @@ -167,5 +167,51 @@ public override IDictionary GetProperties() return properties; } + public IList GetActivityProperties() + { + List telemetryItems = new(); + + if (StartAt.HasValue && FinishedAt.HasValue) + { + telemetryItems.Add(new TelemetryItem("BuildDurationInMilliseconds", (FinishedAt.Value - StartAt.Value).TotalMilliseconds, false)); + } + + if (InnerStartAt.HasValue && FinishedAt.HasValue) + { + telemetryItems.Add(new TelemetryItem("InnerBuildDurationInMilliseconds", (FinishedAt.Value - InnerStartAt.Value).TotalMilliseconds, false)); + } + + if (Host != null) + { + telemetryItems.Add(new TelemetryItem("BuildEngineHost", Host, false)); + } + + if (Success.HasValue) + { + telemetryItems.Add(new TelemetryItem("BuildSuccess", Success, false)); + } + + if (Target != null) + { + telemetryItems.Add(new TelemetryItem("BuildTarget", Target, true)); + } + + if (Version != null) + { + telemetryItems.Add(new TelemetryItem("BuildEngineVersion", Version.ToString(), false)); + } + + if (BuildCheckEnabled != null) + { + telemetryItems.Add(new TelemetryItem("BuildCheckEnabled", BuildCheckEnabled, false)); + } + + if (SACEnabled != null) + { + telemetryItems.Add(new TelemetryItem("SACEnabled", SACEnabled, false)); + } + + return telemetryItems; + } } } diff --git a/src/Framework/Telemetry/NewOpenTelemetry.cs b/src/Framework/Telemetry/NewOpenTelemetry.cs deleted file mode 100644 index 50f0cb5f8e4..00000000000 --- a/src/Framework/Telemetry/NewOpenTelemetry.cs +++ /dev/null @@ -1,296 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -#if NETFRAMEWORK -using Microsoft.VisualStudio.OpenTelemetry.ClientExtensions; -using Microsoft.VisualStudio.OpenTelemetry.ClientExtensions.Exporters; -using Microsoft.VisualStudio.OpenTelemetry.Collector.Interfaces; -using Microsoft.VisualStudio.OpenTelemetry.Collector.Settings; -#endif -using OpenTelemetry; -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; - -namespace Microsoft.Build.Framework.Telemetry -{ - - public static class TelemetryConstants - { - public const string VSNamespace = "Microsoft.VisualStudio.OpenTelemetry.MSBuild"; - public const string MSBuildSourceName = "Microsoft.Build"; - public const string EventPrefix = "VS/MSBuild/"; - public const string PropertyPrefix = "VS.MSBuild."; - public const string Version = "1.0.0"; - } - - public class TelemetryConfiguration - { - private static readonly Lazy _instance = - new(() => new TelemetryConfiguration()); - - public static TelemetryConfiguration Instance => _instance.Value; - - // Will be populated with actual env vars later - public const string OptOutEnvVar = "PLACEHOLDER_OPTOUT"; - public const string VSTelemetryOptOutEnvVar = "PLACEHOLDER_VS_OPTOUT"; - public const string OTLPExportEnvVar = "PLACEHOLDER_OTLP_ENABLE"; - public const string NoCollectorsEnvVar = "PLACEHOLDER_NO_COLLECTORS"; - - private TelemetryConfiguration() - { - RefreshConfiguration(); - } - - public bool IsEnabled { get; private set; } - public bool IsVSTelemetryEnabled { get; private set; } - public bool IsOTLPExportEnabled { get; private set; } - public bool ShouldInitializeCollectors { get; private set; } - - public void RefreshConfiguration() - { - IsEnabled = !IsEnvVarEnabled(OptOutEnvVar); - IsVSTelemetryEnabled = IsEnabled && !IsEnvVarEnabled(VSTelemetryOptOutEnvVar); - // IsOTLPExportEnabled = IsEnabled && IsEnvVarEnabled(OTLPExportEnvVar); -#if DEBUG - IsOTLPExportEnabled = true; -#endif - ShouldInitializeCollectors = IsEnabled && !IsEnvVarEnabled(NoCollectorsEnvVar); - } - - private static bool IsEnvVarEnabled(string name) => - !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(name)); - } - - public static class BuildTelemetryManager - { - private static ITelemetrySession? _currentSession; - - public static void Initialize(bool isVisualStudioBuild, string? hostName = null) - { - if (_currentSession != null) - { - throw new InvalidOperationException("Telemetry session already initialized"); - } - - _currentSession = TelemetrySessionFactory.Create(isVisualStudioBuild, hostName); - } - - public static void Shutdown() - { - if (_currentSession != null) - { - _currentSession.Dispose(); - _currentSession = null; - } - } - - public static Activity? StartActivity(string name, IDictionary? tags = null) - { - return _currentSession?.StartActivity( - $"{TelemetryConstants.EventPrefix}{name}", - tags?.ToDictionary( - kvp => $"{TelemetryConstants.PropertyPrefix}{kvp.Key}", - kvp => kvp.Value)); - } - } - - // This would be internal in reality, shown here for completeness - internal interface ITelemetrySession : IDisposable - { - Activity? StartActivity(string name, IDictionary? tags = null); - } - internal static class TelemetrySessionFactory - { - public static ITelemetrySession Create(bool isVisualStudioBuild, string? hostName) - { - var session = new TelemetrySession(isVisualStudioBuild, hostName); - session.Initialize(); - return session; - } - } - - internal class TelemetrySession : ITelemetrySession - { - private readonly bool _isVisualStudioBuild; - private readonly string? _hostName; - private readonly MSBuildActivitySource _activitySource; - private readonly List _collectors; - private bool _isDisposed; - - public TelemetrySession(bool isVisualStudioBuild, string? hostName) - { - _isVisualStudioBuild = isVisualStudioBuild; - _hostName = hostName; - _activitySource = new MSBuildActivitySource(); - _collectors = new(); - } - - public void Initialize() - { - var config = TelemetryConfiguration.Instance; - - if (config.IsOTLPExportEnabled) - { - _collectors.Add(new OTLPCollector(_activitySource).Initialize()); - } - -#if NETFRAMEWORK - if (_isVisualStudioBuild && config.IsVSTelemetryEnabled) - { - _collectors.Add(new VSCollector(_activitySource).Initialize()); - } -#endif - } - - public Activity? StartActivity(string name, IDictionary? tags = null) - { - if (_isDisposed) - { - return null; - } - - return _activitySource.StartActivity(name, tags); - } - - public void Dispose() - { - if (_isDisposed) - { - return; - } - - _isDisposed = true; - - foreach (var collector in _collectors) - { - collector.Dispose(); - } - - _collectors.Clear(); - } - } - - internal class MSBuildActivitySource - { - private readonly ActivitySource _source; - - public MSBuildActivitySource() - { - _source = new ActivitySource( - TelemetryConstants.MSBuildSourceName, - TelemetryConstants.Version); - } - - public Activity? StartActivity(string name, IDictionary? tags) - { - var activity = Activity.Current?.HasRemoteParent == true - ? _source.StartActivity(name, ActivityKind.Internal, parentId: Activity.Current.ParentId) - : _source.StartActivity(name); - - if (activity != null && tags != null) - { - foreach (var tag in tags) - { - activity.SetTag(tag.Key, tag.Value); - } - } - - return activity; - } - } - - internal class OTLPCollector : IDisposable - { - private readonly MSBuildActivitySource _activitySource; - private TracerProvider? _tracerProvider; - private MeterProvider? _meterProvider; - - public OTLPCollector(MSBuildActivitySource activitySource) - { - _activitySource = activitySource; - } - - public OTLPCollector Initialize() - { - _tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddSource(TelemetryConstants.MSBuildSourceName) - .AddOtlpExporter() - .Build(); - - _meterProvider = Sdk.CreateMeterProviderBuilder() - .AddMeter(TelemetryConstants.MSBuildSourceName) - .Build(); - - return this; - } - - public void Dispose() - { - _tracerProvider?.Dispose(); - _meterProvider?.Dispose(); - } - } - -#if NETFRAMEWORK - internal class VSCollector : IDisposable - { - private const string VsMajorVersion = "17.0"; - - private readonly MSBuildActivitySource _activitySource; - private IOpenTelemetryCollector? _collector; - private TracerProvider? _tracerProvider; - private MeterProvider? _meterProvider; - - public VSCollector(MSBuildActivitySource activitySource) - { - _activitySource = activitySource; - } - - public VSCollector Initialize() - { - var exporterSettings = OpenTelemetryExporterSettingsBuilder - .CreateVSDefault(VsMajorVersion) - .Build(); - - var collectorSettings = OpenTelemetryCollectorSettingsBuilder - .CreateVSDefault(VsMajorVersion) - .Build(); - - _tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddVisualStudioDefaultTraceExporter(exporterSettings) - .AddSource(TelemetryConstants.MSBuildSourceName) - .Build(); - - _meterProvider = Sdk.CreateMeterProviderBuilder() - .AddVisualStudioDefaultMetricExporter(exporterSettings) - .AddMeter(TelemetryConstants.MSBuildSourceName) - .Build(); - - _collector = OpenTelemetryCollectorProvider.CreateCollector(collectorSettings); - - _collector.StartAsync(); - - return this; - } - - public void Dispose() - { - if (_collector != null) - { - _collector.Dispose(); - } - _tracerProvider?.Dispose(); - _meterProvider?.Dispose(); - } - } -#endif - -} - diff --git a/src/Framework/Telemetry/OpenTelemetryManager.cs b/src/Framework/Telemetry/OpenTelemetryManager.cs new file mode 100644 index 00000000000..dbb2a1d46d8 --- /dev/null +++ b/src/Framework/Telemetry/OpenTelemetryManager.cs @@ -0,0 +1,210 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +#if NETFRAMEWORK +using Microsoft.VisualStudio.OpenTelemetry.ClientExtensions; +using Microsoft.VisualStudio.OpenTelemetry.ClientExtensions.Exporters; +using Microsoft.VisualStudio.OpenTelemetry.Collector.Interfaces; +using Microsoft.VisualStudio.OpenTelemetry.Collector.Settings; +using OpenTelemetry; +using OpenTelemetry.Trace; +#endif +#if DEBUG && NETFRAMEWORK +using OpenTelemetry.Exporter; +#endif + +namespace Microsoft.Build.Framework.Telemetry +{ + + /// + /// Class for configuring and managing the telemetry infrastructure with System.Diagnostics.Activity, OpenTelemetry SDK and VS OpenTelemetry Collector. + /// + internal static class OpenTelemetryManager + { + private static bool _initialized = false; + private static readonly object s_initialize_lock = new(); + +#if NETFRAMEWORK + private static TracerProvider? s_tracerProvider; + private static IOpenTelemetryCollector? s_collector; +#endif + + public static MSBuildActivitySource? DefaultActivitySource { get; set; } + + public static void Initialize(bool isStandalone) + { + lock (s_initialize_lock) + { + if (!ShouldInitialize()) + { + return; + } + + // create activity source + DefaultActivitySource = new MSBuildActivitySource(TelemetryConstants.DefaultActivitySourceNamespace); + + // create trace exporter in framework +#if NETFRAMEWORK + var exporterSettings = OpenTelemetryExporterSettingsBuilder + .CreateVSDefault(TelemetryConstants.VSMajorVersion) + .Build(); + + TracerProviderBuilder tracerProviderBuilder = OpenTelemetry.Sdk + .CreateTracerProviderBuilder() + .AddSource(TelemetryConstants.DefaultActivitySourceNamespace) + .AddVisualStudioDefaultTraceExporter(exporterSettings); + + s_tracerProvider = + tracerProviderBuilder +#if DEBUG + .AddOtlpExporter() +#endif + .Build(); + + // create collector if not in vs + if (isStandalone) + { + IOpenTelemetryCollectorSettings collectorSettings = OpenTelemetryCollectorSettingsBuilder + .CreateVSDefault(TelemetryConstants.VSMajorVersion) + .Build(); + + s_collector = OpenTelemetryCollectorProvider + .CreateCollector(collectorSettings); + s_collector.StartAsync().Wait(); + } +#endif + _initialized = true; + } + } + + public static void ForceFlush() + { + lock (s_initialize_lock) + { + if (_initialized) + { +#if NETFRAMEWORK + s_tracerProvider?.ForceFlush(); +#endif + } + } + } + private static bool ShouldInitialize() + { + // only initialize once + if (_initialized) + { + return false; + } + + string? dotnetCliOptout = Environment.GetEnvironmentVariable(TelemetryConstants.DotnetOptOut); + if (dotnetCliOptout == "1" || dotnetCliOptout == "true") + { + return false; + } + + string? msbuildCliOptout = Environment.GetEnvironmentVariable(TelemetryConstants.MSBuildOptout); + if (msbuildCliOptout == "1" || msbuildCliOptout == "true") + { + return false; + } + + return true; + } + + public static void Shutdown() + { + lock (s_initialize_lock) + { + if (_initialized) + { +#if NETFRAMEWORK + s_tracerProvider?.Shutdown(); + s_collector?.Dispose(); +#endif + } + } + } + } + + internal class MSBuildActivitySource + { + private readonly ActivitySource _source; + + public MSBuildActivitySource(string name) + { + _source = new ActivitySource(name); + } + + public Activity? StartActivity(string name) + { + var activity = Activity.Current?.HasRemoteParent == true + ? _source.StartActivity($"{TelemetryConstants.EventPrefix}{name}", ActivityKind.Internal, parentId: Activity.Current.ParentId) + : _source.StartActivity($"{TelemetryConstants.EventPrefix}{name}"); + return activity; + } + } + + internal static class ActivityExtensions + { + public static Activity WithTags(this Activity activity, IActivityTelemetryDataHolder dataHolder) + { + if (dataHolder != null) + { + foreach ((string name, object value, bool hashed) in dataHolder.GetActivityProperties()) + { + object? hashedValue = null; + if (hashed) + { + // TODO: make this work + hashedValue = value; + + // Hash the value via Visual Studio mechanism in Framework & same algo as in core telemetry hashing + // https://github.com/dotnet/sdk/blob/8bd19a2390a6bba4aa80d1ac3b6c5385527cc311/src/Cli/Microsoft.DotNet.Cli.Utils/Sha256Hasher.cs +#if NETFRAMEWORK + // hashedValue = new Microsoft.VisualStudio.Telemetry.TelemetryHashedProperty(value +#endif + } + + activity.SetTag($"{TelemetryConstants.PropertyPrefix}{name}", hashed ? hashedValue : value); + } + } + return activity; + } + + public static Activity WithTags(this Activity activity, IDictionary? tags) + { + if (tags != null) + { + foreach (var tag in tags) + { + activity.SetTag($"{TelemetryConstants.PropertyPrefix}{tag.Key}", tag.Value); + } + } + + return activity; + } + + public static Activity WithTag(this Activity activity, string name, object value, bool hashed = false) + { + activity.SetTag($"{TelemetryConstants.PropertyPrefix}{name}", hashed ? value.GetHashCode() : value); + return activity; + } + + public static Activity WithStartTime(this Activity activity, DateTime? startTime) + { + if (startTime.HasValue) + { + activity.SetStartTime(startTime.Value); + } + return activity; + } + } +} diff --git a/src/Framework/Telemetry/TelemetryConstants.cs b/src/Framework/Telemetry/TelemetryConstants.cs new file mode 100644 index 00000000000..48badb4c813 --- /dev/null +++ b/src/Framework/Telemetry/TelemetryConstants.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +#if NETFRAMEWORK +using Microsoft.VisualStudio.OpenTelemetry.ClientExtensions; +using Microsoft.VisualStudio.OpenTelemetry.ClientExtensions.Exporters; +using Microsoft.VisualStudio.OpenTelemetry.Collector.Interfaces; +using Microsoft.VisualStudio.OpenTelemetry.Collector.Settings; +using OpenTelemetry; +using OpenTelemetry.Trace; +#endif + +#if DEBUG && NETFRAMEWORK +using OpenTelemetry.Exporter; +#endif + +namespace Microsoft.Build.Framework.Telemetry +{ + + internal static class TelemetryConstants + { + /// + /// "Microsoft.VisualStudio.OpenTelemetry.*" namespace is required by VS exporting/collection. + /// + public const string DefaultActivitySourceNamespace = "Microsoft.VisualStudio.OpenTelemetry.MSBuild"; + public const string EventPrefix = "VS/MSBuild/"; + public const string PropertyPrefix = "VS.MSBuild."; + /// + /// For VS OpenTelemetry Collector to apply the correct privacy policy. + /// + public const string VSMajorVersion = "17.0"; + + /// + /// https://learn.microsoft.com/en-us/dotnet/core/tools/telemetry + /// + public const string DotnetOptOut = "DOTNET_CLI_TELEMETRY_OPTOUT"; + public const string MSBuildOptout = "MSBUILD_TELEMETRY_OPTOUT"; + } + + /* + internal class OTLPCollector : IDisposable + { + private readonly MSBuildActivitySource _activitySource; + private TracerProvider? _tracerProvider; + private MeterProvider? _meterProvider; + + public OTLPCollector(MSBuildActivitySource activitySource) + { + _activitySource = activitySource; + } + + public OTLPCollector Initialize() + { + _tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddSource(TelemetryConstants.MSBuildSourceName) + .AddOtlpExporter() + .Build(); + + _meterProvider = Sdk.CreateMeterProviderBuilder() + .AddMeter(TelemetryConstants.MSBuildSourceName) + .Build(); + + return this; + } + + public void Dispose() + { + _tracerProvider?.Dispose(); + _meterProvider?.Dispose(); + } + } + */ + /* +#if NETFRAMEWORK + internal class VSCollector : IDisposable + { + private const string VsMajorVersion = "17.0"; + + private readonly MSBuildActivitySource _activitySource; + private IOpenTelemetryCollector? _collector; + private TracerProvider? _tracerProvider; + private MeterProvider? _meterProvider; + + public VSCollector(MSBuildActivitySource activitySource) + { + _activitySource = activitySource; + } + + public VSCollector Initialize() + { + var exporterSettings = OpenTelemetryExporterSettingsBuilder + .CreateVSDefault(VsMajorVersion) + .Build(); + + var collectorSettings = OpenTelemetryCollectorSettingsBuilder + .CreateVSDefault(VsMajorVersion) + .Build(); + + _tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddVisualStudioDefaultTraceExporter(exporterSettings) + .AddSource(TelemetryConstants.MSBuildSourceName) + .Build(); + + _meterProvider = Sdk.CreateMeterProviderBuilder() + .AddVisualStudioDefaultMetricExporter(exporterSettings) + .AddMeter(TelemetryConstants.MSBuildSourceName) + .Build(); + + _collector = OpenTelemetryCollectorProvider.CreateCollector(collectorSettings); + + _collector.StartAsync(); + + return this; + } + + public void Dispose() + { + if (_collector != null) + { + _collector.Dispose(); + } + _tracerProvider?.Dispose(); + _meterProvider?.Dispose(); + } + } +#endif +*/ +} diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs index aeddef7aba4..85717fb952e 100644 --- a/src/MSBuild/XMake.cs +++ b/src/MSBuild/XMake.cs @@ -246,6 +246,7 @@ string[] args DebuggerLaunchCheck(); + OpenTelemetryManager.Initialize(true); // Initialize new build telemetry and record start of this build. KnownTelemetry.PartialBuildTelemetry = new BuildTelemetry { StartAt = DateTime.UtcNow }; @@ -296,6 +297,7 @@ string[] args DumpCounters(false /* log to console */); } + OpenTelemetryManager.Shutdown(); return exitCode; } From 351ec892ccec0480f8769c63b552536bf3bf35e7 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Wed, 18 Dec 2024 15:00:38 +0100 Subject: [PATCH 07/15] cleanup, move DiagnosticSource pkg include to framework only --- .../Microsoft.Build.Framework.csproj | 2 +- src/Framework/Telemetry/TelemetryConstants.cs | 148 +++--------------- 2 files changed, 19 insertions(+), 131 deletions(-) diff --git a/src/Framework/Microsoft.Build.Framework.csproj b/src/Framework/Microsoft.Build.Framework.csproj index 6a0b9e476e3..dcdd7d1e32c 100644 --- a/src/Framework/Microsoft.Build.Framework.csproj +++ b/src/Framework/Microsoft.Build.Framework.csproj @@ -15,7 +15,6 @@ - @@ -24,6 +23,7 @@ + diff --git a/src/Framework/Telemetry/TelemetryConstants.cs b/src/Framework/Telemetry/TelemetryConstants.cs index 48badb4c813..77911ed5dee 100644 --- a/src/Framework/Telemetry/TelemetryConstants.cs +++ b/src/Framework/Telemetry/TelemetryConstants.cs @@ -1,135 +1,23 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; +namespace Microsoft.Build.Framework.Telemetry; -#if NETFRAMEWORK -using Microsoft.VisualStudio.OpenTelemetry.ClientExtensions; -using Microsoft.VisualStudio.OpenTelemetry.ClientExtensions.Exporters; -using Microsoft.VisualStudio.OpenTelemetry.Collector.Interfaces; -using Microsoft.VisualStudio.OpenTelemetry.Collector.Settings; -using OpenTelemetry; -using OpenTelemetry.Trace; -#endif - -#if DEBUG && NETFRAMEWORK -using OpenTelemetry.Exporter; -#endif - -namespace Microsoft.Build.Framework.Telemetry +internal static class TelemetryConstants { - - internal static class TelemetryConstants - { - /// - /// "Microsoft.VisualStudio.OpenTelemetry.*" namespace is required by VS exporting/collection. - /// - public const string DefaultActivitySourceNamespace = "Microsoft.VisualStudio.OpenTelemetry.MSBuild"; - public const string EventPrefix = "VS/MSBuild/"; - public const string PropertyPrefix = "VS.MSBuild."; - /// - /// For VS OpenTelemetry Collector to apply the correct privacy policy. - /// - public const string VSMajorVersion = "17.0"; - - /// - /// https://learn.microsoft.com/en-us/dotnet/core/tools/telemetry - /// - public const string DotnetOptOut = "DOTNET_CLI_TELEMETRY_OPTOUT"; - public const string MSBuildOptout = "MSBUILD_TELEMETRY_OPTOUT"; - } - - /* - internal class OTLPCollector : IDisposable - { - private readonly MSBuildActivitySource _activitySource; - private TracerProvider? _tracerProvider; - private MeterProvider? _meterProvider; - - public OTLPCollector(MSBuildActivitySource activitySource) - { - _activitySource = activitySource; - } - - public OTLPCollector Initialize() - { - _tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddSource(TelemetryConstants.MSBuildSourceName) - .AddOtlpExporter() - .Build(); - - _meterProvider = Sdk.CreateMeterProviderBuilder() - .AddMeter(TelemetryConstants.MSBuildSourceName) - .Build(); - - return this; - } - - public void Dispose() - { - _tracerProvider?.Dispose(); - _meterProvider?.Dispose(); - } - } - */ - /* -#if NETFRAMEWORK - internal class VSCollector : IDisposable - { - private const string VsMajorVersion = "17.0"; - - private readonly MSBuildActivitySource _activitySource; - private IOpenTelemetryCollector? _collector; - private TracerProvider? _tracerProvider; - private MeterProvider? _meterProvider; - - public VSCollector(MSBuildActivitySource activitySource) - { - _activitySource = activitySource; - } - - public VSCollector Initialize() - { - var exporterSettings = OpenTelemetryExporterSettingsBuilder - .CreateVSDefault(VsMajorVersion) - .Build(); - - var collectorSettings = OpenTelemetryCollectorSettingsBuilder - .CreateVSDefault(VsMajorVersion) - .Build(); - - _tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddVisualStudioDefaultTraceExporter(exporterSettings) - .AddSource(TelemetryConstants.MSBuildSourceName) - .Build(); - - _meterProvider = Sdk.CreateMeterProviderBuilder() - .AddVisualStudioDefaultMetricExporter(exporterSettings) - .AddMeter(TelemetryConstants.MSBuildSourceName) - .Build(); - - _collector = OpenTelemetryCollectorProvider.CreateCollector(collectorSettings); - - _collector.StartAsync(); - - return this; - } - - public void Dispose() - { - if (_collector != null) - { - _collector.Dispose(); - } - _tracerProvider?.Dispose(); - _meterProvider?.Dispose(); - } - } -#endif -*/ + /// + /// "Microsoft.VisualStudio.OpenTelemetry.*" namespace is required by VS exporting/collection. + /// + public const string DefaultActivitySourceNamespace = "Microsoft.VisualStudio.OpenTelemetry.MSBuild"; + public const string EventPrefix = "VS/MSBuild/"; + public const string PropertyPrefix = "VS.MSBuild."; + /// + /// For VS OpenTelemetry Collector to apply the correct privacy policy. + /// + public const string VSMajorVersion = "17.0"; + + /// + /// https://learn.microsoft.com/en-us/dotnet/core/tools/telemetry + /// + public const string DotnetOptOut = "DOTNET_CLI_TELEMETRY_OPTOUT"; + public const string MSBuildOptout = "MSBUILD_TELEMETRY_OPTOUT"; } From dac30f2a7979c2402ab57d06719c6b0a4ad9d74d Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Wed, 18 Dec 2024 15:13:11 +0100 Subject: [PATCH 08/15] move diagnostic source back --- src/Framework/Microsoft.Build.Framework.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Framework/Microsoft.Build.Framework.csproj b/src/Framework/Microsoft.Build.Framework.csproj index dcdd7d1e32c..502624a4d5d 100644 --- a/src/Framework/Microsoft.Build.Framework.csproj +++ b/src/Framework/Microsoft.Build.Framework.csproj @@ -14,6 +14,7 @@ + @@ -23,7 +24,6 @@ - From 540a643b9407ed47de8782bd9d658b2c6d1b30c6 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Wed, 18 Dec 2024 15:35:24 +0100 Subject: [PATCH 09/15] fix usage of activitysource, core otel behavior is opt-in --- .../NodeProviderOutOfProcBase.cs | 2 +- .../Telemetry/OpenTelemetryManager.cs | 22 ++++++++++++++----- src/Framework/Telemetry/TelemetryConstants.cs | 4 +++- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index 25ac326c562..c1080c96c0a 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -334,7 +334,7 @@ bool StartNewNode(int nodeId) #endif // Create the node process INodeLauncher nodeLauncher = (INodeLauncher)_componentHost.GetComponent(BuildComponentType.NodeLauncher); - var activity = OpenTelemetryManager.DefaultActivitySource.StartActivity("NodeLaunching"); + var activity = OpenTelemetryManager.DefaultActivitySource?.StartActivity("NodeLaunching"); Process msbuildProcess = nodeLauncher.Start(msbuildLocation, commandLineArgs, nodeId); _processesToIgnore.TryAdd(GetProcessesToIgnoreKey(hostHandshake, msbuildProcess.Id), default); diff --git a/src/Framework/Telemetry/OpenTelemetryManager.cs b/src/Framework/Telemetry/OpenTelemetryManager.cs index dbb2a1d46d8..472fbd89580 100644 --- a/src/Framework/Telemetry/OpenTelemetryManager.cs +++ b/src/Framework/Telemetry/OpenTelemetryManager.cs @@ -104,19 +104,29 @@ private static bool ShouldInitialize() return false; } - string? dotnetCliOptout = Environment.GetEnvironmentVariable(TelemetryConstants.DotnetOptOut); - if (dotnetCliOptout == "1" || dotnetCliOptout == "true") + string? dotnetCliTelemetryOptOut = Environment.GetEnvironmentVariable(TelemetryConstants.DotnetOptOut); + if (dotnetCliTelemetryOptOut == "1" || dotnetCliTelemetryOptOut == "true") { return false; } - - string? msbuildCliOptout = Environment.GetEnvironmentVariable(TelemetryConstants.MSBuildOptout); - if (msbuildCliOptout == "1" || msbuildCliOptout == "true") +#if NETFRAMEWORK + string? telemetryMSBuildOptOut = Environment.GetEnvironmentVariable(TelemetryConstants.MSBuildFxOptout); + if (telemetryMSBuildOptOut == "1" || telemetryMSBuildOptOut == "true") { return false; } - return true; +#else + string? telemetryOptIn = Environment.GetEnvironmentVariable(TelemetryConstants.MSBuildCoreOptin); + if (telemetryOptIn == "1" || telemetryOptIn == "true") + { + return true; + } + return false; + + +#endif + } public static void Shutdown() diff --git a/src/Framework/Telemetry/TelemetryConstants.cs b/src/Framework/Telemetry/TelemetryConstants.cs index 77911ed5dee..b76d2a93f1e 100644 --- a/src/Framework/Telemetry/TelemetryConstants.cs +++ b/src/Framework/Telemetry/TelemetryConstants.cs @@ -19,5 +19,7 @@ internal static class TelemetryConstants /// https://learn.microsoft.com/en-us/dotnet/core/tools/telemetry /// public const string DotnetOptOut = "DOTNET_CLI_TELEMETRY_OPTOUT"; - public const string MSBuildOptout = "MSBUILD_TELEMETRY_OPTOUT"; + public const string MSBuildFxOptout = "MSBUILD_TELEMETRY_OPTOUT"; + public const string MSBuildCoreOptin = "MSBUILD_TELEMETRY_OPTIN"; + } From 3ae496a5bee8b7640b571c6ea65db96d1186aa8d Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Wed, 18 Dec 2024 15:42:50 +0100 Subject: [PATCH 10/15] ... --- src/Framework/Telemetry/TelemetryConstants.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Framework/Telemetry/TelemetryConstants.cs b/src/Framework/Telemetry/TelemetryConstants.cs index b76d2a93f1e..1f456b34ff2 100644 --- a/src/Framework/Telemetry/TelemetryConstants.cs +++ b/src/Framework/Telemetry/TelemetryConstants.cs @@ -21,5 +21,4 @@ internal static class TelemetryConstants public const string DotnetOptOut = "DOTNET_CLI_TELEMETRY_OPTOUT"; public const string MSBuildFxOptout = "MSBUILD_TELEMETRY_OPTOUT"; public const string MSBuildCoreOptin = "MSBUILD_TELEMETRY_OPTIN"; - } From 77fb1c48b05ef37a57b890ab4dfdd1e35d580499 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Wed, 18 Dec 2024 17:07:36 +0100 Subject: [PATCH 11/15] don't otlp export for ci build? --- src/Framework/Telemetry/OpenTelemetryManager.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Framework/Telemetry/OpenTelemetryManager.cs b/src/Framework/Telemetry/OpenTelemetryManager.cs index 472fbd89580..43e6f640b1a 100644 --- a/src/Framework/Telemetry/OpenTelemetryManager.cs +++ b/src/Framework/Telemetry/OpenTelemetryManager.cs @@ -16,9 +16,9 @@ using OpenTelemetry; using OpenTelemetry.Trace; #endif -#if DEBUG && NETFRAMEWORK -using OpenTelemetry.Exporter; -#endif +// #if DEBUG && NETFRAMEWORK +// using OpenTelemetry.Exporter; +// #endif namespace Microsoft.Build.Framework.Telemetry { @@ -63,10 +63,12 @@ public static void Initialize(bool isStandalone) s_tracerProvider = tracerProviderBuilder + /* #if DEBUG .AddOtlpExporter() #endif - .Build(); + */ + .Build(); // create collector if not in vs if (isStandalone) @@ -207,7 +209,7 @@ public static Activity WithTag(this Activity activity, string name, object value activity.SetTag($"{TelemetryConstants.PropertyPrefix}{name}", hashed ? value.GetHashCode() : value); return activity; } - + public static Activity WithStartTime(this Activity activity, DateTime? startTime) { if (startTime.HasValue) From bfad17a7dd57e713dc0c46d77f441a766f9bac58 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Wed, 18 Dec 2024 18:12:22 +0100 Subject: [PATCH 12/15] add extensions primitives --- eng/Packages.props | 1 + src/Framework/Microsoft.Build.Framework.csproj | 1 + src/MSBuild/app.config | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/eng/Packages.props b/eng/Packages.props index 28dcd31dbe1..671a5ba79d4 100644 --- a/eng/Packages.props +++ b/eng/Packages.props @@ -39,6 +39,7 @@ + diff --git a/src/Framework/Microsoft.Build.Framework.csproj b/src/Framework/Microsoft.Build.Framework.csproj index 502624a4d5d..fb338a80ab6 100644 --- a/src/Framework/Microsoft.Build.Framework.csproj +++ b/src/Framework/Microsoft.Build.Framework.csproj @@ -15,6 +15,7 @@ + diff --git a/src/MSBuild/app.config b/src/MSBuild/app.config index 71ca93c8605..1e15f91cb19 100644 --- a/src/MSBuild/app.config +++ b/src/MSBuild/app.config @@ -68,6 +68,10 @@ + + + + From f6dd0554664fe7e2c665c429d3e1b80ceb165d42 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Thu, 19 Dec 2024 15:38:00 +0100 Subject: [PATCH 13/15] move dependencies --- src/Framework/Microsoft.Build.Framework.csproj | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Framework/Microsoft.Build.Framework.csproj b/src/Framework/Microsoft.Build.Framework.csproj index fb338a80ab6..735264448f0 100644 --- a/src/Framework/Microsoft.Build.Framework.csproj +++ b/src/Framework/Microsoft.Build.Framework.csproj @@ -14,8 +14,6 @@ - - @@ -28,12 +26,20 @@ + + + + --> + + + + + From 8282fa1a2b39076fed299564a11197d744cee6fe Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Fri, 20 Dec 2024 14:21:42 +0100 Subject: [PATCH 14/15] downgrade packages --- eng/Packages.props | 14 ++++++-------- eng/Versions.props | 5 +++++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/eng/Packages.props b/eng/Packages.props index 671a5ba79d4..cfc7e34a83c 100644 --- a/eng/Packages.props +++ b/eng/Packages.props @@ -33,13 +33,11 @@ - - - - - - - - + + + + + + diff --git a/eng/Versions.props b/eng/Versions.props index a773ffaf11f..c496e7ffbd7 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -54,6 +54,11 @@ 8.0.5 8.0.0 8.0.0 + 0.1.700-beta + 1.9.0 + 8.0.0 + 8.0.0 + 8.0.0 From c182d33e2a2ac6451fe1dc7ea38818d89ccf4290 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Fri, 20 Dec 2024 14:27:11 +0100 Subject: [PATCH 15/15] upgrade packages --- eng/Versions.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index c496e7ffbd7..148132bbb1f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -55,10 +55,10 @@ 8.0.0 8.0.0 0.1.700-beta - 1.9.0 - 8.0.0 - 8.0.0 - 8.0.0 + 1.10.0 + 9.0.0 + 9.0.0 + 9.0.0