diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d16edaf5d..8bad461847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +### Features + +- Add `Sentry.setAttributes` and `Sentry.removeAttribute` ([#3352](https://github.com/getsentry/sentry-dart/pull/3352)) + - These attributes are set at the scope level and apply to all logs (and later to metrics and spans). + - When a scope attribute conflicts with a log-level attribute, the log-level attribute always takes precedence. + ### Fixes - Android app crashing on hot-restart in debug mode ([#3358](https://github.com/getsentry/sentry-dart/pull/3358)) diff --git a/packages/dart/lib/src/hub.dart b/packages/dart/lib/src/hub.dart index 2ceb3006ce..5346e2cd69 100644 --- a/packages/dart/lib/src/hub.dart +++ b/packages/dart/lib/src/hub.dart @@ -289,7 +289,7 @@ class Hub { if (!_isEnabled) { _options.log( SentryLevel.warning, - "Instance is disabled and this 'captureFeedback' call is a no-op.", + "Instance is disabled and this 'captureLog' call is a no-op.", ); } else { final item = _peek(); @@ -341,7 +341,31 @@ class Hub { return scope; } - /// Adds a breacrumb to the current Scope + void setAttributes(Map attributes) { + if (!_isEnabled) { + _options.log( + SentryLevel.warning, + "Instance is disabled and this 'setAttributes' call is a no-op.", + ); + } else { + final item = _peek(); + item.scope.setAttributes(attributes); + } + } + + void removeAttribute(String key) { + if (!_isEnabled) { + _options.log( + SentryLevel.warning, + "Instance is disabled and this 'removeAttribute' call is a no-op.", + ); + } else { + final item = _peek(); + item.scope.removeAttribute(key); + } + } + + /// Adds a breadcrumb to the current Scope Future addBreadcrumb(Breadcrumb crumb, {Hint? hint}) async { if (!_isEnabled) { _options.log( diff --git a/packages/dart/lib/src/hub_adapter.dart b/packages/dart/lib/src/hub_adapter.dart index dc5080479f..6491f2a951 100644 --- a/packages/dart/lib/src/hub_adapter.dart +++ b/packages/dart/lib/src/hub_adapter.dart @@ -199,4 +199,11 @@ class HubAdapter implements Hub { @override FutureOr captureLog(SentryLog log) => Sentry.currentHub.captureLog(log); + + @override + void setAttributes(Map attributes) => + Sentry.currentHub.setAttributes(attributes); + + @override + void removeAttribute(String key) => Sentry.currentHub.removeAttribute(key); } diff --git a/packages/dart/lib/src/logs_enricher_integration.dart b/packages/dart/lib/src/logs_enricher_integration.dart index ba9f0479d1..ee39b41a96 100644 --- a/packages/dart/lib/src/logs_enricher_integration.dart +++ b/packages/dart/lib/src/logs_enricher_integration.dart @@ -5,7 +5,7 @@ import 'sdk_lifecycle_hooks.dart'; import 'utils/os_utils.dart'; import 'integration.dart'; import 'hub.dart'; -import 'protocol/sentry_log_attribute.dart'; +import 'protocol/sentry_attribute.dart'; import 'sentry_options.dart'; @internal @@ -20,12 +20,12 @@ class LogsEnricherIntegration extends Integration { final os = getSentryOperatingSystem(); if (os.name != null) { - event.log.attributes['os.name'] = SentryLogAttribute.string( + event.log.attributes['os.name'] = SentryAttribute.string( os.name ?? '', ); } if (os.version != null) { - event.log.attributes['os.version'] = SentryLogAttribute.string( + event.log.attributes['os.version'] = SentryAttribute.string( os.version ?? '', ); } diff --git a/packages/dart/lib/src/noop_hub.dart b/packages/dart/lib/src/noop_hub.dart index e085e0eecd..bb486b9e80 100644 --- a/packages/dart/lib/src/noop_hub.dart +++ b/packages/dart/lib/src/noop_hub.dart @@ -144,4 +144,10 @@ class NoOpHub implements Hub { @override Scope get scope => Scope(_options); + + @override + void setAttributes(Map attributes) {} + + @override + void removeAttribute(String key) {} } diff --git a/packages/dart/lib/src/protocol.dart b/packages/dart/lib/src/protocol.dart index 07b7e0b30b..cb94d143b7 100644 --- a/packages/dart/lib/src/protocol.dart +++ b/packages/dart/lib/src/protocol.dart @@ -43,4 +43,4 @@ export 'protocol/sentry_feature_flag.dart'; export 'protocol/sentry_feature_flags.dart'; export 'protocol/sentry_log.dart'; export 'protocol/sentry_log_level.dart'; -export 'protocol/sentry_log_attribute.dart'; +export 'protocol/sentry_attribute.dart'; diff --git a/packages/dart/lib/src/protocol/sentry_attribute.dart b/packages/dart/lib/src/protocol/sentry_attribute.dart new file mode 100644 index 0000000000..335d36ecdb --- /dev/null +++ b/packages/dart/lib/src/protocol/sentry_attribute.dart @@ -0,0 +1,54 @@ +import 'package:meta/meta.dart'; + +@Deprecated('Use SentryAttribute instead') +class SentryLogAttribute extends SentryAttribute { + SentryLogAttribute._(super.value, super.type); + + factory SentryLogAttribute.string(String value) { + return SentryLogAttribute._(value, 'string'); + } + + factory SentryLogAttribute.bool(bool value) { + return SentryLogAttribute._(value, 'boolean'); + } + + factory SentryLogAttribute.int(int value) { + return SentryLogAttribute._(value, 'integer'); + } + + factory SentryLogAttribute.double(double value) { + return SentryLogAttribute._(value, 'double'); + } +} + +class SentryAttribute { + @internal + final String type; + final dynamic value; + + @internal + SentryAttribute(this.value, this.type); + + factory SentryAttribute.string(String value) { + return SentryAttribute(value, 'string'); + } + + factory SentryAttribute.bool(bool value) { + return SentryAttribute(value, 'boolean'); + } + + factory SentryAttribute.int(int value) { + return SentryAttribute(value, 'integer'); + } + + factory SentryAttribute.double(double value) { + return SentryAttribute(value, 'double'); + } + + Map toJson() { + return { + 'value': value, + 'type': type, + }; + } +} diff --git a/packages/dart/lib/src/protocol/sentry_log.dart b/packages/dart/lib/src/protocol/sentry_log.dart index 55c0174d90..6612b8d700 100644 --- a/packages/dart/lib/src/protocol/sentry_log.dart +++ b/packages/dart/lib/src/protocol/sentry_log.dart @@ -1,13 +1,13 @@ +import 'sentry_attribute.dart'; import 'sentry_id.dart'; import 'sentry_log_level.dart'; -import 'sentry_log_attribute.dart'; class SentryLog { DateTime timestamp; SentryId traceId; SentryLogLevel level; String body; - Map attributes; + Map attributes; int? severityNumber; /// The traceId is initially an empty default value and is populated during event processing; diff --git a/packages/dart/lib/src/protocol/sentry_log_attribute.dart b/packages/dart/lib/src/protocol/sentry_log_attribute.dart deleted file mode 100644 index 63ac85eb87..0000000000 --- a/packages/dart/lib/src/protocol/sentry_log_attribute.dart +++ /dev/null @@ -1,30 +0,0 @@ -class SentryLogAttribute { - final dynamic value; - final String type; - - const SentryLogAttribute._(this.value, this.type); - - factory SentryLogAttribute.string(String value) { - return SentryLogAttribute._(value, 'string'); - } - - factory SentryLogAttribute.bool(bool value) { - return SentryLogAttribute._(value, 'boolean'); - } - - factory SentryLogAttribute.int(int value) { - return SentryLogAttribute._(value, 'integer'); - } - - factory SentryLogAttribute.double(double value) { - return SentryLogAttribute._(value, 'double'); - } - - // In the future the SDK will also support List, List, List, List values. - Map toJson() { - return { - 'value': value, - 'type': type, - }; - } -} diff --git a/packages/dart/lib/src/scope.dart b/packages/dart/lib/src/scope.dart index d36bfafbcd..6024022210 100644 --- a/packages/dart/lib/src/scope.dart +++ b/packages/dart/lib/src/scope.dart @@ -167,6 +167,10 @@ class Scope { List get attachments => List.unmodifiable(_attachments); + final Map _attributes = {}; + + Map get attributes => Map.unmodifiable(_attributes); + Scope(this._options); Breadcrumb? _addBreadCrumbSync(Breadcrumb breadcrumb, Hint hint) { @@ -222,6 +226,16 @@ class Scope { } } + void setAttributes(Map attributes) { + attributes.forEach((key, value) { + _attributes[key] = value; + }); + } + + void removeAttribute(String key) { + _attributes.remove(key); + } + void addAttachment(SentryAttachment attachment) { _attachments.add(attachment); } @@ -258,6 +272,7 @@ class Scope { _eventProcessors.clear(); _replayId = null; propagationContext = PropagationContext(); + _attributes.clear(); _clearBreadcrumbsSync(); _setUserSync(null); @@ -461,6 +476,10 @@ class Scope { clone.addAttachment(attachment); } + if (_attributes.isNotEmpty) { + clone.setAttributes(Map.from(_attributes)); + } + return clone; } diff --git a/packages/dart/lib/src/sentry.dart b/packages/dart/lib/src/sentry.dart index 7b6d79abdf..f65bc721b1 100644 --- a/packages/dart/lib/src/sentry.dart +++ b/packages/dart/lib/src/sentry.dart @@ -295,6 +295,19 @@ class Sentry { static Future addBreadcrumb(Breadcrumb crumb, {Hint? hint}) => _hub.addBreadcrumb(crumb, hint: hint); + /// Adds attributes to the current [Scope]. + /// These attributes will be applied to logs. + /// When the same attribute keys exist on the current log, + /// it takes precedence over an attribute with the same key set on any scope. + static void setAttributes(Map attributes) { + _hub.setAttributes(attributes); + } + + /// Removes the attribute [key] from the scope. + static void removeAttribute(String key) { + _hub.removeAttribute(key); + } + /// Configures the scope through the callback. static FutureOr configureScope(ScopeCallback callback) => _hub.configureScope(callback); diff --git a/packages/dart/lib/src/sentry_client.dart b/packages/dart/lib/src/sentry_client.dart index fc2ec9a092..b53eb998ac 100644 --- a/packages/dart/lib/src/sentry_client.dart +++ b/packages/dart/lib/src/sentry_client.dart @@ -502,21 +502,26 @@ class SentryClient { return; } - log.attributes['sentry.sdk.name'] = SentryLogAttribute.string( + if (scope != null) { + final merged = Map.of(scope.attributes)..addAll(log.attributes); + log.attributes = merged; + } + + log.attributes['sentry.sdk.name'] = SentryAttribute.string( _options.sdk.name, ); - log.attributes['sentry.sdk.version'] = SentryLogAttribute.string( + log.attributes['sentry.sdk.version'] = SentryAttribute.string( _options.sdk.version, ); final environment = _options.environment; if (environment != null) { - log.attributes['sentry.environment'] = SentryLogAttribute.string( + log.attributes['sentry.environment'] = SentryAttribute.string( environment, ); } final release = _options.release; if (release != null) { - log.attributes['sentry.release'] = SentryLogAttribute.string( + log.attributes['sentry.release'] = SentryAttribute.string( release, ); } @@ -527,7 +532,7 @@ class SentryClient { } final span = scope?.span; if (span != null) { - log.attributes['sentry.trace.parent_span_id'] = SentryLogAttribute.string( + log.attributes['sentry.trace.parent_span_id'] = SentryAttribute.string( span.context.spanId.toString(), ); } @@ -537,13 +542,13 @@ class SentryClient { final email = user?.email; final name = user?.name; if (id != null) { - log.attributes['user.id'] = SentryLogAttribute.string(id); + log.attributes['user.id'] = SentryAttribute.string(id); } if (name != null) { - log.attributes['user.name'] = SentryLogAttribute.string(name); + log.attributes['user.name'] = SentryAttribute.string(name); } if (email != null) { - log.attributes['user.email'] = SentryLogAttribute.string(email); + log.attributes['user.email'] = SentryAttribute.string(email); } final beforeSendLog = _options.beforeSendLog; diff --git a/packages/dart/lib/src/sentry_logger.dart b/packages/dart/lib/src/sentry_logger.dart index 2ae95dad9f..1d324e0678 100644 --- a/packages/dart/lib/src/sentry_logger.dart +++ b/packages/dart/lib/src/sentry_logger.dart @@ -3,7 +3,7 @@ import 'hub.dart'; import 'hub_adapter.dart'; import 'protocol/sentry_log.dart'; import 'protocol/sentry_log_level.dart'; -import 'protocol/sentry_log_attribute.dart'; +import 'protocol/sentry_attribute.dart'; import 'sentry_options.dart'; import 'sentry_logger_formatter.dart'; @@ -17,42 +17,42 @@ class SentryLogger { FutureOr trace( String body, { - Map? attributes, + Map? attributes, }) { return _captureLog(SentryLogLevel.trace, body, attributes: attributes); } FutureOr debug( String body, { - Map? attributes, + Map? attributes, }) { return _captureLog(SentryLogLevel.debug, body, attributes: attributes); } FutureOr info( String body, { - Map? attributes, + Map? attributes, }) { return _captureLog(SentryLogLevel.info, body, attributes: attributes); } FutureOr warn( String body, { - Map? attributes, + Map? attributes, }) { return _captureLog(SentryLogLevel.warn, body, attributes: attributes); } FutureOr error( String body, { - Map? attributes, + Map? attributes, }) { return _captureLog(SentryLogLevel.error, body, attributes: attributes); } FutureOr fatal( String body, { - Map? attributes, + Map? attributes, }) { return _captureLog(SentryLogLevel.fatal, body, attributes: attributes); } @@ -62,7 +62,7 @@ class SentryLogger { FutureOr _captureLog( SentryLogLevel level, String body, { - Map? attributes, + Map? attributes, }) { final log = SentryLog( timestamp: _clock(), @@ -73,7 +73,7 @@ class SentryLogger { _hub.options.log( level.toSentryLevel(), - _formatLogMessage(level, body, attributes), + _formatLogMessage(level, body, attributes ?? {}), logger: 'sentry_logger', ); @@ -84,7 +84,7 @@ class SentryLogger { String _formatLogMessage( SentryLogLevel level, String body, - Map? attributes, + Map? attributes, ) { if (attributes == null || attributes.isEmpty) { return body; @@ -98,7 +98,7 @@ class SentryLogger { } /// Format attribute value based on its type - String _formatAttributeValue(SentryLogAttribute attribute) { + String _formatAttributeValue(SentryAttribute attribute) { switch (attribute.type) { case 'string': if (attribute.value is String) { diff --git a/packages/dart/lib/src/sentry_logger_formatter.dart b/packages/dart/lib/src/sentry_logger_formatter.dart index d0ddb820a4..aff7d21e51 100644 --- a/packages/dart/lib/src/sentry_logger_formatter.dart +++ b/packages/dart/lib/src/sentry_logger_formatter.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'protocol/sentry_log_attribute.dart'; +import 'protocol/sentry_attribute.dart'; import 'sentry_template_string.dart'; import 'sentry_logger.dart'; @@ -11,7 +11,7 @@ class SentryLoggerFormatter { FutureOr trace( String templateBody, List arguments, { - Map? attributes, + Map? attributes, }) { return _format( templateBody, @@ -26,7 +26,7 @@ class SentryLoggerFormatter { FutureOr debug( String templateBody, List arguments, { - Map? attributes, + Map? attributes, }) { return _format( templateBody, @@ -41,7 +41,7 @@ class SentryLoggerFormatter { FutureOr info( String templateBody, List arguments, { - Map? attributes, + Map? attributes, }) { return _format( templateBody, @@ -56,7 +56,7 @@ class SentryLoggerFormatter { FutureOr warn( String templateBody, List arguments, { - Map? attributes, + Map? attributes, }) { return _format( templateBody, @@ -71,7 +71,7 @@ class SentryLoggerFormatter { FutureOr error( String templateBody, List arguments, { - Map? attributes, + Map? attributes, }) { return _format( templateBody, @@ -86,7 +86,7 @@ class SentryLoggerFormatter { FutureOr fatal( String templateBody, List arguments, { - Map? attributes, + Map? attributes, }) { return _format( templateBody, @@ -103,16 +103,16 @@ class SentryLoggerFormatter { FutureOr _format( String templateBody, List arguments, - Map? attributes, - FutureOr Function(String, Map) callback, + Map? attributes, + FutureOr Function(String, Map) callback, ) { String formattedBody; - Map templateAttributes; + Map templateAttributes; if (arguments.isEmpty) { // No arguments means no template processing needed formattedBody = templateBody; - templateAttributes = {}; + templateAttributes = {}; } else { // Process template with arguments final templateString = SentryTemplateString(templateBody, arguments); @@ -126,30 +126,29 @@ class SentryLoggerFormatter { return callback(formattedBody, templateAttributes); } - Map _getAllAttributes( + Map _getAllAttributes( String templateBody, List args, ) { final templateAttributes = { - 'sentry.message.template': SentryLogAttribute.string(templateBody), + 'sentry.message.template': SentryAttribute.string(templateBody), }; for (var i = 0; i < args.length; i++) { final argument = args[i]; final key = 'sentry.message.parameter.$i'; if (argument is String) { - templateAttributes[key] = SentryLogAttribute.string(argument); + templateAttributes[key] = SentryAttribute.string(argument); } else if (argument is int) { - templateAttributes[key] = SentryLogAttribute.int(argument); + templateAttributes[key] = SentryAttribute.int(argument); } else if (argument is bool) { - templateAttributes[key] = SentryLogAttribute.bool(argument); + templateAttributes[key] = SentryAttribute.bool(argument); } else if (argument is double) { - templateAttributes[key] = SentryLogAttribute.double(argument); + templateAttributes[key] = SentryAttribute.double(argument); } else { try { - templateAttributes[key] = - SentryLogAttribute.string(argument.toString()); + templateAttributes[key] = SentryAttribute.string(argument.toString()); } catch (e) { - templateAttributes[key] = SentryLogAttribute.string(""); + templateAttributes[key] = SentryAttribute.string(""); } } } diff --git a/packages/dart/test/hub_test.dart b/packages/dart/test/hub_test.dart index eec9bded6d..ab9f11e048 100644 --- a/packages/dart/test/hub_test.dart +++ b/packages/dart/test/hub_test.dart @@ -550,6 +550,45 @@ void main() { ); }); + test('setAttributes sets attributes on scope', () { + hub.setAttributes({ + 'attr1': SentryAttribute.string('value'), + 'attr2': SentryAttribute.int(42), + 'attr3': SentryAttribute.bool(true), + 'attr4': SentryAttribute.double(3.14) + }); + hub.setAttributes({'merged': SentryAttribute.double(12)}); + + final attributes = hub.scope.attributes; + expect(attributes, isNotEmpty); + expect(attributes['attr1']?.value, SentryAttribute.string('value').value); + expect(attributes['attr2']?.value, SentryAttribute.int(42).value); + expect(attributes['attr3']?.value, SentryAttribute.bool(true).value); + expect(attributes['attr4']?.value, SentryAttribute.double(3.14).value); + expect(attributes['merged']?.value, SentryAttribute.double(12).value); + }); + + test('removeAttribute removes attribute on scope', () { + hub.setAttributes({ + 'attr1': SentryAttribute.string('value'), + 'attr2': SentryAttribute.int(42), + 'attr3': SentryAttribute.bool(true), + 'attr4': SentryAttribute.double(3.14) + }); + hub.setAttributes({'merged': SentryAttribute.double(12)}); + + hub.removeAttribute('attr3'); + hub.removeAttribute('merged'); + + final attributes = hub.scope.attributes; + expect(attributes, isNotEmpty); + expect(attributes['attr1']?.value, SentryAttribute.string('value').value); + expect(attributes['attr2']?.value, SentryAttribute.int(42).value); + expect(attributes['attr4']?.value, SentryAttribute.double(3.14).value); + expect(attributes['attr3']?.value, isNull); + expect(attributes['merged']?.value, isNull); + }); + test('should configure scope async', () async { await hub.configureScope((Scope scope) async { await Future.delayed(Duration(milliseconds: 10)); @@ -718,6 +757,42 @@ void main() { fixture = Fixture(); }); + test('withScope can override scope attributes for that call only', + () async { + final hub = fixture.getSut(); + hub.setAttributes({ + 'overridden': SentryAttribute.string('global'), + 'kept': SentryAttribute.bool(true), + }); + + await hub.captureMessage('msg', withScope: (scope) async { + // cloned scope starts with global attributes + expect(scope.attributes['overridden']?.value, 'global'); + expect(scope.attributes['kept']?.value, true); + + // override and add one more + scope.setAttributes({ + 'overridden': SentryAttribute.string('local'), + 'extra': SentryAttribute.int(1), + }); + + expect(scope.attributes['overridden']?.value, 'local'); + expect(scope.attributes['kept']?.value, true); + expect(scope.attributes['extra']?.value, 1); + }); + + // The scope passed to the client should reflect overridden attributes + final capturedScope = fixture.client.captureMessageCalls.last.scope!; + expect(capturedScope.attributes['overridden']?.value, 'local'); + expect(capturedScope.attributes['kept']?.value, true); + expect(capturedScope.attributes['extra']?.value, 1); + + // Global scope remains unchanged + expect(hub.scope.attributes['overridden']?.value, 'global'); + expect(hub.scope.attributes['kept']?.value, true); + expect(hub.scope.attributes.containsKey('extra'), false); + }); + test('captureEvent should create a new scope', () async { final hub = fixture.getSut(); await hub.captureEvent(SentryEvent()); @@ -853,7 +928,7 @@ void main() { level: SentryLogLevel.info, body: 'test', attributes: { - 'attribute': SentryLogAttribute.string('value'), + 'attribute': SentryAttribute.string('value'), }, ); } diff --git a/packages/dart/test/logs_enricher_interation_test.dart b/packages/dart/test/logs_enricher_integration_test.dart similarity index 95% rename from packages/dart/test/logs_enricher_interation_test.dart rename to packages/dart/test/logs_enricher_integration_test.dart index 78eda1b6a4..38644c1c74 100644 --- a/packages/dart/test/logs_enricher_interation_test.dart +++ b/packages/dart/test/logs_enricher_integration_test.dart @@ -5,7 +5,7 @@ import 'package:sentry/src/logs_enricher_integration.dart'; import 'package:test/test.dart'; import 'package:sentry/src/hub.dart'; import 'package:sentry/src/protocol/sentry_log.dart'; -import 'package:sentry/src/protocol/sentry_log_attribute.dart'; +import 'package:sentry/src/protocol/sentry_attribute.dart'; import 'package:sentry/src/protocol/sentry_id.dart'; import 'package:sentry/src/protocol/sentry_log_level.dart'; import 'test_utils.dart'; @@ -19,7 +19,7 @@ void main() { level: SentryLogLevel.info, body: 'test', attributes: { - 'attribute': SentryLogAttribute.string('value'), + 'attribute': SentryAttribute.string('value'), }, ); } diff --git a/packages/dart/test/protocol/rate_limiter_test.dart b/packages/dart/test/protocol/rate_limiter_test.dart index a4845f6430..f61cd20f91 100644 --- a/packages/dart/test/protocol/rate_limiter_test.dart +++ b/packages/dart/test/protocol/rate_limiter_test.dart @@ -292,7 +292,7 @@ void main() { level: SentryLogLevel.info, body: 'test', attributes: { - 'test': SentryLogAttribute.string('test'), + 'test': SentryAttribute.string('test'), }, ); diff --git a/packages/dart/test/protocol/sentry_log_attribute_test.dart b/packages/dart/test/protocol/sentry_attribute_test.dart similarity index 57% rename from packages/dart/test/protocol/sentry_log_attribute_test.dart rename to packages/dart/test/protocol/sentry_attribute_test.dart index 2c0fb7ce31..30d67eed5b 100644 --- a/packages/dart/test/protocol/sentry_log_attribute_test.dart +++ b/packages/dart/test/protocol/sentry_attribute_test.dart @@ -2,8 +2,8 @@ import 'package:test/test.dart'; import 'package:sentry/sentry.dart'; void main() { - test('$SentryLogAttribute string to json', () { - final attribute = SentryLogAttribute.string('test'); + test('$SentryAttribute string to json', () { + final attribute = SentryAttribute.string('test'); final json = attribute.toJson(); expect(json, { 'value': 'test', @@ -11,8 +11,8 @@ void main() { }); }); - test('$SentryLogAttribute bool to json', () { - final attribute = SentryLogAttribute.bool(true); + test('$SentryAttribute bool to json', () { + final attribute = SentryAttribute.bool(true); final json = attribute.toJson(); expect(json, { 'value': true, @@ -20,8 +20,8 @@ void main() { }); }); - test('$SentryLogAttribute int to json', () { - final attribute = SentryLogAttribute.int(1); + test('$SentryAttribute int to json', () { + final attribute = SentryAttribute.int(1); final json = attribute.toJson(); expect(json, { @@ -30,8 +30,8 @@ void main() { }); }); - test('$SentryLogAttribute double to json', () { - final attribute = SentryLogAttribute.double(1.0); + test('$SentryAttribute double to json', () { + final attribute = SentryAttribute.double(1.0); final json = attribute.toJson(); expect(json, { diff --git a/packages/dart/test/protocol/sentry_log_test.dart b/packages/dart/test/protocol/sentry_log_test.dart index 0921df7a32..b138e5340a 100644 --- a/packages/dart/test/protocol/sentry_log_test.dart +++ b/packages/dart/test/protocol/sentry_log_test.dart @@ -12,10 +12,10 @@ void main() { level: SentryLogLevel.info, body: 'fixture-body', attributes: { - 'test': SentryLogAttribute.string('fixture-test'), - 'test2': SentryLogAttribute.bool(true), - 'test3': SentryLogAttribute.int(9001), - 'test4': SentryLogAttribute.double(9000.1), + 'test': SentryAttribute.string('fixture-test'), + 'test2': SentryAttribute.bool(true), + 'test3': SentryAttribute.int(9001), + 'test4': SentryAttribute.double(9000.1), }, severityNumber: 1, ); @@ -56,7 +56,7 @@ void main() { level: SentryLogLevel.trace, body: 'fixture-body', attributes: { - 'test': SentryLogAttribute.string('fixture-test'), + 'test': SentryAttribute.string('fixture-test'), }, ); diff --git a/packages/dart/test/scope_test.dart b/packages/dart/test/scope_test.dart index a7824baac5..44683f1c50 100644 --- a/packages/dart/test/scope_test.dart +++ b/packages/dart/test/scope_test.dart @@ -270,6 +270,54 @@ void main() { expect(sut.attachments.length, 0); }); + test('setAttribute adds attributes to scope', () { + final sut = fixture.getSut(); + + final initial = { + 'str': SentryAttribute.string('foo'), + 'num': SentryAttribute.int(42), + }; + sut.setAttributes(initial); + + expect(sut.attributes['str']?.type, 'string'); + expect(sut.attributes['str']?.value, 'foo'); + expect(sut.attributes['num']?.type, 'integer'); + expect(sut.attributes['num']?.value, 42); + + // override existing key and add a new one + final update = { + 'str': SentryAttribute.string('bar'), + 'bool': SentryAttribute.bool(true), + }; + sut.setAttributes(update); + + expect(sut.attributes['str']?.value, 'bar'); + expect(sut.attributes['bool']?.type, 'boolean'); + expect(sut.attributes['bool']?.value, true); + // previous non-overridden key remains + expect(sut.attributes['num']?.value, 42); + }); + + test('removeAttribute removes attributes from scope', () { + final sut = fixture.getSut(); + + sut.setAttributes({ + 'a': SentryAttribute.string('x'), + 'b': SentryAttribute.int(1), + }); + expect(sut.attributes.length, 2); + + sut.removeAttribute('a'); + expect(sut.attributes.containsKey('a'), false); + expect(sut.attributes['b']?.value, 1); + expect(sut.attributes.length, 1); + + // removing a non-existent key is a no-op + sut.removeAttribute('does-not-exist'); + expect(sut.attributes.length, 1); + expect(sut.attributes.containsKey('b'), true); + }); + test('sets tag', () { final sut = fixture.getSut(); @@ -341,6 +389,7 @@ void main() { expect(sut.extra.length, 0); expect(sut.eventProcessors.length, 0); expect(sut.replayId, isNull); + expect(sut.attributes, isEmpty); }); test('clones', () async { @@ -377,6 +426,35 @@ void main() { expect(sut.replayId, clone.replayId); }); + test('clone copies attributes and keeps them independent', () { + final sut = fixture.getSut(); + sut.setAttributes({ + 'a': SentryAttribute.string('x'), + 'b': SentryAttribute.int(1), + }); + + final clone = sut.clone(); + + // clone has same attributes + expect(clone.attributes['a']?.type, 'string'); + expect(clone.attributes['a']?.value, 'x'); + expect(clone.attributes['b']?.type, 'integer'); + expect(clone.attributes['b']?.value, 1); + + // mutate clone only + clone.setAttributes( + {'a': SentryAttribute.string('y'), 'c': SentryAttribute.bool(true)}); + + // original unchanged + expect(sut.attributes['a']?.value, 'x'); + expect(sut.attributes.containsKey('c'), false); + + // clone reflects changes + expect(clone.attributes['a']?.value, 'y'); + expect(clone.attributes['c']?.type, 'boolean'); + expect(clone.attributes['c']?.value, true); + }); + test('clone does not additionally call observers', () async { final sut = fixture.getSut(scopeObserver: fixture.mockScopeObserver); diff --git a/packages/dart/test/sentry_client_lifecycle_test.dart b/packages/dart/test/sentry_client_lifecycle_test.dart index 7f1ae69697..18c397a46c 100644 --- a/packages/dart/test/sentry_client_lifecycle_test.dart +++ b/packages/dart/test/sentry_client_lifecycle_test.dart @@ -24,7 +24,7 @@ void main() { level: SentryLogLevel.info, body: 'test', attributes: { - 'attribute': SentryLogAttribute.string('value'), + 'attribute': SentryAttribute.string('value'), }, ); } @@ -45,8 +45,7 @@ void main() { fixture.options.lifecycleRegistry .registerCallback((event) { - event.log.attributes['test'] = - SentryLogAttribute.string('test-value'); + event.log.attributes['test'] = SentryAttribute.string('test-value'); }); await client.captureLog(log, scope: scope); diff --git a/packages/dart/test/sentry_client_sdk_lifecycle_test.dart b/packages/dart/test/sentry_client_sdk_lifecycle_test.dart index 7f1ae69697..18c397a46c 100644 --- a/packages/dart/test/sentry_client_sdk_lifecycle_test.dart +++ b/packages/dart/test/sentry_client_sdk_lifecycle_test.dart @@ -24,7 +24,7 @@ void main() { level: SentryLogLevel.info, body: 'test', attributes: { - 'attribute': SentryLogAttribute.string('value'), + 'attribute': SentryAttribute.string('value'), }, ); } @@ -45,8 +45,7 @@ void main() { fixture.options.lifecycleRegistry .registerCallback((event) { - event.log.attributes['test'] = - SentryLogAttribute.string('test-value'); + event.log.attributes['test'] = SentryAttribute.string('test-value'); }); await client.captureLog(log, scope: scope); diff --git a/packages/dart/test/sentry_client_test.dart b/packages/dart/test/sentry_client_test.dart index 9fa77334ea..534f665774 100644 --- a/packages/dart/test/sentry_client_test.dart +++ b/packages/dart/test/sentry_client_test.dart @@ -1729,7 +1729,7 @@ void main() { level: SentryLogLevel.info, body: 'test', attributes: { - 'attribute': SentryLogAttribute.string('value'), + 'attribute': SentryAttribute.string('value'), }, ); } @@ -1839,6 +1839,51 @@ void main() { ); }); + test('should use attributes from given scope', () async { + fixture.options.enableLogs = true; + + final client = fixture.getSut(); + fixture.options.logBatcher = MockLogBatcher(); + final log = givenLog(); + + final scope = Scope(fixture.options); + scope.setAttributes({'from_scope': SentryAttribute.int(12)}); + + await client.captureLog(log, scope: scope); + + final mockLogBatcher = fixture.options.logBatcher as MockLogBatcher; + expect(mockLogBatcher.addLogCalls.length, 1); + final capturedLog = mockLogBatcher.addLogCalls.first; + expect(capturedLog.attributes['from_scope']?.value, 12); + }); + + test('per-log attributes override scope on same key', () async { + fixture.options.enableLogs = true; + + final client = fixture.getSut(); + fixture.options.logBatcher = MockLogBatcher(); + final log = givenLog(); + + final scope = Scope(fixture.options); + scope.setAttributes({ + 'overridden': SentryAttribute.string('fromScope'), + 'kept': SentryAttribute.bool(true), + }); + + log.attributes['overridden'] = SentryAttribute.string('fromLog'); + log.attributes['logOnly'] = SentryAttribute.double(1.23); + + await client.captureLog(log, scope: scope); + + final mockLogBatcher = fixture.options.logBatcher as MockLogBatcher; + expect(mockLogBatcher.addLogCalls.length, 1); + final captured = mockLogBatcher.addLogCalls.first; + + expect(captured.attributes['overridden']?.value, 'fromLog'); + expect(captured.attributes['kept']?.value, true); + expect(captured.attributes['logOnly']?.type, 'double'); + }); + test('should add user info to attributes', () async { fixture.options.enableLogs = true; @@ -2012,7 +2057,7 @@ void main() { fixture.options.lifecycleRegistry .registerCallback((event) { - event.log.attributes['test'] = SentryLogAttribute.string('test-value'); + event.log.attributes['test'] = SentryAttribute.string('test-value'); }); await client.captureLog(log, scope: scope); diff --git a/packages/dart/test/sentry_envelope_item_test.dart b/packages/dart/test/sentry_envelope_item_test.dart index a8b2bd2743..54f534de61 100644 --- a/packages/dart/test/sentry_envelope_item_test.dart +++ b/packages/dart/test/sentry_envelope_item_test.dart @@ -104,7 +104,7 @@ void main() { level: SentryLogLevel.info, body: 'test', attributes: { - 'test': SentryLogAttribute.string('test'), + 'test': SentryAttribute.string('test'), }, ), SentryLog( @@ -113,7 +113,7 @@ void main() { level: SentryLogLevel.info, body: 'test2', attributes: { - 'test2': SentryLogAttribute.int(9001), + 'test2': SentryAttribute.int(9001), }, ), ]; diff --git a/packages/dart/test/sentry_envelope_test.dart b/packages/dart/test/sentry_envelope_test.dart index eaa1af75c4..f0953624c7 100644 --- a/packages/dart/test/sentry_envelope_test.dart +++ b/packages/dart/test/sentry_envelope_test.dart @@ -143,7 +143,7 @@ void main() { level: SentryLogLevel.info, body: 'test', attributes: { - 'test': SentryLogAttribute.string('test'), + 'test': SentryAttribute.string('test'), }, ), SentryLog( @@ -152,7 +152,7 @@ void main() { level: SentryLogLevel.info, body: 'test2', attributes: { - 'test2': SentryLogAttribute.int(9001), + 'test2': SentryAttribute.int(9001), }, ), ]; diff --git a/packages/dart/test/sentry_logger_formatter_test.dart b/packages/dart/test/sentry_logger_formatter_test.dart index af81acd019..443fade82a 100644 --- a/packages/dart/test/sentry_logger_formatter_test.dart +++ b/packages/dart/test/sentry_logger_formatter_test.dart @@ -1,7 +1,7 @@ import 'package:test/test.dart'; import 'package:sentry/src/sentry_logger_formatter.dart'; import 'package:sentry/src/sentry_logger.dart'; -import 'package:sentry/src/protocol/sentry_log_attribute.dart'; +import 'package:sentry/src/protocol/sentry_attribute.dart'; void main() { final fixture = Fixture(); @@ -45,7 +45,7 @@ void main() { sut.trace( "Hello, %s!", ["World"], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.traceCalls.length, 1); @@ -61,7 +61,7 @@ void main() { sut.debug( "Hello, %s!", ["World"], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.debugCalls.length, 1); @@ -77,7 +77,7 @@ void main() { sut.info( "Hello, %s!", ["World"], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.infoCalls.length, 1); @@ -93,7 +93,7 @@ void main() { sut.warn( "Hello, %s!", ["World"], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.warnCalls.length, 1); @@ -109,7 +109,7 @@ void main() { sut.error( "Hello, %s!", ["World"], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.errorCalls.length, 1); @@ -125,7 +125,7 @@ void main() { sut.fatal( "Hello, %s!", ["World"], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.fatalCalls.length, 1); @@ -143,7 +143,7 @@ void main() { sut.trace( "Name: %s, Age: %s, Active: %s, Score: %s", ['Alice', 30, true, 95.5], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.traceCalls.length, 1); @@ -159,7 +159,7 @@ void main() { sut.trace( "Name: %s, Age: %s, Active: %s, Score: %s", ['Alice', 30, true, 95.5], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.traceCalls.length, 1); @@ -175,7 +175,7 @@ void main() { sut.debug( "Name: %s, Age: %s, Active: %s, Score: %s", ['Alice', 30, true, 95.5], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.debugCalls.length, 1); @@ -191,7 +191,7 @@ void main() { sut.info( "Name: %s, Age: %s, Active: %s, Score: %s", ['Alice', 30, true, 95.5], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.infoCalls.length, 1); @@ -207,7 +207,7 @@ void main() { sut.warn( "Name: %s, Age: %s, Active: %s, Score: %s", ['Alice', 30, true, 95.5], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.warnCalls.length, 1); @@ -223,7 +223,7 @@ void main() { sut.error( "Name: %s, Age: %s, Active: %s, Score: %s", ['Alice', 30, true, 95.5], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.errorCalls.length, 1); @@ -239,7 +239,7 @@ void main() { sut.fatal( "Name: %s, Age: %s, Active: %s, Score: %s", ['Alice', 30, true, 95.5], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.fatalCalls.length, 1); @@ -257,7 +257,7 @@ void main() { sut.trace( "Hello, World!", [], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.traceCalls.length, 1); @@ -277,7 +277,7 @@ void main() { sut.debug( "Hello, World!", [], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.debugCalls.length, 1); @@ -297,7 +297,7 @@ void main() { sut.info( "Hello, World!", [], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.infoCalls.length, 1); @@ -317,7 +317,7 @@ void main() { sut.warn( "Hello, World!", [], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.warnCalls.length, 1); @@ -337,7 +337,7 @@ void main() { sut.error( "Hello, World!", [], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.errorCalls.length, 1); @@ -357,7 +357,7 @@ void main() { sut.fatal( "Hello, World!", [], - attributes: {'foo': SentryLogAttribute.string('bar')}, + attributes: {'foo': SentryAttribute.string('bar')}, ); expect(logger.fatalCalls.length, 1); diff --git a/packages/dart/test/sentry_logger_test.dart b/packages/dart/test/sentry_logger_test.dart index 7a3a303256..37e760a204 100644 --- a/packages/dart/test/sentry_logger_test.dart +++ b/packages/dart/test/sentry_logger_test.dart @@ -158,10 +158,10 @@ void main() { ); // Test with special double values - final specialAttributes = { - 'nan': SentryLogAttribute.double(double.nan), - 'positive_infinity': SentryLogAttribute.double(double.infinity), - 'negative_infinity': SentryLogAttribute.double(double.negativeInfinity), + final specialAttributes = { + 'nan': SentryAttribute.double(double.nan), + 'positive_infinity': SentryAttribute.double(double.infinity), + 'negative_infinity': SentryAttribute.double(double.negativeInfinity), }; logger.info('special values', attributes: specialAttributes); @@ -183,6 +183,24 @@ void main() { 'special values {"nan": NaN, "positive_infinity": Infinity, "negative_infinity": -Infinity}'); expect(logCall.logger, 'sentry_logger'); }); + + // This is mostly an edge case but let's cover it just in case + test('per-log attributes override fmt template attributes on same key', () { + final logger = fixture.getSut(); + + logger.fmt.info( + 'Hello, %s!', + ['World'], + attributes: { + 'sentry.message.template': SentryAttribute.string('OVERRIDE'), + 'sentry.message.parameter.0': SentryAttribute.string('Earth'), + }, + ); + + final attrs = fixture.hub.captureLogCalls[0].log.attributes; + expect(attrs['sentry.message.template']?.value, 'OVERRIDE'); + expect(attrs['sentry.message.parameter.0']?.value, 'Earth'); + }); } class Fixture { @@ -190,15 +208,15 @@ class Fixture { final hub = MockHub(); final timestamp = DateTime.fromMicrosecondsSinceEpoch(0); - final attributes = { - 'string': SentryLogAttribute.string('string'), - 'int': SentryLogAttribute.int(1), - 'double': SentryLogAttribute.double(1.23456789), - 'bool': SentryLogAttribute.bool(true), - 'double_int': SentryLogAttribute.double(1.0), - 'nan': SentryLogAttribute.double(double.nan), - 'positive_infinity': SentryLogAttribute.double(double.infinity), - 'negative_infinity': SentryLogAttribute.double(double.negativeInfinity), + final attributes = { + 'string': SentryAttribute.string('string'), + 'int': SentryAttribute.int(1), + 'double': SentryAttribute.double(1.23456789), + 'bool': SentryAttribute.bool(true), + 'double_int': SentryAttribute.double(1.0), + 'nan': SentryAttribute.double(double.nan), + 'positive_infinity': SentryAttribute.double(double.infinity), + 'negative_infinity': SentryAttribute.double(double.negativeInfinity), }; SentryLogger getSut() { diff --git a/packages/flutter/example/lib/main.dart b/packages/flutter/example/lib/main.dart index 88166c8005..d1e2a18fef 100644 --- a/packages/flutter/example/lib/main.dart +++ b/packages/flutter/example/lib/main.dart @@ -548,7 +548,7 @@ class MainScaffold extends StatelessWidget { onPressed: () { Sentry.logger .info('Sentry Log With Test Attribute', attributes: { - 'test-attribute': SentryLogAttribute.string('test-value'), + 'test-attribute': SentryAttribute.string('test-value'), }); }, text: 'Demonstrates the logging with Sentry Log.', diff --git a/packages/flutter/lib/src/integrations/load_contexts_integration.dart b/packages/flutter/lib/src/integrations/load_contexts_integration.dart index 1d972446b5..fa9b625ab5 100644 --- a/packages/flutter/lib/src/integrations/load_contexts_integration.dart +++ b/packages/flutter/lib/src/integrations/load_contexts_integration.dart @@ -66,27 +66,27 @@ class LoadContextsIntegration extends Integration { _mergeNativeWithLocalContexts(contextsMap, contexts); if (contexts.operatingSystem?.name != null) { - event.log.attributes['os.name'] = SentryLogAttribute.string( + event.log.attributes['os.name'] = SentryAttribute.string( contexts.operatingSystem?.name ?? '', ); } if (contexts.operatingSystem?.version != null) { - event.log.attributes['os.version'] = SentryLogAttribute.string( + event.log.attributes['os.version'] = SentryAttribute.string( contexts.operatingSystem?.version ?? '', ); } if (contexts.device?.brand != null) { - event.log.attributes['device.brand'] = SentryLogAttribute.string( + event.log.attributes['device.brand'] = SentryAttribute.string( contexts.device?.brand ?? '', ); } if (contexts.device?.model != null) { - event.log.attributes['device.model'] = SentryLogAttribute.string( + event.log.attributes['device.model'] = SentryAttribute.string( contexts.device?.model ?? '', ); } if (contexts.device?.family != null) { - event.log.attributes['device.family'] = SentryLogAttribute.string( + event.log.attributes['device.family'] = SentryAttribute.string( contexts.device?.family ?? '', ); } diff --git a/packages/flutter/lib/src/integrations/replay_log_integration.dart b/packages/flutter/lib/src/integrations/replay_log_integration.dart index 99266a5328..2747930cdd 100644 --- a/packages/flutter/lib/src/integrations/replay_log_integration.dart +++ b/packages/flutter/lib/src/integrations/replay_log_integration.dart @@ -29,17 +29,17 @@ class ReplayLogIntegration implements Integration { final replayIsBuffering = replayId != null && scopeReplayId == null; if (sessionSampleRate > 0 && replayId != null && !replayIsBuffering) { - event.log.attributes['sentry.replay_id'] = SentryLogAttribute.string( + event.log.attributes['sentry.replay_id'] = SentryAttribute.string( scopeReplayId.toString(), ); } else if (onErrorSampleRate > 0 && replayId != null && replayIsBuffering) { - event.log.attributes['sentry.replay_id'] = SentryLogAttribute.string( + event.log.attributes['sentry.replay_id'] = SentryAttribute.string( replayId.toString(), ); event.log.attributes['sentry._internal.replay_is_buffering'] = - SentryLogAttribute.bool(true); + SentryAttribute.bool(true); } }; options.lifecycleRegistry diff --git a/packages/flutter/test/integrations/load_contexts_integration_test.dart b/packages/flutter/test/integrations/load_contexts_integration_test.dart index becea12315..4dd6c5b00b 100644 --- a/packages/flutter/test/integrations/load_contexts_integration_test.dart +++ b/packages/flutter/test/integrations/load_contexts_integration_test.dart @@ -31,7 +31,7 @@ void main() { level: SentryLogLevel.info, body: 'test', attributes: { - 'attribute': SentryLogAttribute.string('value'), + 'attribute': SentryAttribute.string('value'), }, ); } diff --git a/packages/flutter/test/integrations/replay_log_integration_test.dart b/packages/flutter/test/integrations/replay_log_integration_test.dart index f03fada734..7a667a050e 100644 --- a/packages/flutter/test/integrations/replay_log_integration_test.dart +++ b/packages/flutter/test/integrations/replay_log_integration_test.dart @@ -293,7 +293,7 @@ class Fixture { traceId: SentryId.newId(), level: SentryLogLevel.info, body: 'test log message', - attributes: {}, + attributes: {}, ); } diff --git a/packages/logging/lib/src/logging_integration.dart b/packages/logging/lib/src/logging_integration.dart index e4bb44f550..948faf1df4 100644 --- a/packages/logging/lib/src/logging_integration.dart +++ b/packages/logging/lib/src/logging_integration.dart @@ -100,10 +100,10 @@ class LoggingIntegration implements Integration { if (_options.enableLogs && _isLoggable(record.level, _minSentryLogLevel)) { final attributes = { - 'loggerName': SentryLogAttribute.string(record.loggerName), - 'sequenceNumber': SentryLogAttribute.int(record.sequenceNumber), - 'time': SentryLogAttribute.int(record.time.millisecondsSinceEpoch), - 'sentry.origin': SentryLogAttribute.string(origin), + 'loggerName': SentryAttribute.string(record.loggerName), + 'sequenceNumber': SentryAttribute.int(record.sequenceNumber), + 'time': SentryAttribute.int(record.time.millisecondsSinceEpoch), + 'sentry.origin': SentryAttribute.string(origin), }; // Map log levels based on value ranges diff --git a/packages/logging/test/logging_integration_test.dart b/packages/logging/test/logging_integration_test.dart index bc42f37d32..1147f6099c 100644 --- a/packages/logging/test/logging_integration_test.dart +++ b/packages/logging/test/logging_integration_test.dart @@ -491,7 +491,7 @@ class MockSentryLogger implements SentryLogger { @override Future trace( String body, { - Map? attributes, + Map? attributes, }) async { traceCalls.add(MockLogCall(body, attributes)); } @@ -499,7 +499,7 @@ class MockSentryLogger implements SentryLogger { @override Future debug( String body, { - Map? attributes, + Map? attributes, }) async { debugCalls.add(MockLogCall(body, attributes)); } @@ -507,7 +507,7 @@ class MockSentryLogger implements SentryLogger { @override Future info( String body, { - Map? attributes, + Map? attributes, }) async { infoCalls.add(MockLogCall(body, attributes)); } @@ -515,7 +515,7 @@ class MockSentryLogger implements SentryLogger { @override Future warn( String body, { - Map? attributes, + Map? attributes, }) async { warnCalls.add(MockLogCall(body, attributes)); } @@ -523,7 +523,7 @@ class MockSentryLogger implements SentryLogger { @override Future error( String body, { - Map? attributes, + Map? attributes, }) async { errorCalls.add(MockLogCall(body, attributes)); } @@ -531,7 +531,7 @@ class MockSentryLogger implements SentryLogger { @override Future fatal( String body, { - Map? attributes, + Map? attributes, }) async { fatalCalls.add(MockLogCall(body, attributes)); } @@ -542,7 +542,7 @@ class MockSentryLogger implements SentryLogger { class MockLogCall { final String message; - final Map? attributes; + final Map? attributes; MockLogCall(this.message, this.attributes); }