flex_logger_file 1.0.0
flex_logger_file: ^1.0.0 copied to clipboard
File logging provider for FlexLogger - writes logs to local files with rotation support
example/lib/main.dart
import 'package:flex_logger/flex_logger.dart';
import 'package:flex_logger_file/flex_logger_file.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
/// File logging strategies available in the example.
enum StrategyType {
rotating('Rotating Files', 'Size-based rotation (recommended)'),
singleFile('Single File', 'One file with optional cleanup'),
timeBased('Time-based', 'Daily log files')
;
final String label;
final String description;
const StrategyType(this.label, this.description);
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final appDocDir = await getApplicationDocumentsDirectory();
final logDir = path.join(appDocDir.path, 'logs');
final logPath = path.join(logDir, 'example_app.log');
// Using rotating file strategy (recommended for production)
FlexLogger.instance.configure(
providers: [
FileLoggerProvider.rotating(
filePath: logPath,
maxFileSize: 2 * 1024 * 1024, // 2 MB per file
maxBackupCount: 5, // Keep 5 backup files
filter: const MinLevelFilter(FlexLogLevel.debug),
),
],
);
await FlexLogger.instance.initialize();
FlexLogger.instance.info('Flex Logger File example started');
FlexLogger.instance.debug('Logs are written to file');
runApp(MyApp(logDir: logDir, logPath: logPath));
}
class MyApp extends StatelessWidget {
final String logDir;
final String logPath;
const MyApp({super.key, required this.logDir, required this.logPath});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flex Logger File Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
useMaterial3: true,
),
home: MyHomePage(
title: 'File Example',
logDir: logDir,
logPath: logPath,
),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
final String logDir;
final String logPath;
const MyHomePage({
super.key,
required this.title,
required this.logDir,
required this.logPath,
});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
StrategyType _selectedStrategy = StrategyType.rotating;
@override
void dispose() {
// CRITICAL: Always dispose logger before app terminates
FlexLogger.instance.dispose();
super.dispose();
}
Future<void> _reconfigureLogger() async {
await FlexLogger.instance.dispose();
final providers = <LoggerProvider>[];
// Add file provider based on selected strategy
switch (_selectedStrategy) {
case StrategyType.rotating:
providers.add(
FileLoggerProvider.rotating(
filePath: widget.logPath,
maxFileSize: 2 * 1024 * 1024, // 2 MB per file
maxBackupCount: 5,
filter: const MinLevelFilter(FlexLogLevel.debug),
),
);
case StrategyType.singleFile:
providers.add(
FileLoggerProvider.singleFile(
filePath: widget.logPath,
maxFileSize: 5 * 1024 * 1024, // 5 MB limit
maxAge: const Duration(days: 7), // Keep logs for 7 days
filter: const MinLevelFilter(FlexLogLevel.debug),
),
);
case StrategyType.timeBased:
providers.add(
FileLoggerProvider.timeBased(
directoryPath: widget.logDir,
filePrefix: 'app',
maxAge: const Duration(days: 30), // Keep 30 days
maxFiles: 30, // Or max 30 files
filter: const MinLevelFilter(FlexLogLevel.debug),
),
);
}
FlexLogger.instance.configure(providers: providers);
await FlexLogger.instance.initialize();
FlexLogger.instance.info(
'Logger reconfigured with ${_selectedStrategy.label} strategy',
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Logs are written to file',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'Current path: ${widget.logPath}',
style: Theme.of(context).textTheme.bodySmall,
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
// Strategy selection
_buildStrategySection(),
const SizedBox(height: 24),
const Divider(),
const SizedBox(height: 16),
// All log levels
const Text(
'Test All Log Levels:',
style: TextStyle(fontWeight: FontWeight.w600),
),
const SizedBox(height: 12),
Wrap(
spacing: 8,
runSpacing: 8,
alignment: WrapAlignment.center,
children: [
ElevatedButton(
onPressed: () => FlexLogger.instance.debug('Debug from button'),
child: const Text('Debug'),
),
ElevatedButton(
onPressed: () => FlexLogger.instance.info('Info from button'),
child: const Text('Info'),
),
ElevatedButton(
onPressed: () => FlexLogger.instance.success('Success from button'),
child: const Text('Success'),
),
ElevatedButton(
onPressed: () => FlexLogger.instance.warning('Warning from button'),
child: const Text('Warning'),
),
ElevatedButton(
onPressed: () => FlexLogger.instance.error('Error from button'),
child: const Text('Error'),
),
ElevatedButton(
onPressed: () => FlexLogger.instance.critical('Critical from button'),
child: const Text('Critical'),
),
],
),
const SizedBox(height: 16),
// Exception handling
ElevatedButton.icon(
onPressed: () {
try {
throw Exception('Demo exception for file logging');
} catch (e, st) {
FlexLogger.instance.error('Error with exception', e, st);
}
},
icon: const Icon(Icons.bug_report),
label: const Text('Log Error with Exception'),
),
],
),
),
),
);
}
Widget _buildStrategySection() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'File Strategy:',
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
),
const SizedBox(height: 12),
// Strategy dropdown
DropdownButton<StrategyType>(
value: _selectedStrategy,
isExpanded: true,
items: StrategyType.values
.map(
(strategy) => DropdownMenuItem(
value: strategy,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
strategy.label,
style: const TextStyle(fontWeight: FontWeight.w500),
),
Text(
strategy.description,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
)
.toList(),
onChanged: (value) {
if (value != null) {
setState(() => _selectedStrategy = value);
}
},
),
const SizedBox(height: 16),
// Apply button
ElevatedButton.icon(
onPressed: _reconfigureLogger,
icon: const Icon(Icons.refresh),
label: const Text('Apply Strategy'),
),
const SizedBox(height: 12),
// Info about current strategy
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Current: ${_selectedStrategy.label}',
style: const TextStyle(fontWeight: FontWeight.w500),
),
const SizedBox(height: 4),
Text(
_getStrategyInfo(),
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
],
),
),
);
}
String _getStrategyInfo() {
return switch (_selectedStrategy) {
StrategyType.rotating => 'Files rotate at 2 MB. Keeps 5 backups (~12 MB total).',
StrategyType.singleFile => 'Single file with 5 MB limit and 7-day age limit.',
StrategyType.timeBased => 'New file each day. Keeps 30 days or 30 files.',
};
}
}