flutter_logging_service 0.2.0 copy "flutter_logging_service: ^0.2.0" to clipboard
flutter_logging_service: ^0.2.0 copied to clipboard

A comprehensive logging service for Flutter apps with file persistence, log aggregation, crash tracking, sessions, masking, and isolate-backed writes.

Siglat

Siglat

flutter_logging_service
Production-minded logging for Flutter — persistent files, crash capture,
sessions, masking, live streaming, and an in-app viewer.

repo version flutter license

Install · Quick start · Features · Config · Example · العربية

The name siglat comes from Arabic سجلات (sijillāt): records / registers / logs — plural of سجل (sijill).


Why #

Most Flutter apps start with print / debugPrint, then need:

  • logs that survive process death
  • a separate crash trail
  • PII redaction before anything leaves the device
  • a way to view / export / attach logs to a bug report

Siglat covers that path without pulling in a separate console package. Console output is built in (ANSI-colored); remote sinks are left to your app. File I/O runs off the UI isolate.

Package name on disk: flutter_logging_service · Repo: Zyzto/Siglat


Features at a glance #

Area What you get
Files Rotating main log + dedicated crash file, size + age retention
Console Internal ANSI printer; muted in release by default
APIs Log, Loggable mixin, or LoggingService directly
Sessions sid= on file lines without breaking [LEVEL] markers
Privacy Opt-in default masks + custom MaskingRules
Live LoggingService.stream, ring buffer, LogViewer widget
Export Text dump, zip, keyword search, GitHub issue body helper
Perf Optional isolate writer, write queue, backpressure

Platforms: Android, iOS, Linux, macOS, Windows (full). Web: console + memory buffer (no file isolate).


Install #

dependencies:
  flutter_logging_service:
    git:
      url: https://github.com/Zyzto/Siglat.git
      ref: main   # prefer a tag (e.g. v0.2.0) once published
import 'package:flutter_logging_service/flutter_logging_service.dart';

Quick start #

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await LoggingService.init(
    LoggingConfig(
      appName: 'MyApp',
      enableDefaultMasking: true,
      sessionStartExtra: '1.2.3',
    ),
  );

  runApp(const MyApp());
}
Log.info('User signed in');
Log.warning('Slow network', error: e, stackTrace: st);
Log.severe('Uncaught failure', error: e, stackTrace: st);

await LoggingService.exportLogsZip();

Initialize before other services that log.


Ways to log #

Log — static helper #

Auto-detects *Service / *Dao / *Repository from the stack in debug/profile. In release, stack parsing is skipped (no component); use Loggable if you need a name.

Log.debug('cache miss');
Log.info('synced 12 rows');
Log.error('upload failed', error: e, stackTrace: st);

Loggable — mixin #

Component name from runtimeType (cheap in release).

class ExpenseRepository with Loggable {
  Future<void> save() async {
    logInfo('saving expense');
    try {
      // …
    } catch (e, st) {
      logError('save failed', error: e, stackTrace: st);
    }
  }
}

LoggingService — explicit component #

LoggingService.info('route restored', component: 'Nav');
LoggingService.severe(
  'Flutter framework error',
  component: 'CrashHandler',
  error: details.exception,
  stackTrace: details.stack,
);

Levels #

Level Typical use Notes
DEBUG Dev noise Skipped entirely in release
INFO Normal events Default min level in release
WARNING Recoverable issues
ERROR Handled failures Never aggregated
SEVERE Crashes / fatal Also written to the crash file

Configuration #

Only appName is required. Sensible defaults cover most apps.

await LoggingService.init(
  LoggingConfig(
    appName: 'MyApp',
    // Files
    logFileName: 'myapp.log',
    crashLogFileName: 'myapp_crashes.log',
    logDirectory: null,                    // → documents/logs
    maxLogFileSize: 5 * 1024 * 1024,
    maxLogFiles: 5,
    maxTotalLogSize: 50 * 1024 * 1024,
    logsRetention: const Duration(days: 14),
    // Behavior
    enableAggregation: true,
    aggregationTimeout: const Duration(milliseconds: 100),
    minLogLevel: null,                     // DEBUG in debug, INFO in release
    bufferLimit: 2000,
    startNewSession: true,
    sessionStartExtra: '1.2.3',
    enableDefaultMasking: false,
    // Console
    enableConsole: true,
    enableConsoleInRelease: false,
    // Perf (IO platforms)
    useIsolateWriter: true,
    writeQueueCapacity: 500,
    perLevelQuotas: null,
    quotaWindow: const Duration(seconds: 60),
  ),
);
Flag Default Meaning
enableConsole true ANSI console output
enableConsoleInRelease false Allow console in release (info+ only; debug stays skipped)
useIsolateWriter true File append/rotate/zip on a dedicated isolate
enableDefaultMasking false Built-in email / bearer / JWT / password / api-key patterns
startNewSession true New sid on each init

