From c6e616421da5252b22097db9e0ae509c8e7d53a9 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Wed, 29 Sep 2021 14:59:42 +0200 Subject: [PATCH 01/12] Feat: Sentry Performance for HTTP client --- CHANGELOG.md | 3 +- .../src/http_client/sentry_http_client.dart | 6 +++ dart/lib/src/http_client/tracing_client.dart | 46 +++++++++++++++++ dart/lib/src/hub.dart | 32 ++++++++++++ dart/lib/src/hub_adapter.dart | 3 ++ ...invalid_sentry_trace_header_exception.dart | 7 +++ dart/lib/src/noop_hub.dart | 3 ++ dart/lib/src/noop_sentry_span.dart | 9 ++++ dart/lib/src/protocol.dart | 1 + dart/lib/src/protocol/sentry_span.dart | 9 +++- .../lib/src/protocol/sentry_trace_header.dart | 51 +++++++++++++++++++ dart/lib/src/sentry_span_interface.dart | 5 +- dart/lib/src/sentry_tracer.dart | 3 ++ dart/lib/src/tracing.dart | 1 + dart/test/mocks/mock_hub.dart | 3 ++ flutter/test/mocks.dart | 3 ++ 16 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 dart/lib/src/http_client/tracing_client.dart create mode 100644 dart/lib/src/invalid_sentry_trace_header_exception.dart create mode 100644 dart/lib/src/protocol/sentry_trace_header.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index f5c3d98f3f..021d05dd6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ * Bump Sentry Android SDK to [5.2.0](https://github.com/getsentry/sentry-dart/pull/594) (#594) - [changelog](https://github.com/getsentry/sentry-java/blob/5.2.0/CHANGELOG.md) - [diff](https://github.com/getsentry/sentry-java/compare/5.1.2...5.2.0) -* enrich Dart context with isolate name (#600) +* Feat: Enrich Dart context with isolate name (#600) +* Feat: Sentry Performance for HTTP client (#) # 6.1.0-alpha.1 diff --git a/dart/lib/src/http_client/sentry_http_client.dart b/dart/lib/src/http_client/sentry_http_client.dart index 6c18f3a233..c29e074da2 100644 --- a/dart/lib/src/http_client/sentry_http_client.dart +++ b/dart/lib/src/http_client/sentry_http_client.dart @@ -1,4 +1,5 @@ import 'package:http/http.dart'; +import 'package:sentry/src/http_client/tracing_client.dart'; import '../hub.dart'; import '../hub_adapter.dart'; import '../protocol.dart'; @@ -79,6 +80,7 @@ class SentryHttpClient extends BaseClient { List failedRequestStatusCodes = const [], bool captureFailedRequests = false, bool sendDefaultPii = false, + bool networkTracing = true, }) { _hub = hub ?? HubAdapter(); @@ -101,6 +103,10 @@ class SentryHttpClient extends BaseClient { innerClient = BreadcrumbClient(client: innerClient, hub: _hub); } + if (networkTracing) { + innerClient = TracingClient(client: innerClient, hub: _hub); + } + _client = innerClient; } diff --git a/dart/lib/src/http_client/tracing_client.dart b/dart/lib/src/http_client/tracing_client.dart new file mode 100644 index 0000000000..d2e8a3d3f1 --- /dev/null +++ b/dart/lib/src/http_client/tracing_client.dart @@ -0,0 +1,46 @@ +import 'package:http/http.dart'; +import '../hub.dart'; +import '../hub_adapter.dart'; +import '../protocol.dart'; + +/// A [http](https://pub.dev/packages/http)-package compatible HTTP client +/// which adds support to Sentry Performance feature. +/// https://develop.sentry.dev/sdk/performance +class TracingClient extends BaseClient { + TracingClient({Client? client, Hub? hub}) + : _hub = hub ?? HubAdapter(), + _client = client ?? Client(); + + final Client _client; + final Hub _hub; + + @override + Future send(BaseRequest request) async { + // see https://develop.sentry.dev/sdk/performance/#header-sentry-trace + final span = _hub.getSpan()?.startChild( + 'http.client', + description: '${request.method} ${request.url}', + ); + + StreamedResponse? response; + try { + if (span != null) { + final traceHeader = span.toSentryTrace(); + request.headers[traceHeader.name] = traceHeader.value; + } + + // TODO: tracingOrigins support + + response = await _client.send(request); + span?.status = SpanStatus.fromHttpStatusCode(response.statusCode); + } catch (exception) { + span?.throwable = exception; + span?.status = SpanStatus.internalError(); + + rethrow; + } finally { + await span?.finish(); + } + return response; + } +} diff --git a/dart/lib/src/hub.dart b/dart/lib/src/hub.dart index d42f09f64d..26e4e0385f 100644 --- a/dart/lib/src/hub.dart +++ b/dart/lib/src/hub.dart @@ -400,6 +400,11 @@ class Hub { SentryLevel.warning, "Instance is disabled and this 'getSpan' call is a no-op.", ); + } else if (!_options.isTracingEnabled()) { + _options.logger( + SentryLevel.info, + "Tracing is disabled and this 'getSpan' returns null.", + ); } else { final item = _peek(); @@ -409,6 +414,28 @@ class Hub { return span; } + /// Returns trace header of active transaction or {@code null} if no transaction is active. + SentryTraceHeader? traceHeaders() { + SentryTraceHeader? header; + if (!_isEnabled) { + _options.logger( + SentryLevel.warning, + "Instance is disabled and this 'traceHeaders' call is a no-op.", + ); + } else if (!_options.isTracingEnabled()) { + _options.logger( + SentryLevel.info, + "Tracing is disabled and this 'traceHeaders' returns null.", + ); + } else { + final item = _peek(); + + header = item.scope.span?.toSentryTrace(); + } + + return header; + } + @internal Future captureTransaction(SentryTransaction transaction) async { var sentryId = SentryId.empty(); @@ -418,6 +445,11 @@ class Hub { SentryLevel.warning, "Instance is disabled and this 'captureTransaction' call is a no-op.", ); + } else if (!_options.isTracingEnabled()) { + _options.logger( + SentryLevel.info, + "Tracing is disabled and this 'captureTransaction' call is a no-op.", + ); } else if (!transaction.finished) { _options.logger( SentryLevel.warning, diff --git a/dart/lib/src/hub_adapter.dart b/dart/lib/src/hub_adapter.dart index 11eb8044bd..112908485c 100644 --- a/dart/lib/src/hub_adapter.dart +++ b/dart/lib/src/hub_adapter.dart @@ -132,4 +132,7 @@ class HubAdapter implements Hub { String transaction, ) => Sentry.currentHub.setSpanContext(throwable, span, transaction); + + @override + SentryTraceHeader? traceHeaders() => Sentry.currentHub.traceHeaders(); } diff --git a/dart/lib/src/invalid_sentry_trace_header_exception.dart b/dart/lib/src/invalid_sentry_trace_header_exception.dart new file mode 100644 index 0000000000..4b953fabfa --- /dev/null +++ b/dart/lib/src/invalid_sentry_trace_header_exception.dart @@ -0,0 +1,7 @@ +class InvalidSentryTraceHeaderException implements Exception { + final String _message; + InvalidSentryTraceHeaderException(this._message); + + @override + String toString() => 'Exception: $_message'; +} diff --git a/dart/lib/src/noop_hub.dart b/dart/lib/src/noop_hub.dart index 06163bac20..0f970d045f 100644 --- a/dart/lib/src/noop_hub.dart +++ b/dart/lib/src/noop_hub.dart @@ -96,4 +96,7 @@ class NoOpHub implements Hub { @override void setSpanContext(throwable, ISentrySpan span, String transaction) {} + + @override + SentryTraceHeader? traceHeaders() => null; } diff --git a/dart/lib/src/noop_sentry_span.dart b/dart/lib/src/noop_sentry_span.dart index 3f3caa426c..d1a98c4d87 100644 --- a/dart/lib/src/noop_sentry_span.dart +++ b/dart/lib/src/noop_sentry_span.dart @@ -13,6 +13,12 @@ class NoOpSentrySpan extends ISentrySpan { operation: 'noop', ); + static final _header = SentryTraceHeader( + SentryId.empty(), + SpanId.empty(), + sampled: false, + ); + static final _timestamp = getUtcDateTime(); factory NoOpSentrySpan() { @@ -64,4 +70,7 @@ class NoOpSentrySpan extends ISentrySpan { @override bool? get sampled => null; + + @override + SentryTraceHeader toSentryTrace() => _header; } diff --git a/dart/lib/src/protocol.dart b/dart/lib/src/protocol.dart index b5004d4d36..eae944558c 100644 --- a/dart/lib/src/protocol.dart +++ b/dart/lib/src/protocol.dart @@ -32,3 +32,4 @@ export 'protocol/span_id.dart'; export 'protocol/sentry_transaction.dart'; export 'protocol/sentry_trace_context.dart'; export 'protocol/sentry_span.dart'; +export 'protocol/sentry_trace_header.dart'; diff --git a/dart/lib/src/protocol/sentry_span.dart b/dart/lib/src/protocol/sentry_span.dart index 0b0b09a8b9..8d60f6bdd4 100644 --- a/dart/lib/src/protocol/sentry_span.dart +++ b/dart/lib/src/protocol/sentry_span.dart @@ -1,5 +1,5 @@ import '../hub.dart'; -import 'span_status.dart'; +import '../protocol.dart'; import '../sentry_tracer.dart'; import '../tracing.dart'; @@ -121,4 +121,11 @@ class SentrySpan extends ISentrySpan { Map get tags => _tags; Map get data => _data; + + @override + SentryTraceHeader toSentryTrace() => SentryTraceHeader( + _context.traceId, + _context.spanId, + sampled: sampled, + ); } diff --git a/dart/lib/src/protocol/sentry_trace_header.dart b/dart/lib/src/protocol/sentry_trace_header.dart new file mode 100644 index 0000000000..0e1e11fe00 --- /dev/null +++ b/dart/lib/src/protocol/sentry_trace_header.dart @@ -0,0 +1,51 @@ +import 'package:meta/meta.dart'; + +import '../invalid_sentry_trace_header_exception.dart'; +import '../protocol.dart'; + +/// Represents HTTP header "sentry-trace". +@immutable +class SentryTraceHeader { + static const _traceHeader = 'sentry-trace'; + + final SentryId traceId; + final SpanId spanId; + final bool? sampled; + + String get name => _traceHeader; + + String get value { + if (sampled != null) { + final sampled = this.sampled! ? '1' : '0'; + return '$traceId-$spanId-$sampled'; + } else { + return '$traceId-$spanId'; + } + } + + SentryTraceHeader( + this.traceId, + this.spanId, { + bool? sampled, + }) : sampled = sampled; + + factory SentryTraceHeader.fromTraceHeader(String header) { + final parts = header.split('-'); + bool? sampled; + + if (parts.length < 2) { + throw InvalidSentryTraceHeaderException('Header: $header is invalid.'); + } else if (parts.length == 3) { + sampled = '1' == parts[2]; + } + + final traceId = SentryId.fromId(parts[0]); + final spanId = SpanId.fromId(parts[1]); + + return SentryTraceHeader( + traceId, + spanId, + sampled: sampled, + ); + } +} diff --git a/dart/lib/src/sentry_span_interface.dart b/dart/lib/src/sentry_span_interface.dart index df92ad9056..b85c4a0ab0 100644 --- a/dart/lib/src/sentry_span_interface.dart +++ b/dart/lib/src/sentry_span_interface.dart @@ -1,6 +1,6 @@ import 'package:meta/meta.dart'; -import 'protocol/span_status.dart'; +import 'protocol.dart'; import 'tracing.dart'; /// Represents performance monitoring Span. @@ -55,4 +55,7 @@ abstract class ISentrySpan { @internal bool? get sampled; + + /// Returns the trace information that could be sent as a sentry-trace header. + SentryTraceHeader toSentryTrace(); } diff --git a/dart/lib/src/sentry_tracer.dart b/dart/lib/src/sentry_tracer.dart index 29aad8d2dd..964f2097a7 100644 --- a/dart/lib/src/sentry_tracer.dart +++ b/dart/lib/src/sentry_tracer.dart @@ -132,4 +132,7 @@ class SentryTracer extends ISentrySpan { @override bool? get sampled => _rootSpan.sampled; + + @override + SentryTraceHeader toSentryTrace() => _rootSpan.toSentryTrace(); } diff --git a/dart/lib/src/tracing.dart b/dart/lib/src/tracing.dart index 0bba524710..362463076d 100644 --- a/dart/lib/src/tracing.dart +++ b/dart/lib/src/tracing.dart @@ -3,3 +3,4 @@ export 'sentry_sampling_context.dart'; export 'sentry_span_context.dart'; export 'sentry_span_interface.dart'; export 'noop_sentry_span.dart'; +export 'invalid_sentry_trace_header_exception.dart'; diff --git a/dart/test/mocks/mock_hub.dart b/dart/test/mocks/mock_hub.dart index 6562544e76..f3ab663399 100644 --- a/dart/test/mocks/mock_hub.dart +++ b/dart/test/mocks/mock_hub.dart @@ -148,6 +148,9 @@ class MockHub implements Hub { void setSpanContext(throwable, ISentrySpan span, String transaction) { spanContextCals++; } + + @override + SentryTraceHeader? traceHeaders() => null; } class CaptureEventCall { diff --git a/flutter/test/mocks.dart b/flutter/test/mocks.dart index 3e7ae0e655..5037e059fa 100644 --- a/flutter/test/mocks.dart +++ b/flutter/test/mocks.dart @@ -190,4 +190,7 @@ class NoOpHub implements Hub { @override void setSpanContext(throwable, ISentrySpan span, String transaction) {} + + @override + SentryTraceHeader? traceHeaders() => null; } From e7d0ad1cd38997b88b56579f426ac2e6e75d9292 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Wed, 29 Sep 2021 15:15:20 +0200 Subject: [PATCH 02/12] create transaction for example --- dart/lib/src/http_client/breadcrumb_client.dart | 8 ++++---- .../lib/src/http_client/failed_request_client.dart | 14 +++++++------- dart/lib/src/http_client/sentry_http_client.dart | 2 +- flutter/example/lib/main.dart | 8 ++++++++ 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/dart/lib/src/http_client/breadcrumb_client.dart b/dart/lib/src/http_client/breadcrumb_client.dart index 4459773d83..e8802beee7 100644 --- a/dart/lib/src/http_client/breadcrumb_client.dart +++ b/dart/lib/src/http_client/breadcrumb_client.dart @@ -58,8 +58,8 @@ class BreadcrumbClient extends BaseClient { String? reason; int? responseBodySize; - final stopwatch = Stopwatch(); - stopwatch.start(); + // final stopwatch = Stopwatch(); + // stopwatch.start(); try { final response = await _client.send(request); @@ -73,7 +73,7 @@ class BreadcrumbClient extends BaseClient { requestHadException = true; rethrow; } finally { - stopwatch.stop(); + // stopwatch.stop(); var breadcrumb = Breadcrumb.http( level: requestHadException ? SentryLevel.error : SentryLevel.info, @@ -81,7 +81,7 @@ class BreadcrumbClient extends BaseClient { method: request.method, statusCode: statusCode, reason: reason, - requestDuration: stopwatch.elapsed, + // requestDuration: stopwatch.elapsed, requestBodySize: request.contentLength, responseBodySize: responseBodySize, ); diff --git a/dart/lib/src/http_client/failed_request_client.dart b/dart/lib/src/http_client/failed_request_client.dart index 649f9f4ea4..e277d672db 100644 --- a/dart/lib/src/http_client/failed_request_client.dart +++ b/dart/lib/src/http_client/failed_request_client.dart @@ -102,8 +102,8 @@ class FailedRequestClient extends BaseClient { Object? exception; StackTrace? stackTrace; - final stopwatch = Stopwatch(); - stopwatch.start(); + // final stopwatch = Stopwatch(); + // stopwatch.start(); try { final response = await _client.send(request); @@ -114,7 +114,7 @@ class FailedRequestClient extends BaseClient { stackTrace = st; rethrow; } finally { - stopwatch.stop(); + // stopwatch.stop(); // If captureFailedRequests is true, there statusCode is null. // So just one of these blocks can be called. @@ -124,7 +124,7 @@ class FailedRequestClient extends BaseClient { exception: exception, stackTrace: stackTrace, request: request, - requestDuration: stopwatch.elapsed, + // requestDuration: stopwatch.elapsed, ); } else if (failedRequestStatusCodes.containsStatusCode(statusCode)) { final message = @@ -136,7 +136,7 @@ class FailedRequestClient extends BaseClient { exception: exception ?? httpException, request: request, reason: message, - requestDuration: stopwatch.elapsed, + // requestDuration: stopwatch.elapsed, ); } } @@ -153,7 +153,7 @@ class FailedRequestClient extends BaseClient { required Object? exception, StackTrace? stackTrace, String? reason, - required Duration requestDuration, + // required Duration requestDuration, required BaseRequest request, }) async { // As far as I can tell there's no way to get the uri without the query part @@ -171,7 +171,7 @@ class FailedRequestClient extends BaseClient { data: _getDataFromRequest(request), other: { 'content_length': request.contentLength.toString(), - 'duration': requestDuration.toString(), + // 'duration': requestDuration.toString(), }, ); diff --git a/dart/lib/src/http_client/sentry_http_client.dart b/dart/lib/src/http_client/sentry_http_client.dart index c29e074da2..bc5d612fad 100644 --- a/dart/lib/src/http_client/sentry_http_client.dart +++ b/dart/lib/src/http_client/sentry_http_client.dart @@ -1,5 +1,5 @@ import 'package:http/http.dart'; -import 'package:sentry/src/http_client/tracing_client.dart'; +import 'tracing_client.dart'; import '../hub.dart'; import '../hub_adapter.dart'; import '../protocol.dart'; diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 702c8816e3..361ec3bee5 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -473,6 +473,12 @@ class SecondaryScaffold extends StatelessWidget { } Future makeWebRequest(BuildContext context) async { + final transaction = Sentry.startTransaction( + 'flutterweb', + 'request', + bindToScope: true, + ); + final client = SentryHttpClient( captureFailedRequests: true, failedRequestStatusCodes: [SentryStatusCode.range(400, 500)], @@ -481,6 +487,8 @@ Future makeWebRequest(BuildContext context) async { // In case of an exception, let it get caught and reported to Sentry final response = await client.get(Uri.parse('https://flutter.dev/')); + await transaction.finish(status: SpanStatus.ok()); + await showDialog( context: context, // gets tracked if using SentryNavigatorObserver From 2491ba290d57d0444c0abbebf95af2a1dd40e5c2 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Wed, 29 Sep 2021 17:04:03 +0200 Subject: [PATCH 03/12] review --- dart/lib/src/http_client/breadcrumb_client.dart | 8 ++++---- .../lib/src/http_client/failed_request_client.dart | 14 +++++++------- dart/lib/src/http_client/sentry_http_client.dart | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/dart/lib/src/http_client/breadcrumb_client.dart b/dart/lib/src/http_client/breadcrumb_client.dart index e8802beee7..4459773d83 100644 --- a/dart/lib/src/http_client/breadcrumb_client.dart +++ b/dart/lib/src/http_client/breadcrumb_client.dart @@ -58,8 +58,8 @@ class BreadcrumbClient extends BaseClient { String? reason; int? responseBodySize; - // final stopwatch = Stopwatch(); - // stopwatch.start(); + final stopwatch = Stopwatch(); + stopwatch.start(); try { final response = await _client.send(request); @@ -73,7 +73,7 @@ class BreadcrumbClient extends BaseClient { requestHadException = true; rethrow; } finally { - // stopwatch.stop(); + stopwatch.stop(); var breadcrumb = Breadcrumb.http( level: requestHadException ? SentryLevel.error : SentryLevel.info, @@ -81,7 +81,7 @@ class BreadcrumbClient extends BaseClient { method: request.method, statusCode: statusCode, reason: reason, - // requestDuration: stopwatch.elapsed, + requestDuration: stopwatch.elapsed, requestBodySize: request.contentLength, responseBodySize: responseBodySize, ); diff --git a/dart/lib/src/http_client/failed_request_client.dart b/dart/lib/src/http_client/failed_request_client.dart index e277d672db..649f9f4ea4 100644 --- a/dart/lib/src/http_client/failed_request_client.dart +++ b/dart/lib/src/http_client/failed_request_client.dart @@ -102,8 +102,8 @@ class FailedRequestClient extends BaseClient { Object? exception; StackTrace? stackTrace; - // final stopwatch = Stopwatch(); - // stopwatch.start(); + final stopwatch = Stopwatch(); + stopwatch.start(); try { final response = await _client.send(request); @@ -114,7 +114,7 @@ class FailedRequestClient extends BaseClient { stackTrace = st; rethrow; } finally { - // stopwatch.stop(); + stopwatch.stop(); // If captureFailedRequests is true, there statusCode is null. // So just one of these blocks can be called. @@ -124,7 +124,7 @@ class FailedRequestClient extends BaseClient { exception: exception, stackTrace: stackTrace, request: request, - // requestDuration: stopwatch.elapsed, + requestDuration: stopwatch.elapsed, ); } else if (failedRequestStatusCodes.containsStatusCode(statusCode)) { final message = @@ -136,7 +136,7 @@ class FailedRequestClient extends BaseClient { exception: exception ?? httpException, request: request, reason: message, - // requestDuration: stopwatch.elapsed, + requestDuration: stopwatch.elapsed, ); } } @@ -153,7 +153,7 @@ class FailedRequestClient extends BaseClient { required Object? exception, StackTrace? stackTrace, String? reason, - // required Duration requestDuration, + required Duration requestDuration, required BaseRequest request, }) async { // As far as I can tell there's no way to get the uri without the query part @@ -171,7 +171,7 @@ class FailedRequestClient extends BaseClient { data: _getDataFromRequest(request), other: { 'content_length': request.contentLength.toString(), - // 'duration': requestDuration.toString(), + 'duration': requestDuration.toString(), }, ); diff --git a/dart/lib/src/http_client/sentry_http_client.dart b/dart/lib/src/http_client/sentry_http_client.dart index bc5d612fad..8ba0c8ca64 100644 --- a/dart/lib/src/http_client/sentry_http_client.dart +++ b/dart/lib/src/http_client/sentry_http_client.dart @@ -95,6 +95,10 @@ class SentryHttpClient extends BaseClient { client: innerClient, ); + if (networkTracing) { + innerClient = TracingClient(client: innerClient, hub: _hub); + } + // The ordering here matters. // We don't want to include the breadcrumbs for the current request // when capturing it as a failed request. @@ -103,10 +107,6 @@ class SentryHttpClient extends BaseClient { innerClient = BreadcrumbClient(client: innerClient, hub: _hub); } - if (networkTracing) { - innerClient = TracingClient(client: innerClient, hub: _hub); - } - _client = innerClient; } From e014acf87fd8befeca4fe8b0c0ece5a438038126 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Thu, 30 Sep 2021 09:56:01 +0200 Subject: [PATCH 04/12] fix --- .../src/http_client/breadcrumb_client.dart | 6 ++--- .../http_client/failed_request_client.dart | 6 ++--- dart/lib/src/http_client/tracing_client.dart | 4 ++++ dart/lib/src/hub.dart | 22 ------------------- dart/lib/src/hub_adapter.dart | 3 --- dart/lib/src/noop_hub.dart | 3 --- .../http_client/sentry_http_client_test.dart | 2 ++ dart/test/mocks/mock_hub.dart | 3 --- flutter/test/mocks.dart | 3 --- 9 files changed, 10 insertions(+), 42 deletions(-) diff --git a/dart/lib/src/http_client/breadcrumb_client.dart b/dart/lib/src/http_client/breadcrumb_client.dart index 4459773d83..6f1e41f894 100644 --- a/dart/lib/src/http_client/breadcrumb_client.dart +++ b/dart/lib/src/http_client/breadcrumb_client.dart @@ -90,9 +90,7 @@ class BreadcrumbClient extends BaseClient { } } + /// See https://github.com/getsentry/sentry-dart/pull/226#discussion_r536984785 @override - void close() { - // See https://github.com/getsentry/sentry-dart/pull/226#discussion_r536984785 - _client.close(); - } + void close() => _client.close(); } diff --git a/dart/lib/src/http_client/failed_request_client.dart b/dart/lib/src/http_client/failed_request_client.dart index 649f9f4ea4..5a5af52839 100644 --- a/dart/lib/src/http_client/failed_request_client.dart +++ b/dart/lib/src/http_client/failed_request_client.dart @@ -142,11 +142,9 @@ class FailedRequestClient extends BaseClient { } } + /// See https://github.com/getsentry/sentry-dart/pull/226#discussion_r536984785 @override - void close() { - // See https://github.com/getsentry/sentry-dart/pull/226#discussion_r536984785 - _client.close(); - } + void close() => _client.close(); // See https://develop.sentry.dev/sdk/event-payloads/request/ Future _captureEvent({ diff --git a/dart/lib/src/http_client/tracing_client.dart b/dart/lib/src/http_client/tracing_client.dart index d2e8a3d3f1..a180416f49 100644 --- a/dart/lib/src/http_client/tracing_client.dart +++ b/dart/lib/src/http_client/tracing_client.dart @@ -43,4 +43,8 @@ class TracingClient extends BaseClient { } return response; } + + /// See https://github.com/getsentry/sentry-dart/pull/226#discussion_r536984785 + @override + void close() => _client.close(); } diff --git a/dart/lib/src/hub.dart b/dart/lib/src/hub.dart index 26e4e0385f..e533ef3501 100644 --- a/dart/lib/src/hub.dart +++ b/dart/lib/src/hub.dart @@ -414,28 +414,6 @@ class Hub { return span; } - /// Returns trace header of active transaction or {@code null} if no transaction is active. - SentryTraceHeader? traceHeaders() { - SentryTraceHeader? header; - if (!_isEnabled) { - _options.logger( - SentryLevel.warning, - "Instance is disabled and this 'traceHeaders' call is a no-op.", - ); - } else if (!_options.isTracingEnabled()) { - _options.logger( - SentryLevel.info, - "Tracing is disabled and this 'traceHeaders' returns null.", - ); - } else { - final item = _peek(); - - header = item.scope.span?.toSentryTrace(); - } - - return header; - } - @internal Future captureTransaction(SentryTransaction transaction) async { var sentryId = SentryId.empty(); diff --git a/dart/lib/src/hub_adapter.dart b/dart/lib/src/hub_adapter.dart index 112908485c..11eb8044bd 100644 --- a/dart/lib/src/hub_adapter.dart +++ b/dart/lib/src/hub_adapter.dart @@ -132,7 +132,4 @@ class HubAdapter implements Hub { String transaction, ) => Sentry.currentHub.setSpanContext(throwable, span, transaction); - - @override - SentryTraceHeader? traceHeaders() => Sentry.currentHub.traceHeaders(); } diff --git a/dart/lib/src/noop_hub.dart b/dart/lib/src/noop_hub.dart index 0f970d045f..06163bac20 100644 --- a/dart/lib/src/noop_hub.dart +++ b/dart/lib/src/noop_hub.dart @@ -96,7 +96,4 @@ class NoOpHub implements Hub { @override void setSpanContext(throwable, ISentrySpan span, String transaction) {} - - @override - SentryTraceHeader? traceHeaders() => null; } diff --git a/dart/test/http_client/sentry_http_client_test.dart b/dart/test/http_client/sentry_http_client_test.dart index da8042241b..28e555ee12 100644 --- a/dart/test/http_client/sentry_http_client_test.dart +++ b/dart/test/http_client/sentry_http_client_test.dart @@ -94,6 +94,7 @@ class Fixture { MaxRequestBodySize maxRequestBodySize = MaxRequestBodySize.never, List badStatusCodes = const [], bool recordBreadcrumbs = true, + bool networkTracing = true, }) { final mc = client ?? getClient(); return SentryHttpClient( @@ -103,6 +104,7 @@ class Fixture { failedRequestStatusCodes: badStatusCodes, maxRequestBodySize: maxRequestBodySize, recordBreadcrumbs: recordBreadcrumbs, + networkTracing: networkTracing, ); } diff --git a/dart/test/mocks/mock_hub.dart b/dart/test/mocks/mock_hub.dart index f3ab663399..6562544e76 100644 --- a/dart/test/mocks/mock_hub.dart +++ b/dart/test/mocks/mock_hub.dart @@ -148,9 +148,6 @@ class MockHub implements Hub { void setSpanContext(throwable, ISentrySpan span, String transaction) { spanContextCals++; } - - @override - SentryTraceHeader? traceHeaders() => null; } class CaptureEventCall { diff --git a/flutter/test/mocks.dart b/flutter/test/mocks.dart index 5037e059fa..3e7ae0e655 100644 --- a/flutter/test/mocks.dart +++ b/flutter/test/mocks.dart @@ -190,7 +190,4 @@ class NoOpHub implements Hub { @override void setSpanContext(throwable, ISentrySpan span, String transaction) {} - - @override - SentryTraceHeader? traceHeaders() => null; } From d6a565226f7dead80f6b887790c48cee3f46ff74 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Thu, 30 Sep 2021 11:05:35 +0200 Subject: [PATCH 05/12] tests --- .../src/http_client/breadcrumb_client.dart | 1 - .../http_client/failed_request_client.dart | 1 - .../src/http_client/sentry_http_client.dart | 3 +- dart/lib/src/http_client/tracing_client.dart | 4 +- dart/lib/src/sentry.dart | 2 - dart/lib/src/sentry_span_interface.dart | 1 - .../http_client/sentry_http_client_test.dart | 28 ++- .../test/http_client/tracing_client_test.dart | 164 ++++++++++++++++++ dart/test/hub_test.dart | 21 +++ dart/test/mocks/mock_hub.dart | 3 + .../protocol/sentry_trace_header_test.dart | 30 ++++ dart/test/sentry_span_test.dart | 7 + dart/test/sentry_tracer_test.dart | 7 + flutter/example/lib/main.dart | 3 +- 14 files changed, 265 insertions(+), 10 deletions(-) create mode 100644 dart/test/http_client/tracing_client_test.dart create mode 100644 dart/test/protocol/sentry_trace_header_test.dart diff --git a/dart/lib/src/http_client/breadcrumb_client.dart b/dart/lib/src/http_client/breadcrumb_client.dart index 6f1e41f894..f7d02a97e2 100644 --- a/dart/lib/src/http_client/breadcrumb_client.dart +++ b/dart/lib/src/http_client/breadcrumb_client.dart @@ -90,7 +90,6 @@ class BreadcrumbClient extends BaseClient { } } - /// See https://github.com/getsentry/sentry-dart/pull/226#discussion_r536984785 @override void close() => _client.close(); } diff --git a/dart/lib/src/http_client/failed_request_client.dart b/dart/lib/src/http_client/failed_request_client.dart index 5a5af52839..d4966a859f 100644 --- a/dart/lib/src/http_client/failed_request_client.dart +++ b/dart/lib/src/http_client/failed_request_client.dart @@ -142,7 +142,6 @@ class FailedRequestClient extends BaseClient { } } - /// See https://github.com/getsentry/sentry-dart/pull/226#discussion_r536984785 @override void close() => _client.close(); diff --git a/dart/lib/src/http_client/sentry_http_client.dart b/dart/lib/src/http_client/sentry_http_client.dart index 8ba0c8ca64..072b76513a 100644 --- a/dart/lib/src/http_client/sentry_http_client.dart +++ b/dart/lib/src/http_client/sentry_http_client.dart @@ -80,7 +80,7 @@ class SentryHttpClient extends BaseClient { List failedRequestStatusCodes = const [], bool captureFailedRequests = false, bool sendDefaultPii = false, - bool networkTracing = true, + bool networkTracing = false, }) { _hub = hub ?? HubAdapter(); @@ -116,6 +116,7 @@ class SentryHttpClient extends BaseClient { @override Future send(BaseRequest request) => _client.send(request); + // See https://github.com/getsentry/sentry-dart/pull/226#discussion_r536984785 @override void close() => _client.close(); } diff --git a/dart/lib/src/http_client/tracing_client.dart b/dart/lib/src/http_client/tracing_client.dart index a180416f49..efef25b093 100644 --- a/dart/lib/src/http_client/tracing_client.dart +++ b/dart/lib/src/http_client/tracing_client.dart @@ -17,7 +17,8 @@ class TracingClient extends BaseClient { @override Future send(BaseRequest request) async { // see https://develop.sentry.dev/sdk/performance/#header-sentry-trace - final span = _hub.getSpan()?.startChild( + final currentSpan = _hub.getSpan(); + final span = currentSpan?.startChild( 'http.client', description: '${request.method} ${request.url}', ); @@ -44,7 +45,6 @@ class TracingClient extends BaseClient { return response; } - /// See https://github.com/getsentry/sentry-dart/pull/226#discussion_r536984785 @override void close() => _client.close(); } diff --git a/dart/lib/src/sentry.dart b/dart/lib/src/sentry.dart index 543deb6a13..4ded2d49ce 100644 --- a/dart/lib/src/sentry.dart +++ b/dart/lib/src/sentry.dart @@ -251,8 +251,6 @@ class Sentry { bindToScope: bindToScope, ); - // missing traceHeaders - /// Gets the current active transaction or span. static ISentrySpan? getSpan() => _hub.getSpan(); diff --git a/dart/lib/src/sentry_span_interface.dart b/dart/lib/src/sentry_span_interface.dart index b85c4a0ab0..4942fb736f 100644 --- a/dart/lib/src/sentry_span_interface.dart +++ b/dart/lib/src/sentry_span_interface.dart @@ -42,7 +42,6 @@ abstract class ISentrySpan { /// Returns the star timestamp DateTime get startTimestamp; - // missing toTraceHeader /// Returns true if span is finished bool get finished; diff --git a/dart/test/http_client/sentry_http_client_test.dart b/dart/test/http_client/sentry_http_client_test.dart index 28e555ee12..390cc98561 100644 --- a/dart/test/http_client/sentry_http_client_test.dart +++ b/dart/test/http_client/sentry_http_client_test.dart @@ -73,6 +73,32 @@ void main() { expect(mockHub.captureExceptionCalls.length, 0); verify(mockClient.close()); }); + + test('no captured span if tracing disabled', () async { + final sut = fixture.getSut( + client: fixture.getClient(statusCode: 200, reason: 'OK'), + recordBreadcrumbs: false, + networkTracing: false, + ); + + final response = await sut.get(requestUri); + expect(response.statusCode, 200); + + expect(fixture.hub.getSpanCalls, 0); + }); + + test('captured span if tracing enabled', () async { + final sut = fixture.getSut( + client: fixture.getClient(statusCode: 200, reason: 'OK'), + recordBreadcrumbs: false, + networkTracing: true, + ); + + final response = await sut.get(requestUri); + expect(response.statusCode, 200); + + expect(fixture.hub.getSpanCalls, 1); + }); }); } @@ -94,7 +120,7 @@ class Fixture { MaxRequestBodySize maxRequestBodySize = MaxRequestBodySize.never, List badStatusCodes = const [], bool recordBreadcrumbs = true, - bool networkTracing = true, + bool networkTracing = false, }) { final mc = client ?? getClient(); return SentryHttpClient( diff --git a/dart/test/http_client/tracing_client_test.dart b/dart/test/http_client/tracing_client_test.dart new file mode 100644 index 0000000000..2c709ce2a7 --- /dev/null +++ b/dart/test/http_client/tracing_client_test.dart @@ -0,0 +1,164 @@ +import 'package:http/http.dart'; +import 'package:http/testing.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sentry/sentry.dart'; +import 'package:sentry/src/http_client/failed_request_client.dart'; +import 'package:sentry/src/http_client/tracing_client.dart'; +import 'package:sentry/src/sentry_tracer.dart'; +import 'package:test/test.dart'; + +import '../mocks.dart'; +import '../mocks/mock_hub.dart'; +import '../mocks/mock_transport.dart'; + +final requestUri = Uri.parse('https://example.com?foo=bar'); + +void main() { + group(TracingClient, () { + late Fixture fixture; + + setUp(() { + fixture = Fixture(); + }); + + test('captured span if successful request', () async { + final sut = fixture.getSut( + client: fixture.getClient(statusCode: 200, reason: 'OK'), + ); + final tr = fixture._hub.startTransaction( + 'name', + 'op', + bindToScope: true, + ); + + await sut.get(requestUri); + + await tr.finish(); + + final tracer = (tr as SentryTracer); + final span = tracer.children.first; + + expect(span.status, SpanStatus.ok()); + expect(span.context.operation, 'http.client'); + expect(span.context.description, 'GET https://example.com?foo=bar'); + }); + + test('finish span if errored request', () async { + final sut = fixture.getSut( + client: createThrowingClient(), + ); + final tr = fixture._hub.startTransaction( + 'name', + 'op', + bindToScope: true, + ); + + try { + await sut.get(requestUri); + } catch (_) { + // ignore + } + + await tr.finish(); + + final tracer = (tr as SentryTracer); + final span = tracer.children.first; + + expect(span.finished, isTrue); + }); + + test('associate exception to span if errored request', () async { + final sut = fixture.getSut( + client: createThrowingClient(), + ); + final tr = fixture._hub.startTransaction( + 'name', + 'op', + bindToScope: true, + ); + + dynamic exception; + try { + await sut.get(requestUri); + } catch (error) { + exception = error; + } + + await tr.finish(); + + final tracer = (tr as SentryTracer); + final span = tracer.children.first; + + expect(span.status, SpanStatus.internalError()); + expect(span.throwable, exception); + }); + + test('captured span adds sentry-trace header to the request', () async { + final sut = fixture.getSut( + client: fixture.getClient(statusCode: 200, reason: 'OK'), + ); + final tr = fixture._hub.startTransaction( + 'name', + 'op', + bindToScope: true, + ); + + final response = await sut.get(requestUri); + + await tr.finish(); + + final tracer = (tr as SentryTracer); + final span = tracer.children.first; + + expect(response.request!.headers['sentry-trace'], + '${span.toSentryTrace().value}'); + }); + + test('do not throw if no span bound to the scope', () async { + final sut = fixture.getSut( + client: fixture.getClient(statusCode: 200, reason: 'OK'), + ); + + await sut.get(requestUri); + }); + }); +} + +MockClient createThrowingClient() { + return MockClient( + (request) async { + expect(request.url, requestUri); + throw TestException(); + }, + ); +} + +class CloseableMockClient extends Mock implements BaseClient {} + +class Fixture { + final _options = SentryOptions(dsn: fakeDsn); + late Hub _hub; + final transport = MockTransport(); + Fixture() { + _options.transport = transport; + _options.tracesSampleRate = 1.0; + _hub = Hub(_options); + } + + TracingClient getSut({MockClient? client}) { + final mc = client ?? getClient(); + return TracingClient( + client: mc, + hub: _hub, + ); + } + + MockClient getClient({int statusCode = 200, String? reason}) { + return MockClient((request) async { + expect(request.url, requestUri); + return Response('', statusCode, reasonPhrase: reason); + }); + } +} + +class TestException implements Exception {} diff --git a/dart/test/hub_test.dart b/dart/test/hub_test.dart index ad56f55104..32bf3ab1d9 100644 --- a/dart/test/hub_test.dart +++ b/dart/test/hub_test.dart @@ -258,6 +258,18 @@ void main() { expect(hub.getSpan(), isNull); }); + test('get span does not return span if tracing is disabled', () async { + final hub = fixture.getSut(tracesSampleRate: null); + + hub.startTransaction( + 'name', + 'op', + description: 'desc', + ); + + expect(hub.getSpan(), isNull); + }); + test('transaction isnt captured if not sampled', () async { final hub = fixture.getSut(sampled: false); @@ -267,6 +279,15 @@ void main() { expect(id, SentryId.empty()); }); + test('transaction isnt captured if tracing is disabled', () async { + final hub = fixture.getSut(tracesSampleRate: null); + + var tr = SentryTransaction(fixture.tracer); + final id = await hub.captureTransaction(tr); + + expect(id, SentryId.empty()); + }); + test('transaction is captured', () async { final hub = fixture.getSut(); diff --git a/dart/test/mocks/mock_hub.dart b/dart/test/mocks/mock_hub.dart index 6562544e76..b0e672abbd 100644 --- a/dart/test/mocks/mock_hub.dart +++ b/dart/test/mocks/mock_hub.dart @@ -14,6 +14,7 @@ class MockHub implements Hub { int closeCalls = 0; bool _isEnabled = true; int spanContextCals = 0; + int getSpanCalls = 0; /// Useful for tests. void reset() { @@ -26,6 +27,7 @@ class MockHub implements Hub { _isEnabled = true; spanContextCals = 0; captureTransactionCalls = []; + getSpanCalls = 0; } @override @@ -141,6 +143,7 @@ class MockHub implements Hub { @override ISentrySpan? getSpan() { + getSpanCalls++; return null; } diff --git a/dart/test/protocol/sentry_trace_header_test.dart b/dart/test/protocol/sentry_trace_header_test.dart new file mode 100644 index 0000000000..aa6e3e5640 --- /dev/null +++ b/dart/test/protocol/sentry_trace_header_test.dart @@ -0,0 +1,30 @@ +import 'package:sentry/sentry.dart'; +import 'package:test/test.dart'; + +void main() { + final _traceId = SentryId.newId(); + final _spanId = SpanId.newId(); + test('header adds 1 to sampled', () { + final header = SentryTraceHeader(_traceId, _spanId, sampled: true); + + expect(header.value, '$_traceId-$_spanId-1'); + }); + + test('header adds 0 to not sampled', () { + final header = SentryTraceHeader(_traceId, _spanId, sampled: true); + + expect(header.value, '$_traceId-$_spanId-0'); + }); + + test('header does not add sampled if no sampled decision', () { + final header = SentryTraceHeader(_traceId, _spanId); + + expect(header.value, '$_traceId-$_spanId'); + }); + + test('header return its name', () { + final header = SentryTraceHeader(_traceId, _spanId); + + expect(header.name, 'sentry-trace'); + }); +} diff --git a/dart/test/sentry_span_test.dart b/dart/test/sentry_span_test.dart index 01bc8c47a3..92a7baaf4c 100644 --- a/dart/test/sentry_span_test.dart +++ b/dart/test/sentry_span_test.dart @@ -106,6 +106,13 @@ void main() { expect(sut.finished, true); }); + + test('toSentryTrace returns trace header', () { + final sut = fixture.getSut(); + + expect(sut.toSentryTrace().value, + '${sut.context.traceId}-${sut.context.spanId}-1'); + }); } class Fixture { diff --git a/dart/test/sentry_tracer_test.dart b/dart/test/sentry_tracer_test.dart index 3a9f3631f1..640a249fa8 100644 --- a/dart/test/sentry_tracer_test.dart +++ b/dart/test/sentry_tracer_test.dart @@ -105,6 +105,13 @@ void main() { expect(childSpan.context.operation, 'op'); expect(childSpan.context.parentSpanId.toString(), parentId.toString()); }); + + test('toSentryTrace returns trace header', () { + final sut = fixture.getSut(); + + expect(sut.toSentryTrace().value, + '${sut.context.traceId}-${sut.context.spanId}-1'); + }); } class Fixture { diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 361ec3bee5..a707ad782d 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -474,13 +474,14 @@ class SecondaryScaffold extends StatelessWidget { Future makeWebRequest(BuildContext context) async { final transaction = Sentry.startTransaction( - 'flutterweb', + 'flutterwebrequest', 'request', bindToScope: true, ); final client = SentryHttpClient( captureFailedRequests: true, + networkTracing: true, failedRequestStatusCodes: [SentryStatusCode.range(400, 500)], ); // We don't do any exception handling here. From 4a1643d8974763efab6f6ff00b65bb141815b578 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Thu, 30 Sep 2021 11:06:41 +0200 Subject: [PATCH 06/12] remove import --- dart/test/http_client/tracing_client_test.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/dart/test/http_client/tracing_client_test.dart b/dart/test/http_client/tracing_client_test.dart index 2c709ce2a7..526e3ab411 100644 --- a/dart/test/http_client/tracing_client_test.dart +++ b/dart/test/http_client/tracing_client_test.dart @@ -2,13 +2,11 @@ import 'package:http/http.dart'; import 'package:http/testing.dart'; import 'package:mockito/mockito.dart'; import 'package:sentry/sentry.dart'; -import 'package:sentry/src/http_client/failed_request_client.dart'; import 'package:sentry/src/http_client/tracing_client.dart'; import 'package:sentry/src/sentry_tracer.dart'; import 'package:test/test.dart'; import '../mocks.dart'; -import '../mocks/mock_hub.dart'; import '../mocks/mock_transport.dart'; final requestUri = Uri.parse('https://example.com?foo=bar'); From 7a2c0105047259976f88b8c859bcc6d1c77311b6 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Thu, 30 Sep 2021 11:08:39 +0200 Subject: [PATCH 07/12] fix --- dart/lib/src/http_client/tracing_client.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dart/lib/src/http_client/tracing_client.dart b/dart/lib/src/http_client/tracing_client.dart index efef25b093..8f35277ad9 100644 --- a/dart/lib/src/http_client/tracing_client.dart +++ b/dart/lib/src/http_client/tracing_client.dart @@ -19,9 +19,9 @@ class TracingClient extends BaseClient { // see https://develop.sentry.dev/sdk/performance/#header-sentry-trace final currentSpan = _hub.getSpan(); final span = currentSpan?.startChild( - 'http.client', - description: '${request.method} ${request.url}', - ); + 'http.client', + description: '${request.method} ${request.url}', + ); StreamedResponse? response; try { From 19d7a3545b0c405e011e0c2491a0eef30c69356f Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Thu, 30 Sep 2021 11:14:28 +0200 Subject: [PATCH 08/12] docs --- dart/lib/src/http_client/sentry_http_client.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dart/lib/src/http_client/sentry_http_client.dart b/dart/lib/src/http_client/sentry_http_client.dart index 072b76513a..c767dc1999 100644 --- a/dart/lib/src/http_client/sentry_http_client.dart +++ b/dart/lib/src/http_client/sentry_http_client.dart @@ -8,9 +8,9 @@ import 'failed_request_client.dart'; /// A [http](https://pub.dev/packages/http)-package compatible HTTP client. /// -/// It can record requests as breadcrumbs. This is on by default. +/// It records requests as breadcrumbs. This is on by default. /// -/// It can also capture requests which throw an exception. This is off by +/// It captures requests which throws an exception. This is off by /// default, set [captureFailedRequests] to `true` to enable it. This can be for /// example for the following reasons: /// - In an browser environment this can be requests which fail because of CORS. @@ -33,6 +33,10 @@ import 'failed_request_client.dart'; /// ); /// ``` /// +/// It starts and finishes a Span if there's a transaction bound to the Scope +/// through the [TracingClient] client, it's disabled by default. +/// Set [networkTracing] to `true` to enable it. +/// /// Remarks: If this client is used as a wrapper, a call to close also closes /// the given client. /// From 47c54ca14aa76aa00da2625c53a2ab1ba11dfd04 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Thu, 30 Sep 2021 11:21:23 +0200 Subject: [PATCH 09/12] new test --- dart/test/protocol/sentry_trace_header_test.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dart/test/protocol/sentry_trace_header_test.dart b/dart/test/protocol/sentry_trace_header_test.dart index aa6e3e5640..9f5f30fec4 100644 --- a/dart/test/protocol/sentry_trace_header_test.dart +++ b/dart/test/protocol/sentry_trace_header_test.dart @@ -27,4 +27,15 @@ void main() { expect(header.name, 'sentry-trace'); }); + + test('invalid header throws $InvalidSentryTraceHeaderException', () { + var exception; + try { + SentryTraceHeader.fromTraceHeader('invalidHeader'); + } catch (error) { + exception = error; + } + + expect(exception is InvalidSentryTraceHeaderException, true); + }); } From 798e70303b8f9c19b026831fef9e5a9613da52af Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Thu, 30 Sep 2021 12:09:24 +0200 Subject: [PATCH 10/12] add factory to sentry header --- dart/lib/src/sentry_transaction_context.dart | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dart/lib/src/sentry_transaction_context.dart b/dart/lib/src/sentry_transaction_context.dart index f9f2f9117f..b9d1961143 100644 --- a/dart/lib/src/sentry_transaction_context.dart +++ b/dart/lib/src/sentry_transaction_context.dart @@ -26,6 +26,20 @@ class SentryTransactionContext extends SentrySpanContext { parentSpanId: parentSpanId, ); + factory SentryTransactionContext.fromSentryTrace( + String name, + String operation, + SentryTraceHeader traceHeader, + ) { + return SentryTransactionContext( + name, + operation, + traceId: traceHeader.traceId, + parentSpanId: traceHeader.spanId, + parentSampled: traceHeader.sampled, + ); + } + SentryTransactionContext copyWith({ String? name, String? operation, From 82d9b42b96449499fd2210f88d1f8b701e9330ed Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Thu, 30 Sep 2021 12:24:27 +0200 Subject: [PATCH 11/12] fix --- CHANGELOG.md | 2 +- dart/test/protocol/sentry_trace_header_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 021d05dd6d..2424bad130 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - [changelog](https://github.com/getsentry/sentry-java/blob/5.2.0/CHANGELOG.md) - [diff](https://github.com/getsentry/sentry-java/compare/5.1.2...5.2.0) * Feat: Enrich Dart context with isolate name (#600) -* Feat: Sentry Performance for HTTP client (#) +* Feat: Sentry Performance for HTTP client (#603) # 6.1.0-alpha.1 diff --git a/dart/test/protocol/sentry_trace_header_test.dart b/dart/test/protocol/sentry_trace_header_test.dart index 9f5f30fec4..f106f72580 100644 --- a/dart/test/protocol/sentry_trace_header_test.dart +++ b/dart/test/protocol/sentry_trace_header_test.dart @@ -11,7 +11,7 @@ void main() { }); test('header adds 0 to not sampled', () { - final header = SentryTraceHeader(_traceId, _spanId, sampled: true); + final header = SentryTraceHeader(_traceId, _spanId, sampled: false); expect(header.value, '$_traceId-$_spanId-0'); }); From 9ebaad7226efcbdd8c4d39367f46638380c6de10 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Thu, 30 Sep 2021 13:37:43 +0200 Subject: [PATCH 12/12] add missing test --- .../test/sentry_transaction_context_test.dart | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 dart/test/sentry_transaction_context_test.dart diff --git a/dart/test/sentry_transaction_context_test.dart b/dart/test/sentry_transaction_context_test.dart new file mode 100644 index 0000000000..f760954e28 --- /dev/null +++ b/dart/test/sentry_transaction_context_test.dart @@ -0,0 +1,31 @@ +import 'package:sentry/sentry.dart'; +import 'package:test/test.dart'; + +void main() { + final _traceId = SentryId.newId(); + final _spanId = SpanId.newId(); + + SentryTransactionContext getSentryTransactionContext({bool? sampled}) { + final header = SentryTraceHeader(_traceId, _spanId, sampled: sampled); + return SentryTransactionContext.fromSentryTrace( + 'name', 'operation', header); + } + + test('parent span id is set from header', () { + final context = getSentryTransactionContext(); + + expect(context.parentSpanId, _spanId); + }); + + test('trace id is set from header', () { + final context = getSentryTransactionContext(); + + expect(context.traceId, _traceId); + }); + + test('parent sampled is set from header', () { + final context = getSentryTransactionContext(sampled: true); + + expect(context.parentSampled, true); + }); +}