From 3fd38d3b26a7b300c9fc3f851437675455a99367 Mon Sep 17 00:00:00 2001 From: Hans Kokx Date: Mon, 28 Apr 2025 17:41:52 +0200 Subject: [PATCH] Added logStream to logger. Updated example code. Signed-off-by: Hans Kokx --- example/lib/main.dart | 252 ++++++++++++------ lib/src/services/logging/logging_service.dart | 18 ++ 2 files changed, 189 insertions(+), 81 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index b5bfec4..fe6f5cc 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,3 +1,5 @@ +import "dart:async"; + import "package:arcane_framework/arcane_framework.dart"; import "package:example/config.dart"; import "package:example/interfaces/debug_auth_interface.dart"; @@ -78,102 +80,174 @@ class HomeScreen extends StatefulWidget { } class _HomeScreenState extends State { + late final StreamSubscription _subscription; + final List latestLogs = []; @override Widget build(BuildContext context) { final bool isSignedIn = Arcane.auth.isSignedIn.value; - return GridView.extent( - maxCrossAxisExtent: 300, - padding: const EdgeInsets.all(16), + return Column( children: [ - Card( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - "Theme", - style: Theme.of(context).textTheme.headlineSmall, - ), - Column( - children: [ - Switch( - value: Arcane.theme.currentTheme == ThemeMode.dark, - thumbIcon: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.selected)) { - return const Icon(Icons.dark_mode); - } - return const Icon(Icons.light_mode); - }), - onChanged: (_) { - Arcane.theme.switchTheme(); - }, - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Checkbox( - value: Arcane.theme.isFollowingSystemTheme, - onChanged: (value) { - if (value == true) { - Arcane.theme.followSystemTheme(context); - } else { - Arcane.theme.switchTheme( - themeMode: Arcane.theme.systemTheme, + Expanded( + child: GridView.extent( + maxCrossAxisExtent: 300, + padding: const EdgeInsets.all(16), + children: [ + Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "Theme", + style: Theme.of(context).textTheme.headlineSmall, + ), + Column( + children: [ + Switch( + value: Arcane.theme.currentTheme == ThemeMode.dark, + thumbIcon: + WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return const Icon(Icons.dark_mode); + } + return const Icon(Icons.light_mode); + }), + onChanged: (_) { + final ThemeMode oldTheme = + Arcane.theme.currentTheme; + Arcane.theme.switchTheme(); + Arcane.log( + "Switching theme", + metadata: { + "followingSystemTheme": + "${Arcane.theme.isFollowingSystemTheme}", + "newMode": Arcane.theme.currentTheme.name, + "oldMode": oldTheme.name, + }, ); - } - }, - ), - const Text("Follow system"), - ], - ), - ], + }, + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Checkbox( + value: Arcane.theme.isFollowingSystemTheme, + onChanged: (value) { + final ThemeMode oldTheme = + Arcane.theme.currentTheme; + if (value == true) { + Arcane.theme.followSystemTheme(context); + Arcane.log( + "Switching theme", + metadata: { + "followingSystemTheme": + "${Arcane.theme.isFollowingSystemTheme}", + "newMode": + Arcane.theme.currentTheme.name, + "oldMode": oldTheme.name, + }, + ); + } else { + Arcane.theme.switchTheme( + themeMode: Arcane.theme.systemTheme, + ); + Arcane.log( + "Switching theme", + metadata: { + "followingSystemTheme": + "${Arcane.theme.isFollowingSystemTheme}", + "newMode": + Arcane.theme.currentTheme.name, + "oldMode": oldTheme.name, + }, + ); + } + }, + ), + const Text("Follow system"), + ], + ), + ], + ), + Text( + "The current theme mode is ${context.themeMode.name} and\n" + "is ${Arcane.theme.isFollowingSystemTheme ? "" : "not "}" + "following the system theme.", + ), + ], + ), ), - Text( - "The current theme mode is ${context.themeMode.name} and\n" - "is ${Arcane.theme.isFollowingSystemTheme ? "" : "not "}" - "following the system theme.", + ), + Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "Authentication", + style: Theme.of(context).textTheme.headlineSmall, + ), + ElevatedButton( + child: Text(isSignedIn ? "Sign out" : "Sign in"), + onPressed: () async { + if (isSignedIn) { + await Arcane.auth.logOut( + onLoggedOut: () async { + setState(() {}); + }, + ); + } else { + await Arcane.auth.login( + input: ( + email: "email", + password: "password", + ), + onLoggedIn: () async { + setState(() {}); + }, + ); + } + }, + ), + Center( + child: Text("Status: ${Arcane.auth.status.name}"), + ), + ], + ), ), - ], - ), + ), + ], ), ), - Card( - child: Padding( - padding: const EdgeInsets.all(8.0), + Padding( + padding: const EdgeInsets.all(16.0), + child: SizedBox( + height: 200, child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.stretch, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Authentication", + "Logging", style: Theme.of(context).textTheme.headlineSmall, ), - ElevatedButton( - child: Text(isSignedIn ? "Sign out" : "Sign in"), - onPressed: () async { - if (isSignedIn) { - await Arcane.auth.logOut( - onLoggedOut: () async { - setState(() {}); - }, - ); - } else { - await Arcane.auth.login( - input: ( - email: "email", - password: "password", + if (latestLogs.isEmpty) + Text( + "Log messages will appear here", + style: Theme.of(context).textTheme.labelSmall?.copyWith( + fontStyle: FontStyle.italic, ), - onLoggedIn: () async { - setState(() {}); - }, - ); - } - }, - ), - Center( - child: Text("Status: ${Arcane.auth.status.name}"), + ), + Expanded( + child: ListView.builder( + itemCount: latestLogs.length, + itemBuilder: (context, index) { + return Text(latestLogs[index]); + }, + ), ), ], ), @@ -182,4 +256,20 @@ class _HomeScreenState extends State { ], ); } + + @override + void initState() { + super.initState(); + _subscription = Arcane.logger.logStream.listen((message) { + setState(() { + latestLogs.insert(0, message); + }); + }); + } + + @override + void dispose() { + _subscription.cancel(); + super.dispose(); + } } diff --git a/lib/src/services/logging/logging_service.dart b/lib/src/services/logging/logging_service.dart index 9cb648e..1c2dcfd 100644 --- a/lib/src/services/logging/logging_service.dart +++ b/lib/src/services/logging/logging_service.dart @@ -29,6 +29,16 @@ class ArcaneLogger { /// Additional metadata that is included in all logs. Map get additionalMetadata => I._additionalMetadata; + final StreamController _logStreamController = + StreamController.broadcast( + onCancel: () { + I._logStreamController.close(); + }, + ); + + /// Stream of log messages being received and sent to the registered interfaces. + Stream get logStream => I._logStreamController.stream; + bool _initialized = false; /// Whether the logger has been initialized. @@ -241,6 +251,14 @@ class ArcaneLogger { stackTrace: stackTrace, extra: extra, ); + + _logStreamController.add( + "$message ${{ + "level": level, + "metadata": metadata, + "extra": extra, + }}", + ); } }