Skip to content

Commit

Permalink
Add http fields to span.data (#1497)
Browse files Browse the repository at this point in the history
Co-authored-by: Manoel Aranda Neto <[email protected]>
  • Loading branch information
denrase and marandaneto authored May 31, 2023
1 parent 8932ece commit 5d2b46d
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 14 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

## Unreleased

### Features
### Enhancements

- Add http fields to `span.data` ([#1497](https://github.com/getsentry/sentry-dart/pull/1497))
- Set `http.response.status_code`
- Set `http.response_content_length`
- Improve `SentryException#value`, remove stringified stack trace ([##1470](https://github.com/getsentry/sentry-dart/pull/#1470))

## 7.6.3
Expand Down
2 changes: 2 additions & 0 deletions dart/lib/sentry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@ export 'src/exception_stacktrace_extractor.dart';
export 'src/utils/http_sanitizer.dart';
// ignore: invalid_export_of_internal_element
export 'src/utils/url_details.dart';
// ignore: invalid_export_of_internal_element
export 'src/utils/http_header_utils.dart';
2 changes: 2 additions & 0 deletions dart/lib/src/http_client/tracing_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class TracingClient extends BaseClient {
}

response = await _client.send(request);
span?.setData('http.response.status_code', response.statusCode);
span?.setData('http.response_content_length', response.contentLength);
span?.status = SpanStatus.fromHttpStatusCode(response.statusCode);
} catch (exception) {
span?.throwable = exception;
Expand Down
16 changes: 16 additions & 0 deletions dart/lib/src/utils/http_header_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:meta/meta.dart';

/// Helper to extract header data
@internal
class HttpHeaderUtils {
/// Get `Content-Length` header
static int? getContentLength(Map<String, List<String>> headers) {
final contentLengthHeader =
headers['content-length'] ?? headers['Content-Length'];
if (contentLengthHeader != null && contentLengthHeader.isNotEmpty) {
final headerValue = contentLengthHeader.first;
return int.tryParse(headerValue);
}
return null;
}
}
4 changes: 3 additions & 1 deletion dart/test/http_client/tracing_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ void main() {
expect(span.data['url'], 'https://example.com');
expect(span.data['http.query'], 'foo=bar');
expect(span.data['http.fragment'], 'baz');
expect(span.data['http.response.status_code'], 200);
expect(span.data['http.response_content_length'], 2);
});

test('finish span if errored request', () async {
Expand Down Expand Up @@ -225,7 +227,7 @@ class Fixture {
MockClient getClient({int statusCode = 200, String? reason}) {
return MockClient((request) async {
expect(request.url, requestUri);
return Response('', statusCode, reasonPhrase: reason, request: request);
return Response('{}', statusCode, reasonPhrase: reason, request: request);
});
}
}
Expand Down
18 changes: 18 additions & 0 deletions dart/test/utils/http_header_utils_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:sentry/sentry.dart';
import 'package:test/test.dart';

void main() {
test('get content length lower case', () {
final headers = {
'content-length': ['12']
};
expect(HttpHeaderUtils.getContentLength(headers), 12);
});

test('get content length camel case', () {
final headers = {
'Content-Length': ['12']
};
expect(HttpHeaderUtils.getContentLength(headers), 12);
});
}
7 changes: 2 additions & 5 deletions dio/lib/src/breadcrumb_client_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,8 @@ class BreadcrumbClientAdapter implements HttpClientAdapter {

statusCode = response.statusCode;
reason = response.statusMessage;
final contentLengthHeader = response.headers['content-length'];
if (contentLengthHeader != null && contentLengthHeader.isNotEmpty) {
final headerValue = contentLengthHeader.first;
responseBodySize = int.tryParse(headerValue);
}
// ignore: invalid_use_of_internal_member
responseBodySize = HttpHeaderUtils.getContentLength(response.headers);

return response;
} catch (_) {
Expand Down
7 changes: 7 additions & 0 deletions dio/lib/src/tracing_client_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ class TracingClientAdapter implements HttpClientAdapter {

response = await _client.fetch(options, requestStream, cancelFuture);
span?.status = SpanStatus.fromHttpStatusCode(response.statusCode);
span?.setData('http.response.status_code', response.statusCode);
final contentLengthHeader =
// ignore: invalid_use_of_internal_member
HttpHeaderUtils.getContentLength(response.headers);
if (contentLengthHeader != null) {
span?.setData('http.response_content_length', contentLengthHeader);
}
} catch (exception) {
span?.throwable = exception;
span?.status = const SpanStatus.internalError();
Expand Down
28 changes: 21 additions & 7 deletions dio/test/tracing_client_adapter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ void main() {

test('captured span if successful request', () async {
final sut = fixture.getSut(
client: fixture.getClient(statusCode: 200, reason: 'OK'),
client:
fixture.getClient(statusCode: 200, reason: 'OK', contentLength: 2),
);
final tr = fixture._hub.startTransaction(
'name',
Expand All @@ -46,6 +47,8 @@ void main() {
expect(span.data['url'], 'https://example.com');
expect(span.data['http.query'], 'foo=bar');
expect(span.data['http.fragment'], 'baz');
expect(span.data['http.response.status_code'], 200);
expect(span.data['http.response_content_length'], 2);
});

test('finish span if errored request', () async {
Expand Down Expand Up @@ -175,16 +178,27 @@ class Fixture {
return dio;
}

MockHttpClientAdapter getClient({int statusCode = 200, String? reason}) {
MockHttpClientAdapter getClient({
int statusCode = 200,
String? reason,
int? contentLength,
}) {
return MockHttpClientAdapter((options, requestStream, cancelFuture) async {
expect(options.uri, requestUri);

final headers = options.headers.map(
(key, dynamic value) =>
MapEntry(key, <String>[value?.toString() ?? '']),
);

if (contentLength != null) {
headers['Content-Length'] = [contentLength.toString()];
}

return ResponseBody.fromString(
'',
'{}',
statusCode,
headers: options.headers.map(
(key, dynamic value) =>
MapEntry(key, <String>[value?.toString() ?? '']),
),
headers: headers,
);
});
}
Expand Down

0 comments on commit 5d2b46d

Please sign in to comment.