Sessions #

final sid = LoggingService.startSession(extra: 'resumed');
// LoggingService.sessionId

File lines keep a stable shape for UI colorizers:

[2026-08-06T12:00:00.000Z] [INFO] [MyService] hello sid=s1_abc123

[LEVEL] is never replaced by the session id.


Masking #

Applied before console, stream, aggregation, and file write.

await LoggingService.init(
  LoggingConfig(appName: 'MyApp', enableDefaultMasking: true),
);

LoggingService.addMaskingRule(
  MaskingRule('super-secret', replacement: '[HIDDEN]'),
);
LoggingService.addMaskingRule(
  MaskingRule(r'sk_live_[A-Za-z0-9]+', isRegExp: true, replacement: '[KEY]'),
);

formatLogsForGitHub and exports use masked content.


Live stream & LogViewer #

LoggingService.stream.listen((LogRecord r) {
  // r.maskedMessage, r.levelName, r.component, r.sessionId, …
});

// In-app viewer (i18n-agnostic labels; override via LogViewerLabels)
Navigator.of(context).push(
  MaterialPageRoute<void>(builder: (_) => const LogViewer()),
);

Web and memory-only mode feed the viewer from the ring buffer when files are unavailable.


await LoggingService.flush();                    // aggregation + write queue
final text = await LoggingService.getLogContent(maxLines: 500);
final path = await LoggingService.exportLogs();  // .txt bundle
final zip = await LoggingService.exportLogsZip(type: ExportType.all);
final hits = await LoggingService.searchLogs(keywords: ['ERROR', 'timeout']);
await LoggingService.clearLogs();
await LoggingService.clearLogsBefore(const Duration(days: 7));

final issueBody = await LoggingService.formatLogsForGitHub(
  'Steps to reproduce…',
);

getLogContent, searchLogs, and exports flush for you. If you read raw files yourself after heavy logging, call flush() first.


Console behavior #

  • Format: [appName] [LEVEL] message (ANSI: debug gray, info green, warning blue, error red)
  • Debug / profile: on when enableConsole is true
  • Release: off unless enableConsoleInRelease: true
  • enableConsoleInRelease does not bring back DEBUG (hard skip + default min level INFO)
  • SEVERE → file [SEVERE], console [ERROR] (intentional parity with the old printer)

No easy_logger dependency — printing is internal.


Aggregation #

Similar method(params) messages (same component + level) are batched for the file. Unique messages still hit the console immediately. Errors and severe logs are never aggregated.

[INFO] [AGGREGATED] fetch() called 12 times (id: 1, 2, 3 …)

Isolate writer & backpressure #

On IO platforms with useIsolateWriter: true (default), append / rotate / clear / zip run on a dedicated isolate so the UI isolate only enqueues work.

When the write queue is saturated:

  • debug / info may be dropped
  • error / severe are never dropped

Optional perLevelQuotas + quotaWindow add soft rate limits.


Lifecycle #

LoggingService.setMinLogLevel(LogLevel.warning);
LoggingService.setAggregationEnabled(false);

await LoggingService.flush();
await LoggingService.dispose();   // then init() may be called again

Testing #

setUp(() async {
  await LoggingService.init(
    const LoggingConfig(
      appName: 'Test',
      startNewSession: false,
      enableAggregation: false,
      enableConsole: false,
    ),
    store: MemoryLogFileStore(),
  );
});

tearDown(() async {
  await LoggingService.dispose();
});

LoggingService.disableFileLogging() also forces the memory store. Prefer injecting MemoryLogFileStore for deterministic tests.


Example #

cd example
flutter pub get
flutter run

The example covers sessions, masking, zip export, and the live LogViewer.


License #

MPL-2.0 — weak copyleft, commercial use allowed. Modified package files stay under MPL; your app can remain closed-source.

0
likes
160
points
50
downloads

Documentation

API reference

Publisher

verified publishershenepoy.com

Weekly Downloads

A comprehensive logging service for Flutter apps with file persistence, log aggregation, crash tracking, sessions, masking, and isolate-backed writes.

Repository (GitHub)
View/report issues

License

MPL-2.0 (license)

Dependencies

archive, flutter, path, path_provider

More

Packages that depend on flutter_logging_service