logiq 1.0.0-beta.4 copy "logiq: ^1.0.0-beta.4" to clipboard
logiq: ^1.0.0-beta.4 copied to clipboard

Zero-impact, fire-and-forget local logging system for Flutter. Enterprise-grade with encryption, rotation, and debug UI.

Logiq #

Zero-impact, fire-and-forget local logging system for Flutter

Pub Version License: MIT

Logiq is an enterprise-grade logging solution designed for Flutter apps with a focus on zero UI impact. All heavy operations are offloaded to isolates, ensuring your logging never blocks the main thread.

🎬 Demo #

Logiq Demo

Beautiful log viewer with light/dark themes, real-time filtering, search, and native share integration

✨ Features #

  • ⚑ Zero Impact - Log calls return instantly (~0.001ms), heavy work happens in isolates
  • πŸ”₯ Fire & Forget - Just call Logiq.i(), we handle the rest
  • πŸ”’ Enterprise Security - AES-256-GCM encryption and PII redaction
  • πŸ“¦ Multiple Formats - PlainText, JSON, Compact JSON, CSV, or custom
  • πŸ”„ Smart Rotation - Multi-file or single-file strategies
  • πŸ“Š Debug UI - Built-in log viewer with search and filters
  • πŸ“€ Export - GZip compressed exports with device info
  • 🎯 Session Tracking - Correlate logs across sessions
  • πŸ’Ύ Auto-Cleanup - Retention policies for old logs
  • 🎨 Customizable - Formatters, sinks, hooks, and themes

πŸ“± Platform Support #

Logiq currently supports iOS, Android, macOS, Windows, and Linux.

Web Platform: Logiq is not currently supported on web due to its dependency on dart:io for file system operations. If you need web support, consider:

  • Using console-only logging (ConsoleSink) for web builds
  • Using conditional imports to swap implementations based on platform
  • Contributing a web-compatible file storage implementation

πŸš€ Quick Start #

Installation #

Add to your pubspec.yaml:

dependencies:
  logiq: ^1.0.0-beta.4

Basic Usage #

import 'package:flutter/material.dart';
import 'package:logiq/logiq.dart';

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

  // Initialize Logiq
  await Logiq.init();

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    // Log at different levels
    Logiq.v('APP', 'Verbose message');
    Logiq.d('APP', 'Debug message');
    Logiq.i('APP', 'Info message');
    Logiq.w('APP', 'Warning message');
    Logiq.e('APP', 'Error message');
    Logiq.f('APP', 'Fatal message');

    return MaterialApp(
      title: 'My App',
      home: const HomePage(),
    );
  }
}

With Context #

Logiq.i('BID', 'User placed bid', {
  'bidId': '12345',
  'amount': 1000,
  'currency': 'USD',
});

πŸ“– Configuration #

Auto Configuration #

// Automatically selects best config for debug/release
await Logiq.init(config: LogConfig.auto());

Custom Configuration #

await Logiq.init(
  config: LogConfig(
    minLevel: LogLevel.info,
    bufferSize: 500,
    flushInterval: const Duration(seconds: 30),
    format: FormatConfig.json(),
    rotation: RotationConfig.multiFile(
      maxFileSize: 2 * 1024 * 1024, // 2MB
      maxFiles: 3,
    ),
    redactionPatterns: [
      RedactionPattern.email,
      RedactionPattern.phone,
      RedactionPattern.creditCard,
    ],
    sinks: [
      const ConsoleSink(useColors: true),
    ],
    contextProviders: [
      () => {'appVersion': '1.0.0'},
    ],
    retention: const RetentionConfig(
      maxAge: Duration(days: 7),
      minEntries: 100,
    ),
    debugViewer: const DebugViewerConfig(enabled: true),
  ),
);

🎨 Formatting #

Available Formats #

// Plain text (human-readable)
format: FormatConfig.plainText()

// JSON (NDJSON - one object per line)
format: FormatConfig.json()

// Compact JSON (shortened keys)
format: FormatConfig.compactJson()

// CSV (spreadsheet-compatible)
format: FormatConfig.csv()

// Custom formatter
format: FormatConfig.custom((entry) =>
  '${entry.timestamp} - ${entry.message}'
)

Format Examples #

PlainText:

[2025-01-15T10:30:45.123Z] [INFO   ] [BID] User placed bid {"bidId":"123"}

JSON:

