File: CodeFixesAndRefactorings\FixAllLogger.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Internal.Log;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings;
 
/// <summary>
/// Fix all occurrences logging.
/// </summary>
internal static class FixAllLogger
{
    // correlation id of all events related to same instance of fix all
    public const string CorrelationId = nameof(CorrelationId);
 
    // Fix all context logging.
    private const string CodeFixProvider = nameof(CodeFixProvider);
    private const string CodeRefactoringProvider = nameof(CodeRefactoringProvider);
    private const string CodeActionEquivalenceKey = nameof(CodeActionEquivalenceKey);
    public const string FixAllScope = nameof(FixAllScope);
    private const string LanguageName = nameof(LanguageName);
    private const string DocumentCount = nameof(DocumentCount);
 
    // Fix all computation result logging.
    private const string Result = nameof(Result);
    private const string Completed = nameof(Completed);
    private const string TimedOut = nameof(TimedOut);
    private const string Cancelled = nameof(Cancelled);
    private const string AllChangesApplied = nameof(AllChangesApplied);
    private const string SubsetOfChangesApplied = nameof(SubsetOfChangesApplied);
 
    // Diagnostics and fixes logging.
    private const string DocumentsWithDiagnosticsToFix = nameof(DocumentsWithDiagnosticsToFix);
    private const string ProjectsWithDiagnosticsToFix = nameof(ProjectsWithDiagnosticsToFix);
    private const string TotalDiagnosticsToFix = nameof(TotalDiagnosticsToFix);
    private const string TotalFixesToMerge = nameof(TotalFixesToMerge);
 
    public static void LogState(IFixAllState fixAllState, bool isInternalProvider)
    {
        FunctionId functionId;
        string providerKey;
        switch (fixAllState.FixAllKind)
        {
            case FixAllKind.CodeFix:
                functionId = FunctionId.CodeFixes_FixAllOccurrencesContext;
                providerKey = CodeFixProvider;
                break;
 
            case FixAllKind.Refactoring:
                functionId = FunctionId.Refactoring_FixAllOccurrencesContext;
                providerKey = CodeRefactoringProvider;
                break;
 
            default:
                throw ExceptionUtilities.UnexpectedValue(fixAllState.FixAllKind);
        }
 
        Logger.Log(functionId, KeyValueLogMessage.Create(m =>
        {
            m[CorrelationId] = fixAllState.CorrelationId;
 
            if (isInternalProvider)
            {
                m[providerKey] = fixAllState.Provider.GetType().FullName!;
                m[CodeActionEquivalenceKey] = fixAllState.CodeActionEquivalenceKey;
                m[LanguageName] = fixAllState.Project.Language;
            }
            else
            {
                m[providerKey] = fixAllState.Provider.GetType().FullName!.GetHashCode().ToString();
                m[CodeActionEquivalenceKey] = fixAllState.CodeActionEquivalenceKey?.GetHashCode().ToString();
                m[LanguageName] = fixAllState.Project.Language.GetHashCode().ToString();
            }
 
            m[FixAllScope] = fixAllState.Scope.ToString();
            switch (fixAllState.Scope)
            {
                case CodeFixes.FixAllScope.Project:
                    m[DocumentCount] = fixAllState.Project.DocumentIds.Count;
                    break;
 
                case CodeFixes.FixAllScope.Solution:
                    m[DocumentCount] = fixAllState.Solution.Projects.Sum(p => p.DocumentIds.Count);
                    break;
            }
        }));
    }
 
    public static void LogComputationResult(FixAllKind fixAllKind, int correlationId, bool completed, bool timedOut = false)
    {
        Contract.ThrowIfTrue(completed && timedOut);
 
        string value;
        if (completed)
        {
            value = Completed;
        }
        else if (timedOut)
        {
            value = TimedOut;
        }
        else
        {
            value = Cancelled;
        }
 
        var functionId = fixAllKind switch
        {
            FixAllKind.CodeFix => FunctionId.CodeFixes_FixAllOccurrencesComputation,
            FixAllKind.Refactoring => FunctionId.Refactoring_FixAllOccurrencesComputation,
            _ => throw ExceptionUtilities.UnexpectedValue(fixAllKind)
        };
 
        Logger.Log(functionId, KeyValueLogMessage.Create(m =>
        {
            m[CorrelationId] = correlationId;
            m[Result] = value;
        }));
    }
 
    public static void LogPreviewChangesResult(FixAllKind fixAllKind, int? correlationId, bool applied, bool allChangesApplied = true)
    {
        string value;
        if (applied)
        {
            value = allChangesApplied ? AllChangesApplied : SubsetOfChangesApplied;
        }
        else
        {
            value = Cancelled;
        }
 
        var functionId = fixAllKind switch
        {
            FixAllKind.CodeFix => FunctionId.CodeFixes_FixAllOccurrencesPreviewChanges,
            FixAllKind.Refactoring => FunctionId.Refactoring_FixAllOccurrencesPreviewChanges,
            _ => throw ExceptionUtilities.UnexpectedValue(fixAllKind)
        };
 
        Logger.Log(functionId, KeyValueLogMessage.Create(m =>
        {
            // we might not have this info for suppression
            if (correlationId.HasValue)
            {
                m[CorrelationId] = correlationId;
            }
 
            m[Result] = value;
        }));
    }
 
    public static void LogDiagnosticsStats(int correlationId, ImmutableDictionary<Document, ImmutableArray<Diagnostic>> documentsAndDiagnosticsToFixMap)
    {
        Logger.Log(FunctionId.CodeFixes_FixAllOccurrencesComputation_Document_Diagnostics, KeyValueLogMessage.Create(m =>
        {
            m[CorrelationId] = correlationId;
            m[DocumentsWithDiagnosticsToFix] = documentsAndDiagnosticsToFixMap.Count;
            m[TotalDiagnosticsToFix] = documentsAndDiagnosticsToFixMap.Values.Sum(v => v.Length);
        }));
    }
 
    public static void LogDiagnosticsStats(int correlationId, ImmutableDictionary<Project, ImmutableArray<Diagnostic>> projectsAndDiagnosticsToFixMap)
    {
        Logger.Log(FunctionId.CodeFixes_FixAllOccurrencesComputation_Project_Diagnostics, KeyValueLogMessage.Create(m =>
        {
            m[CorrelationId] = correlationId;
            m[ProjectsWithDiagnosticsToFix] = projectsAndDiagnosticsToFixMap.Count;
            m[TotalDiagnosticsToFix] = projectsAndDiagnosticsToFixMap.Values.Sum(v => v.Length);
        }));
    }
 
    public static void LogFixesToMergeStats(FunctionId functionId, int correlationId, int count)
    {
        Logger.Log(functionId, KeyValueLogMessage.Create(m =>
        {
            m[CorrelationId] = correlationId;
            m[TotalFixesToMerge] = count;
        }));
    }
 
    public static LogMessage CreateCorrelationLogMessage(int correlationId)
        => KeyValueLogMessage.Create(LogType.UserAction, m => m[CorrelationId] = correlationId);
}