diff --git a/CHANGELOG.md b/CHANGELOG.md index 9763bc1a82..c1148c4570 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,8 @@ - Added Scope and Breadcrumb ring buffer #109 - Added Hub to SDK #113 - Ref: Hub passes the Scope to SentryClient -- feat: sentry options #116 +- Feat: sentry options #116 +- Ref: SentryId generates UUID # `package:sentry` changelog diff --git a/dart/lib/src/browser_client.dart b/dart/lib/src/browser_client.dart index 918b8f9d35..25ccfd8374 100644 --- a/dart/lib/src/browser_client.dart +++ b/dart/lib/src/browser_client.dart @@ -35,14 +35,9 @@ class SentryBrowserClient extends SentryClient { /// This parameter is dynamic to maintain backwards compatibility with /// previous use of [Clock](https://pub.dartlang.org/documentation/quiver/latest/quiver.time/Clock-class.html) /// from [`package:quiver`](https://pub.dartlang.org/packages/quiver). - /// - /// If [uuidGenerator] is provided, it is used to generate the "event_id" - /// field instead of the built-in random UUID v4 generator. This is useful in - /// tests. factory SentryBrowserClient(SentryOptions options, {String origin}) { options.httpClient ??= BrowserClient(); options.clock ??= getUtcDateTime; - options.uuidGenerator ??= generateUuidV4WithoutDashes; // origin is necessary for sentry to resolve stacktrace origin ??= '${window.location.origin}/'; diff --git a/dart/lib/src/client.dart b/dart/lib/src/client.dart index 93061c6065..32ad24678e 100644 --- a/dart/lib/src/client.dart +++ b/dart/lib/src/client.dart @@ -122,7 +122,7 @@ abstract class SentryClient { final data = { 'project': projectId, - 'event_id': options.uuidGenerator(), + 'event_id': event.eventId.toString(), 'timestamp': formatDateAsIso8601WithSecondPrecision(now), }; @@ -158,7 +158,7 @@ abstract class SentryClient { } final eventId = json.decode(response.body)['id']; - return eventId != null ? SentryId(eventId) : SentryId.empty(); + return eventId != null ? SentryId.fromId(eventId) : SentryId.empty(); } /// Reports the [throwable] and optionally its [stackTrace] to Sentry.io. diff --git a/dart/lib/src/hub.dart b/dart/lib/src/hub.dart index feac240ae2..e71190e2c5 100644 --- a/dart/lib/src/hub.dart +++ b/dart/lib/src/hub.dart @@ -73,10 +73,9 @@ class Hub { try { sentryId = await item.client.captureEvent(event, scope: item.scope); } catch (err) { - /* TODO add Event.id */ _options.logger( SentryLevel.error, - 'Error while capturing event with id: ${event}', + 'Error while capturing event with id: ${event.eventId.toString()}', ); } finally { _lastEventId = sentryId; diff --git a/dart/lib/src/io_client.dart b/dart/lib/src/io_client.dart index b46f54839e..0e342dd685 100644 --- a/dart/lib/src/io_client.dart +++ b/dart/lib/src/io_client.dart @@ -36,10 +36,6 @@ class SentryIOClient extends SentryClient { /// This parameter is dynamic to maintain backwards compatibility with /// previous use of [Clock](https://pub.dartlang.org/documentation/quiver/latest/quiver.time/Clock-class.html) /// from [`package:quiver`](https://pub.dartlang.org/packages/quiver). - /// - /// If [uuidGenerator] is provided, it is used to generate the "event_id" - /// field instead of the built-in random UUID v4 generator. This is useful in - /// tests. factory SentryIOClient(SentryOptions options) => SentryIOClient._(options); SentryIOClient._(SentryOptions options) : super.base(options); diff --git a/dart/lib/src/protocol/event.dart b/dart/lib/src/protocol/event.dart index a1720391b8..ee4b3403d7 100644 --- a/dart/lib/src/protocol/event.dart +++ b/dart/lib/src/protocol/event.dart @@ -8,7 +8,8 @@ import '../version.dart'; @immutable class Event { /// Creates an event. - const Event({ + Event({ + SentryId eventId, this.loggerName, this.serverName, this.release, @@ -26,7 +27,7 @@ class Event { this.contexts, this.breadcrumbs, this.sdk, - }); + }) : eventId = eventId ?? SentryId.newId(); /// Refers to the default fingerprinting algorithm. /// @@ -34,6 +35,9 @@ class Event { /// fingerprint with custom fingerprints. static const String defaultFingerprint = '{{ default }}'; + /// The ID Sentry.io assigned to the submitted event for future reference. + final SentryId eventId; + /// The logger that logged the event. final String loggerName; @@ -117,6 +121,7 @@ class Event { final Sdk sdk; Event copyWith({ + SentryId eventId, String loggerName, String serverName, String release, @@ -136,6 +141,7 @@ class Event { Sdk sdk, }) => Event( + eventId: eventId ?? this.eventId, loggerName: loggerName ?? this.loggerName, serverName: serverName ?? this.serverName, release: release ?? this.release, @@ -162,6 +168,10 @@ class Event { 'platform': sdkPlatform, }; + if (eventId != null) { + json['event_id'] = eventId.toString(); + } + if (loggerName != null) { json['logger'] = loggerName; } diff --git a/dart/lib/src/protocol/sentry_id.dart b/dart/lib/src/protocol/sentry_id.dart index e1e1abe75a..4be270ca50 100644 --- a/dart/lib/src/protocol/sentry_id.dart +++ b/dart/lib/src/protocol/sentry_id.dart @@ -1,16 +1,26 @@ -/// Sentry response id +import 'package:uuid/uuid.dart'; class SentryId { - static const String _emptyId = '00000000-0000-0000-0000-000000000000'; + static final SentryId _emptyId = + SentryId.fromId('00000000-0000-0000-0000-000000000000'); /// The ID Sentry.io assigned to the submitted event for future reference. - final String _id; + String _id; - // TODO: should we generate the new UUID here with an empty ctor? + final Uuid _uuidGenerator = Uuid(); - const SentryId(this._id); + SentryId._internal({String id}) { + _id = id ?? _uuidGenerator.v4(); + } - factory SentryId.empty() => SentryId(_emptyId); + /// Generates a new SentryId + factory SentryId.newId() => SentryId._internal(); + + /// Generates a SentryId with the given UUID + factory SentryId.fromId(String id) => SentryId._internal(id: id); + + /// SentryId with an empty UUID + factory SentryId.empty() => _emptyId; @override String toString() => _id.replaceAll('-', ''); diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index e02345c42b..154145b6a5 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -3,7 +3,6 @@ import 'package:sentry/sentry.dart'; import 'diagnostic_logger.dart'; import 'hub.dart'; import 'protocol.dart'; -import 'utils.dart'; /// Sentry SDK options class SentryOptions { @@ -39,11 +38,6 @@ class SentryOptions { /// from [`package:quiver`](https://pub.dartlang.org/packages/quiver). dynamic clock; - /// If [uuidGenerator] is provided, it is used to generate the "event_id" - /// field instead of the built-in random UUID v4 generator. This is useful in - /// tests. - UuidGenerator uuidGenerator; - int maxBreadcrumbs; /// Logger interface to log useful debugging information if debug is enabled @@ -145,7 +139,6 @@ class SentryOptions { this.compressPayload, this.httpClient, this.clock, - this.uuidGenerator, }); /// Adds an event processor diff --git a/dart/lib/src/utils.dart b/dart/lib/src/utils.dart index 224256e959..d8ce547bbe 100644 --- a/dart/lib/src/utils.dart +++ b/dart/lib/src/utils.dart @@ -3,11 +3,6 @@ // found in the LICENSE file. import 'package:meta/meta.dart'; -import 'package:uuid/uuid.dart'; - -typedef UuidGenerator = String Function(); - -String generateUuidV4WithoutDashes() => Uuid().v4().replaceAll('-', ''); /// Sentry does not take a timezone and instead expects the date-time to be /// submitted in UTC timezone. diff --git a/dart/test/event_test.dart b/dart/test/event_test.dart index 6a249c91ed..a4a7447a57 100644 --- a/dart/test/event_test.dart +++ b/dart/test/event_test.dart @@ -25,7 +25,8 @@ void main() { ); }); test('$Sdk serializes', () { - const event = Event( + final event = Event( + eventId: SentryId.empty(), sdk: Sdk( name: 'sentry.dart.flutter', version: '4.3.2', @@ -33,6 +34,7 @@ void main() { packages: [Package('npm:@sentry/javascript', '1.3.4')])); expect(event.toJson(), { 'platform': 'dart', + 'event_id': '00000000000000000000000000000000', 'sdk': { 'name': 'sentry.dart.flutter', 'version': '4.3.2', @@ -58,10 +60,9 @@ void main() { final error = StateError('test-error'); - print('error.stackTrace ${error.stackTrace}'); - expect( Event( + eventId: SentryId.empty(), message: Message( 'test-message 1 2', template: 'test-message %d %d', @@ -85,6 +86,7 @@ void main() { ).toJson(), { 'platform': 'dart', + 'event_id': '00000000000000000000000000000000', 'sdk': {'version': sdkVersion, 'name': 'sentry.dart'}, 'message': { 'formatted': 'test-message 1 2', diff --git a/dart/test/test_utils.dart b/dart/test/test_utils.dart index 13107a7ded..50182c6e4b 100644 --- a/dart/test/test_utils.dart +++ b/dart/test/test_utils.dart @@ -69,14 +69,14 @@ Future testCaptureException( fail('Unexpected request on ${request.method} ${request.url} in HttpMock'); }); + var sentryId = SentryId.empty(); final client = SentryClient( SentryOptions( dsn: testDsn, httpClient: httpMock, clock: fakeClockProvider, - uuidGenerator: () => 'X' * 32, compressPayload: compressPayload, - environmentAttributes: const Event( + environmentAttributes: Event( serverName: 'test.server.com', release: '1.2.3', environment: 'staging', @@ -87,8 +87,7 @@ Future testCaptureException( try { throw ArgumentError('Test error'); } catch (error, stackTrace) { - final sentryId = - await client.captureException(error, stackTrace: stackTrace); + sentryId = await client.captureException(error, stackTrace: stackTrace); expect('$sentryId', 'testeventid'); } @@ -108,6 +107,10 @@ Future testCaptureException( } else { data = json.decode(utf8.decode(body)) as Map; } + + // so we assert the generated and returned id + data['event_id'] = sentryId.toString(); + final stacktrace = data.remove('stacktrace') as Map; expect(stacktrace['frames'], const TypeMatcher()); @@ -139,7 +142,7 @@ Future testCaptureException( expect(data, { 'project': '1', - 'event_id': 'X' * 32, + 'event_id': sentryId.toString(), 'timestamp': '2017-01-02T00:00:00', 'platform': 'javascript', 'sdk': {'version': sdkVersion, 'name': 'sentry.dart'}, @@ -157,7 +160,7 @@ Future testCaptureException( expect(data, { 'project': '1', - 'event_id': 'X' * 32, + 'event_id': sentryId.toString(), 'timestamp': '2017-01-02T00:00:00', 'platform': 'dart', 'exception': [ @@ -235,8 +238,7 @@ void runTest({Codec, List> gzip, bool isWeb = false}) { httpClient: httpMock, clock: fakeClockProvider, compressPayload: false, - uuidGenerator: () => 'X' * 32, - environmentAttributes: const Event( + environmentAttributes: Event( serverName: 'test.server.com', release: '1.2.3', environment: 'staging', @@ -292,9 +294,8 @@ void runTest({Codec, List> gzip, bool isWeb = false}) { dsn: testDsn, httpClient: httpMock, clock: fakeClockProvider, - uuidGenerator: () => 'X' * 32, compressPayload: false, - environmentAttributes: const Event( + environmentAttributes: Event( serverName: 'test.server.com', release: '1.2.3', environment: 'staging', @@ -348,9 +349,8 @@ void runTest({Codec, List> gzip, bool isWeb = false}) { dsn: testDsn, httpClient: httpMock, clock: fakeClockProvider, - uuidGenerator: () => 'X' * 32, compressPayload: false, - environmentAttributes: const Event( + environmentAttributes: Event( serverName: 'test.server.com', release: '1.2.3', environment: 'staging', @@ -362,12 +362,17 @@ void runTest({Codec, List> gzip, bool isWeb = false}) { try { throw ArgumentError('Test error'); } catch (error, stackTrace) { - final eventWithoutContext = - Event(exception: error, stackTrace: stackTrace); + final eventWithoutContext = Event( + eventId: SentryId.empty(), + exception: error, + stackTrace: stackTrace, + ); final eventWithContext = Event( - exception: error, - stackTrace: stackTrace, - userContext: eventUserContext); + eventId: SentryId.empty(), + exception: error, + stackTrace: stackTrace, + userContext: eventUserContext, + ); await client.captureEvent(eventWithoutContext); expect(loggedUserId, clientUserContext.id); await client.captureEvent(eventWithContext);