Skip to content

Commit eb84524

Browse files
authored
Simplify Http.Diagnostics (#6174)
1 parent abae3a9 commit eb84524

File tree

11 files changed

+78
-181
lines changed

11 files changed

+78
-181
lines changed

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/DownstreamDependencyMetadataManager.cs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -100,31 +100,40 @@ private static void AddRouteToTrie(RequestMetadata routeMetadata, Dictionary<str
100100
var trieCurrent = routeMetadataTrieRoot;
101101
trieCurrent.Parent = trieCurrent;
102102

103-
ReadOnlySpan<char> requestRouteAsSpan = routeMetadata.RequestRoute.AsSpan();
104-
105-
if (requestRouteAsSpan.Length > 0)
103+
var route = routeMetadata.RequestRoute;
104+
if (!string.IsNullOrEmpty(route))
106105
{
107-
if (requestRouteAsSpan[0] != '/')
106+
var routeSpan = route.AsSpan();
107+
if (routeSpan.StartsWith("//".AsSpan()))
108108
{
109-
requestRouteAsSpan = $"/{routeMetadata.RequestRoute}".AsSpan();
109+
routeSpan = routeSpan.Slice(1);
110110
}
111-
else if (requestRouteAsSpan.StartsWith("//".AsSpan(), StringComparison.OrdinalIgnoreCase))
111+
112+
if (routeSpan.Length > 1 && routeSpan[routeSpan.Length - 1] == '/')
112113
{
113-
requestRouteAsSpan = requestRouteAsSpan.Slice(1);
114+
routeSpan = routeSpan.Slice(0, routeSpan.Length - 1);
114115
}
115116

116-
if (requestRouteAsSpan.Length > 1 && requestRouteAsSpan[requestRouteAsSpan.Length - 1] == '/')
117+
if (routeSpan[0] != '/')
117118
{
118-
requestRouteAsSpan = requestRouteAsSpan.Slice(0, requestRouteAsSpan.Length - 1);
119+
#if NET
120+
route = $"/{routeSpan}";
121+
#else
122+
route = $"/{routeSpan.ToString()}";
123+
#endif
119124
}
125+
else if (routeSpan.Length != route.Length)
126+
{
127+
route = routeSpan.ToString();
128+
}
129+
130+
route = _routeRegex.Replace(route, "*").ToUpperInvariant();
120131
}
121132
else
122133
{
123-
requestRouteAsSpan = "/".AsSpan();
134+
route = "/";
124135
}
125136

126-
var route = _routeRegex.Replace(requestRouteAsSpan.ToString(), "*");
127-
route = route.ToUpperInvariant();
128137
for (int i = 0; i < route.Length; i++)
129138
{
130139
char ch = route[i];

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Latency/HttpClientLatencyTelemetryExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ public static IServiceCollection AddHttpClientLatencyTelemetry(this IServiceColl
3030

3131
_ = services.RegisterCheckpointNames(HttpCheckpoints.Checkpoints);
3232
_ = services.AddOptions<HttpClientLatencyTelemetryOptions>();
33-
_ = services.AddActivatedSingleton<HttpRequestLatencyListener>();
34-
_ = services.AddActivatedSingleton<HttpClientLatencyContext>();
33+
_ = services.AddSingleton<HttpRequestLatencyListener>();
34+
_ = services.AddSingleton<HttpClientLatencyContext>();
3535
_ = services.AddTransient<HttpLatencyTelemetryHandler>();
3636
_ = services.AddHttpClientLogEnricher<HttpClientLatencyLogEnricher>();
3737

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Latency/Internal/HttpClientLatencyContext.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,4 @@ public void Set(ILatencyContext context)
1919
{
2020
_latencyContext.Value = context;
2121
}
22-
23-
public void Unset()
24-
{
25-
_latencyContext.Value = null;
26-
}
2722
}

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Latency/Internal/HttpLatencyTelemetryHandler.cs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using Microsoft.Extensions.Diagnostics.Latency;
99
using Microsoft.Extensions.Http.Diagnostics;
1010
using Microsoft.Extensions.Options;
11-
using Microsoft.Shared.Diagnostics;
1211

1312
namespace Microsoft.Extensions.Http.Latency.Internal;
1413

@@ -24,17 +23,14 @@ internal sealed class HttpLatencyTelemetryHandler : DelegatingHandler
2423
private readonly string _applicationName;
2524

2625
public HttpLatencyTelemetryHandler(HttpRequestLatencyListener latencyListener, ILatencyContextTokenIssuer tokenIssuer, ILatencyContextProvider latencyContextProvider,
27-
IOptions<HttpClientLatencyTelemetryOptions> options, IOptions<ApplicationMetadata> appMetdata)
26+
IOptions<HttpClientLatencyTelemetryOptions> options, IOptions<ApplicationMetadata> appMetadata)
2827
{
29-
var appMetadata = Throw.IfMemberNull(appMetdata, appMetdata.Value);
30-
var telemetryOptions = Throw.IfMemberNull(options, options.Value);
31-
3228
_latencyListener = latencyListener;
3329
_latencyContextProvider = latencyContextProvider;
3430
_handlerStart = tokenIssuer.GetCheckpointToken(HttpCheckpoints.HandlerRequestStart);
35-
_applicationName = appMetdata.Value.ApplicationName;
31+
_applicationName = appMetadata.Value.ApplicationName;
3632

37-
if (telemetryOptions.EnableDetailedLatencyBreakdown)
33+
if (options.Value.EnableDetailedLatencyBreakdown)
3834
{
3935
_latencyListener.Enable();
4036
}
@@ -46,12 +42,8 @@ protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage
4642
context.AddCheckpoint(_handlerStart);
4743
_latencyListener.LatencyContext.Set(context);
4844

49-
request.Headers.Add(TelemetryConstants.ClientApplicationNameHeader, _applicationName);
50-
51-
var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
52-
53-
_latencyListener.LatencyContext.Unset();
45+
_ = request.Headers.TryAddWithoutValidation(TelemetryConstants.ClientApplicationNameHeader, _applicationName);
5446

55-
return response;
47+
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
5648
}
5749
}
Lines changed: 21 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System;
5-
using System.Collections.Concurrent;
64
using System.Collections.Frozen;
75
using System.Collections.Generic;
86
using System.Diagnostics.Tracing;
@@ -17,57 +15,43 @@ internal sealed class HttpRequestLatencyListener : EventListener
1715
private const string HttpProviderName = "System.Net.Http";
1816
private const string NameResolutionProivderName = "System.Net.NameResolution";
1917

20-
private readonly ConcurrentDictionary<string, EventSource?> _eventSources = new()
21-
{
22-
[SocketProviderName] = null,
23-
[HttpProviderName] = null,
24-
[NameResolutionProivderName] = null
25-
};
18+
private readonly FrozenDictionary<string, FrozenDictionary<string, CheckpointToken>> _eventToTokenMap;
2619

2720
internal HttpClientLatencyContext LatencyContext { get; }
2821

29-
private readonly EventToCheckpointToken _eventToCheckpointToken;
30-
3122
private int _enabled;
3223

3324
internal bool Enabled => _enabled == 1;
3425

3526
public HttpRequestLatencyListener(HttpClientLatencyContext latencyContext, ILatencyContextTokenIssuer tokenIssuer)
3627
{
3728
LatencyContext = latencyContext;
38-
_eventToCheckpointToken = new(tokenIssuer);
29+
_eventToTokenMap = EventToCheckpointToken.Build(tokenIssuer);
3930
}
4031

4132
public void Enable()
4233
{
4334
if (Interlocked.CompareExchange(ref _enabled, 1, 0) == 0)
4435
{
45-
foreach (var eventSource in _eventSources)
46-
{
47-
if (eventSource.Value != null)
48-
{
49-
EnableEventSource(eventSource.Value);
50-
}
51-
}
36+
// process already existing listeners once again
37+
EventSourceCreated += (_, args) => OnEventSourceCreated(args.EventSource!);
5238
}
5339
}
5440

5541
internal void OnEventWritten(string eventSourceName, string? eventName)
5642
{
5743
// If event of interest, add a checkpoint for it.
58-
CheckpointToken? token = _eventToCheckpointToken.GetCheckpointToken(eventSourceName, eventName);
59-
if (token.HasValue)
44+
if (eventName != null && _eventToTokenMap[eventSourceName].TryGetValue(eventName, out var token))
6045
{
61-
LatencyContext.Get()?.AddCheckpoint(token.Value);
46+
LatencyContext.Get()?.AddCheckpoint(token);
6247
}
6348
}
6449

6550
internal void OnEventSourceCreated(string eventSourceName, EventSource eventSource)
6651
{
67-
if (_eventSources.ContainsKey(eventSourceName))
52+
if (Enabled && _eventToTokenMap.ContainsKey(eventSourceName))
6853
{
69-
_eventSources[eventSourceName] = eventSource;
70-
EnableEventSource(eventSource);
54+
EnableEvents(eventSource, EventLevel.Informational);
7155
}
7256
}
7357

@@ -81,15 +65,7 @@ protected override void OnEventWritten(EventWrittenEventArgs eventData)
8165
OnEventWritten(eventData.EventSource.Name, eventData.EventName);
8266
}
8367

