| File: Commands\Test\MTP\ArtifactPostProcessingTelemetry.cs | Web Access |
| Project: src\sdk\src\Cli\dotnet\dotnet.csproj (dotnet) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Globalization; using Microsoft.DotNet.Cli.Utils; namespace Microsoft.DotNet.Cli.Commands.Test; /// <summary> /// Reports one event per <c>dotnet test</c> run that planned at least one artifact post-processing /// job. Runs that plan nothing are silent, because the interesting population is the runs where /// post-processing actually had work to do. /// </summary> internal static class ArtifactPostProcessingTelemetry { private const string EventName = "test/artifact-post-processing"; /// <summary> /// Replaces any kind or extension outside the well-known sets below. Artifact kinds and file /// extensions are chosen by whoever wrote the producing extension, so an in-house post-processor /// can carry a product or team name. Bucketing everything unrecognized still answers what the /// event exists to answer — how often post-processing runs, and on which shipped formats. /// </summary> private const string OtherValue = "other"; private static readonly HashSet<string> WellKnownKinds = new(StringComparer.Ordinal) { "microsoft.testing.trx", "microsoft.codecoverage", "coverlet.cobertura", "junit.report", "nunit.report", }; private static readonly HashSet<string> WellKnownExtensions = new(StringComparer.Ordinal) { ".trx", ".coverage", ".cobertura", ".xml", ".json", }; public static void TrackPostProcessing( ArtifactPostProcessingPlan plan, int executedJobs, int failedJobs, TimeSpan duration) => TelemetryEventEntry.TrackEvent( EventName, CreateProperties(plan, executedJobs, failedJobs, duration)); internal static Dictionary<string, string?> CreateProperties( ArtifactPostProcessingPlan plan, int executedJobs, int failedJobs, TimeSpan duration) { ArtifactPostProcessingGroup[] groups = [.. plan.Jobs.SelectMany(job => job.Groups)]; return new Dictionary<string, string?> { ["jobs_planned"] = plan.Jobs.Count.ToString(CultureInfo.InvariantCulture), ["jobs_executed"] = executedJobs.ToString(CultureInfo.InvariantCulture), ["jobs_failed"] = failedJobs.ToString(CultureInfo.InvariantCulture), ["artifact_count"] = groups.Sum(group => group.Artifacts.Count).ToString(CultureInfo.InvariantCulture), ["kinds"] = Join(groups.Where(group => group.IsKind).Select(group => group.Key), WellKnownKinds), ["extensions"] = Join(groups.Where(group => !group.IsKind).Select(group => group.Key), WellKnownExtensions), ["duration_ms"] = ((long)duration.TotalMilliseconds).ToString(CultureInfo.InvariantCulture), }; } private static string Join(IEnumerable<string> values, HashSet<string> wellKnownValues) => string.Join( ';', values .Select(value => wellKnownValues.Contains(value) ? value : OtherValue) .Distinct(StringComparer.Ordinal) .Order(StringComparer.Ordinal)); }