Skip to content

Commit 8bf9970

Browse files
authored
Add test for the order of activity and metrics with HTTP requests (#56592)
1 parent 16fb943 commit 8bf9970

File tree

2 files changed

+53
-4
lines changed

2 files changed

+53
-4
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,10 @@ public void RequestEnd(HttpContext httpContext, Exception? exception, HostingApp
208208
}
209209

210210
var activity = context.Activity;
211-
// Always stop activity if it was started
211+
// Always stop activity if it was started.
212+
// The HTTP activity must be stopped after the HTTP request duration metric is recorded.
213+
// This order means the activity is ongoing while the metric is recorded and libraries like OTEL
214+
// can capture the activity as a metric exemplar.
212215
if (activity is not null)
213216
{
214217
StopActivity(httpContext, activity, context.HasDiagnosticListener);

src/Hosting/Hosting/test/HostingApplicationDiagnosticsTests.cs

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,17 +156,63 @@ public void EventCountersEnabled()
156156
}
157157

158158
[Fact]
159-
public void MetricsEnabled()
159+
public void Metrics_RequestDuration_RecordedWithHttpActivity()
160160
{
161161
// Arrange
162-
var hostingEventSource = new HostingEventSource(Guid.NewGuid().ToString());
162+
Activity measurementActivity = null;
163+
var measureCount = 0;
164+
165+
// Listen to hosting activity source.
166+
var testSource = new ActivitySource(Path.GetRandomFileName());
167+
using var activityListener = new ActivityListener
168+
{
169+
ShouldListenTo = activitySource => ReferenceEquals(activitySource, testSource),
170+
Sample = (ref ActivityCreationOptions<ActivityContext> _) => ActivitySamplingResult.AllData
171+
};
172+
ActivitySource.AddActivityListener(activityListener);
173+
174+
// Listen to http.server.request.duration.
175+
var testMeterFactory = new TestMeterFactory();
176+
var meterListener = new MeterListener();
177+
meterListener.InstrumentPublished = (i, l) =>
178+
{
179+
if (i.Meter.Scope == testMeterFactory && i.Meter.Name == HostingMetrics.MeterName && i.Name == "http.server.request.duration")
180+
{
181+
l.EnableMeasurementEvents(i);
182+
}
183+
};
184+
meterListener.SetMeasurementEventCallback<double>((i, m, t, s) =>
185+
{
186+
if (Interlocked.Increment(ref measureCount) > 1)
187+
{
188+
throw new Exception("Unexpected measurement count.");
189+
}
190+
191+
measurementActivity = Activity.Current;
192+
});
193+
meterListener.Start();
194+
195+
// Act
196+
var hostingApplication = CreateApplication(out var features, activitySource: testSource, meterFactory: testMeterFactory);
197+
var context = hostingApplication.CreateContext(features);
198+
hostingApplication.DisposeContext(context, null);
163199

200+
// Assert
201+
Assert.Equal(1, measureCount);
202+
Assert.NotNull(measurementActivity);
203+
Assert.Equal(HostingApplicationDiagnostics.ActivityName, measurementActivity.OperationName);
204+
}
205+
206+
[Fact]
207+
public void MetricsEnabled()
208+
{
209+
// Arrange
164210
var testMeterFactory = new TestMeterFactory();
165211
using var activeRequestsCollector = new MetricCollector<long>(testMeterFactory, HostingMetrics.MeterName, "http.server.active_requests");
166212
using var requestDurationCollector = new MetricCollector<double>(testMeterFactory, HostingMetrics.MeterName, "http.server.request.duration");
167213

168214
// Act
169-
var hostingApplication = CreateApplication(out var features, eventSource: hostingEventSource, meterFactory: testMeterFactory);
215+
var hostingApplication = CreateApplication(out var features, meterFactory: testMeterFactory);
170216
var context = hostingApplication.CreateContext(features);
171217

172218
// Assert

0 commit comments

Comments
 (0)