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.

License

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

Libraries

flex_logger_isolate
Isolate logging integration for FlexLogger.