| File: TelemetryInfra\TelemetryCollectorProvider.cs | Web Access |
| Project: src\msbuild\src\Build\Microsoft.Build.csproj (Microsoft.Build) |
// 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 Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Framework; using Microsoft.Build.Framework.Telemetry; namespace Microsoft.Build.TelemetryInfra; /// <summary> /// Build component that creates per-BuildRequestEngine <see cref="ITelemetryCollector"/> instances. /// Registered as a singleton, but holds no mutable state — each BuildRequestEngine gets its own /// collector via <see cref="CreateCollector"/>. /// </summary> internal class TelemetryCollectorProvider : IBuildComponent { private bool _telemetryEnabled; /// <summary> /// Creates a new <see cref="ITelemetryCollector"/> scoped to one <see cref="BuildRequestEngine"/>'s build lifetime. /// Returns a no-op collector when telemetry is disabled. /// </summary> internal ITelemetryCollector CreateCollector() => _telemetryEnabled ? new TelemetryCollector() : NullTelemetryCollector.Instance; internal static IBuildComponent CreateComponent(BuildComponentType type) { Assumed.Equal(type, BuildComponentType.TelemetryCollector, $"Cannot create components of type {type}"); return new TelemetryCollectorProvider(); } public void InitializeComponent(IBuildComponentHost host) { Assumed.NotNull(host, "BuildComponentHost was null"); _telemetryEnabled = host!.BuildParameters.IsTelemetryEnabled; } public void ShutdownComponent() { } /// <summary> /// Collects task/target telemetry for one BuildRequestEngine. Not thread-safe — /// only one RequestBuilder is active at a time per BuildRequestEngine. /// </summary> internal class TelemetryCollector : ITelemetryCollector { private WorkerNodeTelemetryData _data = new(); public bool IsTelemetryCollected => true; public void AddTarget(TaskOrTargetTelemetryKey key, bool wasExecuted, TargetSkipReason skipReason = TargetSkipReason.None) { _data.AddTarget(key, wasExecuted, skipReason); } public void AddTask(TaskOrTargetTelemetryKey key, TimeSpan cumulativeExecutionTime, int executionsCount, long totalMemoryConsumed, string? taskFactoryName, string? taskHostRuntime) { _data.AddTask(key, cumulativeExecutionTime, executionsCount, totalMemoryConsumed, taskFactoryName, taskHostRuntime); } public void FinalizeProcessing(LoggingContext loggingContext) { if (_data.IsEmpty) { return; } WorkerNodeTelemetryData snapshot = _data; _data = new(); WorkerNodeTelemetryEventArgs telemetryArgs = new(snapshot) { BuildEventContext = loggingContext.BuildEventContext }; loggingContext.LogBuildEvent(telemetryArgs); } } internal class NullTelemetryCollector : ITelemetryCollector { internal static readonly NullTelemetryCollector Instance = new(); public bool IsTelemetryCollected => false; public void AddTarget(TaskOrTargetTelemetryKey key, bool wasExecuted, TargetSkipReason skipReason = TargetSkipReason.None) { } public void AddTask(TaskOrTargetTelemetryKey key, TimeSpan cumulativeExecutionTime, int executionsCount, long totalMemoryConsumed, string? taskFactoryName, string? taskHostRuntime) { } public void FinalizeProcessing(LoggingContext loggingContext) { } } }