flutter_logan_pro 1.0.4 copy "flutter_logan_pro: ^1.0.4" to clipboard
flutter_logan_pro: ^1.0.4 copied to clipboard

A high-performance, robust log plugin for Flutter based on Meituan Logan. Fixed truncation and decryption issues.

flutter_logan_pro #

pub version license

English | 简体中文

A high-performance, robust logging plugin for Flutter, built on Meituan Dianping's native Logan library. This version fixes known truncation and decryption issues and ships a more robust, developer-friendly API.

Logs are encrypted and stored locally, then uploaded to your own server on demand for analysis.

Features #

  • High performance: asynchronous logging into memory-mapped files.
  • Encrypted: AES-128-CBC encryption protects sensitive data at rest.
  • No lost logs: messages written before init() finishes are queued and replayed (the queue is bounded to avoid unbounded memory growth).
  • Cross-platform: works on both Android and iOS.
  • Feature rich: flush to disk, inspect log files, upload to a server, and control retention.
  • Robust error handling: structured results, request-id–matched callbacks, and graceful handling of native exceptions.

Getting started #

Add the dependency to your pubspec.yaml:

dependencies:
  flutter_logan_pro: ^1.0.4 # Replace with the latest version

Then run flutter pub get.

Usage #

1. Initialization #

Initialize the logger before using it — typically in main(). This sets up the encryption keys and the log directory.

secretKey and secretIV must each be exactly 16 bytes when UTF-8 encoded. A key that is 16 characters but not 16 bytes (e.g. containing CJK characters) is rejected, because a mismatched key produces logs that can never be decrypted.

import 'package:flutter/foundation.dart';
import 'package:flutter_logan_pro/flutter_logan_pro.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await FlutterLoganPro.init(
    secretKey: 'your_16_byte_key', // exactly 16 bytes (UTF-8)
    secretIV: 'your_16_byte_iv_',  // exactly 16 bytes (UTF-8)
    maxFileLen: 1024 * 1024 * 10,  // optional: max single-day file size in bytes (default 50MB)
    isDebug: !kReleaseMode,        // print native-library debug logs
    maxReversedDate: 7,            // retain log files for 7 days
    minSDCardBytes: 50 * 1024 * 1024, // Android only — see note below
  );

  runApp(const MyApp());
}

init() is safe to await from multiple call sites concurrently — only the first performs initialization and the rest await its completion.

minSDCardBytes (Android only): when the device's free storage falls below this many bytes, Logan stops writing new logs to avoid filling the disk. Defaults to the native 50MB. This parameter is ignored on iOS, where the free-space floor is hardcoded at 5MB in the native library and cannot be configured.

2. Logging #

Once initialized, log from anywhere. Logs written before init() completes are queued and written once it finishes.

// Simple log
FlutterLoganPro.log('User logged in successfully.');

// Log with a specific type (integer), useful for filtering server-side
FlutterLoganPro.log('Network request failed', type: 2);

Pre-init queue: buffered logs keep their original timestamp (prefixed to the message) so ordering survives the replay. The queue holds up to 2000 entries; if init() is delayed long enough to overflow it, the oldest entries are dropped and a one-off warning is printed.

3. Sending logs to a server #

Upload a specific day's log file to your backend. The send methods return a Future<LoganSendResult> carrying the server's response.

Future<void> uploadTodaysLog() async {
  try {
    // Prefer the native date so it matches the clock that named the log file.
    final String? date = await FlutterLoganPro.getTodaysDate();
    if (date == null || date.isEmpty) return;

    final LoganSendResult result = await FlutterLoganPro.send(
      url: 'https://your-server.com/logan/upload',
      appId: 'your-app-id',
      date: date,
      deviceId: 'some-device-id',
      timeout: const Duration(seconds: 15),
    );

    debugPrint('Status: ${result.statusCode}, body: ${result.data}');

    if (result.isSuccess()) {
      debugPrint('Log uploaded successfully!');
    } else {
      debugPrint('Log upload failed.');
    }
  } on StateError catch (e) {
    // Thrown if another send is already in progress.
    debugPrint('Send already in progress: $e');
  } catch (e) {
    debugPrint('Unexpected error: $e');
  }
}

When date is omitted, the native side chooses the date using the same clock that named the log file — avoiding an off-by-one-day mismatch around midnight or across time zones.

isSuccess() treats HTTP 200 as the baseline; if the JSON body also carries a code, the conventional Logan success codes (0 or 200) are honored.

You can also send with custom request headers:

await FlutterLoganPro.sendWithHeaders(
  url: 'https://your-server.com/logan/upload',
  date: '2024-01-01',
  headers: {
    'Authorization': 'Bearer your_token',
    'X-Custom-Header': 'value',
  },
);

Platform note: custom headers are honored only on Android. iOS's native Logan upload API accepts no request headers, so on iOS they are ignored (a warning is logged) and the upload proceeds without them. Likewise, buildVersion/appVersion on send() are forwarded only on Android.

Only one send may be in flight at a time; starting a second while one is running throws a StateError. Each send is tagged with a request id, so a callback that arrives after a timeout can never complete a newer send.

You can also observe every completed upload via a broadcast stream:

final sub = FlutterLoganPro.onLoganSend.listen((result) {
  debugPrint('An upload finished: $result');
});
// ... later: sub.cancel();

4. Other utility methods #

// Force buffered logs to disk (called automatically before every send).
await FlutterLoganPro.flush();

// Metadata for all local log files: { 'yyyy-MM-dd': fileSizeInBytes }.
final Map<String, int>? files = await FlutterLoganPro.getAllFilesInfo();
debugPrint('$files'); // e.g. {2024-01-01: 20480, 2024-01-02: 4096}

// Delete all local log files.
await FlutterLoganPro.clearAllLogs();

// Today's date ("yyyy-MM-dd") from the native clock (matches log-file naming).
final String? today = await FlutterLoganPro.getTodaysDate();

// Change retention. Effective on iOS at any time; on Android this is a no-op —
// set `maxReversedDate` in init() instead (it's fixed at init on Android).
await FlutterLoganPro.setMaxReversedDate(7);

// Toggle verbose logging from the underlying native (C) library.
await FlutterLoganPro.setDebug(true);

5. Attaching a log file to a bug report #

Instead of uploading to a Logan server, you can grab the raw log file for a date and attach it to a bug report, email, or share sheet:

// Defaults to today when the date is omitted.
final String? path = await FlutterLoganPro.getUploadPath('2024-01-01');
if (path != null) {
  // e.g. attach with share_plus:
  // await Share.shareXFiles([XFile(path)]);
} else {
  debugPrint('No log file for that date.');
}

For today's date the buffer is flushed and a snapshot copy is returned, so the file is stable and complete. The file is Logan's AES-encrypted, gzipped format — it's meant to be decrypted by a Logan backend or the parsing tools, not read as plain text.

API reference #

Method Description
init({secretKey, secretIV, maxFileLen, isDebug, maxReversedDate, minSDCardBytes}) Initialize the logger. Must be called first. Keys must be 16 bytes (UTF-8). minSDCardBytes is Android-only.
log(message, {type}) Write a log entry. Queued if called before init() completes.
flush() Force buffered logs to disk.
send({url, appId, date, unionId, deviceId, buildVersion, appVersion, autoFlush, timeout}) Upload a day's log; returns LoganSendResult.
sendWithHeaders({url, date, headers, autoFlush, timeout}) Upload with custom headers (Android only).
onLoganSend Broadcast Stream<LoganSendResult> of every completed upload.
getAllFilesInfo() Map<String,int> of date → fileSizeInBytes.
getUploadPath([date]) On-disk path of the (encrypted) log file for a date, or null if none. For attaching to a bug report/email.
clearAllLogs() Delete all local log files.
getTodaysDate() Native-clock date string yyyy-MM-dd.
setMaxReversedDate(days) Set retention (iOS only; no-op on Android).
setDebug(debug) Toggle native-library console logging.

Platform-specific setup #

Android #

Add the INTERNET permission to your AndroidManifest.xml (required for uploading logs):

<!-- android/app/src/main/AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />

Retention (maxReversedDate), max single-file size (maxFileLen), and the free-storage floor (minSDCardBytes) are all fixed at init() time on Android.

iOS #

No extra setup is required — the plugin uses standard APIs. Note that minSDCardBytes has no effect on iOS: the native library refuses to write once free disk space drops below a hardcoded 5MB.

License #

MIT

1
likes
150
points
234
downloads

Documentation

API reference

Publisher

verified publisherjannix.online

Weekly Downloads

A high-performance, robust log plugin for Flutter based on Meituan Logan. Fixed truncation and decryption issues.

Repository (GitHub)

Topics

#logging #performance #monitoring

License

MIT (license)

Dependencies

flutter, flutter_lifecycle_detector, path_provider

More

Packages that depend on flutter_logan_pro

Packages that implement flutter_logan_pro