Add Razor language server telemetry to C# DevKit (#9283)

This commit is contained in:
Allison Chou 2023-09-22 15:01:23 -07:00 коммит произвёл GitHub
Родитель 309b8bcc9a
Коммит 9ff3387560
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
22 изменённых файлов: 811 добавлений и 323 удалений

Просмотреть файл

@ -27,6 +27,7 @@
"src\\Compiler\\tools\\Microsoft.CodeAnalysis.Razor.Tooling.Internal\\Microsoft.CodeAnalysis.Razor.Tooling.Internal.csproj",
"src\\Razor\\src\\Microsoft.CodeAnalysis.Razor.Workspaces\\Microsoft.CodeAnalysis.Razor.Workspaces.csproj",
"src\\Razor\\src\\Microsoft.CodeAnalysis.Remote.Razor\\Microsoft.CodeAnalysis.Remote.Razor.csproj",
"src\\Razor\\src\\Microsoft.VisualStudio.DevKit.Razor\\Microsoft.VisualStudio.DevKit.Razor.csproj",
"src\\Razor\\src\\Microsoft.VisualStudio.Editor.Razor\\Microsoft.VisualStudio.Editor.Razor.csproj",
"src\\Razor\\src\\Microsoft.VisualStudio.LanguageServer.ContainedLanguage\\Microsoft.VisualStudio.LanguageServer.ContainedLanguage.csproj",
"src\\Razor\\src\\Microsoft.VisualStudio.LanguageServices.Razor\\Microsoft.VisualStudio.LanguageServices.Razor.csproj",

Просмотреть файл

@ -179,6 +179,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Razor.Diagnostics.Analyzers
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Razor.Diagnostics.Analyzers.Test", "src\Analyzers\Razor.Diagnostics.Analyzers.Test\Razor.Diagnostics.Analyzers.Test.csproj", "{167F1426-D9AE-49DF-B214-F00536DBC305}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.DevKit.Razor", "src\Razor\src\Microsoft.VisualStudio.DevKit.Razor\Microsoft.VisualStudio.DevKit.Razor.csproj", "{0F2D75D1-89AD-40A6-9F02-D45708AEA3EB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -739,6 +741,14 @@ Global
{167F1426-D9AE-49DF-B214-F00536DBC305}.Release|Any CPU.Build.0 = Release|Any CPU
{167F1426-D9AE-49DF-B214-F00536DBC305}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{167F1426-D9AE-49DF-B214-F00536DBC305}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{0F2D75D1-89AD-40A6-9F02-D45708AEA3EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F2D75D1-89AD-40A6-9F02-D45708AEA3EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F2D75D1-89AD-40A6-9F02-D45708AEA3EB}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{0F2D75D1-89AD-40A6-9F02-D45708AEA3EB}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{0F2D75D1-89AD-40A6-9F02-D45708AEA3EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F2D75D1-89AD-40A6-9F02-D45708AEA3EB}.Release|Any CPU.Build.0 = Release|Any CPU
{0F2D75D1-89AD-40A6-9F02-D45708AEA3EB}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{0F2D75D1-89AD-40A6-9F02-D45708AEA3EB}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -820,6 +830,7 @@ Global
{BAFE178B-7AD4-41AE-A75D-9B920B9EA050} = {3AE210D1-C435-4693-BF79-2EF13ED554B9}
{45B207E2-DDB3-44F0-87C5-DEFB5A9534A5} = {4AA319E0-C81E-47CC-841A-6EFCB6784A1F}
{167F1426-D9AE-49DF-B214-F00536DBC305} = {4AA319E0-C81E-47CC-841A-6EFCB6784A1F}
{0F2D75D1-89AD-40A6-9F02-D45708AEA3EB} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0035341D-175A-4D05-95E6-F1C2785A1E26}

Просмотреть файл

@ -41,4 +41,35 @@
<!-- Build a .zip file from each platform's directory and write it to 'artifacts' -->
<ZipDirectory SourceDirectory="%(LanguageServiceBinary.SourceDir)" DestinationFile="%(LanguageServiceBinary.ZipFile)" />
</Target>
<Target Name="_ZipDevKitTelemetryBinaries" AfterTargets="_ZipLanguageServerBinaries" Condition="'$(ArcadeBuildFromSource)' != 'true'">
<!-- This target is defined in eng/targets/Packaging.targets and included in every project. -->
<MSBuild Projects="$(RepoRoot)src\Razor\src\Microsoft.VisualStudio.DevKit.Razor\Microsoft.VisualStudio.DevKit.Razor.csproj"
Targets="_GetPackageVersionInfo"
SkipNonexistentProjects="false">
<Output TaskParameter="TargetOutputs" ItemName="_ResolvedPackageVersionInfo" />
</MSBuild>
<!-- Build a .zip file from each platform's directory and write it to 'artifacts' -->
<PropertyGroup>
<RidsPublishDir>$(ArtifactsDir)DevKitTelemetry\$(Configuration)\</RidsPublishDir>
<ZipOutputDir>$(RidsPublishDir)</ZipOutputDir>
<_DotNetPath>$(DOTNET_HOST_PATH)</_DotNetPath>
<_DotNetPath Condition="'$(_DotNetPath)' == ''">dotnet</_DotNetPath>
</PropertyGroup>
<ItemGroup>
<DevKitTelemetryBinaryDir Include="$([System.IO.Directory]::GetDirectories(&quot;$(RidsPublishDir)&quot;))" />
<DevKitTelemetryBinary Include="@(DevKitTelemetryBinaryDir)">
<SourceDir>%(DevKitTelemetryBinaryDir.Identity)</SourceDir>
<ZipFile>$(ZipOutputDir)DevKitTelemetry-%(DevKitTelemetryBinaryDir.Filename)-$(_PackageVersion).zip</ZipFile>
</DevKitTelemetryBinary>
</ItemGroup>
<MakeDir Directories="$(ZipOutputDir)" />
<Delete Files="%(DevKitTelemetryBinary.ZipFile)" />
<!-- Build a .zip file from each platform's directory and write it to 'artifacts' -->
<ZipDirectory SourceDirectory="%(DevKitTelemetryBinary.SourceDir)" DestinationFile="%(DevKitTelemetryBinary.ZipFile)" />
</Target>
</Project>

