File: Telemetry\BuildCheckTelemetry.cs
Web Access
Project: ..\..\..\src\Framework\Microsoft.Build.Framework.csproj (Microsoft.Build.Framework)
// 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.Globalization;
using System.Linq;
using Microsoft.Build.Experimental.BuildCheck;
 
namespace Microsoft.Build.Framework.Telemetry;
 
internal class BuildCheckTelemetry
{
    private const string FailedAcquisitionEventName = "buildcheck/acquisitionfailure";
    private const string RunEventName = "buildcheck/run";
    private const string RuleStatsEventName = "buildcheck/rule";
    private Guid _submissionId = Guid.NewGuid();
 
    /// <summary>
    /// Translates failed acquisition event to telemetry transport data.
    /// </summary>
    internal (string, IDictionary<string, string>) ProcessCustomCheckLoadingFailure(string assemblyName,
        Exception exception)
    {
        var properties = new Dictionary<string, string>();
        properties["SubmissionId"] = _submissionId.ToString();
        properties["AssemblyName"] = assemblyName;
        string? exceptionType = exception.GetType().FullName;
        if (exceptionType != null)
        {
            properties["ExceptionType"] = exceptionType;
        }
        if (exception.Message != null)
        {
            properties["ExceptionMessage"] = exception.Message;
        }
 
        return (FailedAcquisitionEventName, properties);
    }
 
    /// <summary>
    /// Translates BuildCheck tracing data to telemetry transport data.
    /// </summary>
    internal IEnumerable<(string, IDictionary<string, string>)> ProcessBuildCheckTracingData(BuildCheckTracingData data)
    {
        int rulesCount = data.TelemetryData.Count;
        int customRulesCount = data.TelemetryData.Count(t => !t.Value.IsBuiltIn);
        int violationsCount = data.TelemetryData.Sum(t => t.Value.ViolationsCount);
        long runtimeTicks = data.ExtractCheckStats().Sum(v => v.Value.Ticks);
        runtimeTicks += data.InfrastructureTracingData.Sum(v => v.Value.Ticks);
        TimeSpan totalRuntime = new TimeSpan(runtimeTicks);
 
        var properties = new Dictionary<string, string>();
        properties["SubmissionId"] = _submissionId.ToString();
        properties["RulesCount"] = rulesCount.ToString(CultureInfo.InvariantCulture);
        properties["CustomRulesCount"] = customRulesCount.ToString(CultureInfo.InvariantCulture);
        properties["ViolationsCount"] = violationsCount.ToString(CultureInfo.InvariantCulture);
        properties["TotalRuntimeInMilliseconds"] = totalRuntime.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
 
        yield return (RunEventName, properties);
 
        foreach (BuildCheckRuleTelemetryData buildCheckRuleTelemetryData in data.TelemetryData.Values)
        {
            properties = new Dictionary<string, string>();
            properties["SubmissionId"] = _submissionId.ToString();
            properties["RuleId"] = buildCheckRuleTelemetryData.RuleId;
            properties["CheckFriendlyName"] = buildCheckRuleTelemetryData.CheckFriendlyName;
            properties["IsBuiltIn"] = buildCheckRuleTelemetryData.IsBuiltIn.ToString(CultureInfo.InvariantCulture);
            properties["DefaultSeverityId"] = ((int)buildCheckRuleTelemetryData.DefaultSeverity).ToString(CultureInfo.InvariantCulture);
            properties["DefaultSeverity"] = buildCheckRuleTelemetryData.DefaultSeverity.ToString();
            properties["EnabledProjectsCount"] = buildCheckRuleTelemetryData.ProjectNamesWhereEnabled.Count.ToString(CultureInfo.InvariantCulture);
 
            if (buildCheckRuleTelemetryData.ExplicitSeverities.Any())
            {
                properties["ExplicitSeverities"] = buildCheckRuleTelemetryData.ExplicitSeverities
                    .Select(s => s.ToString()).ToCsvString(false);
                properties["ExplicitSeveritiesIds"] = buildCheckRuleTelemetryData.ExplicitSeverities
                    .Select(s => ((int)s).ToString(CultureInfo.InvariantCulture)).ToCsvString(false);
            }
 
            properties["ViolationMessagesCount"] = buildCheckRuleTelemetryData.ViolationMessagesCount.ToString(CultureInfo.InvariantCulture);
            properties["ViolationWarningsCount"] = buildCheckRuleTelemetryData.ViolationWarningsCount.ToString(CultureInfo.InvariantCulture);
            properties["ViolationErrorsCount"] = buildCheckRuleTelemetryData.ViolationErrorsCount.ToString(CultureInfo.InvariantCulture);
            properties["IsThrottled"] = buildCheckRuleTelemetryData.IsThrottled.ToString(CultureInfo.InvariantCulture);
            properties["TotalRuntimeInMilliseconds"] = buildCheckRuleTelemetryData.TotalRuntime.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
 
            yield return (RuleStatsEventName, properties);
        }
 
 
        // set for the new submission in case of build server
        _submissionId = Guid.NewGuid();
    }
}