Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 (#603)

# 6.1.0-alpha.1

Expand Down
5 changes: 1 addition & 4 deletions dart/lib/src/http_client/breadcrumb_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,5 @@ class BreadcrumbClient extends BaseClient {
}

@override
void close() {
// See https://github.com/getsentry/sentry-dart/pull/226#discussion_r536984785
_client.close();
}
void close() => _client.close();
}
5 changes: 1 addition & 4 deletions dart/lib/src/http_client/failed_request_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,7 @@ class FailedRequestClient extends BaseClient {
}

@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<void> _captureEvent({
Expand Down
15 changes: 13 additions & 2 deletions dart/lib/src/http_client/sentry_http_client.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:http/http.dart';
import 'tracing_client.dart';
import '../hub.dart';
import '../hub_adapter.dart';
import '../protocol.dart';
Expand All @@ -7,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.
Expand All @@ -32,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.
///
Expand Down Expand Up @@ -79,6 +84,7 @@ class SentryHttpClient extends BaseClient {
List<SentryStatusCode> failedRequestStatusCodes = const [],
bool captureFailedRequests = false,
bool sendDefaultPii = false,
bool networkTracing = false,
}) {
_hub = hub ?? HubAdapter();

Expand All @@ -93,6 +99,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.
Expand All @@ -110,6 +120,7 @@ class SentryHttpClient extends BaseClient {
@override
Future<StreamedResponse> send(BaseRequest request) => _client.send(request);

// See https://github.com/getsentry/sentry-dart/pull/226#discussion_r536984785
@override
void close() => _client.close();
}
Expand Down
50 changes: 50 additions & 0 deletions dart/lib/src/http_client/tracing_client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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<StreamedResponse> send(BaseRequest request) async {
// 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}',
);

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;
}

@override
void close() => _client.close();
}
10 changes: 10 additions & 0 deletions dart/lib/src/hub.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -418,6 +423,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,
Expand Down
7 changes: 7 additions & 0 deletions dart/lib/src/invalid_sentry_trace_header_exception.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class InvalidSentryTraceHeaderException implements Exception {
final String _message;
InvalidSentryTraceHeaderException(this._message);

@override
String toString() => 'Exception: $_message';
}
9 changes: 9 additions & 0 deletions dart/lib/src/noop_sentry_span.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -64,4 +70,7 @@ class NoOpSentrySpan extends ISentrySpan {

@override
bool? get sampled => null;

@override
SentryTraceHeader toSentryTrace() => _header;
}
1 change: 1 addition & 0 deletions dart/lib/src/protocol.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
9 changes: 8 additions & 1 deletion dart/lib/src/protocol/sentry_span.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import '../hub.dart';
import 'span_status.dart';
import '../protocol.dart';

import '../sentry_tracer.dart';
import '../tracing.dart';
Expand Down Expand Up @@ -121,4 +121,11 @@ class SentrySpan extends ISentrySpan {
Map<String, String> get tags => _tags;

Map<String, dynamic> get data => _data;

@override
SentryTraceHeader toSentryTrace() => SentryTraceHeader(
_context.traceId,
_context.spanId,
sampled: sampled,
);
}
51 changes: 51 additions & 0 deletions dart/lib/src/protocol/sentry_trace_header.dart
Original file line number Diff line number Diff line change
@@ -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,
);
}
}
2 changes: 0 additions & 2 deletions dart/lib/src/sentry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,6 @@ class Sentry {
bindToScope: bindToScope,
);

// missing traceHeaders

/// Gets the current active transaction or span.
static ISentrySpan? getSpan() => _hub.getSpan();

Expand Down
6 changes: 4 additions & 2 deletions dart/lib/src/sentry_span_interface.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:meta/meta.dart';

import 'protocol/span_status.dart';
import 'protocol.dart';
import 'tracing.dart';

/// Represents performance monitoring Span.
Expand Down Expand Up @@ -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;
Expand All @@ -55,4 +54,7 @@ abstract class ISentrySpan {

@internal
bool? get sampled;

/// Returns the trace information that could be sent as a sentry-trace header.
SentryTraceHeader toSentryTrace();
}
3 changes: 3 additions & 0 deletions dart/lib/src/sentry_tracer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,7 @@ class SentryTracer extends ISentrySpan {

@override
bool? get sampled => _rootSpan.sampled;

@override
SentryTraceHeader toSentryTrace() => _rootSpan.toSentryTrace();
}
14 changes: 14 additions & 0 deletions dart/lib/src/sentry_transaction_context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions dart/lib/src/tracing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
28 changes: 28 additions & 0 deletions dart/test/http_client/sentry_http_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
}

Expand All @@ -94,6 +120,7 @@ class Fixture {
MaxRequestBodySize maxRequestBodySize = MaxRequestBodySize.never,
List<SentryStatusCode> badStatusCodes = const [],
bool recordBreadcrumbs = true,
bool networkTracing = false,
}) {
final mc = client ?? getClient();
return SentryHttpClient(
Expand All @@ -103,6 +130,7 @@ class Fixture {
failedRequestStatusCodes: badStatusCodes,
maxRequestBodySize: maxRequestBodySize,
recordBreadcrumbs: recordBreadcrumbs,
networkTracing: networkTracing,
);
}

Expand Down
Loading