Просмотреть файл

@ -15,4 +15,16 @@
Targets="PublishAllRids" />
</Target>
<Target Name="_PublishDevKitTelemetryRids" AfterTargets="Pack" Condition="'$(DotNetBuildFromSource)' != 'true'">
<PropertyGroup>
<DevKitTelemetryProject>$(MSBuildThisFileDirectory)..\src\Razor\src\Microsoft.VisualStudio.DevKit.Razor\Microsoft.VisualStudio.DevKit.Razor.csproj</DevKitTelemetryProject>
<RazorSolutionPath>$(MSBuildThisFileDirectory)..\Razor.sln</RazorSolutionPath>
</PropertyGroup>
<MSBuild Projects="$(RazorSolutionPath)"
Targets="Restore" />
<MSBuild Projects="$(DevKitTelemetryProject)"
Targets="PublishAllRids" />
</Target>
</Project>

Просмотреть файл

@ -8,8 +8,8 @@
<ItemGroup>
<!-- Prepare for _UpdatePublishItems target. -->
<_ItemsToPublish Include="$(ArtifactsPackagesDir)**\*.tgz" Condition="'$(OS)' == 'Windows_NT'" />
<_ItemsToPublish Include="$(ArtifactsDir)LanguageServer\**\*.zip" />
<_ItemsToPublish Include="$(ArtifactsDir)DevKitTelemetry\**\*.zip" />
</ItemGroup>
<Target Name="_UpdatePublishItems">
@ -28,7 +28,7 @@
<!-- Packages can be built on all platforms, but are only published on Windows to avoid collisions from the other
platforms. This does not affect the SB intermediate package. -->
<ItemsToPushToBlobFeed Remove="$(ArtifactsDir)**\*.nupkg" Condition="'$(OS)' != 'Windows_NT' and '$(ArcadeBuildFromSource)' != 'true'" />
<ItemsToPushToBlobFeed Include="@(_ItemsToPublish)">
<IsShipping>false</IsShipping>
<ManifestArtifactData>ShipInstaller=dotnetcli;NonShipping=true</ManifestArtifactData>

Просмотреть файл

@ -129,7 +129,7 @@
<MicrosoftVisualStudioInteropPackageVersion>$(MicrosoftVisualStudioShellPackagesVersion)</MicrosoftVisualStudioInteropPackageVersion>
<MicrosoftInternalVisualStudioInteropPackageVersion>$(MicrosoftVisualStudioShellPackagesVersion)</MicrosoftInternalVisualStudioInteropPackageVersion>
<MicrosoftVisualStudioRpcContractsPackageVersion>17.7.9</MicrosoftVisualStudioRpcContractsPackageVersion>
<MicrosoftVisualStudioTelemetryVersion>17.7.8</MicrosoftVisualStudioTelemetryVersion>
<MicrosoftVisualStudioTelemetryVersion>17.8.138</MicrosoftVisualStudioTelemetryVersion>
<MicrosoftVisualStudioTextDataPackageVersion>$(MicrosoftVisualStudioPackagesVersion)</MicrosoftVisualStudioTextDataPackageVersion>
<MicrosoftVisualStudioTextImplementationPackageVersion>$(MicrosoftVisualStudioPackagesVersion)</MicrosoftVisualStudioTextImplementationPackageVersion>
<MicrosoftVisualStudioTextLogicPackageVersion>$(MicrosoftVisualStudioPackagesVersion)</MicrosoftVisualStudioTextLogicPackageVersion>

Просмотреть файл

@ -0,0 +1,96 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.Composition;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Exports;
internal class CustomExportAssemblyLoader(string baseDirectory) : IAssemblyLoader
{
/// <summary>
/// Cache assemblies that are already loaded by AssemblyName comparison
/// </summary>
private readonly Dictionary<AssemblyName, Assembly> _loadedAssemblies = new(AssemblyNameComparer.Instance);
/// <summary>
/// Base directory to search for <see cref="Assembly.LoadFrom(string)"/> if initial load fails
/// </summary>
private readonly string _baseDirectory = baseDirectory;
public Assembly LoadAssembly(AssemblyName assemblyName)
{
Assembly? value;
lock (_loadedAssemblies)
{
_loadedAssemblies.TryGetValue(assemblyName, out value);
}
if (value == null)
{
// Attempt to load the assembly normally, but fall back to Assembly.LoadFrom in the base
// directory if the assembly load fails
try
{
value = Assembly.Load(assemblyName);
}
catch (FileNotFoundException) when (assemblyName.Name is not null)
{
var filePath = Path.Combine(_baseDirectory, assemblyName.Name)
+ (assemblyName.Name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)
? ""
: ".dll");
value = Assembly.LoadFrom(filePath);
if (value is null)
{
throw;
}
}
lock (_loadedAssemblies)
{
_loadedAssemblies[assemblyName] = value;
return value;
}
}
return value;
}
public Assembly LoadAssembly(string assemblyFullName, string? codeBasePath)
{
var assemblyName = new AssemblyName(assemblyFullName);
return LoadAssembly(assemblyName);
}
private class AssemblyNameComparer : IEqualityComparer<AssemblyName>
{
public static AssemblyNameComparer Instance = new AssemblyNameComparer();
public bool Equals(AssemblyName? x, AssemblyName? y)
{
if (x == null && y == null)
{
return true;
}
if (x == null || y == null)
{
return false;
}
return x.Name == y.Name;
}
public int GetHashCode([DisallowNull] AssemblyName obj)
{
return obj.Name?.GetHashCode() ?? 0;
}
}
}

