File: Services\ProcessTelemetry\RemoteProcessTelemetryService.cs
Web Access
Project: src\src\Workspaces\Remote\ServiceHub\Microsoft.CodeAnalysis.Remote.ServiceHub.csproj (Microsoft.CodeAnalysis.Remote.ServiceHub)
// 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 System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Remote.Diagnostics;
using Microsoft.CodeAnalysis.Telemetry;
using Microsoft.VisualStudio.LanguageServices.Telemetry;
using Microsoft.VisualStudio.Telemetry;
using Roslyn.Utilities;
using RoslynLogger = Microsoft.CodeAnalysis.Internal.Log.Logger;
 
namespace Microsoft.CodeAnalysis.Remote;
 
internal sealed partial class RemoteProcessTelemetryService(
    BrokeredServiceBase.ServiceConstructionArguments arguments)
    : BrokeredServiceBase(arguments), IRemoteProcessTelemetryService
{
    internal sealed class Factory : FactoryBase<IRemoteProcessTelemetryService>
    {
        protected override IRemoteProcessTelemetryService CreateService(in ServiceConstructionArguments arguments)
            => new RemoteProcessTelemetryService(arguments);
    }
 
    private readonly CancellationTokenSource _shutdownCancellationSource = new();
 
#pragma warning disable IDE0052 // Remove unread private members
    private PerformanceReporter? _performanceReporter;
#pragma warning restore
 
    /// <summary>
    /// Remote API. Initializes ServiceHub process global state.
    /// </summary>
    public ValueTask InitializeTelemetrySessionAsync(int hostProcessId, string serializedSession, bool logDelta, CancellationToken cancellationToken)
    {
        return RunServiceAsync(cancellationToken =>
        {
            var services = GetWorkspace().Services;
 
            var telemetryService = (RemoteWorkspaceTelemetryService)services.GetRequiredService<IWorkspaceTelemetryService>();
            var telemetrySession = new TelemetrySession(serializedSession);
            telemetrySession.Start();
 
            telemetryService.InitializeTelemetrySession(telemetrySession, logDelta);
            telemetryService.RegisterUnexpectedExceptionLogger(TraceLogger);
            FaultReporter.InitializeFatalErrorHandlers();
 
            // log telemetry that service hub started
            RoslynLogger.Log(FunctionId.RemoteHost_Connect, KeyValueLogMessage.Create(m =>
            {
                m["Host"] = hostProcessId;
                m["Framework"] = RuntimeInformation.FrameworkDescription;
            }));
 
            // start performance reporter
            var diagnosticAnalyzerPerformanceTracker = services.GetService<IPerformanceTrackerService>();
            if (diagnosticAnalyzerPerformanceTracker != null)
            {
                // We know in the remote layer that this type must exist.
                _performanceReporter = new PerformanceReporter(telemetrySession, diagnosticAnalyzerPerformanceTracker, _shutdownCancellationSource.Token);
            }
 
            return ValueTaskFactory.CompletedTask;
        }, cancellationToken);
    }
 
    /// <summary>
    /// Remote API.
    /// </summary>
    public ValueTask EnableLoggingAsync(ImmutableArray<string> loggerTypeNames, ImmutableArray<FunctionId> functionIds, CancellationToken cancellationToken)
    {
        return RunServiceAsync(cancellationToken =>
        {
            var functionIdsSet = new HashSet<FunctionId>(functionIds);
            bool logChecker(FunctionId id) => functionIdsSet.Contains(id);
 
            // we only support 2 types of loggers
            SetRoslynLogger(loggerTypeNames, () => new EtwLogger(logChecker));
            SetRoslynLogger(loggerTypeNames, () => new TraceLogger(logChecker));
 
            return ValueTaskFactory.CompletedTask;
        }, cancellationToken);
    }
 
    private static void SetRoslynLogger<T>(ImmutableArray<string> loggerTypes, Func<T> creator) where T : ILogger
    {
        if (loggerTypes.Contains(typeof(T).Name))
        {
            RoslynLogger.SetLogger(AggregateLogger.AddOrReplace(creator(), RoslynLogger.GetLogger(), l => l is T));
        }
        else
        {
            RoslynLogger.SetLogger(AggregateLogger.Remove(RoslynLogger.GetLogger(), l => l is T));
        }
    }
 
    /// <summary>
    /// Remote API.
    /// </summary>
    public ValueTask<int> InitializeAsync(WorkspaceConfigurationOptions options, CancellationToken cancellationToken)
    {
        return RunServiceAsync(cancellationToken =>
        {
            var service = (RemoteWorkspaceConfigurationService)GetWorkspaceServices().GetRequiredService<IWorkspaceConfigurationService>();
            service.InitializeOptions(options);
 
            return ValueTaskFactory.FromResult(Process.GetCurrentProcess().Id);
        }, cancellationToken);
    }
}