84-
private void EnableEventSource(EventSource eventSource)
85-
{
86-
if (Enabled && !eventSource.IsEnabled())
87-
{
88-
EnableEvents(eventSource, EventLevel.Informational);
89-
}
90-
}
91-
92-
private sealed class EventToCheckpointToken
68+
private static class EventToCheckpointToken
9369
{
9470
private static readonly Dictionary<string, string> _socketMap = new()
9571
{
@@ -117,47 +93,32 @@ private sealed class EventToCheckpointToken
11793
{ "ResponseContentStop", HttpCheckpoints.ResponseContentEnd }
11894
};
11995

120-
private readonly FrozenDictionary<string, FrozenDictionary<string, CheckpointToken>> _eventToTokenMap;
121-
122-
public EventToCheckpointToken(ILatencyContextTokenIssuer tokenIssuer)
96+
public static FrozenDictionary<string, FrozenDictionary<string, CheckpointToken>> Build(ILatencyContextTokenIssuer tokenIssuer)
12397
{
12498
Dictionary<string, CheckpointToken> socket = [];
125-
foreach (string key in _socketMap.Keys)
99+
foreach (var kv in _socketMap)
126100
{
127-
socket[key] = tokenIssuer.GetCheckpointToken(_socketMap[key]);
101+
socket[kv.Key] = tokenIssuer.GetCheckpointToken(kv.Value);
128102
}
129103

130104
Dictionary<string, CheckpointToken> nameResolution = [];
131-
foreach (string key in _nameResolutionMap.Keys)
105+
foreach (var kv in _nameResolutionMap)
132106
{
133-
nameResolution[key] = tokenIssuer.GetCheckpointToken(_nameResolutionMap[key]);
107+
nameResolution[kv.Key] = tokenIssuer.GetCheckpointToken(kv.Value);
134108
}
135109

136110
Dictionary<string, CheckpointToken> http = [];
137-
foreach (string key in _httpMap.Keys)
111+
foreach (var kv in _httpMap)
138112
{
139-
http[key] = tokenIssuer.GetCheckpointToken(_httpMap[key]);
113+
http[kv.Key] = tokenIssuer.GetCheckpointToken(kv.Value);
140114
}
141115

142-
_eventToTokenMap = new Dictionary<string, FrozenDictionary<string, CheckpointToken>>
116+
return new Dictionary<string, FrozenDictionary<string, CheckpointToken>>
143117
{
144-
{ SocketProviderName, socket.ToFrozenDictionary(StringComparer.Ordinal) },
145-
{ NameResolutionProivderName, nameResolution.ToFrozenDictionary(StringComparer.Ordinal) },
146-
{ HttpProviderName, http.ToFrozenDictionary(StringComparer.Ordinal) }
147-
}.ToFrozenDictionary(StringComparer.Ordinal);
148-
}
149-
150-
public CheckpointToken? GetCheckpointToken(string eventSourceName, string? eventName)
151-
{
152-
if (eventName != null && _eventToTokenMap.TryGetValue(eventSourceName, out var events))
153-
{
154-
if (events.TryGetValue(eventName, out var token))
155-
{
156-
return token;
157-
}
158-
}
159-
160-
return null;
118+
{ SocketProviderName, socket.ToFrozenDictionary() },
119+
{ NameResolutionProivderName, nameResolution.ToFrozenDictionary() },
120+
{ HttpProviderName, http.ToFrozenDictionary() }
121+
}.ToFrozenDictionary();
161122
}
162123
}
163124
}

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpClientLogger.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,22 +109,22 @@ internal HttpClientLogger(
109109
}
110110
}
111111

