logiq 1.0.0-beta.4
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
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 #

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) {
// ...
}
Navigation Observer #
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 #
- Initialize Early - Call
Logiq.init()beforerunApp() - Use Categories - Group related logs with meaningful categories
- Add Context - Include relevant data in the context map
- Redact PII - Always configure redaction patterns for production
- Set Min Level - Use
LogLevel.infoor higher in production - Monitor Storage - Check
getStats()to track disk usage - Export Regularly - Set up periodic exports for analysis
- Handle Errors Silently - Logiq never crashes your app
π API Reference #
Logging Methods #
Logiq.v(category, message, [context])- VerboseLogiq.d(category, message, [context])- DebugLogiq.i(category, message, [context])- InfoLogiq.w(category, message, [context])- WarningLogiq.e(category, message, [context])- ErrorLogiq.f(category, message, [context])- Fatal
Management #
Logiq.init({config})- Initialize LogiqLogiq.flush()- Force flush to diskLogiq.clear()- Clear all logsLogiq.clearOlderThan(duration)- Clear old logsLogiq.getStats()- Get statisticsLogiq.export({options})- Export logsLogiq.dispose()- Cleanup on app exit
Configuration #
Logiq.setMinLevel(level)- Change min levelLogiq.setEnabled(bool)- Enable/disable loggingLogiq.addRedaction(pattern)- Add redaction patternLogiq.enterSensitiveMode()- Pause loggingLogiq.exitSensitiveMode()- Resume loggingLogiq.sensitive(callback)- Execute with logging paused
UI #
Logiq.showDebugButton(context)- Show floating buttonLogiq.hideDebugButton()- Hide floating buttonLogiq.openViewer(context)- Open log viewer
Properties #
Logiq.isEnabled- Check if logging is enabledLogiq.logDirectory- Get log directory pathLogiq.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
pointycastlefor encryption - Uses
path_providerfor file access - Compression via
archivepackage
Made with β€οΈ by Ricky-Irfandi