{"timestamp":"2025-01-15T10:30:45.123Z","level":"INFO","category":"BID","message":"User placed bid","context":{"bidId":"123"}}

Compact JSON:

{"t":1705312245123,"l":2,"c":"BID","m":"User placed bid","x":{"bidId":"123"}}

πŸ”’ Security #

PII Redaction #

config: LogConfig(
  // Defaults include email, phone (intl + ID), credit card, IP, JWT, nopol
  redactionPatterns: RedactionPattern.defaults,
)

Before:

User email is john@example.com and phone is +1234567890

After:

User email is [EMAIL_REDACTED] and phone is [PHONE_REDACTED]

Custom Redaction #

// Add custom pattern
Logiq.addRedaction(
  RedactionPattern(
    name: 'api_key',
    pattern: RegExp(r'api_key=[a-zA-Z0-9]+'),
    replacement: 'api_key=[REDACTED]',
  ),
);

Encryption #

import 'dart:typed_data';

// With key provider (recommended)
encryption: EncryptionConfig.aes256(
  keyProvider: () async {
    // Key provider must return Uint8List; keep this fast to avoid blocking flush
    final keyString = await secureStorage.read(key: 'log_key');
    return Uint8List.fromList(utf8.encode(keyString));
  },
)

// With static key (not recommended for production)
encryption: EncryptionConfig.aes256WithKey(
  key: EncryptionConfig.generateKey(), // Generates 32-byte Uint8List
)

// Generate a secure key
final secureKey = EncryptionConfig.generateKey(); // Returns Uint8List

πŸ”„ File Rotation #

Multi-File Rotation #

rotation: RotationConfig.multiFile(
  maxFileSize: 2 * 1024 * 1024, // 2MB
  maxFiles: 3,                   // Keep 3 backup files plus current
)
// Result: current.log β†’ backup_1.log β†’ backup_2.log β†’ backup_3.log β†’ backup_4.log deleted

Single-File Rotation #

rotation: RotationConfig.singleFile(
  maxFileSize: 5 * 1024 * 1024, // 5MB
  trimPercent: 25,               // Remove oldest 25% when full
)

πŸ“Š Debug UI #

Features #

The built-in log viewer features:

  • Light/Dark Themes - Tap the sun/moon icon to toggle
  • Real-time Updates - Auto-refresh every 15 seconds
  • Smart Filtering - Filter by log level with refined chips
  • Search - Quick search with glassmorphism design
  • Dual Views - Card view or compact text view
  • Export & Share - Native share sheet integration
  • Detail Sheets - Tap any log to see full details with context

Show Floating Debug Button #

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const HomePage(),
      builder: (context, child) {
        WidgetsBinding.instance.addPostFrameCallback((_) {
          Logiq.showDebugButton(context);
        });
        return child!;
      },
    );
  }
}

Open Log Viewer #

ElevatedButton(
  onPressed: () => Logiq.openViewer(context),
  child: const Text('View Logs'),
)

Themes #

The log viewer includes two built-in themes:

// Use dark theme (default)
debugViewer: const DebugViewerConfig(
  enabled: true,
  theme: LogViewerTheme.dark,
)

// Use light theme
debugViewer: const DebugViewerConfig(
  enabled: true,
  theme: LogViewerTheme.light,
)

// Custom theme
debugViewer: DebugViewerConfig(
  enabled: true,
  theme: LogViewerTheme(
    backgroundColor: Color(0xFF000000),
    surfaceColor: Color(0xFF1C1C1E),
    textColor: Color(0xFFF2F2F7),
    accentColor: Color(0xFF007AFF),
    // ... more customization
  ),
)

Note: Users can toggle between light and dark mode at runtime using the sun/moon button in the viewer.

Category Tabs #

Organize logs into custom tabs for easier navigation:

debugViewer: DebugViewerConfig(
  enabled: true,
  tabs: [
    DebugTab(
      name: 'Network',
      categories: ['API', 'HTTP', 'Socket'],
      icon: Icons.wifi,
    ),
    DebugTab(
      name: 'Database',
      categories: ['DB', 'SQL'],
      icon: Icons.storage,
    ),
    DebugTab(
      name: 'Auth',
      categories: ['Auth', 'Login'],
      icon: Icons.lock,
    ),
  ],
)

Features:

  • Automatic "All" tab shows every log
  • Filter logs by category per tab
  • Optional icons for visual identification
  • Horizontal scroll for many tabs
  • Works with level filters and search

πŸ“€ Export #

