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 <hans.d.kokx@gmail.com>

* Update Logging feature to replace @LoggingFeature annotation with LoggerName mixin for runtime accessibility

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>

* Bump version to 2.0.2 in pubspec.yaml and update CHANGELOG to reflect the new version

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>

* Add migration steps for replacing @LoggingFeature annotation with LoggerName mixin in CHANGELOG

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>

* Add TestLoggerWithLoggerName class to enhance logging functionality and verify LoggerName mixin behavior

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>

* Fix class declaration in migration example for LoggerName mixin

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>

---------

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-05-27 12:01:39 +02:00
committed by GitHub
parent 45de68683b
commit ebbe8d99ad
6 changed files with 98 additions and 17 deletions
+30
View File
@@ -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 ## 2.0.1
### Arcane Framework ### Arcane Framework
+11 -7
View File
@@ -514,12 +514,13 @@ class ExternalLogger extends LoggingInterface with LoggingInitialization {
} }
``` ```
If you want to tag a destination, annotate the interface with If you want to give a destination a name and access it at runtime,
`@LoggingFeature(...)`: mix in `LoggerName` and override `name`:
```dart ```dart
@LoggingFeature("Analytics") class AnalyticsLogger extends LoggingInterface with LoggerName {
class AnalyticsLogger extends LoggingInterface { @override
String get name => 'Analytics';
@override @override
void log( void log(
@@ -529,6 +530,7 @@ class AnalyticsLogger extends LoggingInterface {
StackTrace? stackTrace, StackTrace? stackTrace,
Object? extra, Object? extra,
}) { }) {
// name is accessible here at runtime.
// Forward to analytics pipeline. // 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. explicit in interceptors.
```dart ```dart
@LoggingFeature("auth") class AuthLogger extends LoggingInterface with LoggerName {
class AuthLogger extends LoggingInterface { @override
String get name => 'auth';
@override @override
void log( void log(
String message, { String message, {
@@ -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: /// Example:
/// `@LoggingFeature("analytics")` /// ```dart
final class LoggingFeature { /// class MyLogger extends LoggingInterface with LoggerName {
const LoggingFeature(this.value); /// @override
/// String get name => 'my-feature';
/// The feature name associated with this logging destination. ///
final String value; /// @override
/// void log(String message, {...}) {
/// print('[$name] $message');
/// }
/// }
/// ```
mixin LoggerName on LoggingInterface {
/// The name associated with this logging interface.
String get name;
} }
@@ -470,8 +470,8 @@ class ArcaneLogger {
void clearPersistentMetadata() => _additionalMetadata.clear(); void clearPersistentMetadata() => _additionalMetadata.clear();
/// Resets the Arcane logging service by clearing all persistent metadata, /// Resets the Arcane logging service by clearing all persistent metadata,
/// clearing all registered [LoggingInterface]s and marking the logging /// clearing all registered [LoggingInterface]s, clearing all interceptors,
/// service as no longer being initialized. /// and marking the logging service as no longer being initialized.
void reset() { void reset() {
dispose(); dispose();
I._interfaceRegistrations.clear(); I._interfaceRegistrations.clear();
+1 -1
View File
@@ -1,7 +1,7 @@
name: arcane_framework name: arcane_framework
description: "Agnostic Reusable Component Architecture for New Ecosystems: a description: "Agnostic Reusable Component Architecture for New Ecosystems: a
modern framework for bootstrapping new applications" modern framework for bootstrapping new applications"
version: 2.0.1 version: 2.0.2
repository: https://github.com/hanskokx/arcane_framework repository: https://github.com/hanskokx/arcane_framework
issue_tracker: https://github.com/hanskokx/arcane_framework/issues issue_tracker: https://github.com/hanskokx/arcane_framework/issues
@@ -110,9 +110,37 @@ class RedactingLogInterceptor implements LogInterceptor {
} }
} }
class TestLoggerWithLoggerName extends LoggingInterface with LoggerName {
@override
String get name => "test-logger";
final List<LogEvent> events = [];
@override
void log(
String message, {
Map<String, Object?>? metadata,
Level? level,
StackTrace? stackTrace,
Object? extra,
}) {
events.add(
LogEvent(
message: message,
metadata: metadata == null ? null : Map<String, Object?>.from(metadata),
level: level,
stackTrace: stackTrace,
extra: extra,
),
);
}
}
void main() { void main() {
late TestLoggingInterface myInterface; late TestLoggingInterface myInterface;
late LogInterceptor prefixInterceptor; late LogInterceptor prefixInterceptor;
final TestLoggerWithLoggerName loggerWithLoggerName =
TestLoggerWithLoggerName();
setUp(() { setUp(() {
Arcane.logger.reset(); 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");
});
} }