File: ExternalAccess\UnitTesting\SolutionCrawler\UnitTestingSolutionCrawlerLogger.cs
Web Access
Project: src\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api;
using Microsoft.CodeAnalysis.Internal.Log;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler;
 
internal static class UnitTestingSolutionCrawlerLogger
{
    private const string Id = nameof(Id);
    private const string Kind = nameof(Kind);
    private const string Analyzer = nameof(Analyzer);
    private const string DocumentCount = nameof(DocumentCount);
    private const string Languages = nameof(Languages);
    private const string HighPriority = nameof(HighPriority);
    private const string Enabled = nameof(Enabled);
    private const string AnalyzerCount = nameof(AnalyzerCount);
    private const string PersistentStorage = nameof(PersistentStorage);
    private const string GlobalOperation = nameof(GlobalOperation);
    private const string HigherPriority = nameof(HigherPriority);
    private const string LowerPriority = nameof(LowerPriority);
    private const string TopLevel = nameof(TopLevel);
    private const string MemberLevel = nameof(MemberLevel);
    private const string NewWorkItem = nameof(NewWorkItem);
    private const string UpdateWorkItem = nameof(UpdateWorkItem);
    private const string ProjectEnqueue = nameof(ProjectEnqueue);
    private const string ResetStates = nameof(ResetStates);
    private const string ProjectNotExist = nameof(ProjectNotExist);
    private const string DocumentNotExist = nameof(DocumentNotExist);
    private const string ProcessProject = nameof(ProcessProject);
    private const string OpenDocument = nameof(OpenDocument);
    private const string CloseDocument = nameof(CloseDocument);
    private const string SolutionHash = nameof(SolutionHash);
    private const string ProcessDocument = nameof(ProcessDocument);
    private const string ProcessDocumentCancellation = nameof(ProcessDocumentCancellation);
    private const string ProcessProjectCancellation = nameof(ProcessProjectCancellation);
    private const string ActiveFileEnqueue = nameof(ActiveFileEnqueue);
    private const string ActiveFileProcessDocument = nameof(ActiveFileProcessDocument);
    private const string ActiveFileProcessDocumentCancellation = nameof(ActiveFileProcessDocumentCancellation);
 
    public static void LogRegistration(int correlationId, string workspaceKind)
    {
        Logger.Log(FunctionId.WorkCoordinatorRegistrationService_Register, KeyValueLogMessage.Create(m =>
        {
            m[Id] = correlationId;
            m[Kind] = workspaceKind;
        }));
    }
 
    public static void LogUnregistration(int correlationId)
    {
        Logger.Log(FunctionId.WorkCoordinatorRegistrationService_Unregister, KeyValueLogMessage.Create(m =>
        {
            m[Id] = correlationId;
        }));
    }
 
    public static void LogReanalyze(
        int correlationId,
        IUnitTestingIncrementalAnalyzer analyzer,
        int documentCount,
        string languages)
    {
        Logger.Log(FunctionId.WorkCoordinatorRegistrationService_Reanalyze, KeyValueLogMessage.Create(m =>
        {
            m[Id] = correlationId;
            m[Analyzer] = analyzer.ToString();
            m[DocumentCount] = documentCount;
            m[Languages] = languages;
        }));
    }
 
    public static void LogAnalyzers(int correlationId, string workspaceKind, ImmutableArray<IUnitTestingIncrementalAnalyzer> reordered, bool onlyHighPriorityAnalyzer)
    {
        if (onlyHighPriorityAnalyzer)
        {
            LogAnalyzersWorker(
                FunctionId.IncrementalAnalyzerProcessor_ActiveFileAnalyzers, FunctionId.IncrementalAnalyzerProcessor_ActiveFileAnalyzer,
                correlationId, workspaceKind, reordered);
        }
        else
        {
            LogAnalyzersWorker(
                FunctionId.IncrementalAnalyzerProcessor_Analyzers, FunctionId.IncrementalAnalyzerProcessor_Analyzer,
                correlationId, workspaceKind, reordered);
        }
    }
 
    private static void LogAnalyzersWorker(
        FunctionId analyzersId, FunctionId analyzerId, int correlationId, string workspaceKind, ImmutableArray<IUnitTestingIncrementalAnalyzer> reordered)
    {
        if (workspaceKind == WorkspaceKind.Preview)
        {
            return;
        }
 
        // log registered analyzers.
        Logger.Log(analyzersId, KeyValueLogMessage.Create(m =>
        {
            m[Id] = correlationId;
            m[AnalyzerCount] = reordered.Length;
        }));
 
        foreach (var analyzer in reordered)
        {
            Logger.Log(analyzerId, KeyValueLogMessage.Create(m =>
            {
                m[Id] = correlationId;
                m[Analyzer] = analyzer.ToString();
            }));
        }
    }
 
    public static void LogWorkCoordinatorShutdownTimeout(int correlationId)
    {
        Logger.Log(FunctionId.WorkCoordinator_ShutdownTimeout, KeyValueLogMessage.Create(m =>
        {
            m[Id] = correlationId;
        }));
    }
 
    public static void LogWorkspaceEvent(CountLogAggregator<WorkspaceChangeKind> logAggregator, WorkspaceChangeKind kind)
        => logAggregator.IncreaseCount(kind);
 
