Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
767153c
Add setAttributeS
buenaflor Nov 18, 2025
578f81d
Update
buenaflor Nov 18, 2025
803f85b
Update
buenaflor Nov 18, 2025
58789a8
Add removeAttribute
buenaflor Nov 18, 2025
ad4bef1
Add scope test
buenaflor Nov 18, 2025
c773c5f
Update
buenaflor Nov 18, 2025
58d3d29
Update
buenaflor Nov 18, 2025
e26d83a
Update
buenaflor Nov 18, 2025
2cf1cdb
Update
buenaflor Nov 18, 2025
1ad1c46
Update
buenaflor Nov 18, 2025
6a6fab2
Update
buenaflor Nov 18, 2025
58126a6
Update
buenaflor Nov 18, 2025
cbd6718
Update
buenaflor Nov 18, 2025
ba81f2f
Update
buenaflor Nov 18, 2025
3b83bd1
Update
buenaflor Nov 18, 2025
968e8d1
Update
buenaflor Nov 19, 2025
1e8dbbc
Update
buenaflor Nov 19, 2025
99a1dc1
Update
buenaflor Nov 19, 2025
9a99d54
Update
buenaflor Nov 19, 2025
f33d4f9
Update
buenaflor Nov 19, 2025
224eab1
Update
buenaflor Nov 19, 2025
ad95fde
Merge scope
buenaflor Nov 20, 2025
2e39532
Update
buenaflor Nov 20, 2025
625bf76
Fix analyzer
buenaflor Nov 20, 2025
796bba9
Fix analyzer
buenaflor Nov 20, 2025
03bbc0e
Merge branch 'main' into feat/set-attributes
buenaflor Nov 20, 2025
ceddd30
Merge branch 'main' into feat/set-attributes
buenaflor Nov 20, 2025
4d96d5c
Add Spanv2 protocol
buenaflor Nov 20, 2025
b233b32
Add Spanv2 protocol
buenaflor Nov 20, 2025
b4c7ce1
Update
buenaflor Nov 24, 2025
0be13b0
Merge branch 'main' into feat/span-protocol
buenaflor Nov 24, 2025
2a1efd5
Rename
buenaflor Nov 24, 2025
fb13e85
Update
buenaflor Nov 24, 2025
6d6e8da
Update
buenaflor Nov 24, 2025
0a60229
Update
buenaflor Nov 24, 2025
288e4be
Add support for spans protocol
buenaflor Nov 24, 2025
7133855
Add envelope structure
buenaflor Nov 24, 2025
2ede0e1
Update
buenaflor Nov 25, 2025
2ccda02
Update
buenaflor Nov 25, 2025
dd033f8
Remove buffer
buenaflor Nov 25, 2025
c6465a3
Update
buenaflor Nov 25, 2025
bded9d6
Update
buenaflor Nov 25, 2025
3707531
Update
buenaflor Nov 25, 2025
ca2ca35
Merge branch 'feat/span-protocol' into feat/spans/dart-protocol
buenaflor Nov 25, 2025
8e9b557
Dedupe item payload creation
buenaflor Nov 25, 2025
5b652cb
Analze
buenaflor Nov 25, 2025
acb0a1a
Analyze
buenaflor Nov 25, 2025
fc315a3
Merge branch 'feat/span-protocol' into feat/spans/dart-protocol
buenaflor Nov 25, 2025
2fd7743
Merge branch 'feat/span-first' into feat/spans/dart-protocol
buenaflor Nov 26, 2025
0b8c304
Update
buenaflor Nov 26, 2025
b84cec3
Update
buenaflor Nov 26, 2025
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
64 changes: 40 additions & 24 deletions packages/dart/lib/src/sentry_envelope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,30 +104,32 @@ class SentryEnvelope {
factory SentryEnvelope.fromLogsData(
List<List<int>> encodedLogs,
SdkVersion sdkVersion,
) {
// Create the payload in the format expected by Sentry
// Format: {"items": [log1, log2, ...]}
final builder = BytesBuilder(copy: false);
builder.add(utf8.encode('{"items":['));
for (int i = 0; i < encodedLogs.length; i++) {
if (i > 0) {
builder.add(utf8.encode(','));
}
builder.add(encodedLogs[i]);
}
builder.add(utf8.encode(']}'));

return SentryEnvelope(
SentryEnvelopeHeader(
null,
sdkVersion,
),
[
SentryEnvelopeItem.fromLogsData(
builder.takeBytes(), encodedLogs.length),
],
);
}
) =>
SentryEnvelope(
SentryEnvelopeHeader(null, sdkVersion),
[
SentryEnvelopeItem.fromLogsData(
_buildItemsPayload(encodedLogs), encodedLogs.length)
],
);

/// Create a [SentryEnvelope] containing raw span data payload.
/// This is used by the span buffer to send pre-encoded spans.
@internal
factory SentryEnvelope.fromSpansData(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not actually used yet but will be used by the span buffer (impl TBD)

List<List<int>> encodedSpans,
SdkVersion sdkVersion, {
String? dsn,
SentryTraceContextHeader? traceContext,
}) =>
SentryEnvelope(
SentryEnvelopeHeader(null, sdkVersion,
dsn: dsn, traceContext: traceContext),
[
SentryEnvelopeItem.fromSpansData(
_buildItemsPayload(encodedSpans), encodedSpans.length)
],
);

/// Stream binary data representation of `Envelope` file encoded.
Stream<List<int>> envelopeStream(SentryOptions options) async* {
Expand Down Expand Up @@ -160,6 +162,20 @@ class SentryEnvelope {
}
}

