Skip to content

Commit

Permalink
Send trace origin (#1534)
Browse files Browse the repository at this point in the history
Co-authored-by: Manoel Aranda Neto <5731772+marandaneto@users.noreply.github.com>
  • Loading branch information
denrase and marandaneto committed Jul 17, 2023
1 parent 6a40d32 commit 1e094d3
Show file tree
Hide file tree
Showing 41 changed files with 667 additions and 160 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog

## Unreleased

### Features

- Send trace origin ([#1534](https://github.com/getsentry/sentry-dart/pull/1534))

[Trace origin](https://develop.sentry.dev/sdk/performance/trace-origin/) indicates what created a trace or a span. Not all transactions and spans contain enough information to tell whether the user or what precisely in the SDK created it. Origin solves this problem. The SDK now sends origin for transactions and spans.

## 7.8.0

### Enhancements
Expand Down
2 changes: 2 additions & 0 deletions dart/lib/sentry.dart
Expand Up @@ -44,3 +44,5 @@ export 'src/utils/http_sanitizer.dart';
export 'src/utils/url_details.dart';
// ignore: invalid_export_of_internal_element
export 'src/utils/http_header_utils.dart';
// ignore: invalid_export_of_internal_element
export 'src/sentry_trace_origins.dart';
2 changes: 2 additions & 0 deletions dart/lib/src/http_client/tracing_client.dart
Expand Up @@ -2,6 +2,7 @@ import 'package:http/http.dart';
import '../hub.dart';
import '../hub_adapter.dart';
import '../protocol.dart';
import '../sentry_trace_origins.dart';
import '../tracing.dart';
import '../utils/tracing_utils.dart';
import '../utils/http_sanitizer.dart';
Expand Down Expand Up @@ -33,6 +34,7 @@ class TracingClient extends BaseClient {
'http.client',
description: description,
);
span?.origin = SentryTraceOrigins.autoHttpHttp;

// if the span is NoOp, we don't want to attach headers
if (span is NoOpSentrySpan) {
Expand Down
7 changes: 7 additions & 0 deletions dart/lib/src/hub.dart
Expand Up @@ -398,6 +398,7 @@ class Hub {
name,
operation,
description: description,
origin: SentryTraceOrigins.manual,
),
startTimestamp: startTimestamp,
bindToScope: bindToScope,
Expand Down Expand Up @@ -442,6 +443,12 @@ class Hub {
transactionContext.copyWith(samplingDecision: samplingDecision);
}

if (transactionContext.origin == null) {
transactionContext = transactionContext.copyWith(
origin: SentryTraceOrigins.manual,
);
}

final tracer = SentryTracer(
transactionContext,
this,
Expand Down
6 changes: 6 additions & 0 deletions dart/lib/src/noop_sentry_span.dart
Expand Up @@ -51,6 +51,12 @@ class NoOpSentrySpan extends ISentrySpan {
@override
SentrySpanContext get context => _spanContext;

@override
String? get origin => null;

@override
set origin(String? origin) {}

@override
SpanStatus? get status => null;

Expand Down
12 changes: 12 additions & 0 deletions dart/lib/src/protocol/sentry_span.dart
Expand Up @@ -36,6 +36,7 @@ class SentrySpan extends ISentrySpan {
}) {
_startTimestamp = startTimestamp?.toUtc() ?? _hub.options.clock();
_finishedCallback = finishedCallback;
_origin = _context.origin;
}

@override
Expand Down Expand Up @@ -145,6 +146,14 @@ class SentrySpan extends ISentrySpan {
@override
SentrySpanContext get context => _context;

String? _origin;

@override
String? get origin => _origin;

@override
set origin(String? origin) => _origin = origin;

Map<String, dynamic> toJson() {
final json = _context.toJson();
json['start_timestamp'] =
Expand All @@ -162,6 +171,9 @@ class SentrySpan extends ISentrySpan {
if (_tags.isNotEmpty) {
json['tags'] = _tags;
}
if (_origin != null) {
json['origin'] = _origin;
}
return json;
}

Expand Down
11 changes: 11 additions & 0 deletions dart/lib/src/protocol/sentry_trace_context.dart
Expand Up @@ -28,6 +28,13 @@ class SentryTraceContext {
/// The Span status
final SpanStatus? status;

/// The origin of the span indicates what created the span.
///
/// @note Gets set by the SDK. It is not expected to be set manually by users.
///
/// @see <https://develop.sentry.dev/sdk/performance/trace-origin>
final String? origin;

factory SentryTraceContext.fromJson(Map<String, dynamic> json) {
return SentryTraceContext(
operation: json['op'] as String,
Expand All @@ -41,6 +48,7 @@ class SentryTraceContext {
? null
: SpanStatus.fromString(json['status'] as String),
sampled: true,
origin: json['origin'] == null ? null : json['origin'] as String?,
);
}

Expand All @@ -53,6 +61,7 @@ class SentryTraceContext {
if (parentSpanId != null) 'parent_span_id': parentSpanId!.toString(),
if (description != null) 'description': description,
if (status != null) 'status': status!.toString(),
if (origin != null) 'origin': origin,
};
}

Expand All @@ -64,6 +73,7 @@ class SentryTraceContext {
status: status,
parentSpanId: parentSpanId,
sampled: sampled,
origin: origin,
);

SentryTraceContext({
Expand All @@ -74,6 +84,7 @@ class SentryTraceContext {
required this.operation,
this.description,
this.status,
this.origin,
}) : traceId = traceId ?? SentryId.newId(),
spanId = spanId ?? SpanId.newId();
}
10 changes: 10 additions & 0 deletions dart/lib/src/sentry_span_context.dart
Expand Up @@ -20,6 +20,13 @@ class SentrySpanContext {
/// consistent across instances of the span.
final String? description;

/// The origin of the span indicates what created the span.
///
/// Gets set by the SDK. It is not expected to be set manually by users.
///
/// See https://develop.sentry.dev/sdk/performance/trace-origin
final String? origin;

/// Item encoded as JSON
Map<String, dynamic> toJson() {
return {
Expand All @@ -28,6 +35,7 @@ class SentrySpanContext {
'op': operation,
if (parentSpanId != null) 'parent_span_id': parentSpanId.toString(),
if (description != null) 'description': description,
if (origin != null) 'origin': origin,
};
}

Expand All @@ -37,6 +45,7 @@ class SentrySpanContext {
this.parentSpanId,
required this.operation,
this.description,
this.origin,
}) : traceId = traceId ?? SentryId.newId(),
spanId = spanId ?? SpanId.newId();

Expand All @@ -53,6 +62,7 @@ class SentrySpanContext {
parentSpanId: parentSpanId,
sampled: sampled,
status: status,
origin: origin,
);
}
}
10 changes: 10 additions & 0 deletions dart/lib/src/sentry_span_interface.dart
Expand Up @@ -36,6 +36,16 @@ abstract class ISentrySpan {
/// Gets the span context.
SentrySpanContext get context;

/// Gets the span origin
String? get origin;

/// Sets span origin.
///
/// Gets set by the SDK. It is not expected to be set manually by users.
///
/// See https://develop.sentry.dev/sdk/performance/trace-origin
set origin(String? origin);

/// Returns the end timestamp if finished
DateTime? get endTimestamp;

Expand Down
21 changes: 21 additions & 0 deletions dart/lib/src/sentry_trace_origins.dart
@@ -0,0 +1,21 @@
import 'package:meta/meta.dart';

@internal
class SentryTraceOrigins {
static const manual = 'manual';

static const autoNavigationRouteObserver = 'auto.navigation.route_observer';
static const autoHttpHttp = 'auto.http.http';
static const autoHttpDioHttpClientAdapter =
'auto.http.dio.http_client_adapter';
static const autoHttpDioTransformer = 'auto.http.dio.transformer';
static const autoFile = 'auto.file';
static const autoFileAssetBundle = 'auto.file.asset_bundle';
static const autoDbSqfliteOpenDatabase = 'auto.db.sqflite.open_database';
static const autoDbSqfliteBatch = 'auto.db.sqflite.batch';
static const autoDbSqfliteDatabase = 'auto.db.sqflite.database';
static const autoDbSqfliteDatabaseExecutor =
'auto.db.sqflite.database_executor';
static const autoDbSqfliteDatabaseFactory =
'auto.db.sqflite.database_factory';
}
6 changes: 6 additions & 0 deletions dart/lib/src/sentry_tracer.dart
Expand Up @@ -255,6 +255,12 @@ class SentryTracer extends ISentrySpan {
@override
SentrySpanContext get context => _rootSpan.context;

@override
String? get origin => _rootSpan.origin;

@override
set origin(String? origin) => _rootSpan.origin = origin;

@override
DateTime get startTimestamp => _rootSpan.startTimestamp;

Expand Down
6 changes: 6 additions & 0 deletions dart/lib/src/sentry_transaction_context.dart
@@ -1,4 +1,5 @@
import 'package:meta/meta.dart';
import 'sentry_trace_origins.dart';

import 'protocol.dart';
import 'sentry_baggage.dart';
Expand All @@ -21,12 +22,14 @@ class SentryTransactionContext extends SentrySpanContext {
SpanId? parentSpanId,
this.transactionNameSource,
this.samplingDecision,
String? origin,
}) : super(
operation: operation,
description: description,
traceId: traceId,
spanId: spanId,
parentSpanId: parentSpanId,
origin: origin,
);

factory SentryTransactionContext.fromSentryTrace(
Expand All @@ -50,6 +53,7 @@ class SentryTransactionContext extends SentrySpanContext {
: null,
transactionNameSource:
transactionNameSource ?? SentryTransactionNameSource.custom,
origin: SentryTraceOrigins.manual,
);
}

Expand All @@ -63,6 +67,7 @@ class SentryTransactionContext extends SentrySpanContext {
SpanId? parentSpanId,
SentryTransactionNameSource? transactionNameSource,
SentryTracesSamplingDecision? samplingDecision,
String? origin,
}) =>
SentryTransactionContext(
name ?? this.name,
Expand All @@ -76,5 +81,6 @@ class SentryTransactionContext extends SentrySpanContext {
transactionNameSource:
transactionNameSource ?? this.transactionNameSource,
samplingDecision: samplingDecision ?? this.samplingDecision,
origin: origin ?? this.origin,
);
}
1 change: 1 addition & 0 deletions dart/test/http_client/tracing_client_test.dart
Expand Up @@ -44,6 +44,7 @@ void main() {
expect(span.data['http.fragment'], 'baz');
expect(span.data['http.response.status_code'], 200);
expect(span.data['http.response_content_length'], 2);
expect(span.origin, SentryTraceOrigins.autoHttpHttp);
});

test('finish span if errored request', () async {
Expand Down
17 changes: 17 additions & 0 deletions dart/test/hub_test.dart
Expand Up @@ -176,6 +176,7 @@ void main() {
expect(tr.context.description, 'desc');
expect(tr.startTimestamp.isAtSameMomentAs(startTime), true);
expect((tr as SentryTracer).name, 'name');
expect(tr.origin, SentryTraceOrigins.manual);
});

test('start transaction binds span to the scope', () async {
Expand Down Expand Up @@ -265,6 +266,22 @@ void main() {
expect(tr.samplingDecision?.sampled, false);
});

test('start transaction with context sets trace origin fallback', () async {
final hub = fixture.getSut();
final tr = hub.startTransactionWithContext(
SentryTransactionContext('name', 'op'),
);
expect(tr.origin, SentryTraceOrigins.manual);
});

test('start transaction with context keeps origin', () async {
final hub = fixture.getSut();
final tr = hub.startTransactionWithContext(
SentryTransactionContext('name', 'op', origin: 'auto.navigation.test'),
);
expect(tr.origin, 'auto.navigation.test');
});

test('start transaction return NoOp if performance is disabled', () async {
final hub = fixture.getSut(tracesSampleRate: null);

Expand Down
12 changes: 7 additions & 5 deletions dart/test/sentry_span_context_test.dart
Expand Up @@ -14,9 +14,10 @@ void main() {
expect(map['op'], 'op');
expect(map['parent_span_id'], isNotNull);
expect(map['description'], 'desc');
expect(map['origin'], 'manual');
});

test('toTraceContext gets sampled and status', () {
test('toTraceContext gets sampled, status, and origin', () {
final sut = fixture.getSut();
final aborted = SpanStatus.aborted();
final traceContext = sut.toTraceContext(
Expand All @@ -31,15 +32,16 @@ void main() {
expect(traceContext.parentSpanId, isNotNull);
expect(traceContext.description, 'desc');
expect(traceContext.status, aborted);
expect(traceContext.origin, 'manual');
});
}

class Fixture {
SentrySpanContext getSut() {
return SentrySpanContext(
operation: 'op',
parentSpanId: SpanId.newId(),
description: 'desc',
);
operation: 'op',
parentSpanId: SpanId.newId(),
description: 'desc',
origin: 'manual');
}
}

0 comments on commit 1e094d3

Please sign in to comment.