112-
public async ValueTask LogRequestStopAsync(
112+
public ValueTask LogRequestStopAsync(
113113
object? context,
114114
HttpRequestMessage request,
115115
HttpResponseMessage response,
116116
TimeSpan elapsed,
117117
CancellationToken cancellationToken = default)
118-
=> await LogResponseAsync(context, request, response, null, elapsed, cancellationToken).ConfigureAwait(false);
118+
=> LogResponseAsync(context, request, response, null, elapsed, cancellationToken);
119119

120-
public async ValueTask LogRequestFailedAsync(
120+
public ValueTask LogRequestFailedAsync(
121121
object? context,
122122
HttpRequestMessage request,
123123
HttpResponseMessage? response,
124124
Exception exception,
125125
TimeSpan elapsed,
126126
CancellationToken cancellationToken = default)
127-
=> await LogResponseAsync(context, request, response, exception, elapsed, cancellationToken).ConfigureAwait(false);
127+
=> LogResponseAsync(context, request, response, exception, elapsed, cancellationToken);
128128

129129
public object? LogRequestStart(HttpRequestMessage request)
130130
=> throw new NotSupportedException(SyncLoggingExceptionMessage);

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestBodyReader.cs

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,14 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Buffers;
56
using System.Collections.Frozen;
67
using System.IO;
78
using System.Net.Http;
89
using System.Text;
910
using System.Threading;
1011
using System.Threading.Tasks;
11-
#if NETCOREAPP3_1_OR_GREATER
12-
using Microsoft.Extensions.ObjectPool;
13-
#endif
1412
using Microsoft.Shared.Diagnostics;
15-
#if NETCOREAPP3_1_OR_GREATER
16-
using Microsoft.Shared.Pools;
17-
#else
18-
using System.Buffers;
19-
#endif
2013

2114
namespace Microsoft.Extensions.Http.Logging.Internal;
2215

@@ -27,9 +20,6 @@ internal sealed class HttpRequestBodyReader
2720
/// </summary>
2821
internal readonly TimeSpan RequestReadTimeout;
2922

30-
#if NETCOREAPP3_1_OR_GREATER
31-
private static readonly ObjectPool<BufferWriter<byte>> _bufferWriterPool = BufferWriterPool.SharedBufferWriterPool;
32-
#endif
3323
private readonly FrozenSet<string> _readableRequestContentTypes;
3424
private readonly int _requestReadLimit;
3525

@@ -93,33 +83,20 @@ private static async ValueTask<string> ReadFromStreamAsync(HttpRequestMessage re
9383
#endif
9484

9585
var readLimit = Math.Min(readSizeLimit, (int)streamToReadFrom.Length);
96-
#if NETCOREAPP3_1_OR_GREATER
97-
var bufferWriter = _bufferWriterPool.Get();
98-
try
99-
{
100-
var memory = bufferWriter.GetMemory(readLimit).Slice(0, readLimit);
101-
var charsWritten = await streamToReadFrom.ReadAsync(memory, cancellationToken).ConfigureAwait(false);
102-
103-
return Encoding.UTF8.GetString(memory[..charsWritten].Span);
104-
}
105-
finally
106-
{
107-
_bufferWriterPool.Return(bufferWriter);
108-
streamToReadFrom.Seek(0, SeekOrigin.Begin);
109-
}
110-
111-
#else
11286
var buffer = ArrayPool<byte>.Shared.Rent(readLimit);
11387
try
11488
{
115-
_ = await streamToReadFrom.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
116-
return Encoding.UTF8.GetString(buffer.AsSpan(0, readLimit).ToArray());
89+
#if NET
90+
var read = await streamToReadFrom.ReadAsync(buffer.AsMemory(0, readLimit), cancellationToken).ConfigureAwait(false);
91+
#else
92+
var read = await streamToReadFrom.ReadAsync(buffer, 0, readLimit, cancellationToken).ConfigureAwait(false);
93+
#endif
94+
return Encoding.UTF8.GetString(buffer, 0, read);
11795
}
11896
finally
11997
{
12098
ArrayPool<byte>.Shared.Return(buffer);
12199
streamToReadFrom.Seek(0, SeekOrigin.Begin);
122100
}
123-
#endif
124101
}
125102
}

0 commit comments

Comments
 (0)