pd_log 0.5.1 copy "pd_log: ^0.5.1" to clipboard
pd_log: ^0.5.1 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,
      fileLoggingMinLevel: LogLevel.info,
      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.d(
                            '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.d(
                            '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.d(
                            '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.d(
                            '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.d('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: true,
                          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'),
                    ),
                    ElevatedButton(
                      onPressed: _runStressTest,
                      child: const Text('压力测试(1000 条)'),
                    ),
                  ],
                ),
                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),
                        );
                      },
                    ),
                  ),
                ),
                // 预览使用新页面
              ],
            ),
          ),
        ),
      ),
    );
  }

  /// 示例:从辅助方法写入一条日志。
  void _logFromHelper() async {
    /// json美化打印
    final Map arg = {
      'name': 'pedro',
      'age': 30,
      'height': 183,
      'no.': '9527',
      'nickName': ['大头', '狗蛋'],
      1: '下标',
      'active': true,
      'balance': 1234.56,
      'createdAt': DateTime.now(),
      'tags': {'flutter', 'dart', '日志'},
      'addresses': [
        {'type': 'home', 'city': '上海', 'zip': 200000},
        {'type': 'work', 'city': '北京', 'zip': 100000},
      ],
      'preferences': {
        'notifications': {'email': true, 'sms': false},
        'theme': {'mode': 'dark', 'accentColor': '#00FFFF'},
      },
      'metrics': {
        'cpu': [0.12, 0.34, 0.56],
        'mem': {'used': 2048, 'total': 8192},
        'latencyMs': [12, 25, 8, 16],
      },
      'mixedList': [
        null,
        '文本',
        42,
        3.1415,
        {
          'nested': [
            'a',
            'b',
            {'deep': 1}
          ]
        },
      ],
      'mapWithNonStringKeys': {1: '一', true: '真', 3.14: 'π'},
      'emoji': '🚀🔥',
      'longText': '这是一个多行文本\n用于测试换行\n以及格式化效果',
      'url': Uri.parse('https://example.com/api?v=1'),
      'bigInt': BigInt.from(1234567890123456789),
      'mapDepthTest': {
        'level1': {
          'level2': {
            'level3': {
              'list': [
                1,
                2,
                {'level4': 'ok'}
              ],
            },
          },
        },
      },
    };
    final list = [arg, arg];
    PDLog.formated(list, level: LogLevel.info, toFile: true);

    /// 检查指定日期的日志文件
    final date = DateTime(2025, 7, 5);
    final dayPath = await PDLog.logFilePathIfExists(date);

    if (dayPath.isNotEmpty) {
      // 文件存在
      _readFileContent(PDLogFile(
        path: dayPath,
        sizeBytes: 1024,
        modifiedMs: 1760172725000,
      ));
    } else {
      PDLog.e('${date.toIso8601String()}日期没有日志文件.');
    }

    final logFiles = await PDLog.listLogFilesByYear(2025);
    PDLog.v(logFiles);
  }

  /// 压力测试:批量写入大量日志以验证缓冲与刷新表现。
  ///
  /// 注意:为避免控制台性能影响,此处禁用控制台输出,仅写入原生缓冲与文件。
  Future<void> _runStressTest() async {
    const total = 1000;
    final started = DateTime.now();
    PDLog.v('压力测试开始: $total 条, started=$started', tag: 'Stress');

    for (var i = 0; i < total; i++) {
      final msg = '压力测试第 $i 条消息\n多行样式验证:第 ${(i % 3) + 1} 行';
      // 使用自定义输出以覆盖控制台/文件开关,避免刷屏影响性能
      PDLog.out(
        msg,
        tag: 'Stress',
        useConsole: false,
        useNative: true,
        toFile: false,
        showTimestamp: true,
        style: const LogStyleConfig(
          foreground: 36, // 青色
          styles: [1], // 粗体
        ),
      );
    }

    await PDLog.flushNativeLogs();
    final elapsed = DateTime.now().difference(started);
    PDLog.v('压力测试完成,用时 ${elapsed.inMilliseconds} ms', tag: 'Stress');
    _refreshFiles();
  }

  /// 应用当前配置到原生日志系统(刷新间隔、缓冲阈值等)。
  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.d(e);
    }
  }

  /// 列出所有日志文件,计算总大小并更新界面。
  Future<void> _refreshFiles() async {
    final files = await PDLog.listLogFiles();
    for (PDLogFile e in files) {
      PDLog.d(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} ===');
          PDLog.v(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();
  }
}
0
likes
140
points
0
downloads

Publisher

unverified uploader

Weekly Downloads

Cross-platform logging plugin for Flutter with buffered native file logging.

Repository

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_web_plugins, plugin_platform_interface, stack_trace

More

Packages that depend on pd_log

Packages that implement pd_log