Просмотреть файл

@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
#if !NET472
using System.IO;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Composition;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Exports;
internal sealed class ExportProviderBuilder
{
public static async Task<ExportProvider> CreateExportProviderAsync(string extensionPath)
{
var baseDirectory = Path.GetDirectoryName(extensionPath);
var assemblyLoader = new CustomExportAssemblyLoader(baseDirectory!);
var resolver = new Resolver(assemblyLoader);
var discovery = PartDiscovery.Combine(
resolver,
new AttributedPartDiscovery(resolver, isNonPublicSupported: true), // "NuGet MEF" attributes (Microsoft.Composition)
new AttributedPartDiscoveryV1(resolver));
// TODO - we should likely cache the catalog so we don't have to rebuild it every time.
var parts = await discovery.CreatePartsAsync(new[] { extensionPath! }).ConfigureAwait(true);
var catalog = ComposableCatalog.Create(resolver)
.AddParts(parts)
.WithCompositionService(); // Makes an ICompositionService export available to MEF parts to import
// Assemble the parts into a valid graph.
var config = CompositionConfiguration.Create(catalog);
// Verify we have no errors.
config.ThrowOnErrors();
// Prepare an ExportProvider factory based on this graph.
var exportProviderFactory = config.CreateExportProviderFactory();
// Create an export provider, which represents a unique container of values.
// You can create as many of these as you want, but typically an app needs just one.
var exportProvider = exportProviderFactory.CreateExportProvider();
return exportProvider;
}
}
#endif

Просмотреть файл

@ -16,6 +16,7 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Workspaces.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Remote.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Remote.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.DevKit.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Editor.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServerClient.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServerClient.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

Просмотреть файл

