diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/DefaultHttpDependencyMetadataResolver.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/DefaultHttpDependencyMetadataResolver.cs
new file mode 100644
index 00000000000..fa15cea6283
--- /dev/null
+++ b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/DefaultHttpDependencyMetadataResolver.cs
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+
+namespace Microsoft.Extensions.Http.Diagnostics;
+
+///
+/// Default implementation of that uses the base
+/// trie-based lookup algorithm.
+///
+public sealed class DefaultHttpDependencyMetadataResolver : HttpDependencyMetadataResolver
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// A collection of HTTP dependency metadata used for request resolution.
+ public DefaultHttpDependencyMetadataResolver(IEnumerable dependencyMetadata)
+ : base(dependencyMetadata)
+ {
+ }
+}
diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/DownstreamDependencyMetadataManager.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs
similarity index 84%
rename from src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/DownstreamDependencyMetadataManager.cs
rename to src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs
index 93c3e534e8e..e9e1845dd90 100644
--- a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/DownstreamDependencyMetadataManager.cs
+++ b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs
@@ -8,11 +8,19 @@
using System.Net;
using System.Net.Http;
using System.Text.RegularExpressions;
-using Microsoft.Extensions.Telemetry.Internal;
+using Microsoft.Shared.Diagnostics;
namespace Microsoft.Extensions.Http.Diagnostics;
-internal sealed class DownstreamDependencyMetadataManager : IDownstreamDependencyMetadataManager
+///
+/// Resolves metadata for HTTP requests based on hostname, path, and method patterns.
+///
+///
+/// This class provides a high-performance way to identify HTTP requests by mapping them to previously
+/// configured metadata using specialized trie-based data structures. This enables efficient lookup
+/// of service information, operation names, and other metadata for telemetry and policy application.
+///
+public abstract class HttpDependencyMetadataResolver
{
internal readonly struct ProcessedMetadata
{
@@ -27,22 +35,35 @@ internal readonly struct ProcessedMetadata
private readonly HostSuffixTrieNode _hostSuffixTrieRoot = new();
private readonly FrozenDictionary _frozenProcessedMetadataMap;
- public DownstreamDependencyMetadataManager(IEnumerable downstreamDependencyMetadata)
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// A collection of HTTP dependency metadata used for request resolution.
+ /// is .
+ protected HttpDependencyMetadataResolver(IEnumerable dependencyMetadata)
{
+ _ = Throw.IfNull(dependencyMetadata);
+
Dictionary dependencyTrieMap = [];
- foreach (var dependency in downstreamDependencyMetadata)
+ foreach (var dependency in dependencyMetadata)
{
AddDependency(dependency, dependencyTrieMap);
}
- _frozenProcessedMetadataMap = ProcessDownstreamDependencyMetadata(dependencyTrieMap).ToFrozenDictionary(StringComparer.Ordinal);
+ _frozenProcessedMetadataMap = ProcessDependencyMetadata(dependencyTrieMap).ToFrozenDictionary(StringComparer.Ordinal);
}
- public RequestMetadata? GetRequestMetadata(HttpRequestMessage requestMessage)
+#if !NET462
+ ///
+ /// Gets request metadata for the specified HTTP request message.
+ ///
+ /// The HTTP request message.
+ /// The resolved if found; otherwise, .
+ public virtual RequestMetadata? GetRequestMetadata(HttpRequestMessage requestMessage)
{
try
{
- if (requestMessage.RequestUri == null)
+ if (requestMessage?.RequestUri == null)
{
return null;
}
@@ -56,8 +77,13 @@ public DownstreamDependencyMetadataManager(IEnumerable
+ /// Gets request metadata for the specified HTTP web request.
+ ///
+ /// The HTTP web request.
+ /// The resolved if found; otherwise, .
+ public virtual RequestMetadata? GetRequestMetadata(HttpWebRequest requestMessage)
{
try
{
@@ -70,12 +96,12 @@ public DownstreamDependencyMetadataManager(IEnumerable ProcessDownstreamDependencyMetadata(Dictionary dependencyTrieMap)
+ private static Dictionary ProcessDependencyMetadata(Dictionary dependencyTrieMap)
{
Dictionary finalArrayDict = [];
foreach (var dep in dependencyTrieMap)
{
- var finalArray = ProcessDownstreamDependencyMetadataInternal(dep.Value);
+ var finalArray = ProcessDependencyMetadataInternal(dep.Value);
finalArrayDict.Add(dep.Key, finalArray);
}
@@ -190,7 +216,7 @@ private static Dictionary ProcessDownstreamDependency
// remove the ExlcudeCodeCoverage attribute and ensure it's covered fully using local runs and enable it back before
// pushing the change to PR.
[ExcludeFromCodeCoverage]
- private static ProcessedMetadata ProcessDownstreamDependencyMetadataInternal(RequestMetadataTrieNode requestMetadataTrieRoot)
+ private static ProcessedMetadata ProcessDependencyMetadataInternal(RequestMetadataTrieNode requestMetadataTrieRoot)
{
Queue queue = new();
queue.Enqueue(requestMetadataTrieRoot);
@@ -278,17 +304,17 @@ private static ProcessedMetadata ProcessDownstreamDependencyMetadataInternal(Req
return null;
}
- private void AddDependency(IDownstreamDependencyMetadata downstreamDependencyMetadata, Dictionary dependencyTrieMap)
+ private void AddDependency(IDownstreamDependencyMetadata dependencyMetadata, Dictionary dependencyTrieMap)
{
- foreach (var hostNameSuffix in downstreamDependencyMetadata.UniqueHostNameSuffixes)
+ foreach (var hostNameSuffix in dependencyMetadata.UniqueHostNameSuffixes)
{
// Add hostname to hostname suffix trie
- AddHostnameToTrie(hostNameSuffix, downstreamDependencyMetadata.DependencyName);
+ AddHostnameToTrie(hostNameSuffix, dependencyMetadata.DependencyName);
}
- foreach (var routeMetadata in downstreamDependencyMetadata.RequestMetadata)
+ foreach (var routeMetadata in dependencyMetadata.RequestMetadata)
{
- routeMetadata.DependencyName = downstreamDependencyMetadata.DependencyName;
+ routeMetadata.DependencyName = dependencyMetadata.DependencyName;
// Add route metadata to the route per dependency trie
AddRouteToTrie(routeMetadata, dependencyTrieMap);
@@ -445,4 +471,4 @@ private void AddHostnameToTrie(string hostNameSuffix, string dependencyName)
hostMetadata.RequestMetadata : // Return the default request metadata for this host if no matching route is found.
routeMetadataTrieRoot.RequestMetadatas[trieCurrent.RequestMetadataEntryIndex];
}
-}
+}
\ No newline at end of file
diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDiagnosticsServiceCollectionExtensions.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDiagnosticsServiceCollectionExtensions.cs
index 6cdcd74b263..8a70a5d6860 100644
--- a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDiagnosticsServiceCollectionExtensions.cs
+++ b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDiagnosticsServiceCollectionExtensions.cs
@@ -4,7 +4,6 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Http.Diagnostics;
-using Microsoft.Extensions.Telemetry.Internal;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Extensions.DependencyInjection;
@@ -23,9 +22,10 @@ public static class HttpDiagnosticsServiceCollectionExtensions
public static IServiceCollection AddDownstreamDependencyMetadata(this IServiceCollection services, IDownstreamDependencyMetadata downstreamDependencyMetadata)
{
_ = Throw.IfNull(services);
- services.TryAddSingleton();
- _ = services.AddSingleton(downstreamDependencyMetadata);
+ _ = Throw.IfNull(downstreamDependencyMetadata);
+ services.TryAddEnumerable(ServiceDescriptor.Singleton(downstreamDependencyMetadata));
+ services.TryAddSingleton();
return services;
}
@@ -39,9 +39,40 @@ public static IServiceCollection AddDownstreamDependencyMetadata(this IServiceCo
where T : class, IDownstreamDependencyMetadata
{
_ = Throw.IfNull(services);
- services.TryAddSingleton();
- _ = services.AddSingleton();
+ services.TryAddEnumerable(ServiceDescriptor.Singleton());
+ services.TryAddSingleton();
+ return services;
+ }
+
+ ///
+ /// Adds services required for HTTP dependency metadata resolution.
+ ///
+ /// The to add the services to.
+ /// The so that additional calls can be chained.
+ public static IServiceCollection AddHttpDependencyMetadataResolver(this IServiceCollection services)
+ {
+ services.TryAddSingleton();
+ return services;
+ }
+
+ ///
+ /// Adds services required for HTTP dependency metadata resolution with the specified metadata providers.
+ ///
+ /// The to add the services to.
+ /// The HTTP dependency metadata providers to register.
+ /// The so that additional calls can be chained.
+ public static IServiceCollection AddHttpDependencyMetadataResolver(
+ this IServiceCollection services,
+ params IDownstreamDependencyMetadata[] providers)
+ {
+ _ = Throw.IfNull(services);
+
+ foreach (var provider in providers)
+ {
+ services.TryAddEnumerable(ServiceDescriptor.Singleton(provider));
+ }
+ services.TryAddSingleton();
return services;
}
}
diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestReader.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestReader.cs
index 78cb73bbddc..c9d7883a22c 100644
--- a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestReader.cs
+++ b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestReader.cs
@@ -12,7 +12,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http.Diagnostics;
using Microsoft.Extensions.Options;
-using Microsoft.Extensions.Telemetry.Internal;
using Microsoft.Shared.Diagnostics;
using Microsoft.Shared.Pools;
@@ -43,7 +42,7 @@ internal sealed class HttpRequestReader : IHttpRequestReader
private readonly OutgoingPathLoggingMode _outgoingPathLogMode;
private readonly IOutgoingRequestContext _requestMetadataContext;
- private readonly IDownstreamDependencyMetadataManager? _downstreamDependencyMetadataManager;
+ private readonly HttpDependencyMetadataResolver? _dependencyMetadataResolver;
public HttpRequestReader(
IServiceProvider serviceProvider,
@@ -51,7 +50,7 @@ public HttpRequestReader(
IHttpRouteFormatter routeFormatter,
IHttpRouteParser httpRouteParser,
IOutgoingRequestContext requestMetadataContext,
- IDownstreamDependencyMetadataManager? downstreamDependencyMetadataManager = null,
+ HttpDependencyMetadataResolver? dependencyMetadataResolver = null,
[ServiceKey] string? serviceKey = null)
: this(
optionsMonitor.GetKeyedOrCurrent(serviceKey),
@@ -59,7 +58,7 @@ public HttpRequestReader(
httpRouteParser,
serviceProvider.GetRequiredOrKeyedRequiredService(serviceKey),
requestMetadataContext,
- downstreamDependencyMetadataManager)
+ dependencyMetadataResolver)
{
}
@@ -69,7 +68,7 @@ internal HttpRequestReader(
IHttpRouteParser httpRouteParser,
IHttpHeadersReader httpHeadersReader,
IOutgoingRequestContext requestMetadataContext,
- IDownstreamDependencyMetadataManager? downstreamDependencyMetadataManager = null)
+ HttpDependencyMetadataResolver? dependencyMetadataResolver = null)
{
_outgoingPathLogMode = Throw.IfOutOfRange(options.RequestPathLoggingMode);
_httpHeadersReader = httpHeadersReader;
@@ -77,7 +76,7 @@ internal HttpRequestReader(
_routeFormatter = routeFormatter;
_httpRouteParser = httpRouteParser;
_requestMetadataContext = requestMetadataContext;
- _downstreamDependencyMetadataManager = downstreamDependencyMetadataManager;
+ _dependencyMetadataResolver = dependencyMetadataResolver;
_defaultSensitiveParameters = options.RouteParameterDataClasses.ToFrozenDictionary(StringComparer.Ordinal);
_queryParameterDataClasses = options.RequestQueryParametersDataClasses.ToFrozenDictionary(StringComparer.Ordinal);
@@ -234,7 +233,7 @@ private void GetRedactedPathAndParameters(HttpRequestMessage request, LogRecord
var requestMetadata = request.GetRequestMetadata() ??
_requestMetadataContext.RequestMetadata ??
- _downstreamDependencyMetadataManager?.GetRequestMetadata(request);
+ _dependencyMetadataResolver?.GetRequestMetadata(request);
if (requestMetadata == null)
{
diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/IDownstreamDependencyMetadataManager.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/IDownstreamDependencyMetadataManager.cs
index 5c49827a3b0..2da95ca2b95 100644
--- a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/IDownstreamDependencyMetadataManager.cs
+++ b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/IDownstreamDependencyMetadataManager.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using System.Net;
using System.Net.Http;
using Microsoft.Extensions.Http.Diagnostics;
@@ -8,8 +9,9 @@
namespace Microsoft.Extensions.Telemetry.Internal;
///
-/// Interface to manage dependency metadata.
+/// (Obsolete) Use .
///
+[Obsolete("Use HttpDependencyMetadataResolver instead. This internal interface will be removed.")]
internal interface IDownstreamDependencyMetadataManager
{
///
diff --git a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Telemetry/DownstreamDependencyMetadataManagerTests.cs b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Telemetry/DownstreamDependencyMetadataManagerTests.cs
index e7cdb4d3f32..c0763cc7ed8 100644
--- a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Telemetry/DownstreamDependencyMetadataManagerTests.cs
+++ b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Telemetry/DownstreamDependencyMetadataManagerTests.cs
@@ -4,14 +4,14 @@
using System;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Telemetry.Internal;
+using Microsoft.Extensions.Http.Diagnostics;
using Xunit;
namespace Microsoft.Extensions.Telemetry.Telemetry;
public class DownstreamDependencyMetadataManagerTests : IDisposable
{
- private readonly IDownstreamDependencyMetadataManager _depMetadataManager;
+ private readonly HttpDependencyMetadataResolver _metadataResolver;
private readonly ServiceProvider _sp;
public DownstreamDependencyMetadataManagerTests()
@@ -20,7 +20,7 @@ public DownstreamDependencyMetadataManagerTests()
.AddDownstreamDependencyMetadata(new BackslashDownstreamDependencyMetadata())
.AddDownstreamDependencyMetadata(new EmptyRouteDependencyMetadata())
.BuildServiceProvider();
- _depMetadataManager = _sp.GetRequiredService();
+ _metadataResolver = _sp.GetRequiredService();
}
[Theory]
@@ -51,7 +51,7 @@ public void GetRequestMetadata_RoutesRegisteredWithBackslashes_ShouldReturnHostM
RequestUri = new Uri(uriString: urlString)
};
- var requestMetadata = _depMetadataManager.GetRequestMetadata(requestMessage);
+ var requestMetadata = _metadataResolver.GetRequestMetadata(requestMessage);
Assert.NotNull(requestMetadata);
Assert.Equal(new BackslashDownstreamDependencyMetadata().DependencyName, requestMetadata.DependencyName);
Assert.Equal(expectedRequestName, requestMetadata.RequestName);
@@ -71,7 +71,7 @@ public void GetRequestMetadata_EmptyRouteRegisteredForDependency_ShouldReturnMet
RequestUri = new Uri(uriString: urlString)
};
- var requestMetadata = _depMetadataManager.GetRequestMetadata(requestMessage);
+ var requestMetadata = _metadataResolver.GetRequestMetadata(requestMessage);
Assert.NotNull(requestMetadata);
Assert.Equal("EmptyRouteService", requestMetadata.DependencyName);