flex_logger_isolate 1.0.0
flex_logger_isolate: ^1.0.0 copied to clipboard
Isolate logging integration for FlexLogger - enables logging from Dart isolates to the main isolate logger.
flex_logger_isolate #
Log from Dart isolates to the main isolate’s FlexLogger so all observers (console, file, Sentry) see worker logs.
Contents #
- Features
- Installation
- Usage
- API Reference
- How it works
- Best Practices
- Troubleshooting
- Contributing
- Example App
Features #
- Isolate → main – Log from worker isolates; messages forwarded to main isolate's FlexLogger
- All Log Levels - Support for debug, info, success, warning, error, and critical logs
- logCustom – From isolates, level/message/error/stackTrace are sent and appear as Isolate*Log on main
- Provider pattern – IsolateLoggerProvider; optional extension so you don't pass the provider around
- Cleanup – Call disposeIsolate when done; Finalizer closes ports and logs a warning if forgotten
- Output – Isolate logs use [tag] in formattedTitle (e.g. [worker-1] DEBUG)
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
flex_logger: ^1.0.0
flex_logger_isolate: ^1.0.0
Then run:
flutter pub get
Usage #
Basic Setup #
Add IsolateLoggerProvider to FlexLogger, then use the extension so you don't need to pass the provider around:
import 'dart:isolate';
import 'package:flex_logger/flex_logger.dart';
import 'package:flex_logger_isolate/flex_logger_isolate.dart';
import 'package:flex_logger_console/flex_logger_console.dart';
void main() async {
FlexLogger.instance.configure(
providers: [
ConsoleLoggerProvider(),
IsolateLoggerProvider(),
],
);
await FlexLogger.instance.initialize();
final sendPort = FlexLogger.instance.initializeIsolate('worker-1');
await Isolate.spawn(workerEntryPoint, sendPort);
// When worker is done
FlexLogger.instance.disposeIsolate('worker-1');
}
void workerEntryPoint(SendPort sendPort) {
final logger = IsolateLogger(tag: 'worker-1', sendPort: sendPort);
logger.info('Worker started');
try {
performHeavyComputation();
logger.success('Computation completed');
} catch (e, st) {
logger.error('Computation failed', e, st);
}
}
Advanced Usage #
Multiple Isolates
for (int i = 0; i < 4; i++) {
final sendPort = FlexLogger.instance.initializeIsolate('worker-$i');
await Isolate.spawn(workerEntryPoint, sendPort);
}
void workerEntryPoint(SendPort sendPort) {
// Pass tag via a record/list if you need dynamic id; here use a fixed tag for simplicity
final logger = IsolateLogger(tag: 'worker', sendPort: sendPort);
logger.info('Worker started');
}
// When done: FlexLogger.instance.disposeIsolate('worker-$i') for each
logCustom from isolate
IsolateLogger.logCustom(FlexLog) sends the log's level, message, error, and stackTrace to the main isolate. The main side receives an Isolate*Log (e.g. IsolateInfoLog), not the custom type, because only simple data crosses the isolate boundary.
void workerEntryPoint(SendPort sendPort) {
final logger = IsolateLogger(tag: 'processor', sendPort: sendPort);
logger.logCustom(SomeCustomFlexLog('Processed 1000 records')); // Main sees IsolateInfoLog with same message/level
}
Monitoring active isolates
final provider = FlexLogger.instance.getProvider<IsolateLoggerProvider>();
if (provider != null) {
FlexLogger.instance.info('Active isolates: ${provider.activeIsolateCount}');
for (final id in provider.activeIsolateIds) {
FlexLogger.instance.debug('Active isolate: $id');
}
}
API Reference #
IsolateLoggerProvider #
Implements LoggerProvider. Creates one ReceivePort per isolate; logs received are forwarded with FlexLogger.instance.logCustom(...).
SendPort initializeIsolate(String isolateId); // returns SendPort to pass to Isolate.spawn
void disposeIsolate(String isolateId);
int get activeIsolateCount;
List<String> get activeIsolateIds;
Or use the extension on FlexLogger: FlexLogger.instance.initializeIsolate(id), FlexLogger.instance.disposeIsolate(id).
IsolateLogger #
Logger for use inside worker isolates. Implements Logger.
IsolateLogger({
required String tag, // isolate identifier (e.g. 'worker-1')
required SendPort sendPort,
});
// Standard Logger methods
void debug(String message, [Object? error, StackTrace? stackTrace]);
void info(String message, [Object? error, StackTrace? stackTrace]);
void success(String message, [Object? error, StackTrace? stackTrace]);
void warning(String message, [Object? error, StackTrace? stackTrace]);
void error(String message, [Object? error, StackTrace? stackTrace]);
void critical(String message, [Object? error, StackTrace? stackTrace]);
void log(String message, { FlexLogLevel level = FlexLogLevel.info, Object? error, StackTrace? stackTrace });
void logCustom(FlexLog log); // sends level/message/error/stackTrace; main receives Isolate*Log
How it works #
- Main isolate:
IsolateLoggerProvidercreates aReceivePortper isolate and returns itsSendPort. - Spawn: You pass that
SendPortto the worker viaIsolate.spawn(entryPoint, sendPort). - Worker:
IsolateLogger(tag: 'worker-1', sendPort: sendPort)sendsIsolateLogmessages (e.g.IsolateInfoLog) over the port. - Main: The provider receives messages and forwards them with
FlexLogger.instance.logCustom(message). - Observers: All configured observers (console, file, Sentry, etc.) see the logs; filtering is done there.
Best Practices #
1. Always clean up #
Call disposeIsolate(id) when the isolate is done (or use try/finally so it runs even if spawn fails). Otherwise the ReceivePort stays open until the Finalizer runs and logs a warning.
2. Unique isolate IDs #
Use a unique tag per isolate (e.g. worker-0, worker-1). Reusing the same id in initializeIsolate replaces the previous port (provider calls disposeIsolate for the old one).
3. Dispose with logger #
await FlexLogger.instance.dispose() disposes all providers, including IsolateLoggerProvider (all ReceivePorts are closed).
Troubleshooting #
Logs Not Appearing #
Problem: Logs from isolate are not showing up
Solution: Ensure IsolateLoggerProvider is in FlexLogger.instance.configure(providers: [...]), await FlexLogger.instance.initialize() was called before spawn, and the worker uses the SendPort returned from initializeIsolate(id).
Memory Leaks #
Problem: Warning about ReceivePorts not being closed
Solution: Call disposeIsolate(id) when the isolate is done, or FlexLogger.instance.dispose() to close all providers.
Isolate Crashes #
Problem: Isolate crashes with "SendPort closed" error
Solution: Don't dispose the isolate logger before the isolate finishes logging. Dispose only after the isolate exits or is done logging.
Contributing #
- Fork the project
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Example App #
A complete example application is available in the example/ directory that demonstrates:
- Basic isolate logging setup
- Heavy computation in isolates
- Multiple concurrent isolates
- Error handling in isolates
- Active isolate tracking
To run the example:
cd example
flutter run -d macos # or ios, android
See example/README.md for detailed documentation.
Where to go next #
- API reference – pub.dev package page for API docs and the Examples tab.
- FlexLogger – Core package: flex_logger.
- Contributing – See Contributing above.
License #
This project is licensed under the MIT License - see the LICENSE file for details.