@ -8,6 +8,7 @@ namespace Microsoft.AspNetCore.Razor.Telemetry;
internal interface ITelemetryReporter
{
void InitializeSession(string telemetryLevel, string? sessionId, bool isDefaultSession);
IDisposable BeginBlock(string name, Severity severity);
IDisposable BeginBlock(string name, Severity severity, ImmutableDictionary<string, object?> values);
IDisposable TrackLspRequest(string lspMethodName, string lspServerName, Guid correlationId);

Просмотреть файл

@ -14,6 +14,10 @@ internal class NoOpTelemetryReporter : ITelemetryReporter
{
}
public void InitializeSession(string telemetryLevel, string? sessionId, bool isDefaultSession)
{
}
public void ReportEvent(string name, Severity severity)
{
}

Просмотреть файл

@ -0,0 +1,69 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(DefaultNetCoreTargetFrameworks)</TargetFrameworks>
<PublishTargetFramework>net7.0</PublishTargetFramework>
<Description>Razor is a markup syntax for adding server-side logic to web pages. This package contains the language server assets for C# DevKit.</Description>
<EnableApiCheck>false</EnableApiCheck>
<RuntimeIdentifiers Condition="$([MSBuild]::IsOSPlatform('Windows'))">win-x64;win-x86;win-arm64</RuntimeIdentifiers>
<RuntimeIdentifiers Condition="$([MSBuild]::IsOSPlatform('Linux'))">linux-x64;linux-musl-x64;linux-arm64;linux-musl-arm64</RuntimeIdentifiers>
<RuntimeIdentifiers Condition="$([MSBuild]::IsOSPlatform('OSX'))">osx-x64;osx-arm64</RuntimeIdentifiers>
<IsShippingPackage>false</IsShippingPackage>
<RemoveDevicePlatformSupport>true</RemoveDevicePlatformSupport>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Microsoft.VisualStudio.Editor.Razor\Telemetry\NullTelemetryScope.cs" Link="Telemetry\NullTelemetryScope.cs" />
<Compile Include="..\Microsoft.VisualStudio.Editor.Razor\Telemetry\TelemetryHelpers.cs" Link="Telemetry\TelemetryHelpers.cs" />
<Compile Include="..\Microsoft.VisualStudio.Editor.Razor\Telemetry\TelemetryReporter.cs" Link="Telemetry\TelemetryReporter.cs" />
<Compile Include="..\Microsoft.VisualStudio.Editor.Razor\Telemetry\TelemetryScope.cs" Link="Telemetry\TelemetryScope.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Telemetry" Version="$(MicrosoftVisualStudioTelemetryVersion)" />
</ItemGroup>
<ItemGroup>
<Content Include="$(RepositoryRoot)NOTICE.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Razor.ProjectEngineHost\Microsoft.AspNetCore.Razor.ProjectEngineHost.csproj" PrivateAssets="all" />
</ItemGroup>
<!--
Technique for publishing multiple RIDs from
https://github.com/dotnet/cli/issues/9221#issuecomment-387512008
Example usage:
dotnet msbuild -restore -t:PublishAllRids -p:Configuration=Release
-->
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<!-- Enable roll-forward to latest patch. This allows one restore operation
to apply to all of the self-contained publish operations. -->
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<RidsPublishDir>$(ArtifactsDir)DevKitTelemetry\$(Configuration)\</RidsPublishDir>
</PropertyGroup>
<Target Name="PublishAllRids">
<ItemGroup>
<!-- Transform RuntimeIdentifiers property to item -->
<RuntimeIdentifierForPublish Include="$(RuntimeIdentifiers)" />
<!-- Transform RuntimeIdentifierForPublish items to project items to pass to MSBuild task -->
<ProjectToPublish Include="@(RuntimeIdentifierForPublish->'$(MSBuildProjectFullPath)')">
<AdditionalProperties>RuntimeIdentifier=%(RuntimeIdentifierForPublish.Identity);PublishDir=$(RidsPublishDir)%(RuntimeIdentifierForPublish.Identity)\;TargetFramework=$(PublishTargetFramework)</AdditionalProperties>
</ProjectToPublish>
<ProjectToPublish_PlatformAgnostic Include="$(MSBuildProjectFullPath)">
<AdditionalProperties>PublishDir=$(RidsPublishDir)\PlatformAgnostic\;UseAppHost=false;TargetFramework=$(PublishTargetFramework)</AdditionalProperties>
</ProjectToPublish_PlatformAgnostic>
</ItemGroup>
<MSBuild Projects="@(ProjectToPublish)" Targets="Publish" BuildInParallel="false" />
<MSBuild Projects="@(ProjectToPublish_PlatformAgnostic)" Targets="Publish" BuildInParallel="false" Condition="'$(OS)' == 'WINDOWS_NT'" />
</Target>
</Project>

Просмотреть файл

@ -0,0 +1,103 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics;
using System.Text;
using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.VisualStudio.Telemetry;
namespace Microsoft.VisualStudio.DevKit.Razor.Telemetry;
[Shared]
[Export(typeof(ITelemetryReporter))]
internal sealed class DevKitTelemetryReporter : TelemetryReporter
{
private const string CollectorApiKey = "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255";
[ImportingConstructor]
public DevKitTelemetryReporter() : base(telemetrySessions: [])
{
}
public override void InitializeSession(string telemetryLevel, string? sessionId, bool isDefaultSession)
{
Debug.Assert(TelemetrySessions.IsDefaultOrEmpty);
var sessionSettingsJson = CreateSessionSettingsJson(telemetryLevel, sessionId);
var session = new TelemetrySession($"{{{sessionSettingsJson}}}");
if (isDefaultSession)
{
TelemetryService.SetDefaultSession(session);
}
session.Start();
session.RegisterForReliabilityEvent();
TelemetrySessions = ImmutableArray.Create<TelemetrySession>(session);
}
private static string CreateSessionSettingsJson(string telemetryLevel, string? sessionId)
{
sessionId ??= Guid.NewGuid().ToString();
// Generate a new startTime for process to be consumed by Telemetry Settings
using var curProcess = Process.GetCurrentProcess();
var processStartTime = curProcess.StartTime.ToFileTimeUtc().ToString();
var sb = new StringBuilder();
var kvp = new Dictionary<string, string>
{
{ "Id", StringToJsonValue(sessionId) },
{ "HostName", StringToJsonValue("Default") },
// Insert Telemetry Level instead of Opt-Out status. The telemetry service handles
// validation of this value so there is no need to do so on this end. If it's invalid,
// it defaults to off.
{ "TelemetryLevel", StringToJsonValue(telemetryLevel) },
// this sets the Telemetry Session Created by LSP Server to be the Root Initial session
// This means that the SessionID set here by "Id" will be the SessionID used by cloned session
// further down stream
{ "IsInitialSession", "true" },
{ "CollectorApiKey", StringToJsonValue(CollectorApiKey) },
// using 1010 to indicate VS Code and not to match it to devenv 1000
{ "AppId", "1010" },
{ "ProcessStartTime", processStartTime },
};
foreach (var keyValue in kvp)
{
sb.AppendFormat("\"{0}\":{1},", keyValue.Key, keyValue.Value);
}
return sb.ToString().TrimEnd(',');
static string StringToJsonValue(string? value)
{
if (value == null)
{
return "null";
}
return '"' + value + '"';
}
}
public override bool HandleException(Exception exception, string? message, params object?[] @params)
=> false;
public override void LogTrace(string? message, params object?[] args)
{
}
public override void LogError(Exception exception, string? message, params object?[] args)
{
}
}

Просмотреть файл

@ -23,7 +23,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj" />
<ProjectReference Include="..\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj" />
</ItemGroup>
<ItemGroup Label="String Resources">

Просмотреть файл

@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.Razor.Telemetry;
internal class NullTelemetryScope : IDisposable
{
public static NullTelemetryScope Instance { get; } = new NullTelemetryScope();
private NullTelemetryScope() { }
public void Dispose() { }
}

Просмотреть файл

@ -0,0 +1,93 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using Microsoft.VisualStudio.Telemetry;
namespace Microsoft.AspNetCore.Razor.Telemetry;
internal static class TelemetryHelpers
{
public static string GetTelemetryName(string name) => "dotnet/razor/" + name;
public static string GetPropertyName(string name) => "dotnet.razor." + name;
public static string GetDescription(Exception exception)
{
const string CodeAnalysisNamespace = nameof(Microsoft) + "." + nameof(CodeAnalysis);
const string AspNetCoreNamespace = nameof(Microsoft) + "." + nameof(AspNetCore);
// Be resilient to failing here. If we can't get a suitable name, just fall back to the standard name we
// used to report.
try
{
// walk up the stack looking for the first call from a type that isn't in the ErrorReporting namespace.
var frames = new StackTrace(exception).GetFrames();
// On the .NET Framework, GetFrames() can return null even though it's not documented as such.
// At least one case here is if the exception's stack trace itself is null.
if (frames != null)
{
foreach (var frame in frames)
{
var method = frame?.GetMethod();
var methodName = method?.Name;
if (methodName is null)
{
continue;
}
var declaringTypeName = method?.DeclaringType?.FullName;
if (declaringTypeName == null)
{
continue;
}
if (!declaringTypeName.StartsWith(CodeAnalysisNamespace) &&
!declaringTypeName.StartsWith(AspNetCoreNamespace))
{
continue;
}
return declaringTypeName + "." + methodName;
}
}
}
catch
{
}
// If we couldn't get a stack, do this
return exception.Message;
}
public static TelemetrySeverity ToTelemetrySeverity(Severity severity)
=> severity switch
{
Severity.Normal => TelemetrySeverity.Normal,
Severity.Low => TelemetrySeverity.Low,
Severity.High => TelemetrySeverity.High,
_ => throw new InvalidOperationException($"Unknown severity: {severity}")
};
public static bool IsNumeric(object? o)
=> o is not null &&
!o.GetType().IsEnum &&
Type.GetTypeCode(o.GetType()) switch
{
TypeCode.Char or
TypeCode.SByte or
TypeCode.Byte or
TypeCode.Int16 or
TypeCode.Int32 or
TypeCode.Int64 or
TypeCode.Double or
TypeCode.Single or
TypeCode.UInt16 or
TypeCode.UInt32 or
TypeCode.UInt64
=> true,
_ => false
};
}

Просмотреть файл

@ -0,0 +1,178 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.VisualStudio.Telemetry;
#if DEBUG
using System.Linq;
#endif
namespace Microsoft.AspNetCore.Razor.Telemetry;
internal abstract class TelemetryReporter : ITelemetryReporter
{
public ImmutableArray<TelemetrySession> TelemetrySessions { get; set; }
public TelemetryReporter(ImmutableArray<TelemetrySession> telemetrySessions)
{
// Get the DefaultSession for telemetry. This is set by VS with
// TelemetryService.SetDefaultSession and provides the correct
// appinsights keys etc
TelemetrySessions = telemetrySessions;
}
public void ReportEvent(string name, Severity severity)
{
var telemetryEvent = new TelemetryEvent(TelemetryHelpers.GetTelemetryName(name), TelemetryHelpers.ToTelemetrySeverity(severity));
Report(telemetryEvent);
}
public void ReportEvent(string name, Severity severity, ImmutableDictionary<string, object?> values)
{
var telemetryEvent = new TelemetryEvent(TelemetryHelpers.GetTelemetryName(name), TelemetryHelpers.ToTelemetrySeverity(severity));
foreach (var (propertyName, propertyValue) in values)
{
if (TelemetryHelpers.IsNumeric(propertyValue))
{
telemetryEvent.Properties.Add(TelemetryHelpers.GetPropertyName(propertyName), propertyValue);
}
else
{
telemetryEvent.Properties.Add(TelemetryHelpers.GetPropertyName(propertyName), new TelemetryComplexProperty(propertyValue));
}
}
Report(telemetryEvent);
}
public void ReportFault(Exception exception, string? message, params object?[] @params)
{
try
{
if (exception is OperationCanceledException { InnerException: { } oceInnerException })
{
ReportFault(oceInnerException, message, @params);
return;
}
if (exception is AggregateException aggregateException)
{
// We (potentially) have multiple exceptions; let's just report each of them
foreach (var innerException in aggregateException.Flatten().InnerExceptions)
{
ReportFault(innerException, message, @params);
}
return;
}
if (HandleException(exception, message, @params))
{
return;
}
var currentProcess = Process.GetCurrentProcess();
var faultEvent = new FaultEvent(
eventName: TelemetryHelpers.GetTelemetryName("fault"),
description: TelemetryHelpers.GetDescription(exception),
FaultSeverity.General,
exceptionObject: exception,
gatherEventDetails: faultUtility =>
{
foreach (var data in @params)
{
if (data is null)
{
continue;
}
faultUtility.AddErrorInformation(data.ToString());
}
// Returning "0" signals that, if sampled, we should send data to Watson.
// Any other value will cancel the Watson report. We never want to trigger a process dump manually,
// we'll let TargetedNotifications determine if a dump should be collected.
// See https://aka.ms/roslynnfwdocs for more details
return 0;
});
Report(faultEvent);
}
catch (Exception)
{
}
}
private void Report(TelemetryEvent telemetryEvent)
{
try
{
#if !DEBUG
foreach (var session in TelemetrySessions)
{
session.PostEvent(telemetryEvent);
}
#else
// In debug we only log to normal logging. This makes it much easier to add and debug telemetry events
// before we're ready to send them to the cloud
var name = telemetryEvent.Name;
var propertyString = string.Join(",", telemetryEvent.Properties.Select(kvp => $"[ {kvp.Key}:{kvp.Value} ]"));
LogTrace("Telemetry Event: {name} \n Properties: {propertyString}\n", name, propertyString);
if (telemetryEvent is FaultEvent)
{
var eventType = telemetryEvent.GetType();
var description = eventType.GetProperty("Description", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.GetValue(telemetryEvent, null);
var exception = eventType.GetProperty("ExceptionObject", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.GetValue(telemetryEvent, null);
var message = $"Fault Event: {name} \n Exception Info: {exception ?? description} \n Properties: {propertyString}";
Debug.Assert(true, message);
}
#endif
}
catch (Exception e)
{
// No need to do anything here. We failed to report telemetry
// which isn't good, but not catastrophic for a user
LogError(e, "Failed logging telemetry event");
}
}
public IDisposable BeginBlock(string name, Severity severity)
{
return BeginBlock(name, severity, ImmutableDictionary<string, object?>.Empty);
}
public IDisposable BeginBlock(string name, Severity severity, ImmutableDictionary<string, object?> values)
{
return new TelemetryScope(this, name, severity, values.ToImmutableDictionary((tuple) => tuple.Key, (tuple) => (object?)tuple.Value));
}
public IDisposable TrackLspRequest(string lspMethodName, string languageServerName, Guid correlationId)
{
if (correlationId == Guid.Empty)
{
return NullTelemetryScope.Instance;
}
return BeginBlock("TrackLspRequest", Severity.Normal, ImmutableDictionary.CreateRange(new KeyValuePair<string, object?>[]
{
new("eventscope.method", lspMethodName),
new("eventscope.languageservername", languageServerName),
new("eventscope.correlationid", correlationId),
}));
}
public abstract void InitializeSession(string telemetryLevel, string? sessionId, bool isDefaultSession);
public abstract bool HandleException(Exception exception, string? message, params object?[] @params);
public abstract void LogTrace(string? message, params object?[] args);
public abstract void LogError(Exception exception, string? message, params object?[] args);
}

Просмотреть файл

@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.PooledObjects;
namespace Microsoft.AspNetCore.Razor.Telemetry;
internal class TelemetryScope : IDisposable
{
private readonly ITelemetryReporter _telemetryReporter;
private readonly string _name;
private readonly Severity _severity;
private readonly ImmutableDictionary<string, object?> _values;
private readonly Stopwatch _stopwatch;
private bool _disposed;
public TelemetryScope(
ITelemetryReporter telemetryReporter,
string name,
Severity severity,
ImmutableDictionary<string, object?> values)
{
_telemetryReporter = telemetryReporter;
_name = name;
_severity = severity;
_values = values;
_stopwatch = StopwatchPool.Default.Get();
_stopwatch.Restart();
}
public void Dispose()
{
if (_disposed)
{
return;
}
_disposed = true;
_stopwatch.Stop();
var values = _values.Add("eventscope.ellapsedms", _stopwatch.ElapsedMilliseconds);
_telemetryReporter.ReportEvent(_name, _severity, values);
StopwatchPool.Default.Return(_stopwatch);
}
}

Просмотреть файл

@ -0,0 +1,65 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.Telemetry;
namespace Microsoft.AspNetCore.Razor.Telemetry;
[Shared]
[Export(typeof(ITelemetryReporter))]
internal class VSTelemetryReporter : TelemetryReporter
{
private readonly IEnumerable<IFaultExceptionHandler> _faultExceptionHandlers;
private readonly ILogger? _logger;
[ImportingConstructor]
public VSTelemetryReporter(
[Import(AllowDefault = true)] ILoggerFactory? loggerFactory = null,
[ImportMany] IEnumerable<IFaultExceptionHandler>? faultExceptionHandlers = null)
// Get the DefaultSession for telemetry. This is set by VS with
// TelemetryService.SetDefaultSession and provides the correct
// appinsights keys etc
: base(ImmutableArray.Create(TelemetryService.DefaultSession))
{
_faultExceptionHandlers = faultExceptionHandlers ?? Array.Empty<IFaultExceptionHandler>();
_logger = loggerFactory?.CreateLogger<VSTelemetryReporter>();
}
public override bool HandleException(Exception exception, string? message, params object?[] @params)
{
var handled = false;
foreach (var handler in _faultExceptionHandlers)
{
if (handler.HandleException(this, exception, message, @params))
{
// This behavior means that each handler still gets a chance
// to respond to the exception. There's no real reason for this other
// than best guess. When it was added, there was only one handler but
// it was intended to be easy to add more.
handled = true;
}
}
return handled;
}
public override void InitializeSession(string telemetryLevel, string? sessionId, bool isDefaultSession)
{
// We don't need to do anything here. We're using the default session
// which is already initialized by VS.
throw new Exception("InitializeSession should not be called in VS.");
}
public override void LogTrace(string? message, params object?[] args)
=> _logger?.LogTrace(message, args);
public override void LogError(Exception exception, string? message, params object?[] args)
=> _logger?.LogError(exception, message, args);
}

Просмотреть файл

@ -1,318 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.Telemetry;
#if DEBUG
using System.Linq;
#endif
namespace Microsoft.AspNetCore.Razor.Telemetry;
[Shared]
[Export(typeof(ITelemetryReporter))]
internal class TelemetryReporter : ITelemetryReporter
{
private readonly ImmutableArray<TelemetrySession> _telemetrySessions;
private readonly IEnumerable<IFaultExceptionHandler> _faultExceptionHandlers;
private readonly ILogger? _logger;
[ImportingConstructor]
public TelemetryReporter(
[Import(AllowDefault = true)] ILoggerFactory? loggerFactory = null,
[ImportMany] IEnumerable<IFaultExceptionHandler>? faultExceptionHandlers = null)
{
// Get the DefaultSession for telemetry. This is set by VS with
// TelemetryService.SetDefaultSession and provides the correct
// appinsights keys etc
_telemetrySessions = ImmutableArray.Create(TelemetryService.DefaultSession);
_faultExceptionHandlers = faultExceptionHandlers ?? Array.Empty<IFaultExceptionHandler>();
_logger = loggerFactory?.CreateLogger<TelemetryReporter>();
}
public void ReportEvent(string name, Severity severity)
{
var telemetryEvent = new TelemetryEvent(GetTelemetryName(name), ToTelemetrySeverity(severity));
Report(telemetryEvent);
}
public void ReportEvent(string name, Severity severity, ImmutableDictionary<string, object?> values)
{
var telemetryEvent = new TelemetryEvent(GetTelemetryName(name), ToTelemetrySeverity(severity));
foreach (var (propertyName, propertyValue) in values)
{
if (IsNumeric(propertyValue))
{
telemetryEvent.Properties.Add(GetPropertyName(propertyName), propertyValue);
}
else
{
telemetryEvent.Properties.Add(GetPropertyName(propertyName), new TelemetryComplexProperty(propertyValue));
}
}
Report(telemetryEvent);
}
public void ReportFault(Exception exception, string? message, params object?[] @params)
{
try
{
if (exception is OperationCanceledException { InnerException: { } oceInnerException })
{
ReportFault(oceInnerException, message, @params);
return;
}
if (exception is AggregateException aggregateException)
{
// We (potentially) have multiple exceptions; let's just report each of them
foreach (var innerException in aggregateException.Flatten().InnerExceptions)
{
ReportFault(innerException, message, @params);
}
return;
}
var handled = false;
foreach (var handler in _faultExceptionHandlers)
{
if (handler.HandleException(this, exception, message, @params))
{
// This behavior means that each handler still gets a chance
// to respond to the exception. There's no real reason for this other
// than best guess. When it was added, there was only one handler but
// it was intended to be easy to add more.
handled = true;
}
}
if (handled)
{
return;
}
var currentProcess = Process.GetCurrentProcess();
var faultEvent = new FaultEvent(
eventName: GetTelemetryName("fault"),
description: GetDescription(exception),
FaultSeverity.General,
exceptionObject: exception,
gatherEventDetails: faultUtility =>
{
foreach (var data in @params)
{
if (data is null)
{
continue;
}
faultUtility.AddErrorInformation(data.ToString());
}
// Returning "0" signals that, if sampled, we should send data to Watson.
// Any other value will cancel the Watson report. We never want to trigger a process dump manually,
// we'll let TargetedNotifications determine if a dump should be collected.
// See https://aka.ms/roslynnfwdocs for more details
return 0;
});
Report(faultEvent);
}
catch (Exception)
{
}
}
private static string GetTelemetryName(string name) => "dotnet/razor/" + name;
private static string GetPropertyName(string name) => "dotnet.razor." + name;
private void Report(TelemetryEvent telemetryEvent)
{
try
{
#if !DEBUG
foreach (var session in _telemetrySessions)
{
session.PostEvent(telemetryEvent);
}
#else
// In debug we only log to normal logging. This makes it much easier to add and debug telemetry events
// before we're ready to send them to the cloud
var name = telemetryEvent.Name;
var propertyString = string.Join(",", telemetryEvent.Properties.Select(kvp => $"[ {kvp.Key}:{kvp.Value} ]"));
_logger?.LogTrace("Telemetry Event: {name} \n Properties: {propertyString}\n", name, propertyString);
if (telemetryEvent is FaultEvent)
{
var eventType = telemetryEvent.GetType();
var description = eventType.GetProperty("Description", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.GetValue(telemetryEvent, null);
var exception = eventType.GetProperty("ExceptionObject", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.GetValue(telemetryEvent, null);
var message = $"Fault Event: {name} \n Exception Info: {exception ?? description} \n Properties: {propertyString}";
Debug.Assert(true, message);
}
#endif
}
catch (Exception e)
{
// No need to do anything here. We failed to report telemetry
// which isn't good, but not catastrophic for a user
_logger?.LogError(e, "Failed logging telemetry event");
}
}
private static string GetDescription(Exception exception)
{
const string CodeAnalysisNamespace = nameof(Microsoft) + "." + nameof(CodeAnalysis);
const string AspNetCoreNamespace = nameof(Microsoft) + "." + nameof(AspNetCore);
// Be resilient to failing here. If we can't get a suitable name, just fall back to the standard name we
// used to report.
try
{
// walk up the stack looking for the first call from a type that isn't in the ErrorReporting namespace.
var frames = new StackTrace(exception).GetFrames();
// On the .NET Framework, GetFrames() can return null even though it's not documented as such.
// At least one case here is if the exception's stack trace itself is null.
if (frames != null)
{
foreach (var frame in frames)
{
var method = frame?.GetMethod();
var methodName = method?.Name;
if (methodName is null)
{
continue;
}
var declaringTypeName = method?.DeclaringType?.FullName;
if (declaringTypeName == null)
{
continue;
}
if (!declaringTypeName.StartsWith(CodeAnalysisNamespace) &&
!declaringTypeName.StartsWith(AspNetCoreNamespace))
{
continue;
}
return declaringTypeName + "." + methodName;
}
}
}
catch
{
}
// If we couldn't get a stack, do this
return exception.Message;
}
private static TelemetrySeverity ToTelemetrySeverity(Severity severity)
=> severity switch
{
Severity.Normal => TelemetrySeverity.Normal,
Severity.Low => TelemetrySeverity.Low,
Severity.High => TelemetrySeverity.High,
_ => throw new InvalidOperationException($"Unknown severity: {severity}")
};
public IDisposable BeginBlock(string name, Severity severity)
{
return BeginBlock(name, severity, ImmutableDictionary<string, object?>.Empty);
}
public IDisposable BeginBlock(string name, Severity severity, ImmutableDictionary<string, object?> values)
{
return new TelemetryScope(this, name, severity, values.ToImmutableDictionary((tuple) => tuple.Key, (tuple) => (object?)tuple.Value));
}
public IDisposable TrackLspRequest(string lspMethodName, string languageServerName, Guid correlationId)
{
if (correlationId == Guid.Empty)
{
return NullTelemetryScope.Instance;
}
return BeginBlock("TrackLspRequest", Severity.Normal, ImmutableDictionary.CreateRange(new KeyValuePair<string, object?>[]
{
new("eventscope.method", lspMethodName),
new("eventscope.languageservername", languageServerName),
new("eventscope.correlationid", correlationId),
}));
}
private static bool IsNumeric(object? o)
=> o is not null &&
!o.GetType().IsEnum &&
Type.GetTypeCode(o.GetType()) switch
{
TypeCode.Char or
TypeCode.SByte or
TypeCode.Byte or
TypeCode.Int16 or
TypeCode.Int32 or
TypeCode.Int64 or
TypeCode.Double or
TypeCode.Single or
TypeCode.UInt16 or
TypeCode.UInt32 or
TypeCode.UInt64
=> true,
_ => false
};
private class NullTelemetryScope : IDisposable
{
public static NullTelemetryScope Instance { get; } = new NullTelemetryScope();
private NullTelemetryScope() { }
public void Dispose() { }
}
private class TelemetryScope : IDisposable
{
private readonly ITelemetryReporter _telemetryReporter;
private string _name;
private Severity _severity;
private ImmutableDictionary<string, object?> _values;
private bool _disposed;
private Stopwatch _stopwatch;
public TelemetryScope(ITelemetryReporter telemetryReporter, string name, Severity severity, ImmutableDictionary<string, object?> values)
{
_telemetryReporter = telemetryReporter;
_name = name;
_severity = severity;
_values = values;
_stopwatch = StopwatchPool.Default.Get();
_stopwatch.Restart();
}
public void Dispose()
{
if (_disposed)
{
return;
}
_disposed = true;
_stopwatch.Stop();
var values = _values.Add("eventscope.ellapsedms", _stopwatch.ElapsedMilliseconds);
_telemetryReporter.ReportEvent(_name, _severity, values);
StopwatchPool.Default.Return(_stopwatch);
}
}
}

Просмотреть файл

@ -3,9 +3,11 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Exports;
using Microsoft.AspNetCore.Razor.Telemetry;
namespace Microsoft.AspNetCore.Razor.LanguageServer;
@ -15,6 +17,9 @@ public class Program
public static async Task Main(string[] args)
{
var trace = Trace.Messages;
var telemetryLevel = string.Empty;
var sessionId = string.Empty;
var telemetryExtensionPath = string.Empty;
for (var i = 0; i < args.Length; i++)
{
@ -49,16 +54,42 @@ public class Program
await Console.Error.WriteLineAsync($"Invalid Razor trace '{traceArg}'. Defaulting to {trace}.").ConfigureAwait(true);
}
}
if (args[i] == "--telemetryLevel" && i + 1 < args.Length)
{
telemetryLevel = args[++i];
}
if (args[i] == "--sessionId" && i + 1 < args.Length)
{
sessionId = args[++i];
}
if (args[i] == "--telemetryExtensionPath" && i + 1 < args.Length)
{
telemetryExtensionPath = args[++i];
}
}
var languageServerFeatureOptions = new ConfigurableLanguageServerFeatureOptions(args);
ITelemetryReporter? devKitTelemetryReporter = null;
if (!telemetryExtensionPath.IsNullOrEmpty())
{
using var exportProvider = await ExportProviderBuilder.CreateExportProviderAsync(
telemetryExtensionPath).ConfigureAwait(true);
// Initialize the telemetry reporter if available
devKitTelemetryReporter = exportProvider.GetExports<ITelemetryReporter>().SingleOrDefault()?.Value;
devKitTelemetryReporter?.InitializeSession(telemetryLevel, sessionId, isDefaultSession: true);
}
var logger = new LspLogger(trace);
var server = RazorLanguageServerWrapper.Create(
Console.OpenStandardInput(),
Console.OpenStandardOutput(),
logger,
NoOpTelemetryReporter.Instance,
devKitTelemetryReporter ?? NoOpTelemetryReporter.Instance,
featureOptions: languageServerFeatureOptions);
logger.LogInformation("Razor Language Server started successfully.");

Просмотреть файл

@ -27,6 +27,7 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.ProjectEngineHost, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Workspaces, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Remote.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.DevKit.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Editor.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServer.ContainedLanguage, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServerClient.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]