Skip to content

Commit 9e5b381

Browse files
authored
Support independently enabling hosting event counters and metrics (#50565)
1 parent cec3e37 commit 9e5b381

File tree

3 files changed

+78
-8
lines changed

3 files changed

+78
-8
lines changed

src/Hosting/Hosting/src/Internal/HostingApplication.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ public Activity? Activity
144144

145145
public long StartTimestamp { get; set; }
146146
internal bool HasDiagnosticListener { get; set; }
147-
public bool EventLogOrMetricsEnabled { get; set; }
147+
public bool MetricsEnabled { get; set; }
148+
public bool EventLogEnabled { get; set; }
148149

149150
internal HttpActivityFeature? HttpActivityFeature;
150151
internal HttpMetricsTagsFeature? MetricsTagsFeature;
@@ -159,7 +160,8 @@ public void Reset()
159160

160161
StartTimestamp = 0;
161162
HasDiagnosticListener = false;
162-
EventLogOrMetricsEnabled = false;
163+
MetricsEnabled = false;
164+
EventLogEnabled = false;
163165
MetricsTagsFeature?.TagsList.Clear();
164166
}
165167
}

src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,27 @@ public void BeginRequest(HttpContext httpContext, HostingApplication.Context con
5454
{
5555
long startTimestamp = 0;
5656

57-
if (_eventSource.IsEnabled() || _metrics.IsEnabled())
57+
if (_metrics.IsEnabled())
5858
{
59-
context.EventLogOrMetricsEnabled = true;
59+
context.MetricsEnabled = true;
6060
context.MetricsTagsFeature ??= new HttpMetricsTagsFeature();
6161
httpContext.Features.Set<IHttpMetricsTagsFeature>(context.MetricsTagsFeature);
6262

6363
startTimestamp = Stopwatch.GetTimestamp();
6464

65+
// To keep the hot path short we defer logging in this function to non-inlines
66+
RecordRequestStartMetrics(httpContext);
67+
}
68+
69+
if (_eventSource.IsEnabled())
70+
{
71+
context.EventLogEnabled = true;
72+
73+
if (startTimestamp == 0)
74+
{
75+
startTimestamp = Stopwatch.GetTimestamp();
76+
}
77+
6578
// To keep the hot path short we defer logging in this function to non-inlines
6679
RecordRequestStartEventLog(httpContext);
6780
}
@@ -135,7 +148,7 @@ public void RequestEnd(HttpContext httpContext, Exception? exception, HostingApp
135148
// Non-inline
136149
LogRequestFinished(context, startTimestamp, currentTimestamp);
137150

138-
if (context.EventLogOrMetricsEnabled)
151+
if (context.MetricsEnabled)
139152
{
140153
var route = httpContext.GetEndpoint()?.Metadata.GetMetadata<IRouteDiagnosticsMetadata>()?.Route;
141154
var customTags = context.MetricsTagsFeature?.TagsList;
@@ -197,7 +210,7 @@ public void RequestEnd(HttpContext httpContext, Exception? exception, HostingApp
197210
StopActivity(httpContext, activity, context.HasDiagnosticListener);
198211
}
199212

200-
if (context.EventLogOrMetricsEnabled)
213+
if (context.EventLogEnabled)
201214
{
202215
if (exception != null)
203216
{
@@ -219,7 +232,7 @@ public void RequestEnd(HttpContext httpContext, Exception? exception, HostingApp
219232
[MethodImpl(MethodImplOptions.AggressiveInlining)]
220233
public void ContextDisposed(HostingApplication.Context context)
221234
{
222-
if (context.EventLogOrMetricsEnabled)
235+
if (context.EventLogEnabled)
223236
{
224237
_eventSource.RequestStop();
225238
}
@@ -352,10 +365,15 @@ internal UnhandledExceptionData(HttpContext httpContext, long timestamp, Excepti
352365
[MethodImpl(MethodImplOptions.NoInlining)]
353366
private void RecordRequestStartEventLog(HttpContext httpContext)
354367
{
355-
_metrics.RequestStart(httpContext.Request.IsHttps, httpContext.Request.Scheme, httpContext.Request.Method, httpContext.Request.Host);
356368
_eventSource.RequestStart(httpContext.Request.Method, httpContext.Request.Path);
357369
}
358370

371+
[MethodImpl(MethodImplOptions.NoInlining)]
372+
private void RecordRequestStartMetrics(HttpContext httpContext)
373+
{
374+
_metrics.RequestStart(httpContext.Request.IsHttps, httpContext.Request.Scheme, httpContext.Request.Method, httpContext.Request.Host);
375+
}
376+
359377
[MethodImpl(MethodImplOptions.NoInlining)]
360378
private Activity? StartActivity(HttpContext httpContext, bool loggingEnabled, bool diagnosticListenerActivityCreationEnabled, out bool hasDiagnosticListener)
361379
{

src/Hosting/Hosting/test/HostingApplicationDiagnosticsTests.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,56 @@ public async Task EventCountersAndMetricsValues()
124124
m => Assert.True(m.Value > 0));
125125
}
126126

127+
[Fact]
128+
public void EventCountersEnabled()
129+
{
130+
// Arrange
131+
var hostingEventSource = new HostingEventSource(Guid.NewGuid().ToString());
132+
133+
var eventListener = new TestCounterListener(new[]
134+
{
135+
"requests-per-second",
136+
"total-requests",
137+
"current-requests",
138+
"failed-requests"
139+
});
140+
141+
eventListener.EnableEvents(hostingEventSource, EventLevel.Informational, EventKeywords.None,
142+
new Dictionary<string, string>
143+
{
144+
{ "EventCounterIntervalSec", "1" }
145+
});
146+
147+
var testMeterFactory = new TestMeterFactory();
148+
149+
// Act
150+
var hostingApplication = CreateApplication(out var features, eventSource: hostingEventSource, meterFactory: testMeterFactory);
151+
var context = hostingApplication.CreateContext(features);
152+
153+
// Assert
154+
Assert.True(context.EventLogEnabled);
155+
Assert.False(context.MetricsEnabled);
156+
}
157+
158+
[Fact]
159+
public void MetricsEnabled()
160+
{
161+
// Arrange
162+
var hostingEventSource = new HostingEventSource(Guid.NewGuid().ToString());
163+
164+
var testMeterFactory = new TestMeterFactory();
165+
using var activeRequestsCollector = new MetricCollector<long>(testMeterFactory, HostingMetrics.MeterName, "http.server.active_requests");
166+
using var requestDurationCollector = new MetricCollector<double>(testMeterFactory, HostingMetrics.MeterName, "http.server.request.duration");
167+
168+
// Act
169+
var hostingApplication = CreateApplication(out var features, eventSource: hostingEventSource, meterFactory: testMeterFactory);
170+
var context = hostingApplication.CreateContext(features);
171+
172+
// Assert
173+
Assert.True(context.MetricsEnabled);
174+
Assert.False(context.EventLogEnabled);
175+
}
176+
127177
[Fact]
128178
public void DisposeContextDoesNotThrowWhenContextScopeIsNull()
129179
{

0 commit comments

Comments
 (0)