From ebbe8d99ad17c6ebca74546cc3e74628dce08a05 Mon Sep 17 00:00:00 2001 From: Hans Kokx Date: Wed, 27 May 2026 12:01:39 +0200 Subject: [PATCH] v2.0.2: Fix ArcaneLogger LoggerName mixin to be runtime safe (#9) * Enhance reset method in ArcaneLogger to clear interceptors along with metadata and interfaces Signed-off-by: Hans Kokx * Update Logging feature to replace @LoggingFeature annotation with LoggerName mixin for runtime accessibility Signed-off-by: Hans Kokx * Bump version to 2.0.2 in pubspec.yaml and update CHANGELOG to reflect the new version Signed-off-by: Hans Kokx * Add migration steps for replacing @LoggingFeature annotation with LoggerName mixin in CHANGELOG Signed-off-by: Hans Kokx * Add TestLoggerWithLoggerName class to enhance logging functionality and verify LoggerName mixin behavior Signed-off-by: Hans Kokx * Fix class declaration in migration example for LoggerName mixin Signed-off-by: Hans Kokx --------- Signed-off-by: Hans Kokx --- CHANGELOG.md | 30 ++++++++++++++++ README.md | 18 ++++++---- .../services/logging/logging_interface.dart | 25 +++++++++---- lib/src/services/logging/logging_service.dart | 4 +-- pubspec.yaml | 2 +- .../logging/logging_service_test.dart | 36 +++++++++++++++++++ 6 files changed, 98 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc8e467..74772ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +## 2.0.2 + +### Logging Service + +- [BREAKING] Replaced the `@LoggingFeature(...)` annotation (compile-time only, + not readable at runtime in Flutter) with the `LoggerName` mixin. + Mix `LoggerName` into a `LoggingInterface` subclass and override + `name` to expose a runtime-accessible name inside `log()`. + +#### Migration Steps (LoggingFeature) + +1. Replace any `@LoggingFeature("...")` annotation with `with LoggerName` and + add an `@override String get name => '...';` getter to the class body. + +Before: + +```dart +@LoggingFeature("my-feature") +class MyLogger extends LoggingInterface {...} +``` + +After: + +```dart +class MyLogger extends LoggingInterface with LoggerName { + @override + String get name => "my-feature"; +} +``` + ## 2.0.1 ### Arcane Framework diff --git a/README.md b/README.md index ed54be5..a3aa921 100644 --- a/README.md +++ b/README.md @@ -514,12 +514,13 @@ class ExternalLogger extends LoggingInterface with LoggingInitialization { } ``` -If you want to tag a destination, annotate the interface with -`@LoggingFeature(...)`: +If you want to give a destination a name and access it at runtime, +mix in `LoggerName` and override `name`: ```dart -@LoggingFeature("Analytics") -class AnalyticsLogger extends LoggingInterface { +class AnalyticsLogger extends LoggingInterface with LoggerName { + @override + String get name => 'Analytics'; @override void log( @@ -529,6 +530,7 @@ class AnalyticsLogger extends LoggingInterface { StackTrace? stackTrace, Object? extra, }) { + // name is accessible here at runtime. // Forward to analytics pipeline. } } @@ -545,12 +547,14 @@ Arcane.logger.interceptors.add( ); ``` -You can use this tag as source-level documentation and keep destination routing +You can use `name` inside `log()` and keep destination routing explicit in interceptors. ```dart -@LoggingFeature("auth") -class AuthLogger extends LoggingInterface { +class AuthLogger extends LoggingInterface with LoggerName { + @override + String get name => 'auth'; + @override void log( String message, { diff --git a/lib/src/services/logging/logging_interface.dart b/lib/src/services/logging/logging_interface.dart index 85386c4..88e6b2e 100644 --- a/lib/src/services/logging/logging_interface.dart +++ b/lib/src/services/logging/logging_interface.dart @@ -41,13 +41,24 @@ mixin LoggingInitialization implements LoggingInitializable { } } -/// Annotation used to tag a logging destination with a feature name. +/// Opt-in mixin that exposes a [name] string on a [LoggingInterface]. +/// +/// Mix this into a concrete logger to declare what [name] should be associated +/// with this logging interface. /// /// Example: -/// `@LoggingFeature("analytics")` -final class LoggingFeature { - const LoggingFeature(this.value); - - /// The feature name associated with this logging destination. - final String value; +/// ```dart +/// class MyLogger extends LoggingInterface with LoggerName { +/// @override +/// String get name => 'my-feature'; +/// +/// @override +/// void log(String message, {...}) { +/// print('[$name] $message'); +/// } +/// } +/// ``` +mixin LoggerName on LoggingInterface { + /// The name associated with this logging interface. + String get name; } diff --git a/lib/src/services/logging/logging_service.dart b/lib/src/services/logging/logging_service.dart index 734950d..8b0fac4 100644 --- a/lib/src/services/logging/logging_service.dart +++ b/lib/src/services/logging/logging_service.dart @@ -470,8 +470,8 @@ class ArcaneLogger { void clearPersistentMetadata() => _additionalMetadata.clear(); /// Resets the Arcane logging service by clearing all persistent metadata, - /// clearing all registered [LoggingInterface]s and marking the logging - /// service as no longer being initialized. + /// clearing all registered [LoggingInterface]s, clearing all interceptors, + /// and marking the logging service as no longer being initialized. void reset() { dispose(); I._interfaceRegistrations.clear(); diff --git a/pubspec.yaml b/pubspec.yaml index 442102a..09c6a33 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: arcane_framework description: "Agnostic Reusable Component Architecture for New Ecosystems: a modern framework for bootstrapping new applications" -version: 2.0.1 +version: 2.0.2 repository: https://github.com/hanskokx/arcane_framework issue_tracker: https://github.com/hanskokx/arcane_framework/issues diff --git a/test/services/logging/logging_service_test.dart b/test/services/logging/logging_service_test.dart index 4998ad0..5a22d70 100644 --- a/test/services/logging/logging_service_test.dart +++ b/test/services/logging/logging_service_test.dart @@ -110,9 +110,37 @@ class RedactingLogInterceptor implements LogInterceptor { } } +class TestLoggerWithLoggerName extends LoggingInterface with LoggerName { + @override + String get name => "test-logger"; + + final List events = []; + + @override + void log( + String message, { + Map? metadata, + Level? level, + StackTrace? stackTrace, + Object? extra, + }) { + events.add( + LogEvent( + message: message, + metadata: metadata == null ? null : Map.from(metadata), + level: level, + stackTrace: stackTrace, + extra: extra, + ), + ); + } +} + void main() { late TestLoggingInterface myInterface; late LogInterceptor prefixInterceptor; + final TestLoggerWithLoggerName loggerWithLoggerName = + TestLoggerWithLoggerName(); setUp(() { Arcane.logger.reset(); @@ -677,4 +705,12 @@ void main() { }); }); }); + + test("LoggerName mixin exposes name at runtime", () { + expect(loggerWithLoggerName.name, "test-logger"); + + loggerWithLoggerName.log("Test message"); + expect(loggerWithLoggerName.events.length, 1); + expect(loggerWithLoggerName.events.first.message, "Test message"); + }); }