File: CodeFixesAndRefactorings\FixAllLogger.cs
Web Access
Project: ..\..\..\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);
    }
}