diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/CodeActionEndpoint.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/CodeActionEndpoint.cs index 2a2c86bcab2..d7c781c8f1e 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/CodeActionEndpoint.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/CodeActionEndpoint.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Razor.Telemetry; using Microsoft.CodeAnalysis.Razor.CodeActions; using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions; +using Microsoft.CodeAnalysis.Razor.Workspaces.Telemetry; using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions; @@ -57,7 +58,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(VSCodeActionParams reque } var correlationId = Guid.NewGuid(); - using var __ = _telemetryReporter.TrackLspRequest(LspEndpointName, LanguageServerConstants.RazorLanguageServerName, correlationId); + using var __ = _telemetryReporter.TrackLspRequest(LspEndpointName, LanguageServerConstants.RazorLanguageServerName, TelemetryThresholds.CodeActionRazorTelemetryThreshold, correlationId); cancellationToken.ThrowIfCancellationRequested(); return await _codeActionsService.GetCodeActionsAsync(request, documentContext, _supportsCodeActionResolve, correlationId, cancellationToken).ConfigureAwait(false); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionEndpoint.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionEndpoint.cs index 298e83b4bb7..5c29cd9a222 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionEndpoint.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionEndpoint.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts; using Microsoft.AspNetCore.Razor.Telemetry; +using Microsoft.CodeAnalysis.Razor.Workspaces.Telemetry; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -77,7 +78,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(CompletionParams request } var correlationId = Guid.NewGuid(); - using var _ = _telemetryReporter?.TrackLspRequest(Methods.TextDocumentCompletionName, LanguageServerConstants.RazorLanguageServerName, correlationId); + using var _ = _telemetryReporter?.TrackLspRequest(Methods.TextDocumentCompletionName, LanguageServerConstants.RazorLanguageServerName, TelemetryThresholds.CompletionRazorTelemetryThreshold, correlationId); var completionList = await _completionListProvider.GetCompletionListAsync( hostDocumentIndex, completionContext, diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Diagnostics/DocumentPullDiagnosticsEndpoint.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Diagnostics/DocumentPullDiagnosticsEndpoint.cs index ae6dbff95e5..ad2838c9a73 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Diagnostics/DocumentPullDiagnosticsEndpoint.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Diagnostics/DocumentPullDiagnosticsEndpoint.cs @@ -19,6 +19,7 @@ using Microsoft.CodeAnalysis.Razor.Protocol; using Microsoft.CodeAnalysis.Razor.Protocol.Diagnostics; using Microsoft.CodeAnalysis.Razor.Workspaces; +using Microsoft.CodeAnalysis.Razor.Workspaces.Telemetry; using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.AspNetCore.Razor.LanguageServer.Diagnostics; @@ -30,7 +31,7 @@ internal class DocumentPullDiagnosticsEndpoint : IRazorRequestHandler _lastReporedProjectTagHelperCount = ImmutableDictionary.Empty; + private ImmutableDictionary _lastReportedProjectTagHelperCount = ImmutableDictionary.Empty; public DocumentPullDiagnosticsEndpoint( LanguageServerFeatureOptions languageServerFeatureOptions, @@ -71,7 +72,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(VSInternalDocumentDiagno } var correlationId = Guid.NewGuid(); - using var __ = _telemetryReporter?.TrackLspRequest(VSInternalMethods.DocumentPullDiagnosticName, LanguageServerConstants.RazorLanguageServerName, correlationId); + using var __ = _telemetryReporter?.TrackLspRequest(VSInternalMethods.DocumentPullDiagnosticName, LanguageServerConstants.RazorLanguageServerName, TelemetryThresholds.DiagnosticsRazorTelemetryThreshold, correlationId); var documentContext = context.DocumentContext; if (documentContext is null) { @@ -202,7 +203,7 @@ private async ValueTask ReportRZ10012TelemetryAsync(DocumentContext documentCont var shouldReport = false; ImmutableInterlocked.AddOrUpdate( - ref _lastReporedProjectTagHelperCount, + ref _lastReportedProjectTagHelperCount, documentContext.Project.Key, (k) => { diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/MapCode/MapCodeEndpoint.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/MapCode/MapCodeEndpoint.cs index 029ddb93535..628052c6f19 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/MapCode/MapCodeEndpoint.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/MapCode/MapCodeEndpoint.cs @@ -21,6 +21,7 @@ using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Microsoft.CodeAnalysis.Razor.Protocol; using Microsoft.CodeAnalysis.Razor.Workspaces; +using Microsoft.CodeAnalysis.Razor.Workspaces.Telemetry; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; using Location = Microsoft.VisualStudio.LanguageServer.Protocol.Location; @@ -70,7 +71,7 @@ public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, V } var mapCodeCorrelationId = mapperParams.MapCodeCorrelationId ?? Guid.NewGuid(); - using var ts = _telemetryReporter.TrackLspRequest(VSInternalMethods.WorkspaceMapCodeName, LanguageServerConstants.RazorLanguageServerName, mapCodeCorrelationId); + using var ts = _telemetryReporter.TrackLspRequest(VSInternalMethods.WorkspaceMapCodeName, LanguageServerConstants.RazorLanguageServerName, TelemetryThresholds.MapCodeRazorTelemetryThreshold, mapCodeCorrelationId); return await HandleMappingsAsync(mapperParams.Mappings, mapCodeCorrelationId, cancellationToken).ConfigureAwait(false); } diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Semantic/SemanticTokensRangeEndpoint.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Semantic/SemanticTokensRangeEndpoint.cs index c75e1b44112..5f3e72b28dc 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Semantic/SemanticTokensRangeEndpoint.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Semantic/SemanticTokensRangeEndpoint.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Razor.Telemetry; using Microsoft.CodeAnalysis.Razor.SemanticTokens; using Microsoft.CodeAnalysis.Razor.Workspaces; +using Microsoft.CodeAnalysis.Razor.Workspaces.Telemetry; using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.AspNetCore.Razor.LanguageServer.Semantic; @@ -49,7 +50,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(SemanticTokensRangeParam var colorBackground = _razorLSPOptionsMonitor.CurrentValue.ColorBackground; var correlationId = Guid.NewGuid(); - using var _ = _telemetryReporter?.TrackLspRequest(Methods.TextDocumentSemanticTokensRangeName, LanguageServerConstants.RazorLanguageServerName, correlationId); + using var _ = _telemetryReporter?.TrackLspRequest(Methods.TextDocumentSemanticTokensRangeName, LanguageServerConstants.RazorLanguageServerName, TelemetryThresholds.SemanticTokensRazorTelemetryThreshold, correlationId); var data = await _semanticTokensInfoService.GetSemanticTokensAsync(documentContext, request.Range.ToLinePositionSpan(), colorBackground, correlationId, cancellationToken).ConfigureAwait(false); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/ITelemetryReporter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/ITelemetryReporter.cs index c10db6823d3..613d071914f 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/ITelemetryReporter.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/ITelemetryReporter.cs @@ -7,13 +7,13 @@ namespace Microsoft.AspNetCore.Razor.Telemetry; internal interface ITelemetryReporter { - TelemetryScope BeginBlock(string name, Severity severity); - TelemetryScope BeginBlock(string name, Severity severity, Property property); - TelemetryScope BeginBlock(string name, Severity severity, Property property1, Property property2); - TelemetryScope BeginBlock(string name, Severity severity, Property property1, Property property2, Property property3); - TelemetryScope BeginBlock(string name, Severity severity, params Property[] properties); + TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport); + TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, Property property); + TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, Property property1, Property property2); + TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, Property property1, Property property2, Property property3); + TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, params ReadOnlySpan properties); - TelemetryScope TrackLspRequest(string lspMethodName, string lspServerName, Guid correlationId); + TelemetryScope TrackLspRequest(string lspMethodName, string lspServerName, TimeSpan minTimeToReport, Guid correlationId); void ReportEvent(string name, Severity severity); void ReportEvent(string name, Severity severity, Property property); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/ITelemetryReporterExtensions.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/ITelemetryReporterExtensions.cs new file mode 100644 index 00000000000..fb21f9c2328 --- /dev/null +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/ITelemetryReporterExtensions.cs @@ -0,0 +1,25 @@ +// 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 static class ITelemetryReporterExtensions +{ + // These extensions effectively make TimeSpan an optional parameter on BeginBlock + public static TelemetryScope BeginBlock(this ITelemetryReporter reporter, string name, Severity severity) + => reporter.BeginBlock(name, severity, minTimeToReport: TimeSpan.Zero); + + public static TelemetryScope BeginBlock(this ITelemetryReporter reporter, string name, Severity severity, Property property) + => reporter.BeginBlock(name, severity, minTimeToReport: TimeSpan.Zero, property); + + public static TelemetryScope BeginBlock(this ITelemetryReporter reporter, string name, Severity severity, Property property1, Property property2) + => reporter.BeginBlock(name, severity, minTimeToReport: TimeSpan.Zero, property1, property2); + + public static TelemetryScope BeginBlock(this ITelemetryReporter reporter, string name, Severity severity, Property property1, Property property2, Property property3) + => reporter.BeginBlock(name, severity, minTimeToReport: TimeSpan.Zero, property1, property2, property3); + + public static TelemetryScope BeginBlock(this ITelemetryReporter reporter, string name, Severity severity, params ReadOnlySpan properties) + => reporter.BeginBlock(name, severity, minTimeToReport: TimeSpan.Zero, properties); +} diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/NoOpTelemetryReporter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/NoOpTelemetryReporter.cs index 3ad1fa031b3..d88f044ab0d 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/NoOpTelemetryReporter.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/NoOpTelemetryReporter.cs @@ -13,19 +13,19 @@ private NoOpTelemetryReporter() { } - public TelemetryScope BeginBlock(string name, Severity severity) + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport) => TelemetryScope.Null; - public TelemetryScope BeginBlock(string name, Severity severity, Property property) + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, Property property) => TelemetryScope.Null; - public TelemetryScope BeginBlock(string name, Severity severity, Property property1, Property property2) + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, Property property1, Property property2) => TelemetryScope.Null; - public TelemetryScope BeginBlock(string name, Severity severity, Property property1, Property property2, Property property3) + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, Property property1, Property property2, Property property3) => TelemetryScope.Null; - public TelemetryScope BeginBlock(string name, Severity severity, params Property[] properties) + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, params ReadOnlySpan properties) => TelemetryScope.Null; public void ReportEvent(string name, Severity severity) @@ -52,7 +52,7 @@ public void ReportFault(Exception exception, string? message, params object?[] @ { } - public TelemetryScope TrackLspRequest(string lspMethodName, string lspServerName, Guid correlationId) + public TelemetryScope TrackLspRequest(string lspMethodName, string lspServerName, TimeSpan minTimeToReport, Guid correlationId) => TelemetryScope.Null; public void ReportRequestTiming(string name, string? language, TimeSpan queuedDuration, TimeSpan requestDuration, TelemetryResult result) diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/TelemetryScope.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/TelemetryScope.cs index 641048d44ee..26edd0b2103 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/TelemetryScope.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Telemetry/TelemetryScope.cs @@ -16,6 +16,7 @@ internal sealed class TelemetryScope : IDisposable private readonly Severity _severity; private readonly Property[] _properties; private readonly Stopwatch _stopwatch; + private readonly TimeSpan _minTimeToReport; private bool _disposed; private TelemetryScope() @@ -32,6 +33,7 @@ private TelemetryScope() private TelemetryScope( ITelemetryReporter reporter, string name, + TimeSpan minTimeToReport, Severity severity, Property[] properties) { @@ -45,6 +47,7 @@ private TelemetryScope( _properties = properties; _stopwatch = StopwatchPool.Default.Get(); + _minTimeToReport = minTimeToReport; _stopwatch.Restart(); } @@ -59,54 +62,57 @@ public void Dispose() _stopwatch.Stop(); - // We know that we were created with an array of at least length one. - _properties[^1] = new("eventscope.ellapsedms", _stopwatch.ElapsedMilliseconds); + var elapsed = _stopwatch.Elapsed; + if (elapsed >= _minTimeToReport) + { + // We know that we were created with an array of at least length one. + _properties[^1] = new("eventscope.ellapsedms", _stopwatch.ElapsedMilliseconds); - _reporter.ReportEvent(_name, _severity, _properties); + _reporter.ReportEvent(_name, _severity, _properties); + } StopwatchPool.Default.Return(_stopwatch); } - public static TelemetryScope Create(ITelemetryReporter reporter, string name, Severity severity) + public static TelemetryScope Create(ITelemetryReporter reporter, string name, Severity severity, TimeSpan minTimeToReport) { var array = new Property[1]; - return new(reporter, name, severity, array); + return new(reporter, name, minTimeToReport, severity, array); } - public static TelemetryScope Create(ITelemetryReporter reporter, string name, Severity severity, Property property) + public static TelemetryScope Create(ITelemetryReporter reporter, string name, Severity severity, TimeSpan minTimeToReport, Property property) { var array = new Property[2]; array[0] = property; - return new(reporter, name, severity, array); + return new(reporter, name, minTimeToReport, severity, array); } - public static TelemetryScope Create(ITelemetryReporter reporter, string name, Severity severity, Property property1, Property property2) + public static TelemetryScope Create(ITelemetryReporter reporter, string name, Severity severity, TimeSpan minTimeToReport, Property property1, Property property2) { var array = new Property[3]; array[0] = property1; array[1] = property2; - return new(reporter, name, severity, array); + return new(reporter, name, minTimeToReport, severity, array); } - public static TelemetryScope Create(ITelemetryReporter reporter, string name, Severity severity, Property property1, Property property2, Property property3) + public static TelemetryScope Create(ITelemetryReporter reporter, string name, Severity severity, TimeSpan minTimeToReport, Property property1, Property property2, Property property3) { var array = new Property[4]; array[0] = property1; array[1] = property2; array[2] = property3; - return new(reporter, name, severity, array); + return new(reporter, name, minTimeToReport, severity, array); } - public static TelemetryScope Create(ITelemetryReporter reporter, string name, Severity severity, Property[] properties) + public static TelemetryScope Create(ITelemetryReporter reporter, string name, Severity severity, TimeSpan minTimeToReport, ReadOnlySpan properties) { var array = new Property[properties.Length + 1]; - - Array.Copy(properties, array, properties.Length); - - return new(reporter, name, severity, array); + properties.CopyTo(array); + + return new(reporter, name, minTimeToReport, severity, array); } } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Telemetry/TelemetryThresholds.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Telemetry/TelemetryThresholds.cs new file mode 100644 index 00000000000..636dc392c83 --- /dev/null +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Telemetry/TelemetryThresholds.cs @@ -0,0 +1,28 @@ +// 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.CodeAnalysis.Razor.Workspaces.Telemetry; + +/// +/// A set of constants used to reduce the telemetry emitted to the set that help us understand +/// which LSP is taking the most time in the case that the overall call is lengthy. +/// +internal static class TelemetryThresholds +{ + internal static readonly TimeSpan CodeActionRazorTelemetryThreshold = TimeSpan.FromMilliseconds(2000); + internal static readonly TimeSpan CodeActionSubLSPTelemetryThreshold = TimeSpan.FromMilliseconds(1000); + + internal static readonly TimeSpan CompletionRazorTelemetryThreshold = TimeSpan.FromMilliseconds(4000); + internal static readonly TimeSpan CompletionSubLSPTelemetryThreshold = TimeSpan.FromMilliseconds(2000); + + internal static readonly TimeSpan DiagnosticsRazorTelemetryThreshold = TimeSpan.FromMilliseconds(4000); + internal static readonly TimeSpan DiagnosticsSubLSPTelemetryThreshold = TimeSpan.FromMilliseconds(2000); + + internal static readonly TimeSpan MapCodeRazorTelemetryThreshold = TimeSpan.FromMilliseconds(2000); + internal static readonly TimeSpan MapCodeSubLSPTelemetryThreshold = TimeSpan.FromMilliseconds(1000); + + internal static readonly TimeSpan SemanticTokensRazorTelemetryThreshold = TimeSpan.FromMilliseconds(2000); + internal static readonly TimeSpan SemanticTokensSubLSPTelemetryThreshold = TimeSpan.FromMilliseconds(1000); +} diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/SemanticTokens/RemoteCSharpSemanticTokensProvider.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/SemanticTokens/RemoteCSharpSemanticTokensProvider.cs index fd1fe4a78d2..f797efb1be6 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/SemanticTokens/RemoteCSharpSemanticTokensProvider.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/SemanticTokens/RemoteCSharpSemanticTokensProvider.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Microsoft.CodeAnalysis.Razor.SemanticTokens; using Microsoft.CodeAnalysis.Razor.Workspaces; +using Microsoft.CodeAnalysis.Razor.Workspaces.Telemetry; using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem; using Microsoft.CodeAnalysis.Text; @@ -26,7 +27,10 @@ internal class RemoteCSharpSemanticTokensProvider(IFilePathService filePathServi public async Task GetCSharpSemanticTokensResponseAsync(DocumentContext documentContext, ImmutableArray csharpRanges, Guid correlationId, CancellationToken cancellationToken) { - using var _ = _telemetryReporter.TrackLspRequest(nameof(SemanticTokensRange.GetSemanticTokensAsync), Constants.ExternalAccessServerName, correlationId); + using var _ = _telemetryReporter.TrackLspRequest(nameof(SemanticTokensRange.GetSemanticTokensAsync), + Constants.ExternalAccessServerName, + TelemetryThresholds.SemanticTokensRazorTelemetryThreshold, + correlationId); // We have a razor document, lets find the generated C# document Debug.Assert(documentContext is RemoteDocumentContext, "This method only works on document snapshots created in the OOP process"); diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostSemanticTokensRangeEndpoint.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostSemanticTokensRangeEndpoint.cs index 811c9ea4b98..a2fe70bd472 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostSemanticTokensRangeEndpoint.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostSemanticTokensRangeEndpoint.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; using Microsoft.CodeAnalysis.Razor.Remote; using Microsoft.CodeAnalysis.Razor.SemanticTokens; +using Microsoft.CodeAnalysis.Razor.Workspaces.Telemetry; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.Razor.Settings; @@ -79,7 +80,7 @@ public ImmutableArray GetRegistrations(VSInternalClientCapabilitie var colorBackground = _clientSettingsManager.GetClientSettings().AdvancedSettings.ColorBackground; var correlationId = Guid.NewGuid(); - using var _ = _telemetryReporter.TrackLspRequest(Methods.TextDocumentSemanticTokensRangeName, RazorLSPConstants.CohostLanguageServerName, correlationId); + using var _ = _telemetryReporter.TrackLspRequest(Methods.TextDocumentSemanticTokensRangeName, RazorLSPConstants.CohostLanguageServerName, TelemetryThresholds.SemanticTokensRazorTelemetryThreshold, correlationId); var tokens = await _remoteServiceInvoker.TryInvokeAsync( razorDocument.Project.Solution, diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_CodeActions.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_CodeActions.cs index e8f5cd0af35..49510c508eb 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_CodeActions.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_CodeActions.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Razor.Protocol; using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions; +using Microsoft.CodeAnalysis.Razor.Workspaces.Telemetry; using Microsoft.VisualStudio.LanguageServer.ContainedLanguage; using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.Threading; @@ -56,7 +57,7 @@ internal partial class RazorCustomMessageTarget var textBuffer = virtualDocumentSnapshot.Snapshot.TextBuffer; var lspMethodName = Methods.TextDocumentCodeActionName; - using var _ = _telemetryReporter.TrackLspRequest(lspMethodName, languageServerName, codeActionParams.CorrelationId); + using var _ = _telemetryReporter.TrackLspRequest(lspMethodName, languageServerName, TelemetryThresholds.CodeActionSubLSPTelemetryThreshold, codeActionParams.CorrelationId); var requests = _requestInvoker.ReinvokeRequestOnMultipleServersAsync>( textBuffer, lspMethodName, diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Completion.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Completion.cs index 8a85d553cdf..0bd1655150d 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Completion.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Completion.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.CodeAnalysis.Razor.Protocol; using Microsoft.CodeAnalysis.Razor.Protocol.Completion; +using Microsoft.CodeAnalysis.Razor.Workspaces.Telemetry; using Microsoft.VisualStudio.LanguageServer.ContainedLanguage; using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.Razor.Snippets; @@ -121,7 +122,7 @@ internal partial class RazorCustomMessageTarget var textBuffer = virtualDocumentSnapshot.Snapshot.TextBuffer; var lspMethodName = Methods.TextDocumentCompletion.Name; ReinvocationResponse? response; - using (_telemetryReporter.TrackLspRequest(lspMethodName, languageServerName, request.CorrelationId)) + using (_telemetryReporter.TrackLspRequest(lspMethodName, languageServerName, TelemetryThresholds.CompletionSubLSPTelemetryThreshold, request.CorrelationId)) { response = await _requestInvoker.ReinvokeRequestOnServerAsync( textBuffer, diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Diagnostics.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Diagnostics.cs index 3329cf7980e..cfdbc4b3b1d 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Diagnostics.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Diagnostics.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.Razor.Logging; using Microsoft.CodeAnalysis.Razor.Protocol; using Microsoft.CodeAnalysis.Razor.Protocol.Diagnostics; +using Microsoft.CodeAnalysis.Razor.Workspaces.Telemetry; using Microsoft.VisualStudio.LanguageServer.ContainedLanguage; using Microsoft.VisualStudio.LanguageServer.Protocol; using StreamJsonRpc; @@ -78,7 +79,7 @@ internal partial class RazorCustomMessageTarget }; var lspMethodName = VSInternalMethods.DocumentPullDiagnosticName; - using var _ = _telemetryReporter.TrackLspRequest(lspMethodName, delegatedLanguageServerName, correlationId); + using var _ = _telemetryReporter.TrackLspRequest(lspMethodName, delegatedLanguageServerName, TelemetryThresholds.DiagnosticsSubLSPTelemetryThreshold, correlationId); var response = await _requestInvoker.ReinvokeRequestOnServerAsync( virtualDocument.Snapshot.TextBuffer, lspMethodName, diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_MapCode.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_MapCode.cs index 6588cdc5d95..0499515498b 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_MapCode.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_MapCode.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Razor.Protocol; +using Microsoft.CodeAnalysis.Razor.Workspaces.Telemetry; using Microsoft.VisualStudio.LanguageServer.Protocol; using StreamJsonRpc; @@ -39,7 +40,7 @@ internal partial class RazorCustomMessageTarget var textBuffer = delegationDetails.Value.TextBuffer; var lspMethodName = VSInternalMethods.WorkspaceMapCodeName; var languageServerName = delegationDetails.Value.LanguageServerName; - using var _ = _telemetryReporter.TrackLspRequest(lspMethodName, languageServerName, request.MapCodeCorrelationId); + using var _ = _telemetryReporter.TrackLspRequest(lspMethodName, languageServerName, TelemetryThresholds.MapCodeSubLSPTelemetryThreshold, request.MapCodeCorrelationId); var response = await _requestInvoker.ReinvokeRequestOnServerAsync( textBuffer, diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_SemanticTokens.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_SemanticTokens.cs index d21608c4108..9cabff2fb78 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_SemanticTokens.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_SemanticTokens.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.Razor.Logging; using Microsoft.CodeAnalysis.Razor.Protocol; using Microsoft.CodeAnalysis.Razor.Workspaces.Protocol.SemanticTokens; +using Microsoft.CodeAnalysis.Razor.Workspaces.Telemetry; using Microsoft.VisualStudio.LanguageServer.Protocol; using StreamJsonRpc; @@ -84,7 +85,7 @@ internal partial class RazorCustomMessageTarget var languageServerName = RazorLSPConstants.RazorCSharpLanguageServerName; SemanticTokens? response; - using (var disposable = _telemetryReporter.TrackLspRequest(lspMethodName, languageServerName, semanticTokensParams.CorrelationId)) + using (var disposable = _telemetryReporter.TrackLspRequest(lspMethodName, languageServerName, TelemetryThresholds.SemanticTokensSubLSPTelemetryThreshold, semanticTokensParams.CorrelationId)) { try { diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Telemetry/TelemetryReporter.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Telemetry/TelemetryReporter.cs index a7637298b55..af902d253f9 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Telemetry/TelemetryReporter.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Telemetry/TelemetryReporter.cs @@ -237,7 +237,7 @@ protected virtual void Report(TelemetryEvent telemetryEvent) } } - protected virtual bool HandleException(Exception exception, string? message, params object?[] @params) + protected virtual bool HandleException(Exception exception, string? message, params ReadOnlySpan @params) => false; protected virtual void LogTrace(string message) @@ -248,34 +248,30 @@ protected virtual void LogError(Exception exception, string message) { } - public TelemetryScope BeginBlock(string name, Severity severity) - => TelemetryScope.Create(this, name, severity); + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport) + => TelemetryScope.Create(this, name, severity, minTimeToReport); - public TelemetryScope BeginBlock(string name, Severity severity, Property property) - => TelemetryScope.Create(this, name, severity, property); + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, Property property) + => TelemetryScope.Create(this, name, severity, minTimeToReport, property); - public TelemetryScope BeginBlock(string name, Severity severity, Property property1, Property property2) - => TelemetryScope.Create(this, name, severity, property1, property2); + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, Property property1, Property property2) + => TelemetryScope.Create(this, name, severity, minTimeToReport, property1, property2); - public TelemetryScope BeginBlock(string name, Severity severity, Property property1, Property property2, Property property3) - => TelemetryScope.Create(this, name, severity, property1, property2, property3); + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, Property property1, Property property2, Property property3) + => TelemetryScope.Create(this, name, severity, minTimeToReport, property1, property2, property3); - public TelemetryScope BeginBlock(string name, Severity severity, params Property[] properties) - => TelemetryScope.Create(this, name, severity, properties); + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, params ReadOnlySpan properties) + => TelemetryScope.Create(this, name, severity, minTimeToReport, properties); - public TelemetryScope TrackLspRequest(string lspMethodName, string languageServerName, Guid correlationId) + public TelemetryScope TrackLspRequest(string lspMethodName, string languageServerName, TimeSpan minTimeToReport, Guid correlationId) { if (correlationId == Guid.Empty) { return TelemetryScope.Null; } - ReportEvent("BeginLspRequest", Severity.Normal, - new("eventscope.method", lspMethodName), - new("eventscope.languageservername", languageServerName), - new("eventscope.correlationid", correlationId)); - return BeginBlock("TrackLspRequest", Severity.Normal, + minTimeToReport, new("eventscope.method", lspMethodName), new("eventscope.languageservername", languageServerName), new("eventscope.correlationid", correlationId)); diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Telemetry/VSTelemetryReporter.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Telemetry/VSTelemetryReporter.cs index 9983997d9f5..f3df00677be 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Telemetry/VSTelemetryReporter.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Telemetry/VSTelemetryReporter.cs @@ -21,7 +21,7 @@ public void Dispose() Flush(); } - protected override bool HandleException(Exception exception, string? message, params object?[] @params) + protected override bool HandleException(Exception exception, string? message, params ReadOnlySpan @params) { if (exception is RemoteInvocationException remoteInvocationException) { @@ -34,7 +34,7 @@ protected override bool HandleException(Exception exception, string? message, pa return false; } - private bool ReportRemoteInvocationException(RemoteInvocationException remoteInvocationException, object?[] @params) + private bool ReportRemoteInvocationException(RemoteInvocationException remoteInvocationException, ReadOnlySpan @params) { if (remoteInvocationException.InnerException is Exception innerException) { diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/RazorCustomMessageTargetTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/RazorCustomMessageTargetTest.cs index 99132c4935c..ff972ab7869 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/RazorCustomMessageTargetTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/RazorCustomMessageTargetTest.cs @@ -266,7 +266,9 @@ async IAsyncEnumerable> var documentSynchronizer = GetDocumentSynchronizer(GetCSharpSnapshot()); var telemetryReporter = new Mock(MockBehavior.Strict); - telemetryReporter.Setup(r => r.TrackLspRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(TelemetryScope.Null); + telemetryReporter.Setup(r => r.TrackLspRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s1, s2, ts, g) => Assert.NotEqual(TimeSpan.Zero, ts)) + .Returns(TelemetryScope.Null); var csharpVirtualDocumentAddListener = new CSharpVirtualDocumentAddListener(LoggerFactory); var target = new RazorCustomMessageTarget( @@ -520,8 +522,13 @@ public async Task ProvideSemanticTokensAsync_ContainsRange_ReturnsSemanticTokens It.IsAny())) .ReturnsAsync(new DefaultLSPDocumentSynchronizer.SynchronizedResult(true, csharpVirtualDocument)); var telemetryReporter = new Mock(MockBehavior.Strict); - telemetryReporter.Setup(r => r.BeginBlock(It.IsAny(), It.IsAny(), It.IsAny())).Returns(TelemetryScope.Null); - telemetryReporter.Setup(r => r.TrackLspRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(TelemetryScope.Null); + telemetryReporter.Setup(r => r.BeginBlock(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s1, s2, ts, g) => Assert.NotEqual(TimeSpan.Zero, ts)) + .Returns(TelemetryScope.Null); + + telemetryReporter.Setup(r => r.TrackLspRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s1, s2, ts, g) => Assert.NotEqual(TimeSpan.Zero, ts)) + .Returns(TelemetryScope.Null); var csharpVirtualDocumentAddListener = new CSharpVirtualDocumentAddListener(LoggerFactory); var target = new RazorCustomMessageTarget( @@ -598,8 +605,13 @@ public async Task ProvideSemanticTokensAsync_EmptyRange_ReturnsNoSemanticTokens( It.IsAny())) .ReturnsAsync(new DefaultLSPDocumentSynchronizer.SynchronizedResult(true, csharpVirtualDocument)); var telemetryReporter = new Mock(MockBehavior.Strict); - telemetryReporter.Setup(r => r.BeginBlock(It.IsAny(), It.IsAny(), It.IsAny())).Returns(TelemetryScope.Null); - telemetryReporter.Setup(r => r.TrackLspRequest(It.IsAny(), It.IsAny(), It.IsAny())).Returns(TelemetryScope.Null); + telemetryReporter.Setup(r => r.BeginBlock(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, sev, ts, p) => Assert.NotEqual(TimeSpan.Zero, ts)) + .Returns(TelemetryScope.Null); + + telemetryReporter.Setup(r => r.TrackLspRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s1, s2, ts, g) => Assert.NotEqual(TimeSpan.Zero, ts)) + .Returns(TelemetryScope.Null); var csharpVirtualDocumentAddListener = new CSharpVirtualDocumentAddListener(LoggerFactory); var target = new RazorCustomMessageTarget( diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Telemetry/TelemetryReporterTests.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Telemetry/TelemetryReporterTests.cs index 2eeab073a0e..f718a5d4c8f 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Telemetry/TelemetryReporterTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Telemetry/TelemetryReporterTests.cs @@ -168,7 +168,7 @@ public void Block_OneArgument() public void Block_TwoArguments() { using var reporter = new TestTelemetryReporter(LoggerFactory); - using (reporter.BeginBlock("EventName", Severity.Normal, new("P1", false), new("P2", "test"))) + using (reporter.BeginBlock("EventName", Severity.Normal, TimeSpan.Zero, new("P1", false), new("P2", "test"))) { } @@ -192,6 +192,7 @@ public void Block_ThreeArguments() var p3Value = Guid.NewGuid(); using (reporter.BeginBlock("EventName", Severity.Normal, + TimeSpan.Zero, new("P1", false), new("P2", "test"), new("P3", p3Value))) @@ -222,6 +223,7 @@ public void Block_FourArguments() var p3Value = Guid.NewGuid(); using (reporter.BeginBlock("EventName", Severity.Normal, + TimeSpan.Zero, new("P1", false), new("P2", "test"), new("P3", p3Value), @@ -296,7 +298,7 @@ public void TrackLspRequest() { using var reporter = new TestTelemetryReporter(LoggerFactory); var correlationId = Guid.NewGuid(); - using (reporter.TrackLspRequest("MethodName", "ServerName", correlationId)) + using (reporter.TrackLspRequest("MethodName", "ServerName", TimeSpan.Zero, correlationId)) { } @@ -304,29 +306,16 @@ public void TrackLspRequest() e1 => { Assert.Equal(TelemetrySeverity.Normal, e1.Severity); - Assert.Equal("dotnet/razor/beginlsprequest", e1.Name); + Assert.Equal("dotnet/razor/tracklsprequest", e1.Name); Assert.True(e1.HasProperties); + Assert.True(e1.Properties["dotnet.razor.eventscope.ellapsedms"] is long); Assert.Equal("MethodName", e1.Properties["dotnet.razor.eventscope.method"]); Assert.Equal("ServerName", e1.Properties["dotnet.razor.eventscope.languageservername"]); var correlationProperty = e1.Properties["dotnet.razor.eventscope.correlationid"] as TelemetryComplexProperty; Assert.NotNull(correlationProperty); Assert.Equal(correlationId, correlationProperty.Value); - }, - e2 => - { - Assert.Equal(TelemetrySeverity.Normal, e2.Severity); - Assert.Equal("dotnet/razor/tracklsprequest", e2.Name); - Assert.True(e2.HasProperties); - - Assert.True(e2.Properties["dotnet.razor.eventscope.ellapsedms"] is long); - Assert.Equal("MethodName", e2.Properties["dotnet.razor.eventscope.method"]); - Assert.Equal("ServerName", e2.Properties["dotnet.razor.eventscope.languageservername"]); - - var correlationProperty = e2.Properties["dotnet.razor.eventscope.correlationid"] as TelemetryComplexProperty; - Assert.NotNull(correlationProperty); - Assert.Equal(correlationId, correlationProperty.Value); }); }