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.

Libraries

flutter_logging_service
A comprehensive logging service for Flutter apps.