    public static void LogWorkCoordinatorShutdown(int correlationId, CountLogAggregator<WorkspaceChangeKind> logAggregator)
    {
        Logger.Log(FunctionId.WorkCoordinator_Shutdown, KeyValueLogMessage.Create(m =>
        {
            m[Id] = correlationId;
 
            foreach (var kv in logAggregator)
            {
                var change = kv.Key.ToString();
                m[change] = kv.Value.GetCount();
            }
        }));
    }
 
    public static void LogGlobalOperation(CountLogAggregator<object> logAggregator)
        => logAggregator.IncreaseCount(GlobalOperation);
 
    public static void LogActiveFileEnqueue(CountLogAggregator<object> logAggregator)
        => logAggregator.IncreaseCount(ActiveFileEnqueue);
 
    public static void LogWorkItemEnqueue(CountLogAggregator<object> logAggregator, ProjectId _)
        => logAggregator.IncreaseCount(ProjectEnqueue);
 
    public static void LogWorkItemEnqueue(
        CountLogAggregator<object> logAggregator, string language, DocumentId? documentId, UnitTestingInvocationReasons reasons, bool lowPriority, SyntaxPath? activeMember, bool added)
    {
        logAggregator.IncreaseCount(language);
        logAggregator.IncreaseCount(added ? NewWorkItem : UpdateWorkItem);
 
        if (documentId != null)
        {
            logAggregator.IncreaseCount(activeMember == null ? TopLevel : MemberLevel);
 
            if (lowPriority)
            {
                logAggregator.IncreaseCount(LowerPriority);
                logAggregator.IncreaseCount(ValueTuple.Create(LowerPriority, documentId.Id));
            }
        }
 
        foreach (var reason in reasons)
        {
            logAggregator.IncreaseCount(reason);
        }
    }
 
    public static void LogHigherPriority(CountLogAggregator<object> logAggregator, Guid documentId)
    {
        logAggregator.IncreaseCount(HigherPriority);
        logAggregator.IncreaseCount(ValueTuple.Create(HigherPriority, documentId));
    }
 
    public static void LogResetStates(CountLogAggregator<object> logAggregator)
        => logAggregator.IncreaseCount(ResetStates);
 
    public static void LogIncrementalAnalyzerProcessorStatistics(int correlationId, Solution solution, CountLogAggregator<object> logAggregator)
    {
        Logger.Log(FunctionId.IncrementalAnalyzerProcessor_Shutdown, KeyValueLogMessage.Create(m =>
        {
            var solutionHash = GetSolutionHash(solution);
 
            m[Id] = correlationId;
            m[SolutionHash] = solutionHash.ToString();
 
            var statMap = new Dictionary<string, List<int>>();
            foreach (var (key, counter) in logAggregator)
            {
                if (key is string stringKey)
                {
                    m[stringKey] = counter.GetCount();
                }
                else if (key is ValueTuple<string, Guid> propertyNameAndId)
                {
                    var list = statMap.GetOrAdd(propertyNameAndId.Item1, _ => []);
                    list.Add(counter.GetCount());
                }
                else
                {
                    throw ExceptionUtilities.Unreachable();
                }
            }
 
            foreach (var (propertyName, propertyValues) in statMap)
            {
                var result = StatisticResult.FromList(propertyValues);
 
                result.WriteTelemetryPropertiesTo(m, prefix: propertyName);
            }
        }));
    }
 
    private static int GetSolutionHash(Solution solution)
    {
        if (solution != null && solution.FilePath != null)
        {
            return solution.FilePath.ToLowerInvariant().GetHashCode();
        }
 
        return 0;
    }
 
    public static void LogProcessCloseDocument(CountLogAggregator<object> logAggregator, Guid documentId)
    {
        logAggregator.IncreaseCount(CloseDocument);
        logAggregator.IncreaseCount(ValueTuple.Create(CloseDocument, documentId));
    }
 
    public static void LogProcessOpenDocument(CountLogAggregator<object> logAggregator, Guid documentId)
    {
        logAggregator.IncreaseCount(OpenDocument);
        logAggregator.IncreaseCount(ValueTuple.Create(OpenDocument, documentId));
    }
 
    public static void LogProcessActiveFileDocument(CountLogAggregator<object> logAggregator, Guid _, bool processed)
    {
        if (processed)
        {
            logAggregator.IncreaseCount(ActiveFileProcessDocument);
        }
        else
        {
            logAggregator.IncreaseCount(ActiveFileProcessDocumentCancellation);
        }
    }
 
    public static void LogProcessDocument(CountLogAggregator<object> logAggregator, Guid documentId, bool processed)
    {
        if (processed)
        {
            logAggregator.IncreaseCount(ProcessDocument);
        }
        else
        {
            logAggregator.IncreaseCount(ProcessDocumentCancellation);
        }
 
        logAggregator.IncreaseCount(ValueTuple.Create(ProcessDocument, documentId));
    }
 
    public static void LogProcessDocumentNotExist(CountLogAggregator<object> logAggregator)
        => logAggregator.IncreaseCount(DocumentNotExist);
 
    public static void LogProcessProject(CountLogAggregator<object> logAggregator, Guid projectId, bool processed)
    {
        if (processed)
        {
            logAggregator.IncreaseCount(ProcessProject);
        }
        else
        {
            logAggregator.IncreaseCount(ProcessProjectCancellation);
        }
 
        logAggregator.IncreaseCount(ValueTuple.Create(ProcessProject, projectId));
    }
 
    public static void LogProcessProjectNotExist(CountLogAggregator<object> logAggregator)
        => logAggregator.IncreaseCount(ProjectNotExist);
}