File: RoslynActivityLogger.cs
Web Access
Project: src\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_dqtbjz24_wpftmp.csproj (Microsoft.VisualStudio.LanguageServices)
// 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.
 
#nullable disable
 
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.Internal.Log;
using Roslyn.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices;
 
/// <summary>
/// Let people to inject <see cref="TraceSource"/> to monitor Roslyn activity
/// 
/// Here, we don't technically use TraceSource as it is meant to be used. but just as an easy 
/// way to log data to listeners.
/// 
/// this also involves creating string, boxing and etc. so, perf wise, it will impact VS quite a bit.
/// this also won't collect trace from Roslyn OOP for now. only in proc activity
/// </summary>
internal static class RoslynActivityLogger
{
    private static readonly object s_gate = new();
 
    public static void SetLogger(TraceSource traceSource)
    {
        Contract.ThrowIfNull(traceSource);
 
        lock (s_gate)
        {
            // internally, it just uses our existing ILogger
            Logger.SetLogger(AggregateLogger.AddOrReplace(new TraceSourceLogger(traceSource), Logger.GetLogger(), l => (l as TraceSourceLogger)?.TraceSource == traceSource));
        }
    }
 
    public static void RemoveLogger(TraceSource traceSource)
    {
        Contract.ThrowIfNull(traceSource);
 
        lock (s_gate)
        {
            // internally, it just uses our existing ILogger
            Logger.SetLogger(AggregateLogger.Remove(Logger.GetLogger(), l => (l as TraceSourceLogger)?.TraceSource == traceSource));
        }
    }
 
    private class TraceSourceLogger : ILogger
    {
        private const int LogEventId = 0;
        private const int StartEventId = 1;
        private const int EndEventId = 2;
 
        public readonly TraceSource TraceSource;
 
        public TraceSourceLogger(TraceSource traceSource)
            => TraceSource = traceSource;
 
        public bool IsEnabled(FunctionId functionId)
        {
            // we log every roslyn activity
            return true;
        }
 
        public void Log(FunctionId functionId, LogMessage logMessage)
            => TraceSource.TraceData(TraceEventType.Verbose, LogEventId, functionId.Convert(), logMessage.GetMessage());
 
        public void LogBlockStart(FunctionId functionId, LogMessage logMessage, int uniquePairId, CancellationToken cancellationToken)
            => TraceSource.TraceData(TraceEventType.Verbose, StartEventId, functionId.Convert(), uniquePairId);
 
        public void LogBlockEnd(FunctionId functionId, LogMessage logMessage, int uniquePairId, int delta, CancellationToken cancellationToken)
            => TraceSource.TraceData(TraceEventType.Verbose, EndEventId, functionId.Convert(), uniquePairId, cancellationToken.IsCancellationRequested, delta, logMessage.GetMessage());
    }
}