Skip to content

Commit d4ca2ce

Browse files
authored
Record exception in HttpClient native instrumentation (#115959)
* Record exception in HttpClient native instrumentation * Fix Possible null reference argument for parameter 'exception' * Fix test * Fix * Fix * Update links * Set exception event timestamp to match activity end time * Remove trailing space * Add assert --------- Co-authored-by: joegoldman2 <[email protected]>
1 parent dec20cd commit d4ca2ce

File tree

2 files changed

+25
-1
lines changed

2 files changed

+25
-1
lines changed

src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,16 @@ await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false)
207207
activity.SetTag("error.type", errorType);
208208

209209
// The presence of error.type indicates that the conditions for setting Error status are also met.
210-
// https://github.com/open-telemetry/semantic-conventions/blob/v1.26.0/docs/http/http-spans.md#status
210+
// https://github.com/open-telemetry/semantic-conventions/blob/v1.34.0/docs/http/http-spans.md#status
211211
activity.SetStatus(ActivityStatusCode.Error);
212+
213+
if (exception is not null)
214+
{
215+
// Records the exception as per https://github.com/open-telemetry/opentelemetry-specification/blob/v1.45.0/specification/trace/exceptions.md.
216+
// Add the exception event with a timestamp matching the activity's end time
217+
// to ensure it falls within the activity's duration.
218+
activity.AddException(exception, timestamp: activity.StartTimeUtc + activity.Duration);
219+
}
212220
}
213221
}
214222

src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,14 @@ static async Task RunTest(string useVersion, string testAsync, string failureTyp
784784
ActivityAssert.HasTag(conn, "error.type", "name_resolution_error");
785785
ActivityAssert.HasTag(wait, "error.type", "name_resolution_error");
786786

787+
ActivityEvent evt = req.Events.Single(e => e.Name == "exception");
788+
Dictionary<string, object?> tags = evt.Tags.ToDictionary(t => t.Key, t => t.Value);
789+
Assert.Contains("exception.type", tags.Keys);
790+
Assert.Contains("exception.message", tags.Keys);
791+
Assert.Contains("exception.stacktrace", tags.Keys);
792+
Assert.Equal(typeof(HttpRequestException).FullName, tags["exception.type"]);
793+
Assert.InRange(evt.Timestamp - req.StartTimeUtc, new TimeSpan(1), req.Duration);
794+
787795
// Whether System.Net.Quic uses System.Net.Dns is an implementation detail.
788796
if (version != HttpVersion30)
789797
{
@@ -803,6 +811,14 @@ static async Task RunTest(string useVersion, string testAsync, string failureTyp
803811
ActivityAssert.HasTag(conn, "error.type", "connection_error");
804812
ActivityAssert.HasTag(wait, "error.type", "connection_error");
805813

814+
ActivityEvent evt = req.Events.Single(e => e.Name == "exception");
815+
Dictionary<string, object?> tags = evt.Tags.ToDictionary(t => t.Key, t => t.Value);
816+
Assert.Contains("exception.type", tags.Keys);
817+
Assert.Contains("exception.message", tags.Keys);
818+
Assert.Contains("exception.stacktrace", tags.Keys);
819+
Assert.Equal(typeof(HttpRequestException).FullName, tags["exception.type"]);
820+
Assert.InRange(evt.Timestamp - req.StartTimeUtc, new TimeSpan(1), req.Duration);
821+
806822
if (version != HttpVersion30)
807823
{
808824
Activity sock = socketRecorder.VerifyActivityRecordedOnce();

0 commit comments

Comments
 (0)