flex_logger_isolate 1.0.0 copy "flex_logger_isolate: ^1.0.0" to clipboard
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 #

Pub Version License: MIT Flutter Dart

Log from Dart isolates to the main isolate’s FlexLogger so all observers (console, file, Sentry) see worker logs.

Contents #

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 #

  1. Main isolate: IsolateLoggerProvider creates a ReceivePort per isolate and returns its SendPort.
  2. Spawn: You pass that SendPort to the worker via Isolate.spawn(entryPoint, sendPort).
  3. Worker: IsolateLogger(tag: 'worker-1', sendPort: sendPort) sends IsolateLog messages (e.g. IsolateInfoLog) over the port.
  4. Main: The provider receives messages and forwards them with FlexLogger.instance.logCustom(message).
  5. 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 #

  1. Fork the project
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. 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 #

License #

This project is licensed under the MIT License - see the LICENSE file for details.

0
likes
0
points
274
downloads

Publisher

verified publisherkrajna.dev

Weekly Downloads

Isolate logging integration for FlexLogger - enables logging from Dart isolates to the main isolate logger.

Homepage
Repository (GitLab)
View/report issues

Topics

#logging #isolate #concurrency #dart

License

unknown (license)

Dependencies

flex_logger, flutter

More

Packages that depend on flex_logger_isolate