// Export all logs
final result = await Logiq.export();
print('Exported ${result.entryCount} entries');
print('File: ${result.file.path}');
print('Compressed: ${result.compressionPercent.toStringAsFixed(1)}% saved');

// Export with options
final result = await Logiq.export(
  timeRange: const Duration(hours: 24), // Last 24 hours
  compress: true,                        // GZip compression
  includeDeviceInfo: true,               // Add device metadata
);
// Exports are limited to 50MB uncompressed to protect low-memory devices

πŸ“ˆ Statistics #

final stats = await Logiq.getStats();
print(stats.toString());
// Output:
// LogStats(
//   totalLogged: 1234
//   bufferedCount: 42
//   droppedCount: 0
//   writeFailures: 0
//   storageUsed: 1.23 MB
//   fileCount: 3
//   sessionId: sess_abc123
// )

🎯 Advanced Features #

Sensitive Mode #

Pause logging during sensitive operations:

// Option 1: Explicit control
Logiq.enterSensitiveMode();
// ... sensitive operations ...
Logiq.exitSensitiveMode();

// Option 2: Automatic (recommended)
await Logiq.sensitive(() async {
  // Logs won't be recorded during this callback
  await processPayment();
});

Runtime Configuration #

// Change minimum level
Logiq.setMinLevel(LogLevel.error);

// Disable/enable logging
Logiq.setEnabled(false);

// Check status
if (Logiq.isEnabled) {
  // ...
}

Automatically log navigation events with Logiq.navigationObserver:

MaterialApp(
  navigatorObservers: [Logiq.navigationObserver],
  home: const HomePage(),
)

This logs push, pop, replace, and remove events with context:

  • route: The route name
  • previousRoute: The previous route
  • routeType: MaterialPageRoute, etc.
  • arguments: Route arguments (if any)

Custom configuration:

MaterialApp(
  navigatorObservers: [
    LogiqNavigatorObserver(
      logLevel: LogLevel.debug,
      category: 'NAVIGATION',
      logRouteArguments: true,
    ),
  ],
)

Network Logging #

Log HTTP requests without depending on any HTTP library:

// Quick one-liner (in your interceptor)
Logiq.network(
  method: 'GET',
  url: 'https://api.example.com/users',
  statusCode: 200,
  duration: Duration(milliseconds: 234),
);

// Or use typed objects for detailed logging
Logiq.logRequest(LogiqRequest(method: 'POST', url: '/users', body: {...}));
Logiq.logResponse(LogiqResponse(statusCode: 201, url: '/users'));

Example Dio Interceptor:

class LogiqDioInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    Logiq.logRequest(LogiqRequest(
      method: options.method,
      url: options.uri.toString(),
      headers: options.headers,
      body: options.data,
    ));
    handler.next(options);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    Logiq.logResponse(LogiqResponse(
      statusCode: response.statusCode ?? 0,
      url: response.requestOptions.uri.toString(),
      body: response.data,
      requestMethod: response.requestOptions.method,
    ));
    handler.next(response);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    Logiq.network(
      method: err.requestOptions.method,
      url: err.requestOptions.uri.toString(),
      error: err.message,
    );
    handler.next(err);
  }
}

// Usage
final dio = Dio()..interceptors.add(LogiqDioInterceptor());

Context Providers #

Auto-inject data into every log:

contextProviders: [
  () => {'appVersion': '1.0.0'},
  () => {'userId': currentUser.id},
  () => {'deviceId': deviceId},
]

Hooks & Callbacks #

hooks: LogHooks(
  onLog: (entry) => print('Logged: ${entry.message}'),
  onFlush: (count) => print('Flushed $count entries'),
  onRotate: () => print('Log file rotated'),
  onError: (error, stackTrace) => print('Error: $error'),
)

Custom Sinks #

sinks: [
  const ConsoleSink(useColors: true),
  CustomSink(
    onWrite: (entry) {
      // Send to external service
      analyticsService.track(entry);
    },
  ),
]

Manual Flush #

// Force write buffer to disk
await Logiq.flush();

Cleanup #

// Clear all logs
await Logiq.clear();

// Clear logs older than 7 days
await Logiq.clearOlderThan(const Duration(days: 7));

πŸ“¦ Complete Example #

