From b692ce05b85e24edfaf7be0d1cc7227794524e55 Mon Sep 17 00:00:00 2001 From: Job Guldemeester Date: Tue, 8 Sep 2026 13:24:00 +0200 Subject: [PATCH] :bug: Fix some logger issues --- example/pubspec.lock | 53 +++++++----------- lib/logger/bolt_logger.dart | 2 +- lib/logger/charges/debug_console_charge.dart | 2 +- lib/logger/charges/file_charge.dart | 43 +++++++++++--- lib/logger/charges/memory_charge.dart | 13 ++++- lib/logger/ui/bolt_logger_view.dart | 6 +- lib/logger/util/ansi_support_web.dart | 4 +- pubspec.lock | 59 ++++++++------------ test/logger/bolt_logger_test.dart | 12 ++++ 9 files changed, 107 insertions(+), 87 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 7b50b2c..06c1250 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,23 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" + sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f url: "https://pub.dev" source: hosted - version: "76.0.0" - _macros: - dependency: transitive - description: dart - source: sdk - version: "0.3.3" + version: "85.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" + sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d" url: "https://pub.dev" source: hosted - version: "6.11.0" + version: "7.7.1" args: dependency: transitive description: @@ -212,7 +207,7 @@ packages: path: ".." relative: true source: path - version: "0.0.15" + version: "0.0.19" equatable: dependency: transitive description: @@ -225,10 +220,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.3" file: dependency: transitive description: @@ -403,26 +398,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -439,14 +434,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - macros: - dependency: transitive - description: - name: macros - sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" - url: "https://pub.dev" - source: hosted - version: "0.1.3-main.0" matcher: dependency: transitive description: @@ -467,10 +454,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" mime: dependency: transitive description: @@ -648,10 +635,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.7" timing: dependency: transitive description: @@ -672,10 +659,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -733,5 +720,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.7.0 <4.0.0" + dart: ">=3.8.0-0 <4.0.0" flutter: ">=3.29.0" diff --git a/lib/logger/bolt_logger.dart b/lib/logger/bolt_logger.dart index 086ebab..addc193 100644 --- a/lib/logger/bolt_logger.dart +++ b/lib/logger/bolt_logger.dart @@ -91,7 +91,7 @@ class BoltLogger { StackTrace? stacktrace; void zapMap(Object? value) { - if (value is Exception || value is Error && error == null) { + if ((value is Exception || value is Error) && error == null) { error = value; } else if (value is StackTrace && stacktrace == null) { stacktrace = value; diff --git a/lib/logger/charges/debug_console_charge.dart b/lib/logger/charges/debug_console_charge.dart index fb4ac0a..8d827f3 100644 --- a/lib/logger/charges/debug_console_charge.dart +++ b/lib/logger/charges/debug_console_charge.dart @@ -1,7 +1,7 @@ import 'package:dcc_toolkit/logger/charges/bolt_charge.dart'; import 'package:dcc_toolkit/logger/util/ansi_support.dart' if (dart.library.io) 'package:dcc_toolkit/logger/util/ansi_support_io.dart' - if (dart.library.html) 'package:dcc_toolkit/logger/util/ansi_support_web.dart'; + if (dart.library.js_interop) 'package:dcc_toolkit/logger/util/ansi_support_web.dart'; import 'package:dcc_toolkit/logger/zap_event.dart'; import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; diff --git a/lib/logger/charges/file_charge.dart b/lib/logger/charges/file_charge.dart index 0849e23..c14d890 100644 --- a/lib/logger/charges/file_charge.dart +++ b/lib/logger/charges/file_charge.dart @@ -15,10 +15,7 @@ import 'package:intl/intl.dart'; class FileCharge implements BoltCharge { /// {@macro file_charge} FileCharge(this.path, {this.bufferSize = 1000, this.writeDelay = const Duration(seconds: 5)}) { - final fileName = '${DateFormat('yyyy-MM-dd').format(DateTime.now())}.log'; - _file = File('$path/$fileName'); - - Timer.periodic(writeDelay, (_) => _flush()); + _timer = Timer.periodic(writeDelay, (_) => _flush()); } @override String get name => 'FileCharge'; @@ -31,9 +28,9 @@ class FileCharge implements BoltCharge { /// The delay between writing to the file. final Duration writeDelay; - File? _file; final List _buffer = []; IOSink? _sink; + String? _sinkFileName; Timer? _timer; @override @@ -48,21 +45,49 @@ class FileCharge implements BoltCharge { void _flush() { if (_buffer.isEmpty) return; - _sink ??= _file?.openWrite(mode: FileMode.append); + final sink = _openSink(); + if (sink == null) { + _buffer.clear(); + return; + } for (final event in _buffer) { - _sink?.writeAll(event.lines, '\n'); - _sink?.writeln(); + sink + ..writeAll(event.lines, '\n') + ..writeln(); } _buffer.clear(); } + /// Opens (or reopens) the sink for today's log file. Returns `null` when the + /// file cannot be opened, so that logging never crashes the app. + IOSink? _openSink() { + final fileName = '${DateFormat('yyyy-MM-dd').format(DateTime.now())}.log'; + + if (_sink != null && _sinkFileName == fileName) return _sink; + + unawaited(_sink?.close().catchError((_) {})); + _sink = null; + _sinkFileName = null; + + try { + Directory(path).createSync(recursive: true); + _sink = File('$path/$fileName').openWrite(mode: FileMode.append); + _sinkFileName = fileName; + } on FileSystemException { + return null; + } + + return _sink; + } + @override void discharge() { _timer?.cancel(); _timer = null; _flush(); - _sink?.close(); + unawaited(_sink?.close().catchError((_) {})); _sink = null; + _sinkFileName = null; } } diff --git a/lib/logger/charges/memory_charge.dart b/lib/logger/charges/memory_charge.dart index 4b8fcd6..50b0485 100644 --- a/lib/logger/charges/memory_charge.dart +++ b/lib/logger/charges/memory_charge.dart @@ -15,14 +15,17 @@ class MemoryCharge implements BoltCharge { /// {@macro memory_charge} MemoryCharge({this.maxItems = 1000}); + /// The name used to register and look up this charge. + static const chargeName = 'MemoryCharge'; + @override - String get name => 'MemoryCharge'; + String get name => chargeName; /// The maximum amount of logs to store in memory. final int maxItems; final List _items = []; - final StreamController _controller = StreamController.broadcast(); + StreamController _controller = StreamController.broadcast(); /// The stream of [ZapEvent]s. Stream get stream => _controller.stream; @@ -32,6 +35,10 @@ class MemoryCharge implements BoltCharge { @override void logOutput(ZapEvent event) { + if (_controller.isClosed) { + // The charge was discharged and is being reused; start a fresh stream. + _controller = StreamController.broadcast(); + } if (_items.length >= maxItems) { _items.removeAt(0); } @@ -41,7 +48,7 @@ class MemoryCharge implements BoltCharge { @override void discharge() { - _controller.close(); + unawaited(_controller.close()); _items.clear(); } } diff --git a/lib/logger/ui/bolt_logger_view.dart b/lib/logger/ui/bolt_logger_view.dart index 210b6ff..8299953 100644 --- a/lib/logger/ui/bolt_logger_view.dart +++ b/lib/logger/ui/bolt_logger_view.dart @@ -18,9 +18,9 @@ class _BoltLoggerViewState extends State { @override void initState() { super.initState(); - final hasCharge = BoltLogger.getCharge('MemoryCharge'); - if (hasCharge != null) { - charge = hasCharge as MemoryCharge; + final existing = BoltLogger.getCharge(MemoryCharge.chargeName); + if (existing is MemoryCharge) { + charge = existing; } else { charge = MemoryCharge(); BoltLogger.charge([charge]); diff --git a/lib/logger/util/ansi_support_web.dart b/lib/logger/util/ansi_support_web.dart index f86b551..a088649 100644 --- a/lib/logger/util/ansi_support_web.dart +++ b/lib/logger/util/ansi_support_web.dart @@ -1,2 +1,4 @@ /// This file contains the implementation of the [supportsAnsiEscapes] getter for Web systems. -bool get supportsAnsiEscapes => true; +/// +/// Browser consoles do not interpret ANSI escape sequences, so they are disabled. +bool get supportsAnsiEscapes => false; diff --git a/pubspec.lock b/pubspec.lock index d72bf14..0feb4c3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,23 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" + sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d url: "https://pub.dev" source: hosted - version: "76.0.0" - _macros: - dependency: transitive - description: dart - source: sdk - version: "0.3.3" + version: "91.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" + sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 url: "https://pub.dev" source: hosted - version: "6.11.0" + version: "8.4.1" args: dependency: transitive description: @@ -130,10 +125,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.3" file: dependency: transitive description: @@ -236,26 +231,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" logging: dependency: "direct main" description: @@ -264,14 +259,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - macros: - dependency: transitive - description: - name: macros - sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" - url: "https://pub.dev" - source: hosted - version: "0.1.3-main.0" matcher: dependency: transitive description: @@ -292,10 +279,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" mime: dependency: transitive description: @@ -489,26 +476,26 @@ packages: dependency: transitive description: name: test - sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" + sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" url: "https://pub.dev" source: hosted - version: "1.25.15" + version: "1.26.3" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.7" test_core: dependency: transitive description: name: test_core - sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" + sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" url: "https://pub.dev" source: hosted - version: "0.6.8" + version: "0.6.12" typed_data: dependency: transitive description: @@ -521,10 +508,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" very_good_analysis: dependency: "direct dev" description: @@ -598,5 +585,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.7.0 <4.0.0" + dart: ">=3.9.0 <4.0.0" flutter: ">=3.29.0" diff --git a/test/logger/bolt_logger_test.dart b/test/logger/bolt_logger_test.dart index f6a0bef..a1d281e 100644 --- a/test/logger/bolt_logger_test.dart +++ b/test/logger/bolt_logger_test.dart @@ -222,6 +222,18 @@ void main() { expect(memoryCharge.items[1].origin.stackTrace, stackTrace); }); + test('zap/shock asserts when a List contains more than one Exception', () { + final exception = Exception('exception'); + final exception2 = Exception('exception2'); + if (useBoltLogger) { + expect(() => BoltLogger.zap(['zap', exception, exception2]), throwsAssertionError); + expect(() => BoltLogger.shock(['shock', exception, exception2]), throwsAssertionError); + } else { + expect(() => TestReferenceClass().zapExtension(['zap', exception, exception2]), throwsAssertionError); + expect(() => TestReferenceClass().shockExtension(['shock', exception, exception2]), throwsAssertionError); + } + }); + test('zap/shock sends a List with more than 3 elements via a ZapEvent to a Charge', () { final exception = Exception('exception'); final stackTrace = StackTrace.current;