diff --git a/lib/sentry.dart b/lib/sentry.dart index aa46678652..7b0cbd7c81 100644 --- a/lib/sentry.dart +++ b/lib/sentry.dart @@ -306,6 +306,7 @@ class Event { this.release, this.environment, this.message, + this.transaction, this.exception, this.stackTrace, this.level, @@ -314,6 +315,7 @@ class Event { this.extra, this.fingerprint, this.userContext, + this.breadcrumbs, }); /// The logger that logged the event. @@ -344,6 +346,10 @@ class Event { /// Can be `null`, a [String], or a [StackTrace]. final dynamic stackTrace; + /// The name of the transaction which generated this event, + /// for example, the route name: `"/users//"`. + final String transaction; + /// How important this event is. final SeverityLevel level; @@ -359,6 +365,12 @@ class Event { /// they must be JSON-serializable. final Map extra; + /// List of breadcrumbs for this event. + /// + /// See also: + /// * https://docs.sentry.io/enriching-error-data/breadcrumbs/?platform=javascript + final List breadcrumbs; + /// Information about the current user. /// /// The value in this field overrides the user context @@ -401,6 +413,8 @@ class Event { if (message != null) json['message'] = message; + if (transaction != null) json['transaction'] = transaction; + if (exception != null) { json['exception'] = [ { @@ -433,6 +447,12 @@ class Event { if (fingerprint != null && fingerprint.isNotEmpty) json['fingerprint'] = fingerprint; + if (breadcrumbs != null && breadcrumbs.isNotEmpty) { + json['breadcrumbs'] = >>{ + 'values': breadcrumbs.map((b) => b.toJson()).toList(growable: false) + }; + } + return json; } } @@ -494,3 +514,64 @@ class User { }; } } + +/// Structed data to describe more information pior to the event [captured][SentryClient.capture]. +/// +/// The outgoing JSON representation is: +/// +/// ``` +/// { +/// "timestamp": 1000 +/// "message": "message", +/// "category": "category", +/// "data": {"key": "value"}, +/// "level": "info", +/// "type": "default" +/// } +/// ``` +/// See also: +/// * https://docs.sentry.io/development/sdk-dev/event-payloads/breadcrumbs/ +class Breadcrumb { + final String message; + final String category; + final Map data; + final SeverityLevel level; + + /// Describes what type of breadcrumb this is. + /// + /// Possible values: "default", "http", "navigation". + /// + /// See also: + /// + /// * https://docs.sentry.io/development/sdk-dev/event-payloads/breadcrumbs/#breadcrumb-types + final String type; + + /// The time the breadcrumb was recorded. + /// + /// The value is submitted to Sentry with second precision. + final DateTime timestamp; + const Breadcrumb(this.message, this.timestamp, + {this.category, this.data, this.level = SeverityLevel.info, this.type}); + + Map toJson() { + var json = { + 'timestamp': formatDateAsIso8601WithSecondPrecision(timestamp), + }; + if (message != null) { + json['message'] = message; + } + if (category != null) { + json['category'] = category; + } + if (data != null && data.isNotEmpty) { + json['data'] = Map.of(data); + } + if (level != null) { + json['level'] = level.name; + } + if (type != null) { + json['type'] = type; + } + return json; + } +} diff --git a/test/sentry_test.dart b/test/sentry_test.dart index 6dc563d1ad..330acf0f99 100644 --- a/test/sentry_test.dart +++ b/test/sentry_test.dart @@ -308,6 +308,22 @@ void main() { }); group('$Event', () { + test('$Breadcrumb serializes', () { + expect( + Breadcrumb( + "example log", + DateTime.utc(2019), + level: SeverityLevel.debug, + category: "test", + ).toJson(), + { + 'timestamp': '2019-01-01T00:00:00', + 'message': 'example log', + 'category': 'test', + 'level': 'debug', + }, + ); + }); test('serializes to JSON', () { final user = new User( id: "user_id", @@ -315,9 +331,16 @@ void main() { email: "email@email.com", ipAddress: "127.0.0.1", extras: {"foo": "bar"}); + + final breadcrumbs = [ + Breadcrumb("test log", DateTime.utc(2019), + level: SeverityLevel.debug, category: "test"), + ]; + expect( new Event( message: 'test-message', + transaction: '/test/1', exception: new StateError('test-error'), level: SeverityLevel.debug, culprit: 'Professor Moriarty', @@ -331,11 +354,13 @@ void main() { }, fingerprint: [Event.defaultFingerprint, 'foo'], userContext: user, + breadcrumbs: breadcrumbs, ).toJson(), { 'platform': 'dart', 'sdk': {'version': sdkVersion, 'name': 'dart'}, 'message': 'test-message', + 'transaction': '/test/1', 'exception': [ {'type': 'StateError', 'value': 'Bad state: test-error'} ], @@ -351,6 +376,16 @@ void main() { 'ip_address': '127.0.0.1', 'extras': {'foo': 'bar'} }, + 'breadcrumbs': { + 'values': [ + { + 'timestamp': '2019-01-01T00:00:00', + 'message': 'test log', + 'category': 'test', + 'level': 'debug', + }, + ] + }, }, ); });