pd_log 0.2.2
pd_log: ^0.2.2 copied to clipboard
Cross-platform logging plugin for Flutter with buffered native file logging.
example/lib/main.dart
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:pd_log/pd_log.dart';
/// 应用入口:启动示例应用。
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
/// 构造函数:创建应用根部件。
const MyApp({super.key});
@override
/// 创建应用的状态对象。
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
final _pdLogPlugin = PDLog();
bool _useConsole = true;
bool _useNative = false;
LogLevel _minLevel = LogLevel.verbose;
bool _showCaller = true;
bool _showTimestamp = true;
// 文件日志相关状态
bool _nativeFileEnabled = true;
final _flushIntervalCtrl = TextEditingController(text: '2000');
final _maxEntriesCtrl = TextEditingController(text: '100');
final _maxBytesCtrl = TextEditingController(text: '65536');
String? _logRootPath;
List<PDLogFile> _files = [];
int _totalSize = 0;
// 预览内容改为新页面展示
@override
/// 初始化:配置日志并拉取平台信息与初始文件列表。
void initState() {
super.initState();
// Configure logging defaults.
PDLog.configure(PDLogConfig(
defaultTag: 'Example 测试项目',
minLevel: _minLevel,
useNative: _useNative,
useConsole: _useConsole,
showCaller: _showCaller,
showTimestamp: _showTimestamp,
nativeFileLoggingEnabled: _nativeFileEnabled,
nativeFileLoggingFlushIntervalMs:
int.tryParse(_flushIntervalCtrl.text) ?? 2000,
nativeFileLoggingMaxBufferEntries:
int.tryParse(_maxEntriesCtrl.text) ?? 100,
nativeFileLoggingMaxBufferBytes:
int.tryParse(_maxBytesCtrl.text) ?? 65536,
));
PDLog.i('App starting...');
initPlatformState();
_refreshRootPath();
_refreshFiles();
}
// Platform messages are asynchronous, so we initialize in an async method.
/// 异步获取平台版本信息并更新界面状态。
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
// We also handle the message potentially returning null.
try {
platformVersion =
await _pdLogPlugin.getPlatformVersion() ?? 'Unknown platform version';
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
@override
/// 构建应用界面,包括日志配置与文件列表展示。
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Running on: $_platformVersion'),
const SizedBox(height: 12),
Row(
children: [
const Text('Use console logging'),
Switch(
value: _useConsole,
onChanged: (v) {
setState(() {
_useConsole = v;
PDLog.updateConfigure(
useConsole: _useConsole,
);
PDLog.i(
'Console logging: ${_useNative ? 'ON' : 'OFF'}',
);
});
},
),
],
),
const SizedBox(height: 12),
Row(
children: [
const Text('Use native logging'),
Switch(
value: _useNative,
onChanged: (v) {
setState(() {
_useNative = v;
PDLog.updateConfigure(
useNative: _useNative,
);
PDLog.i(
'Native logging: ${_useNative ? 'ON' : 'OFF'}',
);
});
},
),
],
),
const SizedBox(height: 12),
Row(
children: [
const Text('Show caller info'),
Switch(
value: _showCaller,
onChanged: (v) {
setState(() {
_showCaller = v;
PDLog.updateConfigure(
showCaller: _showCaller,
);
PDLog.i(
'Show caller: ${_showCaller ? 'ON' : 'OFF'}',
);
});
},
),
],
),
const SizedBox(height: 12),
Row(
children: [
const Text('Show Timestamp'),
Switch(
value: _showTimestamp,
onChanged: (v) {
setState(() {
_showTimestamp = v;
PDLog.updateConfigure(
showTimestamp: _showTimestamp,
);
PDLog.i(
'Show Timestamp: ${_showTimestamp ? 'ON' : 'OFF'}',
);
});
},
),
],
),
const SizedBox(height: 12),
Row(
children: [
const Text('Min level:'),
const SizedBox(width: 8),
DropdownButton<LogLevel>(
value: _minLevel,
onChanged: (level) {
if (level == null) return;
setState(() {
_minLevel = level;
PDLog.updateConfigure(
minLevel: _minLevel,
);
PDLog.i('Min level set to $level');
});
},
items: const [
DropdownMenuItem(
value: LogLevel.verbose,
child: Text('Verbose'),
),
DropdownMenuItem(
value: LogLevel.debug,
child: Text('Debug'),
),
DropdownMenuItem(
value: LogLevel.info,
child: Text('Info'),
),
DropdownMenuItem(
value: LogLevel.warn,
child: Text('Warn'),
),
DropdownMenuItem(
value: LogLevel.error,
child: Text('Error'),
),
],
),
],
),
const SizedBox(height: 16),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
ElevatedButton(
onPressed: () => PDLog.v('Verbose pressed'),
child: const Text('Verbose'),
),
ElevatedButton(
onPressed: () => PDLog.d('Debug pressed'),
child: const Text('Debug'),
),
ElevatedButton(
onPressed: () => PDLog.i('Info pressed'),
child: const Text('Info'),
),
ElevatedButton(
onPressed: () => PDLog.w('Warn pressed'),
child: const Text('Warn'),
),
ElevatedButton(
onPressed: () => PDLog.e('Error pressed'),
child: const Text('Error'),
),
ElevatedButton(
onPressed: () {
PDLog.out(
'自定义的错误警告输出: Customize pressed \n 测试换行后的文本样式',
tag: '自定义输出',
useConsole: false,
useNative: true,
toFile: false,
showTimestamp: false,
style: const LogStyleConfig(
foreground: 37, // 白色文本
background: 41, // 红色背景
styles: [1, 4, 5], // 粗体, 下划线, 闪烁
),
);
},
child: const Text('Customize'),
),
ElevatedButton(
onPressed: _logFromHelper,
child: const Text('Log from helper method'),
),
],
),
const Divider(height: 32),
const Text(
'Native File Logging',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Row(
children: [
const Text('Enable native file logging'),
Switch(
value: _nativeFileEnabled,
onChanged: (v) {
setState(() {
_nativeFileEnabled = v;
_applyConfig();
});
},
),
],
),
const SizedBox(height: 8),
Row(children: [
const SizedBox(
width: 140,
child: Text('Flush interval (ms)'),
),
Expanded(
child: TextField(
controller: _flushIntervalCtrl,
keyboardType: TextInputType.number,
),
),
]),
const SizedBox(height: 8),
Row(children: [
const SizedBox(
width: 140,
child: Text('Max buffer entries'),
),
Expanded(
child: TextField(
controller: _maxEntriesCtrl,
keyboardType: TextInputType.number,
),
),
]),
const SizedBox(height: 8),
Row(children: [
const SizedBox(
width: 140,
child: Text('Max buffer bytes'),
),
Expanded(
child: TextField(
controller: _maxBytesCtrl,
keyboardType: TextInputType.number,
),
),
]),
const SizedBox(height: 8),
Wrap(spacing: 8, children: [
ElevatedButton(
onPressed: _applyConfig,
child: const Text('应用配置'),
),
ElevatedButton(
onPressed: _flushNow,
child: const Text('立即刷新写盘'),
),
ElevatedButton(
onPressed: _refreshRootPath,
child: const Text('获取日志根路径'),
),
ElevatedButton(
onPressed: _refreshFiles,
child: const Text('列出日志文件'),
),
ElevatedButton(
onPressed: _deleteAllLogs,
child: const Text('删除全部日志'),
),
]),
const SizedBox(height: 8),
Text('日志根路径: ${_logRootPath ?? '(当前平台不支持或目录未创建)'}'),
const SizedBox(height: 8),
Text('当前总日志大小: $_totalSize 字节'),
const SizedBox(height: 8),
SizedBox(
height: 240,
child: Card(
child: ListView.builder(
itemCount: _files.length,
itemBuilder: (context, index) {
final f = _files[index];
return ListTile(
title: Text(f.path),
subtitle: Text(
'size=${f.sizeBytes}B modified=${DateTime.fromMillisecondsSinceEpoch(f.modifiedMs)}',
),
trailing: IconButton(
icon: const Icon(Icons.delete_outline),
onPressed: () => _deleteFile(f),
),
onTap: () => _readFileContent(f),
);
},
),
),
),
// 预览使用新页面
],
),
),
),
),
);
}
/// 示例:从辅助方法写入一条 Debug 日志。
void _logFromHelper() {
PDLog.d('This log originates from a helper method');
}
/// 应用当前配置到原生日志系统(刷新间隔、缓冲阈值等)。
void _applyConfig() {
final flushMs = int.tryParse(_flushIntervalCtrl.text) ?? 2000;
final maxEntries = int.tryParse(_maxEntriesCtrl.text) ?? 100;
final maxBytes = int.tryParse(_maxBytesCtrl.text) ?? 65536;
PDLog.updateConfigure(
nativeFileLoggingEnabled: _nativeFileEnabled,
nativeFileLoggingFlushIntervalMs: flushMs,
nativeFileLoggingMaxBufferEntries: maxEntries,
nativeFileLoggingMaxBufferBytes: maxBytes,
);
PDLog.i(
'Applied file logging config: enabled=$_nativeFileEnabled, interval=${flushMs}ms, entries=$maxEntries, bytes=$maxBytes',
);
}
/// 立即触发原生侧刷新,将缓冲区写入磁盘并刷新文件列表。
Future<void> _flushNow() async {
await PDLog.flushNativeLogs();
_refreshFiles();
}
/// 获取日志根路径并打印年份目录(仅一级目录)。
Future<void> _refreshRootPath() async {
final path = await PDLog.logRootPath();
setState(() => _logRootPath = path);
final years = await PDLog.listYearFolders();
for (String e in years) {
PDLog.i(e);
}
}
/// 列出所有日志文件,计算总大小并更新界面。
Future<void> _refreshFiles() async {
final files = await PDLog.listLogFiles();
for (PDLogFile e in files) {
PDLog.i(e.toJson());
}
final total = files.fold<int>(0, (sum, f) => sum + f.sizeBytes);
setState(() {
_files = files;
_totalSize = total;
});
}
/// 读取指定日志文件内容并打印到控制台。
Future<void> _readFileContent(PDLogFile f) async {
try {
final file = File(f.path);
if (await file.exists()) {
final content = await file.readAsString();
if (kDebugMode) {
print('=== 文件内容: ${f.path} ===');
print(content);
print('=== 文件内容结束 ===');
}
} else {
if (kDebugMode) {
print('文件不存在: ${f.path}');
}
}
} catch (e) {
if (kDebugMode) {
print('读取文件失败: $e');
}
}
}
/// 删除指定日志文件并刷新列表。
Future<void> _deleteFile(PDLogFile f) async {
final ok = await PDLog.deleteLogFile(f.path);
if (ok) {
_refreshFiles();
}
}
/// 删除所有日志文件并刷新列表。
Future<void> _deleteAllLogs() async {
final n = await PDLog.deleteAllLogFiles();
PDLog.w('Deleted $n log files');
_refreshFiles();
}
}