From 767153c54536ede4754ebc6c088bb7a7c1332da5 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 13:50:03 +0100 Subject: [PATCH 01/25] Add setAttributeS --- packages/dart/lib/src/hub.dart | 2 + .../dart/lib/src/protocol/sentry_log.dart | 2 +- .../src/protocol/sentry_log_attribute.dart | 66 +++++++++++++++++-- packages/dart/lib/src/scope.dart | 6 ++ packages/dart/lib/src/sentry.dart | 6 ++ packages/dart/lib/src/sentry_logger.dart | 18 ++--- packages/dart/test/sentry_logger_test.dart | 26 ++++---- 7 files changed, 98 insertions(+), 28 deletions(-) diff --git a/packages/dart/lib/src/hub.dart b/packages/dart/lib/src/hub.dart index 2ceb3006ce..2f8a87903d 100644 --- a/packages/dart/lib/src/hub.dart +++ b/packages/dart/lib/src/hub.dart @@ -341,6 +341,8 @@ class Hub { return scope; } + void setAttributes(Map attributes) {} + /// Adds a breacrumb to the current Scope Future addBreadcrumb(Breadcrumb crumb, {Hint? hint}) async { if (!_isEnabled) { diff --git a/packages/dart/lib/src/protocol/sentry_log.dart b/packages/dart/lib/src/protocol/sentry_log.dart index 55c0174d90..550834e783 100644 --- a/packages/dart/lib/src/protocol/sentry_log.dart +++ b/packages/dart/lib/src/protocol/sentry_log.dart @@ -7,7 +7,7 @@ class SentryLog { 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 index 63ac85eb87..34ab63f156 100644 --- a/packages/dart/lib/src/protocol/sentry_log_attribute.dart +++ b/packages/dart/lib/src/protocol/sentry_log_attribute.dart @@ -1,8 +1,8 @@ -class SentryLogAttribute { - final dynamic value; - final String type; +import 'package:meta/meta.dart'; - const SentryLogAttribute._(this.value, this.type); +@Deprecated('Use SentryAttribute instead') +class SentryLogAttribute extends SentryAttribute { + SentryLogAttribute._(super.value, super.type); factory SentryLogAttribute.string(String value) { return SentryLogAttribute._(value, 'string'); @@ -19,12 +19,68 @@ class SentryLogAttribute { factory SentryLogAttribute.double(double value) { return SentryLogAttribute._(value, 'double'); } +} + +class SentryAttribute { + final String type; + final dynamic value; + SentryUnit? unit; + + @internal + SentryAttribute(this.value, this.type, {this.unit}); + + factory SentryAttribute.string(String value, {SentryUnit? unit}) { + return SentryAttribute(value, 'string', unit: unit); + } + + factory SentryAttribute.bool(bool value, {SentryUnit? unit}) { + return SentryAttribute(value, 'boolean', unit: unit); + } + + factory SentryAttribute.int(int value, {SentryUnit? unit}) { + return SentryAttribute(value, 'integer', unit: unit); + } + + factory SentryAttribute.double(double value, {SentryUnit? unit}) { + return SentryAttribute(value, 'double', unit: unit); + } + + factory SentryAttribute.stringArr(List value, {SentryUnit? unit}) { + return SentryAttribute(value, 'string[]', unit: unit); + } + + factory SentryAttribute.intArr(List value, {SentryUnit? unit}) { + return SentryAttribute(value, 'integer[]', unit: unit); + } + + factory SentryAttribute.doubleArr(List value, {SentryUnit? unit}) { + return SentryAttribute(value, 'double[]', unit: unit); + } - // In the future the SDK will also support List, List, List, List values. Map toJson() { return { 'value': value, 'type': type, + if (unit != null) 'unit': unit!.asString, }; } } + +enum SentryUnit { milliseconds, seconds, bytes, count, percent } + +extension SentryUnitExtension on SentryUnit { + String get asString { + switch (this) { + case SentryUnit.milliseconds: + return "ms"; + case SentryUnit.seconds: + return "s"; + case SentryUnit.bytes: + return "bytes"; + case SentryUnit.count: + return "count"; + case SentryUnit.percent: + return "percent"; + } + } +} diff --git a/packages/dart/lib/src/scope.dart b/packages/dart/lib/src/scope.dart index d36bfafbcd..8f8f5ae353 100644 --- a/packages/dart/lib/src/scope.dart +++ b/packages/dart/lib/src/scope.dart @@ -167,6 +167,8 @@ class Scope { List get attachments => List.unmodifiable(_attachments); + final Map _attributes = {}; + Scope(this._options); Breadcrumb? _addBreadCrumbSync(Breadcrumb breadcrumb, Hint hint) { @@ -222,6 +224,10 @@ class Scope { } } + void setAttributes(Map attributes) { + _attributes = attributes; + } + void addAttachment(SentryAttachment attachment) { _attachments.add(attachment); } diff --git a/packages/dart/lib/src/sentry.dart b/packages/dart/lib/src/sentry.dart index 7b6d79abdf..8d8a32b949 100644 --- a/packages/dart/lib/src/sentry.dart +++ b/packages/dart/lib/src/sentry.dart @@ -295,6 +295,12 @@ 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) {} + /// Configures the scope through the callback. static FutureOr configureScope(ScopeCallback callback) => _hub.configureScope(callback); diff --git a/packages/dart/lib/src/sentry_logger.dart b/packages/dart/lib/src/sentry_logger.dart index 2ae95dad9f..1c88c46cca 100644 --- a/packages/dart/lib/src/sentry_logger.dart +++ b/packages/dart/lib/src/sentry_logger.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(), @@ -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/test/sentry_logger_test.dart b/packages/dart/test/sentry_logger_test.dart index 7a3a303256..667a0d1d39 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); @@ -190,15 +190,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() { From 578f81d3072e77a0af5838d2929b6b953740a93d Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 14:14:42 +0100 Subject: [PATCH 02/25] Update --- packages/dart/lib/src/hub_adapter.dart | 4 ++ packages/dart/lib/src/noop_hub.dart | 3 ++ packages/dart/lib/src/scope.dart | 6 ++- packages/dart/lib/src/sentry_logger.dart | 7 +++- .../dart/lib/src/sentry_logger_formatter.dart | 37 +++++++++--------- .../test/sentry_logger_formatter_test.dart | 38 +++++++++---------- 6 files changed, 54 insertions(+), 41 deletions(-) diff --git a/packages/dart/lib/src/hub_adapter.dart b/packages/dart/lib/src/hub_adapter.dart index dc5080479f..7916a81ab1 100644 --- a/packages/dart/lib/src/hub_adapter.dart +++ b/packages/dart/lib/src/hub_adapter.dart @@ -199,4 +199,8 @@ class HubAdapter implements Hub { @override FutureOr captureLog(SentryLog log) => Sentry.currentHub.captureLog(log); + + @override + void setAttributes(Map attributes) => + Sentry.currentHub.setAttributes(attributes); } diff --git a/packages/dart/lib/src/noop_hub.dart b/packages/dart/lib/src/noop_hub.dart index e085e0eecd..07d46732f1 100644 --- a/packages/dart/lib/src/noop_hub.dart +++ b/packages/dart/lib/src/noop_hub.dart @@ -144,4 +144,7 @@ class NoOpHub implements Hub { @override Scope get scope => Scope(_options); + + @override + void setAttributes(Map attributes) {} } diff --git a/packages/dart/lib/src/scope.dart b/packages/dart/lib/src/scope.dart index 8f8f5ae353..68b72fb035 100644 --- a/packages/dart/lib/src/scope.dart +++ b/packages/dart/lib/src/scope.dart @@ -169,6 +169,8 @@ class Scope { final Map _attributes = {}; + Map get attributes => Map.unmodifiable(_attributes); + Scope(this._options); Breadcrumb? _addBreadCrumbSync(Breadcrumb breadcrumb, Hint hint) { @@ -225,7 +227,9 @@ class Scope { } void setAttributes(Map attributes) { - _attributes = attributes; + attributes.forEach((key, value) { + _attributes[key] = value; + }); } void addAttachment(SentryAttachment attachment) { diff --git a/packages/dart/lib/src/sentry_logger.dart b/packages/dart/lib/src/sentry_logger.dart index 1c88c46cca..b7ec7ea069 100644 --- a/packages/dart/lib/src/sentry_logger.dart +++ b/packages/dart/lib/src/sentry_logger.dart @@ -64,16 +64,19 @@ class SentryLogger { String body, { Map? attributes, }) { + final mergedAttributes = {} + ..addAll(_hub.scope.attributes) + ..addAll(attributes ?? {}); final log = SentryLog( timestamp: _clock(), level: level, body: body, - attributes: attributes ?? {}, + attributes: mergedAttributes, ); _hub.options.log( level.toSentryLevel(), - _formatLogMessage(level, body, attributes), + _formatLogMessage(level, body, mergedAttributes), logger: 'sentry_logger', ); diff --git a/packages/dart/lib/src/sentry_logger_formatter.dart b/packages/dart/lib/src/sentry_logger_formatter.dart index d0ddb820a4..0d9e489cf8 100644 --- a/packages/dart/lib/src/sentry_logger_formatter.dart +++ b/packages/dart/lib/src/sentry_logger_formatter.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/sentry_logger_formatter_test.dart b/packages/dart/test/sentry_logger_formatter_test.dart index af81acd019..32cbdfc44c 100644 --- a/packages/dart/test/sentry_logger_formatter_test.dart +++ b/packages/dart/test/sentry_logger_formatter_test.dart @@ -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); From 803f85bf45a9e62f084f1a84ed435603d18df1a1 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 14:52:29 +0100 Subject: [PATCH 03/25] Update --- packages/dart/lib/src/hub.dart | 12 +- packages/dart/lib/src/sentry.dart | 4 +- .../test/protocol/sentry_attribute_test.dart | 138 ++++++++++++++++++ .../protocol/sentry_log_attribute_test.dart | 42 ------ .../dart/test/protocol/sentry_log_test.dart | 26 +++- packages/dart/test/sentry_logger_test.dart | 81 ++++++++++ .../logging/lib/src/logging_integration.dart | 14 +- .../test/logging_integration_test.dart | 14 +- 8 files changed, 268 insertions(+), 63 deletions(-) create mode 100644 packages/dart/test/protocol/sentry_attribute_test.dart delete mode 100644 packages/dart/test/protocol/sentry_log_attribute_test.dart diff --git a/packages/dart/lib/src/hub.dart b/packages/dart/lib/src/hub.dart index 2f8a87903d..f6c8ce4b0a 100644 --- a/packages/dart/lib/src/hub.dart +++ b/packages/dart/lib/src/hub.dart @@ -341,7 +341,17 @@ class Hub { return scope; } - void setAttributes(Map attributes) {} + 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); + } + } /// Adds a breacrumb to the current Scope Future addBreadcrumb(Breadcrumb crumb, {Hint? hint}) async { diff --git a/packages/dart/lib/src/sentry.dart b/packages/dart/lib/src/sentry.dart index 8d8a32b949..c3cd076ed6 100644 --- a/packages/dart/lib/src/sentry.dart +++ b/packages/dart/lib/src/sentry.dart @@ -299,7 +299,9 @@ class Sentry { /// 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) {} + static void setAttributes(Map attributes) { + _hub.setAttributes(attributes); + } /// Configures the scope through the callback. static FutureOr configureScope(ScopeCallback callback) => diff --git a/packages/dart/test/protocol/sentry_attribute_test.dart b/packages/dart/test/protocol/sentry_attribute_test.dart new file mode 100644 index 0000000000..edf9fdf73a --- /dev/null +++ b/packages/dart/test/protocol/sentry_attribute_test.dart @@ -0,0 +1,138 @@ +import 'package:test/test.dart'; +import 'package:sentry/sentry.dart'; + +void main() { + test('$SentryAttribute string to json', () { + final attribute = SentryAttribute.string('test'); + final json = attribute.toJson(); + expect(json, { + 'value': 'test', + 'type': 'string', + }); + }); + + test('$SentryAttribute bool to json', () { + final attribute = SentryAttribute.bool(true); + final json = attribute.toJson(); + expect(json, { + 'value': true, + 'type': 'boolean', + }); + }); + + test('$SentryAttribute int to json', () { + final attribute = SentryAttribute.int(1); + final json = attribute.toJson(); + + expect(json, { + 'value': 1, + 'type': 'integer', + }); + }); + + test('$SentryAttribute double to json', () { + final attribute = SentryAttribute.double(1.0); + final json = attribute.toJson(); + + expect(json, { + 'value': 1.0, + 'type': 'double', + }); + }); + + test('$SentryAttribute units to json', () { + final cases = { + SentryUnit.milliseconds: 'ms', + SentryUnit.seconds: 's', + SentryUnit.bytes: 'bytes', + SentryUnit.count: 'count', + SentryUnit.percent: 'percent', + }; + + cases.forEach((unit, unitString) { + final attribute = SentryAttribute.int(1, unit: unit); + final json = attribute.toJson(); + expect(json, { + 'value': 1, + 'type': 'integer', + 'unit': unitString, + }); + }); + }); + + test('$SentryAttribute stringArr to json', () { + final attribute = SentryAttribute.stringArr(['a', 'b']); + final json = attribute.toJson(); + expect(json, { + 'value': ['a', 'b'], + 'type': 'string[]', + }); + }); + + test('$SentryAttribute stringArr with unit to json', () { + final attribute = + SentryAttribute.stringArr(['x', 'y'], unit: SentryUnit.count); + final json = attribute.toJson(); + expect(json, { + 'value': ['x', 'y'], + 'type': 'string[]', + 'unit': 'count', + }); + }); + + test('$SentryAttribute intArr to json', () { + final attribute = SentryAttribute.intArr([1, 2, 3]); + final json = attribute.toJson(); + expect(json, { + 'value': [1, 2, 3], + 'type': 'integer[]', + }); + }); + + test('$SentryAttribute intArr with unit to json', () { + final attribute = SentryAttribute.intArr([4, 5], unit: SentryUnit.count); + final json = attribute.toJson(); + expect(json, { + 'value': [4, 5], + 'type': 'integer[]', + 'unit': 'count', + }); + }); + + test('$SentryAttribute doubleArr to json', () { + final attribute = SentryAttribute.doubleArr([1.0, 2.5]); + final json = attribute.toJson(); + expect(json, { + 'value': [1.0, 2.5], + 'type': 'double[]', + }); + }); + + test('$SentryAttribute doubleArr with unit to json', () { + final attribute = + SentryAttribute.doubleArr([0.1, 0.2], unit: SentryUnit.seconds); + final json = attribute.toJson(); + expect(json, { + 'value': [0.1, 0.2], + 'type': 'double[]', + 'unit': 's', + }); + }); + + test('$SentryAttribute unit set after construction to json', () { + final attribute = SentryAttribute.double(2.5); + final jsonWithoutUnit = attribute.toJson(); + expect(jsonWithoutUnit, { + 'value': 2.5, + 'type': 'double', + }); + + attribute.unit = SentryUnit.bytes; + final jsonWithUnit = attribute.toJson(); + expect(jsonWithUnit, { + 'value': 2.5, + 'type': 'double', + 'unit': 'bytes', + }); + }); +} diff --git a/packages/dart/test/protocol/sentry_log_attribute_test.dart b/packages/dart/test/protocol/sentry_log_attribute_test.dart deleted file mode 100644 index 2c0fb7ce31..0000000000 --- a/packages/dart/test/protocol/sentry_log_attribute_test.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:test/test.dart'; -import 'package:sentry/sentry.dart'; - -void main() { - test('$SentryLogAttribute string to json', () { - final attribute = SentryLogAttribute.string('test'); - final json = attribute.toJson(); - expect(json, { - 'value': 'test', - 'type': 'string', - }); - }); - - test('$SentryLogAttribute bool to json', () { - final attribute = SentryLogAttribute.bool(true); - final json = attribute.toJson(); - expect(json, { - 'value': true, - 'type': 'boolean', - }); - }); - - test('$SentryLogAttribute int to json', () { - final attribute = SentryLogAttribute.int(1); - final json = attribute.toJson(); - - expect(json, { - 'value': 1, - 'type': 'integer', - }); - }); - - test('$SentryLogAttribute double to json', () { - final attribute = SentryLogAttribute.double(1.0); - final json = attribute.toJson(); - - expect(json, { - 'value': 1.0, - 'type': 'double', - }); - }); -} diff --git a/packages/dart/test/protocol/sentry_log_test.dart b/packages/dart/test/protocol/sentry_log_test.dart index 0921df7a32..2d1b280549 100644 --- a/packages/dart/test/protocol/sentry_log_test.dart +++ b/packages/dart/test/protocol/sentry_log_test.dart @@ -12,10 +12,14 @@ 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), + 'test5': SentryAttribute.intArr([1, 2, 3]), + 'test6': SentryAttribute.doubleArr([1.1, 2.2, 3.3]), + 'test7': SentryAttribute.stringArr(['a', 'b', 'c']), + 'test8': SentryAttribute.int(12, unit: SentryUnit.count), }, severityNumber: 1, ); @@ -44,6 +48,18 @@ void main() { 'value': 9000.1, 'type': 'double', }, + 'test5': { + 'value': [1, 2, 3], + 'type': 'integer[]', + }, + 'test6': { + 'value': [1.1, 2.2, 3.3], + 'type': 'double[]', + }, + 'test7': { + 'value': ['a', 'b', 'c'], + 'type': 'string[]', + }, }, 'severity_number': 1, }); @@ -56,7 +72,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/sentry_logger_test.dart b/packages/dart/test/sentry_logger_test.dart index 667a0d1d39..d365b12340 100644 --- a/packages/dart/test/sentry_logger_test.dart +++ b/packages/dart/test/sentry_logger_test.dart @@ -183,6 +183,87 @@ void main() { 'special values {"nan": NaN, "positive_infinity": Infinity, "negative_infinity": -Infinity}'); expect(logCall.logger, 'sentry_logger'); }); + + test('applies scope attributes to all log methods', () { + final logger = fixture.getSut(); + + final scopeAttributes = { + 'scopeString': SentryAttribute.string('fromScope'), + 'scopeInt': SentryAttribute.int(42), + }; + fixture.hub.scope.setAttributes(scopeAttributes); + + logger.trace('trace message'); + logger.debug('debug message'); + logger.info('info message'); + logger.warn('warn message'); + logger.error('error message'); + logger.fatal('fatal message'); + + expect(fixture.hub.captureLogCalls.length, 6); + for (final call in fixture.hub.captureLogCalls) { + final attrs = call.log.attributes; + expect(attrs['scopeString'], scopeAttributes['scopeString']); + expect(attrs['scopeInt'], scopeAttributes['scopeInt']); + } + }); + + test('per-log attributes override scope on same key', () { + final logger = fixture.getSut(); + + final scopeAttributes = { + 'overridden': SentryAttribute.string('fromScope'), + 'kept': SentryAttribute.bool(true), + }; + fixture.hub.scope.setAttributes(scopeAttributes); + + final overrideAttr = SentryAttribute.string('fromLog'); + logger.info('override test', attributes: { + 'overridden': overrideAttr, + 'logOnly': SentryAttribute.double(1.23), + }); + + expect(fixture.hub.captureLogCalls.length, 1); + final captured = fixture.hub.captureLogCalls[0].log; + // overridden key should come from per-log attributes + expect(captured.attributes['overridden'], overrideAttr); + // scope-only key should still be present + expect(captured.attributes['kept'], scopeAttributes['kept']); + // log-only key should be present + expect(captured.attributes['logOnly']?.type, 'double'); + }); + + test('formatter path merges template, per-log and scope attributes', () { + final logger = fixture.getSut(); + + final scopeAttributes = { + 'scopeOnly': SentryAttribute.string('present'), + }; + fixture.hub.scope.setAttributes(scopeAttributes); + + logger.fmt.info( + 'Hello, %s!', + ['World'], + attributes: {'callOnly': SentryAttribute.string('present')}, + ); + + expect(fixture.hub.captureLogCalls.length, 1); + final captured = fixture.hub.captureLogCalls[0].log; + final attrs = captured.attributes; + + // template attributes + expect(attrs['sentry.message.template']?.type, 'string'); + expect(attrs['sentry.message.template']?.value, 'Hello, %s!'); + expect(attrs['sentry.message.parameter.0']?.type, 'string'); + expect(attrs['sentry.message.parameter.0']?.value, 'World'); + + // per-log attribute + expect(attrs['callOnly']?.type, 'string'); + expect(attrs['callOnly']?.value, 'present'); + + // scope attribute + expect(attrs['scopeOnly'], scopeAttributes['scopeOnly']); + }); } class Fixture { diff --git a/packages/logging/lib/src/logging_integration.dart b/packages/logging/lib/src/logging_integration.dart index e4bb44f550..5527610a18 100644 --- a/packages/logging/lib/src/logging_integration.dart +++ b/packages/logging/lib/src/logging_integration.dart @@ -39,9 +39,9 @@ class LoggingIntegration implements Integration { Level minBreadcrumbLevel = Level.INFO, Level minEventLevel = Level.SEVERE, Level minSentryLogLevel = Level.INFO, - }) : _minBreadcrumbLevel = minBreadcrumbLevel, - _minEventLevel = minEventLevel, - _minSentryLogLevel = minSentryLogLevel; + }) : _minBreadcrumbLevel = minBreadcrumbLevel, + _minEventLevel = minEventLevel, + _minSentryLogLevel = minSentryLogLevel; final Level _minBreadcrumbLevel; final Level _minEventLevel; @@ -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); } From 58789a80d4a87f338717966893d55883eb30bb2c Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 14:56:43 +0100 Subject: [PATCH 04/25] Add removeAttribute --- packages/dart/lib/src/hub.dart | 12 ++++++++++++ packages/dart/lib/src/hub_adapter.dart | 3 +++ packages/dart/lib/src/noop_hub.dart | 3 +++ packages/dart/lib/src/scope.dart | 4 ++++ packages/dart/lib/src/sentry.dart | 5 +++++ 5 files changed, 27 insertions(+) diff --git a/packages/dart/lib/src/hub.dart b/packages/dart/lib/src/hub.dart index f6c8ce4b0a..056238e98d 100644 --- a/packages/dart/lib/src/hub.dart +++ b/packages/dart/lib/src/hub.dart @@ -353,6 +353,18 @@ class Hub { } } + void removeAttribute(String key) { + if (!_isEnabled) { + _options.log( + SentryLevel.warning, + "Instance is disabled and this 'setAttributes' call is a no-op.", + ); + } else { + final item = _peek(); + item.scope.removeAttribute(key); + } + } + /// Adds a breacrumb to the current Scope Future addBreadcrumb(Breadcrumb crumb, {Hint? hint}) async { if (!_isEnabled) { diff --git a/packages/dart/lib/src/hub_adapter.dart b/packages/dart/lib/src/hub_adapter.dart index 7916a81ab1..6491f2a951 100644 --- a/packages/dart/lib/src/hub_adapter.dart +++ b/packages/dart/lib/src/hub_adapter.dart @@ -203,4 +203,7 @@ class HubAdapter implements Hub { @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/noop_hub.dart b/packages/dart/lib/src/noop_hub.dart index 07d46732f1..bb486b9e80 100644 --- a/packages/dart/lib/src/noop_hub.dart +++ b/packages/dart/lib/src/noop_hub.dart @@ -147,4 +147,7 @@ class NoOpHub implements Hub { @override void setAttributes(Map attributes) {} + + @override + void removeAttribute(String key) {} } diff --git a/packages/dart/lib/src/scope.dart b/packages/dart/lib/src/scope.dart index 68b72fb035..e38b1b94e3 100644 --- a/packages/dart/lib/src/scope.dart +++ b/packages/dart/lib/src/scope.dart @@ -232,6 +232,10 @@ class Scope { }); } + void removeAttribute(String key) { + _attributes.remove(key); + } + void addAttachment(SentryAttachment attachment) { _attachments.add(attachment); } diff --git a/packages/dart/lib/src/sentry.dart b/packages/dart/lib/src/sentry.dart index c3cd076ed6..f65bc721b1 100644 --- a/packages/dart/lib/src/sentry.dart +++ b/packages/dart/lib/src/sentry.dart @@ -303,6 +303,11 @@ class Sentry { _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); From ad4bef19ecf31ded3a4fa752c992fd34aab694ad Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 15:04:01 +0100 Subject: [PATCH 05/25] Add scope test --- packages/dart/test/scope_test.dart | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/packages/dart/test/scope_test.dart b/packages/dart/test/scope_test.dart index a7824baac5..aa3bb275b0 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(); From c773c5fb226a2c9bec65c2e9839b2a0f17d7691d Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 15:07:22 +0100 Subject: [PATCH 06/25] Update --- packages/dart/test/protocol/sentry_log_test.dart | 5 +++++ packages/dart/test/sentry_logger_test.dart | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/dart/test/protocol/sentry_log_test.dart b/packages/dart/test/protocol/sentry_log_test.dart index 2d1b280549..5704cb9e6a 100644 --- a/packages/dart/test/protocol/sentry_log_test.dart +++ b/packages/dart/test/protocol/sentry_log_test.dart @@ -60,6 +60,11 @@ void main() { 'value': ['a', 'b', 'c'], 'type': 'string[]', }, + 'test8': { + 'value': 12, + 'type': 'integer', + 'unit': 'count', + }, }, 'severity_number': 1, }); diff --git a/packages/dart/test/sentry_logger_test.dart b/packages/dart/test/sentry_logger_test.dart index d365b12340..4463909624 100644 --- a/packages/dart/test/sentry_logger_test.dart +++ b/packages/dart/test/sentry_logger_test.dart @@ -188,7 +188,7 @@ void main() { final logger = fixture.getSut(); final scopeAttributes = { - 'scopeString': SentryAttribute.string('fromScope'), + 'scopeString': SentryAttribute.string('fromScope'),\ 'scopeInt': SentryAttribute.int(42), }; fixture.hub.scope.setAttributes(scopeAttributes); From 58d3d2947356db5335fa9ba7477aeab682ca08c7 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 15:07:33 +0100 Subject: [PATCH 07/25] Update --- packages/dart/test/sentry_logger_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dart/test/sentry_logger_test.dart b/packages/dart/test/sentry_logger_test.dart index 4463909624..d365b12340 100644 --- a/packages/dart/test/sentry_logger_test.dart +++ b/packages/dart/test/sentry_logger_test.dart @@ -188,7 +188,7 @@ void main() { final logger = fixture.getSut(); final scopeAttributes = { - 'scopeString': SentryAttribute.string('fromScope'),\ + 'scopeString': SentryAttribute.string('fromScope'), 'scopeInt': SentryAttribute.int(42), }; fixture.hub.scope.setAttributes(scopeAttributes); From e26d83a0c1471b0df1dfdd717202125f76f2f5ce Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 15:12:21 +0100 Subject: [PATCH 08/25] Update --- .../dart/lib/src/logs_enricher_integration.dart | 4 ++-- packages/dart/lib/src/sentry_client.dart | 16 ++++++++-------- packages/dart/test/hub_test.dart | 2 +- .../dart/test/logs_enricher_interation_test.dart | 2 +- .../dart/test/protocol/rate_limiter_test.dart | 2 +- .../dart/test/sentry_client_lifecycle_test.dart | 5 ++--- .../test/sentry_client_sdk_lifecycle_test.dart | 5 ++--- packages/dart/test/sentry_client_test.dart | 4 ++-- .../dart/test/sentry_envelope_item_test.dart | 4 ++-- packages/dart/test/sentry_envelope_test.dart | 4 ++-- 10 files changed, 23 insertions(+), 25 deletions(-) diff --git a/packages/dart/lib/src/logs_enricher_integration.dart b/packages/dart/lib/src/logs_enricher_integration.dart index ba9f0479d1..e5ea6ff350 100644 --- a/packages/dart/lib/src/logs_enricher_integration.dart +++ b/packages/dart/lib/src/logs_enricher_integration.dart @@ -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/sentry_client.dart b/packages/dart/lib/src/sentry_client.dart index fc2ec9a092..c9d82ad681 100644 --- a/packages/dart/lib/src/sentry_client.dart +++ b/packages/dart/lib/src/sentry_client.dart @@ -502,21 +502,21 @@ class SentryClient { return; } - log.attributes['sentry.sdk.name'] = SentryLogAttribute.string( + 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 +527,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 +537,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/test/hub_test.dart b/packages/dart/test/hub_test.dart index eec9bded6d..d75918589a 100644 --- a/packages/dart/test/hub_test.dart +++ b/packages/dart/test/hub_test.dart @@ -853,7 +853,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_interation_test.dart index 78eda1b6a4..1750ec717a 100644 --- a/packages/dart/test/logs_enricher_interation_test.dart +++ b/packages/dart/test/logs_enricher_interation_test.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/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..c3b8a07954 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'), }, ); } @@ -2012,7 +2012,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), }, ), ]; From 2cf1cdb5a5a00f7a2175c46dba4d47cce1fad216 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 15:13:06 +0100 Subject: [PATCH 09/25] Update --- packages/logging/lib/src/logging_integration.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/logging/lib/src/logging_integration.dart b/packages/logging/lib/src/logging_integration.dart index 5527610a18..948faf1df4 100644 --- a/packages/logging/lib/src/logging_integration.dart +++ b/packages/logging/lib/src/logging_integration.dart @@ -39,9 +39,9 @@ class LoggingIntegration implements Integration { Level minBreadcrumbLevel = Level.INFO, Level minEventLevel = Level.SEVERE, Level minSentryLogLevel = Level.INFO, - }) : _minBreadcrumbLevel = minBreadcrumbLevel, - _minEventLevel = minEventLevel, - _minSentryLogLevel = minSentryLogLevel; + }) : _minBreadcrumbLevel = minBreadcrumbLevel, + _minEventLevel = minEventLevel, + _minSentryLogLevel = minSentryLogLevel; final Level _minBreadcrumbLevel; final Level _minEventLevel; From 1ad1c46728a580c81c48ce64c8d80c305981295a Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 15:26:59 +0100 Subject: [PATCH 10/25] Update --- packages/dart/lib/src/scope.dart | 4 ++++ packages/dart/test/hub_test.dart | 35 ++++++++++++++++++++++++++++++ packages/dart/test/scope_test.dart | 29 +++++++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/packages/dart/lib/src/scope.dart b/packages/dart/lib/src/scope.dart index e38b1b94e3..e97aca011c 100644 --- a/packages/dart/lib/src/scope.dart +++ b/packages/dart/lib/src/scope.dart @@ -475,6 +475,10 @@ class Scope { clone.addAttachment(attachment); } + if (_attributes.isNotEmpty) { + clone.setAttributes(Map.from(attributes)); + } + return clone; } diff --git a/packages/dart/test/hub_test.dart b/packages/dart/test/hub_test.dart index d75918589a..eaa1045154 100644 --- a/packages/dart/test/hub_test.dart +++ b/packages/dart/test/hub_test.dart @@ -718,6 +718,41 @@ 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()); diff --git a/packages/dart/test/scope_test.dart b/packages/dart/test/scope_test.dart index aa3bb275b0..a618006a98 100644 --- a/packages/dart/test/scope_test.dart +++ b/packages/dart/test/scope_test.dart @@ -425,6 +425,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); From 6a6fab2107e28d78ad7e1d8bc6244c49c53abd40 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 15:36:07 +0100 Subject: [PATCH 11/25] Update --- packages/dart/lib/src/logs_enricher_integration.dart | 2 +- packages/dart/lib/src/protocol.dart | 2 +- .../{sentry_log_attribute.dart => sentry_attribute.dart} | 0 packages/dart/lib/src/protocol/sentry_log.dart | 2 +- packages/dart/lib/src/sentry_logger.dart | 2 +- packages/dart/lib/src/sentry_logger_formatter.dart | 3 +-- packages/dart/test/protocol/sentry_attribute_test.dart | 5 +++++ 7 files changed, 10 insertions(+), 6 deletions(-) rename packages/dart/lib/src/protocol/{sentry_log_attribute.dart => sentry_attribute.dart} (100%) diff --git a/packages/dart/lib/src/logs_enricher_integration.dart b/packages/dart/lib/src/logs_enricher_integration.dart index e5ea6ff350..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 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_log_attribute.dart b/packages/dart/lib/src/protocol/sentry_attribute.dart similarity index 100% rename from packages/dart/lib/src/protocol/sentry_log_attribute.dart rename to packages/dart/lib/src/protocol/sentry_attribute.dart diff --git a/packages/dart/lib/src/protocol/sentry_log.dart b/packages/dart/lib/src/protocol/sentry_log.dart index 550834e783..6612b8d700 100644 --- a/packages/dart/lib/src/protocol/sentry_log.dart +++ b/packages/dart/lib/src/protocol/sentry_log.dart @@ -1,6 +1,6 @@ +import 'sentry_attribute.dart'; import 'sentry_id.dart'; import 'sentry_log_level.dart'; -import 'sentry_log_attribute.dart'; class SentryLog { DateTime timestamp; diff --git a/packages/dart/lib/src/sentry_logger.dart b/packages/dart/lib/src/sentry_logger.dart index b7ec7ea069..14178d5ae3 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'; diff --git a/packages/dart/lib/src/sentry_logger_formatter.dart b/packages/dart/lib/src/sentry_logger_formatter.dart index 0d9e489cf8..9241640e27 100644 --- a/packages/dart/lib/src/sentry_logger_formatter.dart +++ b/packages/dart/lib/src/sentry_logger_formatter.dart @@ -1,7 +1,6 @@ import 'dart:async'; -import 'protocol/sentry_log_attribute.dart'; +import 'protocol/sentry_attribute.dart'; import 'sentry_template_string.dart'; -import 'sentry_logger.dart'; class SentryLoggerFormatter { SentryLoggerFormatter(this._logger); diff --git a/packages/dart/test/protocol/sentry_attribute_test.dart b/packages/dart/test/protocol/sentry_attribute_test.dart index edf9fdf73a..4b2e922d34 100644 --- a/packages/dart/test/protocol/sentry_attribute_test.dart +++ b/packages/dart/test/protocol/sentry_attribute_test.dart @@ -135,4 +135,9 @@ void main() { 'unit': 'bytes', }); }); + + test('$SentryUnit asString correctly maps', () { + final strings = SentryUnit.values.map((e) => e.asString); + expect(strings, ['ms', 's', 'bytes', 'count', 'percent']); + }); } From 58126a60f30255c82a43682e3e10054ac87006e9 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 15:38:05 +0100 Subject: [PATCH 12/25] Update --- packages/dart/lib/src/sentry_logger_formatter.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/dart/lib/src/sentry_logger_formatter.dart b/packages/dart/lib/src/sentry_logger_formatter.dart index 9241640e27..aff7d21e51 100644 --- a/packages/dart/lib/src/sentry_logger_formatter.dart +++ b/packages/dart/lib/src/sentry_logger_formatter.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'protocol/sentry_attribute.dart'; import 'sentry_template_string.dart'; +import 'sentry_logger.dart'; class SentryLoggerFormatter { SentryLoggerFormatter(this._logger); From cbd6718385864ab5f09e83bf7a585e762841f7bd Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 16:41:14 +0100 Subject: [PATCH 13/25] Update --- packages/dart/lib/src/hub.dart | 2 +- packages/dart/lib/src/sentry_log_batcher.dart | 1 + packages/dart/test/logs_enricher_interation_test.dart | 2 +- packages/dart/test/sentry_logger_formatter_test.dart | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/dart/lib/src/hub.dart b/packages/dart/lib/src/hub.dart index 056238e98d..be74f34db9 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(); diff --git a/packages/dart/lib/src/sentry_log_batcher.dart b/packages/dart/lib/src/sentry_log_batcher.dart index 4c5867fc16..eb2795fc10 100644 --- a/packages/dart/lib/src/sentry_log_batcher.dart +++ b/packages/dart/lib/src/sentry_log_batcher.dart @@ -29,6 +29,7 @@ class SentryLogBatcher { /// Adds a log to the buffer. void addLog(SentryLog log) { try { + print('log added: ${log.toJson()}'); final encodedLog = utf8JsonEncoder.convert(log.toJson()); _encodedLogs.add(encodedLog); diff --git a/packages/dart/test/logs_enricher_interation_test.dart b/packages/dart/test/logs_enricher_interation_test.dart index 1750ec717a..38644c1c74 100644 --- a/packages/dart/test/logs_enricher_interation_test.dart +++ b/packages/dart/test/logs_enricher_interation_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'; diff --git a/packages/dart/test/sentry_logger_formatter_test.dart b/packages/dart/test/sentry_logger_formatter_test.dart index 32cbdfc44c..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(); From ba81f2fd94f73af863508c06478490da508bad53 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 16:42:02 +0100 Subject: [PATCH 14/25] Update --- ...r_interation_test.dart => logs_enricher_integration_test.dart} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/dart/test/{logs_enricher_interation_test.dart => logs_enricher_integration_test.dart} (100%) diff --git a/packages/dart/test/logs_enricher_interation_test.dart b/packages/dart/test/logs_enricher_integration_test.dart similarity index 100% rename from packages/dart/test/logs_enricher_interation_test.dart rename to packages/dart/test/logs_enricher_integration_test.dart From 3b83bd136ec2d812b448a06c5498c3a7ce09355b Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 18 Nov 2025 16:48:45 +0100 Subject: [PATCH 15/25] Update --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31992e0034..42f3388858 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. + ### Enhancements - Flush logs if client/hub/sdk is closed ([#3335](https://github.com/getsentry/sentry-dart/pull/3335) From 968e8d1781e2cc46be8f353f547938384e3f5302 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Wed, 19 Nov 2025 13:22:06 +0100 Subject: [PATCH 16/25] Update --- .../lib/src/protocol/sentry_attribute.dart | 55 ++-------- packages/dart/lib/src/scope.dart | 2 +- packages/dart/lib/src/sentry_log_batcher.dart | 1 - .../test/protocol/sentry_attribute_test.dart | 101 ------------------ .../dart/test/protocol/sentry_log_test.dart | 21 ---- packages/dart/test/sentry_client_test.dart | 15 +++ packages/dart/test/sentry_logger_test.dart | 2 + 7 files changed, 29 insertions(+), 168 deletions(-) diff --git a/packages/dart/lib/src/protocol/sentry_attribute.dart b/packages/dart/lib/src/protocol/sentry_attribute.dart index 34ab63f156..fe98688774 100644 --- a/packages/dart/lib/src/protocol/sentry_attribute.dart +++ b/packages/dart/lib/src/protocol/sentry_attribute.dart @@ -22,65 +22,32 @@ class SentryLogAttribute extends SentryAttribute { } class SentryAttribute { - final String type; + final String _type; final dynamic value; - SentryUnit? unit; @internal - SentryAttribute(this.value, this.type, {this.unit}); + SentryAttribute(this.value, this._type); - factory SentryAttribute.string(String value, {SentryUnit? unit}) { - return SentryAttribute(value, 'string', unit: unit); + factory SentryAttribute.string(String value) { + return SentryAttribute(value, 'string'); } - factory SentryAttribute.bool(bool value, {SentryUnit? unit}) { - return SentryAttribute(value, 'boolean', unit: unit); + factory SentryAttribute.bool(bool value) { + return SentryAttribute(value, 'boolean'); } - factory SentryAttribute.int(int value, {SentryUnit? unit}) { - return SentryAttribute(value, 'integer', unit: unit); + factory SentryAttribute.int(int value) { + return SentryAttribute(value, 'integer'); } - factory SentryAttribute.double(double value, {SentryUnit? unit}) { - return SentryAttribute(value, 'double', unit: unit); - } - - factory SentryAttribute.stringArr(List value, {SentryUnit? unit}) { - return SentryAttribute(value, 'string[]', unit: unit); - } - - factory SentryAttribute.intArr(List value, {SentryUnit? unit}) { - return SentryAttribute(value, 'integer[]', unit: unit); - } - - factory SentryAttribute.doubleArr(List value, {SentryUnit? unit}) { - return SentryAttribute(value, 'double[]', unit: unit); + factory SentryAttribute.double(double value) { + return SentryAttribute(value, 'double'); } Map toJson() { return { 'value': value, - 'type': type, - if (unit != null) 'unit': unit!.asString, + 'type': _type, }; } } - -enum SentryUnit { milliseconds, seconds, bytes, count, percent } - -extension SentryUnitExtension on SentryUnit { - String get asString { - switch (this) { - case SentryUnit.milliseconds: - return "ms"; - case SentryUnit.seconds: - return "s"; - case SentryUnit.bytes: - return "bytes"; - case SentryUnit.count: - return "count"; - case SentryUnit.percent: - return "percent"; - } - } -} diff --git a/packages/dart/lib/src/scope.dart b/packages/dart/lib/src/scope.dart index e97aca011c..c14026da5b 100644 --- a/packages/dart/lib/src/scope.dart +++ b/packages/dart/lib/src/scope.dart @@ -476,7 +476,7 @@ class Scope { } if (_attributes.isNotEmpty) { - clone.setAttributes(Map.from(attributes)); + clone.setAttributes(Map.from(_attributes)); } return clone; diff --git a/packages/dart/lib/src/sentry_log_batcher.dart b/packages/dart/lib/src/sentry_log_batcher.dart index eb2795fc10..4c5867fc16 100644 --- a/packages/dart/lib/src/sentry_log_batcher.dart +++ b/packages/dart/lib/src/sentry_log_batcher.dart @@ -29,7 +29,6 @@ class SentryLogBatcher { /// Adds a log to the buffer. void addLog(SentryLog log) { try { - print('log added: ${log.toJson()}'); final encodedLog = utf8JsonEncoder.convert(log.toJson()); _encodedLogs.add(encodedLog); diff --git a/packages/dart/test/protocol/sentry_attribute_test.dart b/packages/dart/test/protocol/sentry_attribute_test.dart index 4b2e922d34..30d67eed5b 100644 --- a/packages/dart/test/protocol/sentry_attribute_test.dart +++ b/packages/dart/test/protocol/sentry_attribute_test.dart @@ -39,105 +39,4 @@ void main() { 'type': 'double', }); }); - - test('$SentryAttribute units to json', () { - final cases = { - SentryUnit.milliseconds: 'ms', - SentryUnit.seconds: 's', - SentryUnit.bytes: 'bytes', - SentryUnit.count: 'count', - SentryUnit.percent: 'percent', - }; - - cases.forEach((unit, unitString) { - final attribute = SentryAttribute.int(1, unit: unit); - final json = attribute.toJson(); - expect(json, { - 'value': 1, - 'type': 'integer', - 'unit': unitString, - }); - }); - }); - - test('$SentryAttribute stringArr to json', () { - final attribute = SentryAttribute.stringArr(['a', 'b']); - final json = attribute.toJson(); - expect(json, { - 'value': ['a', 'b'], - 'type': 'string[]', - }); - }); - - test('$SentryAttribute stringArr with unit to json', () { - final attribute = - SentryAttribute.stringArr(['x', 'y'], unit: SentryUnit.count); - final json = attribute.toJson(); - expect(json, { - 'value': ['x', 'y'], - 'type': 'string[]', - 'unit': 'count', - }); - }); - - test('$SentryAttribute intArr to json', () { - final attribute = SentryAttribute.intArr([1, 2, 3]); - final json = attribute.toJson(); - expect(json, { - 'value': [1, 2, 3], - 'type': 'integer[]', - }); - }); - - test('$SentryAttribute intArr with unit to json', () { - final attribute = SentryAttribute.intArr([4, 5], unit: SentryUnit.count); - final json = attribute.toJson(); - expect(json, { - 'value': [4, 5], - 'type': 'integer[]', - 'unit': 'count', - }); - }); - - test('$SentryAttribute doubleArr to json', () { - final attribute = SentryAttribute.doubleArr([1.0, 2.5]); - final json = attribute.toJson(); - expect(json, { - 'value': [1.0, 2.5], - 'type': 'double[]', - }); - }); - - test('$SentryAttribute doubleArr with unit to json', () { - final attribute = - SentryAttribute.doubleArr([0.1, 0.2], unit: SentryUnit.seconds); - final json = attribute.toJson(); - expect(json, { - 'value': [0.1, 0.2], - 'type': 'double[]', - 'unit': 's', - }); - }); - - test('$SentryAttribute unit set after construction to json', () { - final attribute = SentryAttribute.double(2.5); - final jsonWithoutUnit = attribute.toJson(); - expect(jsonWithoutUnit, { - 'value': 2.5, - 'type': 'double', - }); - - attribute.unit = SentryUnit.bytes; - final jsonWithUnit = attribute.toJson(); - expect(jsonWithUnit, { - 'value': 2.5, - 'type': 'double', - 'unit': 'bytes', - }); - }); - - test('$SentryUnit asString correctly maps', () { - final strings = SentryUnit.values.map((e) => e.asString); - expect(strings, ['ms', 's', 'bytes', 'count', 'percent']); - }); } diff --git a/packages/dart/test/protocol/sentry_log_test.dart b/packages/dart/test/protocol/sentry_log_test.dart index 5704cb9e6a..b138e5340a 100644 --- a/packages/dart/test/protocol/sentry_log_test.dart +++ b/packages/dart/test/protocol/sentry_log_test.dart @@ -16,10 +16,6 @@ void main() { 'test2': SentryAttribute.bool(true), 'test3': SentryAttribute.int(9001), 'test4': SentryAttribute.double(9000.1), - 'test5': SentryAttribute.intArr([1, 2, 3]), - 'test6': SentryAttribute.doubleArr([1.1, 2.2, 3.3]), - 'test7': SentryAttribute.stringArr(['a', 'b', 'c']), - 'test8': SentryAttribute.int(12, unit: SentryUnit.count), }, severityNumber: 1, ); @@ -48,23 +44,6 @@ void main() { 'value': 9000.1, 'type': 'double', }, - 'test5': { - 'value': [1, 2, 3], - 'type': 'integer[]', - }, - 'test6': { - 'value': [1.1, 2.2, 3.3], - 'type': 'double[]', - }, - 'test7': { - 'value': ['a', 'b', 'c'], - 'type': 'string[]', - }, - 'test8': { - 'value': 12, - 'type': 'integer', - 'unit': 'count', - }, }, 'severity_number': 1, }); diff --git a/packages/dart/test/sentry_client_test.dart b/packages/dart/test/sentry_client_test.dart index c3b8a07954..7c7f7daeb9 100644 --- a/packages/dart/test/sentry_client_test.dart +++ b/packages/dart/test/sentry_client_test.dart @@ -1839,6 +1839,21 @@ void main() { ); }); + test('should use attributes from given scope', () async { + fixture.options.logBatcher = MockLogBatcher(); + final scope = Scope(fixture.options); + scope.setAttributes({'from_scope': SentryAttribute.int(12)}); + final client = fixture.getSut(); + final log = givenLog(); + + 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('should add user info to attributes', () async { fixture.options.enableLogs = true; diff --git a/packages/dart/test/sentry_logger_test.dart b/packages/dart/test/sentry_logger_test.dart index d365b12340..3e97d2836a 100644 --- a/packages/dart/test/sentry_logger_test.dart +++ b/packages/dart/test/sentry_logger_test.dart @@ -263,6 +263,8 @@ void main() { // scope attribute expect(attrs['scopeOnly'], scopeAttributes['scopeOnly']); + + // TODO logger.fmt }); } From 1e8dbbc148b0623861a81d15c1d8a82c45a7f6e6 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Wed, 19 Nov 2025 13:27:47 +0100 Subject: [PATCH 17/25] Update --- .../lib/src/protocol/sentry_attribute.dart | 7 ++++--- packages/dart/test/sentry_logger_test.dart | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/dart/lib/src/protocol/sentry_attribute.dart b/packages/dart/lib/src/protocol/sentry_attribute.dart index fe98688774..335d36ecdb 100644 --- a/packages/dart/lib/src/protocol/sentry_attribute.dart +++ b/packages/dart/lib/src/protocol/sentry_attribute.dart @@ -22,11 +22,12 @@ class SentryLogAttribute extends SentryAttribute { } class SentryAttribute { - final String _type; + @internal + final String type; final dynamic value; @internal - SentryAttribute(this.value, this._type); + SentryAttribute(this.value, this.type); factory SentryAttribute.string(String value) { return SentryAttribute(value, 'string'); @@ -47,7 +48,7 @@ class SentryAttribute { Map toJson() { return { 'value': value, - 'type': _type, + 'type': type, }; } } diff --git a/packages/dart/test/sentry_logger_test.dart b/packages/dart/test/sentry_logger_test.dart index 3e97d2836a..1e0e90830a 100644 --- a/packages/dart/test/sentry_logger_test.dart +++ b/packages/dart/test/sentry_logger_test.dart @@ -263,8 +263,24 @@ void main() { // scope attribute expect(attrs['scopeOnly'], scopeAttributes['scopeOnly']); + }); + + // 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'), + }, + ); - // TODO logger.fmt + final attrs = fixture.hub.captureLogCalls[0].log.attributes; + expect(attrs['sentry.message.template']?.value, 'OVERRIDE'); + expect(attrs['sentry.message.parameter.0']?.value, 'Earth'); }); } From 99a1dc12ac47008f27d6ea76fb727cc619117316 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Wed, 19 Nov 2025 13:28:13 +0100 Subject: [PATCH 18/25] Update --- packages/dart/test/hub_test.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/dart/test/hub_test.dart b/packages/dart/test/hub_test.dart index eaa1045154..221cb91e88 100644 --- a/packages/dart/test/hub_test.dart +++ b/packages/dart/test/hub_test.dart @@ -718,7 +718,8 @@ void main() { fixture = Fixture(); }); - test('withScope can override scope attributes for that call only', () async { + test('withScope can override scope attributes for that call only', + () async { final hub = fixture.getSut(); hub.setAttributes({ 'overridden': SentryAttribute.string('global'), From 9a99d54d2945440f031088ccdeede65a7ec35c7b Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Wed, 19 Nov 2025 13:28:55 +0100 Subject: [PATCH 19/25] Update --- packages/dart/lib/src/hub.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dart/lib/src/hub.dart b/packages/dart/lib/src/hub.dart index be74f34db9..beada8eaad 100644 --- a/packages/dart/lib/src/hub.dart +++ b/packages/dart/lib/src/hub.dart @@ -357,7 +357,7 @@ class Hub { if (!_isEnabled) { _options.log( SentryLevel.warning, - "Instance is disabled and this 'setAttributes' call is a no-op.", + "Instance is disabled and this 'removeAttribute' call is a no-op.", ); } else { final item = _peek(); From f33d4f9dad6de3ec593d12d77fd45de1de8eb47c Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Wed, 19 Nov 2025 14:06:27 +0100 Subject: [PATCH 20/25] Update --- packages/dart/lib/src/hub.dart | 2 +- packages/dart/test/hub_test.dart | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/dart/lib/src/hub.dart b/packages/dart/lib/src/hub.dart index beada8eaad..5346e2cd69 100644 --- a/packages/dart/lib/src/hub.dart +++ b/packages/dart/lib/src/hub.dart @@ -365,7 +365,7 @@ class Hub { } } - /// Adds a breacrumb to the current Scope + /// Adds a breadcrumb to the current Scope Future addBreadcrumb(Breadcrumb crumb, {Hint? hint}) async { if (!_isEnabled) { _options.log( diff --git a/packages/dart/test/hub_test.dart b/packages/dart/test/hub_test.dart index 221cb91e88..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)); From 224eab16548167f8b399fc82b9978dd923de6315 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Wed, 19 Nov 2025 14:41:09 +0100 Subject: [PATCH 21/25] Update --- packages/dart/lib/src/scope.dart | 1 + packages/dart/test/scope_test.dart | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/dart/lib/src/scope.dart b/packages/dart/lib/src/scope.dart index c14026da5b..6024022210 100644 --- a/packages/dart/lib/src/scope.dart +++ b/packages/dart/lib/src/scope.dart @@ -272,6 +272,7 @@ class Scope { _eventProcessors.clear(); _replayId = null; propagationContext = PropagationContext(); + _attributes.clear(); _clearBreadcrumbsSync(); _setUserSync(null); diff --git a/packages/dart/test/scope_test.dart b/packages/dart/test/scope_test.dart index a618006a98..44683f1c50 100644 --- a/packages/dart/test/scope_test.dart +++ b/packages/dart/test/scope_test.dart @@ -389,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 { From ad95fde7adb49a58f947724479502f7faf07d85f Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Thu, 20 Nov 2025 10:47:09 +0100 Subject: [PATCH 22/25] Merge scope --- packages/dart/lib/src/sentry_client.dart | 4 ++++ packages/dart/test/sentry_client_test.dart | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/dart/lib/src/sentry_client.dart b/packages/dart/lib/src/sentry_client.dart index c9d82ad681..ca5fba8c92 100644 --- a/packages/dart/lib/src/sentry_client.dart +++ b/packages/dart/lib/src/sentry_client.dart @@ -502,6 +502,10 @@ class SentryClient { return; } + if (scope != null) { + log.attributes.addAll(scope.attributes); + } + log.attributes['sentry.sdk.name'] = SentryAttribute.string( _options.sdk.name, ); diff --git a/packages/dart/test/sentry_client_test.dart b/packages/dart/test/sentry_client_test.dart index 7c7f7daeb9..5e4059c0ed 100644 --- a/packages/dart/test/sentry_client_test.dart +++ b/packages/dart/test/sentry_client_test.dart @@ -1840,11 +1840,14 @@ 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)}); - final client = fixture.getSut(); - final log = givenLog(); await client.captureLog(log, scope: scope); From 2e39532162fd719421ec55b4a1695c3c946e8ac9 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Thu, 20 Nov 2025 12:11:14 +0100 Subject: [PATCH 23/25] Update --- packages/dart/lib/src/sentry_client.dart | 3 +- packages/dart/lib/src/sentry_logger.dart | 7 +- packages/dart/test/sentry_client_test.dart | 27 ++++++++ packages/dart/test/sentry_logger_test.dart | 81 ---------------------- 4 files changed, 31 insertions(+), 87 deletions(-) diff --git a/packages/dart/lib/src/sentry_client.dart b/packages/dart/lib/src/sentry_client.dart index ca5fba8c92..b53eb998ac 100644 --- a/packages/dart/lib/src/sentry_client.dart +++ b/packages/dart/lib/src/sentry_client.dart @@ -503,7 +503,8 @@ class SentryClient { } if (scope != null) { - log.attributes.addAll(scope.attributes); + final merged = Map.of(scope.attributes)..addAll(log.attributes); + log.attributes = merged; } log.attributes['sentry.sdk.name'] = SentryAttribute.string( diff --git a/packages/dart/lib/src/sentry_logger.dart b/packages/dart/lib/src/sentry_logger.dart index 14178d5ae3..1d324e0678 100644 --- a/packages/dart/lib/src/sentry_logger.dart +++ b/packages/dart/lib/src/sentry_logger.dart @@ -64,19 +64,16 @@ class SentryLogger { String body, { Map? attributes, }) { - final mergedAttributes = {} - ..addAll(_hub.scope.attributes) - ..addAll(attributes ?? {}); final log = SentryLog( timestamp: _clock(), level: level, body: body, - attributes: mergedAttributes, + attributes: attributes ?? {}, ); _hub.options.log( level.toSentryLevel(), - _formatLogMessage(level, body, mergedAttributes), + _formatLogMessage(level, body, attributes ?? {}), logger: 'sentry_logger', ); diff --git a/packages/dart/test/sentry_client_test.dart b/packages/dart/test/sentry_client_test.dart index 5e4059c0ed..534f665774 100644 --- a/packages/dart/test/sentry_client_test.dart +++ b/packages/dart/test/sentry_client_test.dart @@ -1857,6 +1857,33 @@ void main() { 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; diff --git a/packages/dart/test/sentry_logger_test.dart b/packages/dart/test/sentry_logger_test.dart index 1e0e90830a..37e760a204 100644 --- a/packages/dart/test/sentry_logger_test.dart +++ b/packages/dart/test/sentry_logger_test.dart @@ -184,87 +184,6 @@ void main() { expect(logCall.logger, 'sentry_logger'); }); - test('applies scope attributes to all log methods', () { - final logger = fixture.getSut(); - - final scopeAttributes = { - 'scopeString': SentryAttribute.string('fromScope'), - 'scopeInt': SentryAttribute.int(42), - }; - fixture.hub.scope.setAttributes(scopeAttributes); - - logger.trace('trace message'); - logger.debug('debug message'); - logger.info('info message'); - logger.warn('warn message'); - logger.error('error message'); - logger.fatal('fatal message'); - - expect(fixture.hub.captureLogCalls.length, 6); - for (final call in fixture.hub.captureLogCalls) { - final attrs = call.log.attributes; - expect(attrs['scopeString'], scopeAttributes['scopeString']); - expect(attrs['scopeInt'], scopeAttributes['scopeInt']); - } - }); - - test('per-log attributes override scope on same key', () { - final logger = fixture.getSut(); - - final scopeAttributes = { - 'overridden': SentryAttribute.string('fromScope'), - 'kept': SentryAttribute.bool(true), - }; - fixture.hub.scope.setAttributes(scopeAttributes); - - final overrideAttr = SentryAttribute.string('fromLog'); - logger.info('override test', attributes: { - 'overridden': overrideAttr, - 'logOnly': SentryAttribute.double(1.23), - }); - - expect(fixture.hub.captureLogCalls.length, 1); - final captured = fixture.hub.captureLogCalls[0].log; - // overridden key should come from per-log attributes - expect(captured.attributes['overridden'], overrideAttr); - // scope-only key should still be present - expect(captured.attributes['kept'], scopeAttributes['kept']); - // log-only key should be present - expect(captured.attributes['logOnly']?.type, 'double'); - }); - - test('formatter path merges template, per-log and scope attributes', () { - final logger = fixture.getSut(); - - final scopeAttributes = { - 'scopeOnly': SentryAttribute.string('present'), - }; - fixture.hub.scope.setAttributes(scopeAttributes); - - logger.fmt.info( - 'Hello, %s!', - ['World'], - attributes: {'callOnly': SentryAttribute.string('present')}, - ); - - expect(fixture.hub.captureLogCalls.length, 1); - final captured = fixture.hub.captureLogCalls[0].log; - final attrs = captured.attributes; - - // template attributes - expect(attrs['sentry.message.template']?.type, 'string'); - expect(attrs['sentry.message.template']?.value, 'Hello, %s!'); - expect(attrs['sentry.message.parameter.0']?.type, 'string'); - expect(attrs['sentry.message.parameter.0']?.value, 'World'); - - // per-log attribute - expect(attrs['callOnly']?.type, 'string'); - expect(attrs['callOnly']?.value, 'present'); - - // scope attribute - expect(attrs['scopeOnly'], scopeAttributes['scopeOnly']); - }); - // 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(); From 625bf76be0b9d69cf2c25e879f9aedc5c9ac6be3 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Thu, 20 Nov 2025 12:12:22 +0100 Subject: [PATCH 24/25] Fix analyzer --- packages/flutter/example/lib/main.dart | 2 +- .../src/integrations/load_contexts_integration.dart | 10 +++++----- .../lib/src/integrations/replay_log_integration.dart | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) 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 From 796bba92b4f8ea41016c5a668cd06579970b4ae7 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Thu, 20 Nov 2025 12:14:37 +0100 Subject: [PATCH 25/25] Fix analyzer --- .../test/integrations/load_contexts_integration_test.dart | 2 +- .../flutter/test/integrations/replay_log_integration_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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: {}, ); }