/// Builds a payload in the format {"items": [item1, item2, ...]}
static Uint8List _buildItemsPayload(List<List<int>> encodedItems) {
final builder = BytesBuilder(copy: false);
builder.add(utf8.encode('{"items":['));
for (int i = 0; i < encodedItems.length; i++) {
if (i > 0) {
builder.add(utf8.encode(','));
}
builder.add(encodedItems[i]);
}
builder.add(utf8.encode(']}'));
return builder.takeBytes();
}

/// Add an envelope item containing client report data.
void addClientReport(ClientReport? clientReport) {
if (clientReport != null) {
Expand Down
15 changes: 15 additions & 0 deletions packages/dart/lib/src/sentry_envelope_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,21 @@ class SentryEnvelopeItem {
);
}

/// Create a [SentryEnvelopeItem] which holds pre-encoded span data.
/// This is used by the spans buffer to send pre-encoded spans.
@internal
factory SentryEnvelopeItem.fromSpansData(List<int> payload, int spansCount) {
return SentryEnvelopeItem(
SentryEnvelopeItemHeader(
SentryItemType.span,
itemCount: spansCount,
contentType: 'application/vnd.sentry.items.span.v2+json',
),
() => payload,
originalObject: null,
);
}

/// Header with info about type and length of data in bytes.
final SentryEnvelopeItemHeader header;

Expand Down
1 change: 1 addition & 0 deletions packages/dart/lib/src/sentry_item_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ class SentryItemType {
static const String profile = 'profile';
static const String statsd = 'statsd';
static const String log = 'log';
static const String span = 'span';
static const String unknown = '__unknown__';
}
35 changes: 35 additions & 0 deletions packages/dart/test/sentry_envelope_item_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,41 @@ void main() {
expect(actualData, expectedData);
});

test('fromSpansData', () async {
final span1 = jsonEncode({
'trace_id':
Sentry.currentHub.scope.propagationContext.traceId.toString(),
'span_id': SpanId.newId().toString(),
'name': 'GET /users',
'status': 'ok',
'is_segment': true,
'start_timestamp': 1742921669.158209,
'end_timestamp': 1742921669.180536,
});
final span2 = jsonEncode({
'trace_id':
Sentry.currentHub.scope.propagationContext.traceId.toString(),
'span_id': SpanId.newId().toString(),
'name': 'GET /posts',
'status': 'ok',
'is_segment': true,
'start_timestamp': 1742921669.158209,
'end_timestamp': 1742921669.180536,
});
final payload = utf8.encode('{"items":[$span1,$span2]');
final spansCount = 2;

final sut = SentryEnvelopeItem.fromSpansData(payload, spansCount);

expect(
sut.header.contentType, 'application/vnd.sentry.items.span.v2+json');
expect(sut.header.type, SentryItemType.span);
expect(sut.header.itemCount, spansCount);

final actualData = await sut.dataFactory();
expect(actualData, payload);
});

test('fromLogsData', () async {
final payload =
utf8.encode('{"items":[{"test":"data1"},{"test":"data2"}]');
Expand Down
60 changes: 60 additions & 0 deletions packages/dart/test/sentry_envelope_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,66 @@ void main() {
expect(actualItemData, expectedItemData);
});

test('fromSpansData', () async {
final span1 = utf8JsonEncoder.convert({
'trace_id':
Sentry.currentHub.scope.propagationContext.traceId.toString(),
'span_id': SpanId.newId().toString(),
'name': 'GET /users',
'status': 'ok',
'is_segment': true,
'start_timestamp': 1742921669.158209,
'end_timestamp': 1742921669.180536,
});
final span2 = utf8JsonEncoder.convert({
'trace_id':
Sentry.currentHub.scope.propagationContext.traceId.toString(),
'span_id': SpanId.newId().toString(),
'name': 'GET /posts',
'status': 'ok',
'is_segment': true,
'start_timestamp': 1742921669.158209,
'end_timestamp': 1742921669.180536,
});
final spansCount = 2;
final sdkVersion = SdkVersion(
name: 'fixture-name',
version: 'fixture-version',
);
final traceContext = SentryTraceContextHeader.fromJson(<String, dynamic>{
'trace_id': '${SentryId.newId()}',
'public_key': '123',
});

final sut = SentryEnvelope.fromSpansData([span1, span2], sdkVersion,
dsn: fakeDsn, traceContext: traceContext);

expect(sut.header.eventId, null);
expect(sut.header.sdkVersion, sdkVersion);
expect(sut.header.traceContext, traceContext);
expect(sut.header.dsn, fakeDsn);
expect(sut.items.length, 1);

final expectedEnvelopeItem = SentryEnvelopeItem.fromSpansData(
// The envelope should create the final payload with {"items": [...]} wrapper
utf8.encode('{"items":[') +
span1 +
utf8.encode(',') +
span2 +
utf8.encode(']}'),
spansCount,
);

expect(sut.items[0].header.contentType,
expectedEnvelopeItem.header.contentType);
expect(sut.items[0].header.type, expectedEnvelopeItem.header.type);
expect(sut.items[0].header.itemCount, spansCount);

final actualItem = await sut.items[0].dataFactory();
final expectedItem = await expectedEnvelopeItem.dataFactory();
expect(actualItem, expectedItem);
});

test('fromLogsData', () async {
final encodedLogs = [
utf8.encode(
Expand Down
Loading