File: TelemetryInfra\TelemetryForwarderProvider.cs
Web Access
Project: ..\..\..\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.Shared;
 
namespace Microsoft.Build.TelemetryInfra;
 
/// <summary>
/// A build component responsible for accumulating telemetry data from worker node and then sending it to main node
/// at the end of the build.
/// </summary>
internal class TelemetryForwarderProvider : IBuildComponent
{
    private ITelemetryForwarder? _instance;
 
    public ITelemetryForwarder Instance => _instance ?? new NullTelemetryForwarder();
 
    internal static IBuildComponent CreateComponent(BuildComponentType type)
    {
        ErrorUtilities.VerifyThrow(type == BuildComponentType.TelemetryForwarder, "Cannot create components of type {0}", type);
        return new TelemetryForwarderProvider();
    }
 
    public void InitializeComponent(IBuildComponentHost host)
    {
        ErrorUtilities.VerifyThrow(host != null, "BuildComponentHost was null");
 
        if (_instance == null)
        {
            if (host!.BuildParameters.IsTelemetryEnabled)
            {
                _instance = new TelemetryForwarder();
            }
            else
            {
                _instance = new NullTelemetryForwarder();
            }
        }
    }
 
    public void ShutdownComponent()
    {
        /* Too late here for any communication to the main node or for logging anything. Just cleanup. */
        _instance = null;
    }
 
    public class TelemetryForwarder : ITelemetryForwarder
    {
        private readonly WorkerNodeTelemetryData _workerNodeTelemetryData = new();
 
        // in future, this might be per event type
        public bool IsTelemetryCollected => true;
 
        public void AddTask(string name, TimeSpan cumulativeExecutionTime, short executionsCount, long totalMemoryConsumed, bool isCustom, bool isFromNugetCache)
        {
            var key = GetKey(name, isCustom, false, isFromNugetCache);
            _workerNodeTelemetryData.AddTask(key, cumulativeExecutionTime, executionsCount, totalMemoryConsumed);
        }
 
        public void AddTarget(string name, bool wasExecuted, bool isCustom, bool isMetaproj, bool isFromNugetCache)
        {
            var key = GetKey(name, isCustom, isMetaproj, isFromNugetCache);
            _workerNodeTelemetryData.AddTarget(key, wasExecuted);
        }
 
        private static TaskOrTargetTelemetryKey GetKey(string name, bool isCustom, bool isMetaproj,
            bool isFromNugetCache)
            => new TaskOrTargetTelemetryKey(name, isCustom, isFromNugetCache, isMetaproj);
 
        public void FinalizeProcessing(LoggingContext loggingContext)
        {
            WorkerNodeTelemetryEventArgs telemetryArgs = new(_workerNodeTelemetryData)
                { BuildEventContext = loggingContext.BuildEventContext };
            loggingContext.LogBuildEvent(telemetryArgs);
        }
    }
 
    public class NullTelemetryForwarder : ITelemetryForwarder
    {
        public bool IsTelemetryCollected => false;
 
        public void AddTask(string name, TimeSpan cumulativeExecutionTime, short executionsCount, long totalMemoryConsumed, bool isCustom, bool isFromNugetCache) { }
        public void AddTarget(string name, bool wasExecuted, bool isCustom, bool isMetaproj, bool isFromNugetCache) { }
 
        public void FinalizeProcessing(LoggingContext loggingContext) { }
    }
}