import 'package:flutter/material.dart';
import 'package:logiq/logiq.dart';

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

  await Logiq.init(
    config: LogConfig(
      minLevel: LogLevel.verbose,
      format: FormatConfig.json(),
      rotation: RotationConfig.multiFile(
        maxFileSize: 2 * 1024 * 1024,
        maxFiles: 3,
      ),
      encryption: EncryptionConfig.aes256WithKey(
        key: EncryptionConfig.generateKey(),
      ),
      redactionPatterns: [
        RedactionPattern.email,
        RedactionPattern.phone,
        RedactionPattern.creditCard,
      ],
      sinks: [
        const ConsoleSink(useColors: true),
      ],
      contextProviders: [
        () => {'appVersion': '1.0.0'},
      ],
      retention: const RetentionConfig(
        maxAge: Duration(days: 7),
        cleanupInterval: Duration(hours: 6),
      ),
      hooks: LogHooks(
        onError: (error, stackTrace) {
          // Handle internal errors
        },
      ),
      debugViewer: const DebugViewerConfig(enabled: true),
    ),
  );

  Logiq.i('APP', 'Application started');

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      home: const HomePage(),
      builder: (context, child) {
        WidgetsBinding.instance.addPostFrameCallback((_) {
          Logiq.showDebugButton(context);
        });
        return child!;
      },
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    // Log page view
    Logiq.i('NAVIGATION', 'HomePage viewed');

    return Scaffold(
      appBar: AppBar(title: const Text('Home')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Logiq.i('BUTTON', 'Button pressed');
            Logiq.openViewer(context);
          },
          child: const Text('View Logs'),
        ),
      ),
    );
  }
}

🎯 Best Practices #

  1. Initialize Early - Call Logiq.init() before runApp()
  2. Use Categories - Group related logs with meaningful categories
  3. Add Context - Include relevant data in the context map
  4. Redact PII - Always configure redaction patterns for production
  5. Set Min Level - Use LogLevel.info or higher in production
  6. Monitor Storage - Check getStats() to track disk usage
  7. Export Regularly - Set up periodic exports for analysis
  8. Handle Errors Silently - Logiq never crashes your app

πŸ“„ API Reference #

Logging Methods #

  • Logiq.v(category, message, [context]) - Verbose
  • Logiq.d(category, message, [context]) - Debug
  • Logiq.i(category, message, [context]) - Info
  • Logiq.w(category, message, [context]) - Warning
  • Logiq.e(category, message, [context]) - Error
  • Logiq.f(category, message, [context]) - Fatal

Management #

  • Logiq.init({config}) - Initialize Logiq
  • Logiq.flush() - Force flush to disk
  • Logiq.clear() - Clear all logs
  • Logiq.clearOlderThan(duration) - Clear old logs
  • Logiq.getStats() - Get statistics
  • Logiq.export({options}) - Export logs
  • Logiq.dispose() - Cleanup on app exit

Configuration #

  • Logiq.setMinLevel(level) - Change min level
  • Logiq.setEnabled(bool) - Enable/disable logging
  • Logiq.addRedaction(pattern) - Add redaction pattern
  • Logiq.enterSensitiveMode() - Pause logging
  • Logiq.exitSensitiveMode() - Resume logging
  • Logiq.sensitive(callback) - Execute with logging paused

UI #

  • Logiq.showDebugButton(context) - Show floating button
  • Logiq.hideDebugButton() - Hide floating button
  • Logiq.openViewer(context) - Open log viewer

Properties #

  • Logiq.isEnabled - Check if logging is enabled
  • Logiq.logDirectory - Get log directory path
  • Logiq.config - Get current configuration

🀝 Contributing #

Contributions are welcome! Please open an issue or submit a pull request.

πŸ“ License #

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

Issues #

If you encounter any issues or have suggestions, please file them in the GitHub Issues.

Changelog #

See CHANGELOG.md for a detailed changelog.

πŸ™ Acknowledgments #

  • Built with ❀️ for the Flutter community
  • Powered by pointycastle for encryption
  • Uses path_provider for file access
  • Compression via archive package

Made with ❀️ by Ricky-Irfandi

1
likes
160
points
287
downloads

Publisher

verified publisherlogique.co.id

Weekly Downloads

Zero-impact, fire-and-forget local logging system for Flutter. Enterprise-grade with encryption, rotation, and debug UI.

Repository (GitHub)
View/report issues

Topics

#logging #encryption #rotation #redaction #retention

Documentation

API reference

License

MIT (license)

Dependencies

archive, crypto, flutter, intl, path, path_provider, pointycastle, share_plus, synchronized

More

Packages that depend on logiq