Flutter日志记录插件cr_logger的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter日志记录插件cr_logger的使用

Flutter插件用于日志记录

  • 简单的日志记录到logcat。
  • 网络请求拦截。
  • 日志导出(JSON格式)。
  • 为“Charles”设置代理。
  • 按级别记录日志。

支持的Dart http客户端插件:

  • ✔️ Dio
  • ✔️ Chopper
  • ✔️ Http from http/http package
  • ✔️ HttpClient from dart:io package

目录

截图

开始使用

  1. 在项目中添加插件:
dependencies:
  cr_logger: ^2.2.0
  1. 初始化日志记录器。在main.dart文件中:
void main()  {
  ...
  CRLoggerInitializer.instance.init(
    theme: ThemeData.light(),
    levelColors: {
      Level.debug: Colors.grey.shade300,
      Level.warning: Colors.orange,
    },
    hiddenFields: [
      'token',
    ],
    logFileName: 'my_logs',
    printLogs: true,
    useCrLoggerInReleaseBuild: false,
    useDatabase: false,
  );
}
  • printLogs - 当printLogstrue时打印所有日志。
  • useCrLoggerInReleaseBuild - 当kReleaseModetrue时,所有日志将被打印并使用数据库。
  • useDatabase - 使用数据库保存日志历史记录。它只会在useCrLoggerInReleaseBuild设置为true时工作。
  • theme - 自定义日志记录器主题。
  • levelColors - 消息类型的颜色(debug, verbose, info, warning, error, wtf)。
  • hiddenFields - 需要替换为字符串’Hidden’的键列表。
  • logFileName - 导出日志时的文件名。
  • maxLogsCount - 每种类型的日志(http, debug, info, error)的最大数量,默认为50。
  • maxDatabaseLogsCount - 保存到数据库的每种类型日志的最大数量,默认为50。
  • logger - 自定义日志记录器。
  • printLogsCompactly - 如果值为false,则除了HTTP日志外,所有日志都将有边框,并且会有一个链接到调用位置和创建时间的时间戳。否则,它只会写入日志消息。默认为true
  1. 可选:初始化检查器:
return MaterialApp(
  home: const MainPage(),
  builder: (context, child) => CrInspector(child: child!),
);
  1. 定义变量:

4.1 appInfo - 可以提供自定义信息以显示在AppInfo页面:

CRLoggerInitializer.instance.appInfo = {
  'Build type': buildType.toString(),
  'Endpoint': 'https/cr_logger/example/',
};

4.2 logFileName - 导出日志时的文件名。

4.3 hiddenFields - 需要隐藏的网络日志头的键列表。

  1. 添加覆盖按钮:
CRLoggerInitializer.instance.showDebugButton(context);
  • button - 自定义浮动按钮。
  • left - X轴起始位置。
  • top - Y轴起始位置。
  1. 支持从json导入日志:
await CRLoggerInitializer.instance.createLogsFromJson(json);
  1. 您可以获取当前的代理设置来初始化Charles:
final proxy = CRLoggerInitializer.instance.getProxySettings();
if (proxy != null) {
  RestClient.instance.init(proxy);
}

使用方法

如果启用了日志记录器,屏幕上会出现一个浮动按钮;它还指示项目的构建号。点击浮动按钮即可显示日志记录器的主要屏幕。也可以通过双击按钮来调用快速操作

快速操作

使用此弹出菜单,您可以快速访问所需的CRLogger选项。通过长按或双击调试按钮调用。

应用信息

允许查看包名应用版本构建版本

清除日志

清除某些日志或所有日志。可以从数据库中清除日志。

显示检查器

如果启用了检查器,则屏幕右侧会出现一个面板,带有大小检查和颜色选择器的按钮。

设置Charles代理

需要设置代理设置以供Charles使用。

搜索

提供日志搜索。可以通过路径搜索HTTP日志。还可以搜索数据库中的日志。

分享日志

与团队分享日志。

创建带参数的日志

您可以创建带有参数的日志。为此,使用{{parameter}}模式来突出显示需要作为参数显示的文本。

例如:

const parameter = 'PARAMETER';
log.d('Debug message with param: {{$parameter}}');
log.v('Verbose message with param: {{$parameter}}');
log.i('Info message with param: {{$parameter}}');
log.e('Error message with param: {{$parameter}}');

现在,只需单击详细信息中的参数值即可复制该值。

动作和值

打开包含动作按钮和值通知器的页面。

动作按钮允许您添加不同的回调进行测试。

  1. 添加动作:
CRLoggerInitializer.instance.addActionButton('Log Hi', () => log.i('Hi'));
CRLoggerInitializer.instance.addActionButton(
  'Log By',
  () => log.i('By'),
  connectedWidgetId: 'some identifier',
);
  1. 通过指定ID移除动作:
CRLoggerInitializer.instance.removeActionsById('some identifier');

值通知器帮助跟踪ValueNotifier类型的变量更改。

  1. 类型通知器:
/// 类型通知器
final boolNotifier = ValueNotifier<bool>(false);
final stringNotifier = ValueNotifier<String>('integer: ');

/// 小部件通知器
final boolWithWidgetNotifier = ValueNotifier<bool>(false);
final boolWidget = ValueListenableBuilder<bool>(
  valueListenable: boolWithWidgetNotifier,
  builder: (_, value, __) => SwitchListTile(
    title: const Text('Bool'),
    subtitle: Text(value.toString()),
    value: value,
    onChanged: (newValue) => boolWithWidgetNotifier.value = newValue,
  ),
);
final textNotifier = ValueNotifier<Text>(
  const Text('Widget text'),
);
final textWidget = ValueListenableBuilder<Text>(
  valueListenable: textNotifier,
  builder: (_, value, __) => Row(
    children: [
      const Text('Icon'),
      const Spacer(),
      value,
      const Spacer(),
    ],
  ),
);
  1. 添加通知器: 如果您只想查看通知器的值,最好使用name + notifier。如果需要更改通知器的值,例如通过开关器,最好添加widget
CRLoggerInitializer.instance.addValueNotifier(
  widget: boolWidget,
);

CRLoggerInitializer.instance.addValueNotifier(
  name: 'Bool',
  notifier: boolNotifier,
);
CRLoggerInitializer.instance.addValueNotifier(
  widget: textWidget,
);
  1. 通过指定ID移除通知器:
CRLoggerInitializer.instance.removeNotifiersById('some identifier');
  1. 清除所有通知器:
CRLoggerInitializer.instance.notifierListClear();
  1. 可选:初始化以下回调:
  • ShareLogsFileCallback - 当需要在应用程序端共享日志文件时:
CRLoggerInitializer.instance.onShareLogsFile = (path) async {
  await Share.shareFiles([path]);
};

设置

在IntelliJ/Studio中,您可以折叠请求/响应体:

Gif showing collapsible body

通过转到Preferences -> Editor -> General -> Console并在Fold console lines that contain下添加这两条规则:,并在Exceptions下添加一条规则:╔╣

Settings

示例代码

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:chopper/chopper.dart' as chopper;
import 'package:cr_logger/cr_logger.dart';
import 'package:cr_logger_example/generated/assets.dart';
import 'package:cr_logger_example/rest_client.dart';
import 'package:cr_logger_example/widgets/example_btn.dart';
import 'package:dio/dio.dart' as dio;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dropzone/flutter_dropzone.dart';
import 'package:http/http.dart' as http;
import 'package:logger/logger.dart';
import 'package:share_plus/share_plus.dart';

Future<void> main() async {
  // 如果主函数是异步的,首先调用此函数
  WidgetsFlutterBinding.ensureInitialized();

  // 首先!初始化日志记录器
  await CRLoggerInitializer.instance.init(
    useDatabase: true,
    theme: ThemeData.light(),
    levelColors: {
      Level.debug: Colors.lightGreenAccent,
      Level.warning: Colors.orange,
      Level.trace: Colors.blueAccent,
      Level.info: Colors.blueAccent,
      Level.error: Colors.red,
      Level.fatal: Colors.red.shade900,
      Level.off: Colors.grey.shade300,
      Level.all: Colors.grey.shade300,
    },
    hiddenFields: [
      'Test',
      'Test3',
      'Test7',
      'freeform',
      'qwe',
    ],
    hiddenHeaders: [
      'content-type',
      'Test3',
      'Authorization',
    ],
    logFileName: 'my_logs',

    /// 为了在pub.dev上的web示例中显示日志
    /// https://cleveroad.github.io/cr_logger/#/
    useCrLoggerInReleaseBuild: !kReleaseMode,
  );

  // 第二!定义变量
  CRLoggerInitializer.instance.appInfo = {
    'Build type': 'release',
    'Endpoint': 'https/cr_logger/example/',
  };

  final proxy = CRLoggerInitializer.instance.getProxySettings();
  if (proxy != null) {
    RestClient.instance.initDioProxyForCharles(proxy);
  }

  CRLoggerInitializer.instance.onShareLogsFile = (String path) async {
    await Share.shareXFiles([XFile(path)]);
  };
  runApp(const MyApp());
}

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const MainPage(),
      builder: (context, child) => CrInspector(child: child!),
      theme: ThemeData(fontFamily: 'Epilogue'),
    );
  }
}

class MainPage extends StatefulWidget {
  const MainPage({super.key});

  [@override](/user/override)
  _MainPageState createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  static const platform = MethodChannel('com.cleveroad.cr_logger_example/logs');
  final _debouncer = Debouncer(100);
  late DropzoneViewController _dropCtrl;
  bool _dragging = false;

  [@override](/user/override)
  void initState() {
    super.initState();

    // 第三!初始化调试按钮
    CRLoggerInitializer.instance.showDebugButton(
      context,
      left: 16,
    );

    /// 类型通知器
    final doubleNotifier = ValueNotifier<double>(0);
    final boolNotifier = ValueNotifier<bool>(false);
    final stringNotifier = ValueNotifier<String>('integer: ');

    /// 小部件通知器
    final iconNotifier = ValueNotifier<IconData>(Icons.clear);
    final iconWidget = ValueListenableBuilder<IconData>(
      valueListenable: iconNotifier,
      builder: (_, value, __) => Row(
        children: [
          const Text('Icon'),
          const Spacer(),
          Icon(value),
          const Spacer(),
        ],
      ),
    );
    final textNotifier = ValueNotifier<Text>(
      const Text('Widget text'),
    );
    final textWidget = ValueListenableBuilder<Text>(
      valueListenable: textNotifier,
      builder: (_, value, __) => Row(
        children: [
          const Text('Icon'),
          const Spacer(),
          value,
          const Spacer(),
        ],
      ),
    );

    final integerNotifier = ValueNotifier<int>(0);
    final intWidget = ValueListenableBuilder<int>(
      valueListenable: integerNotifier,
      builder: (_, value, __) => Text('Int: ${value.toString()}'),
    );
    final boolWithWidgetNotifier = ValueNotifier<bool>(false);

    final boolWidget = ValueListenableBuilder<bool>(
      valueListenable: boolWithWidgetNotifier,
      builder: (_, value, __) => SwitchListTile(
        title: const Text('Bool'),
        subtitle: Text(value.toString()),
        value: value,
        onChanged: (newValue) => boolWithWidgetNotifier.value = newValue,
      ),
    );

    /// 类型通知器变化
    Timer.periodic(const Duration(seconds: 1), (_) => integerNotifier.value++);
    Timer.periodic(
      const Duration(milliseconds: 1),
      (_) => doubleNotifier
        ..value += 0.1
        ..value = double.parse(doubleNotifier.value.toStringAsFixed(3)),
    );
    Timer.periodic(
      const Duration(seconds: 1),
      (_) => boolNotifier.value = !boolNotifier.value,
    );
    Timer.periodic(
      const Duration(seconds: 1),
      (_) => stringNotifier.value = 'number: ${integerNotifier.value}',
    );

    /// 小部件通知器变化
    Timer.periodic(
      const Duration(seconds: 1),
      (_) => Icon(boolNotifier.value
          ? Icons.airline_seat_flat
          : Icons.airline_seat_flat_angled),
    );

    Timer.periodic(
      const Duration(seconds: 1),
      (_) => iconNotifier.value = boolNotifier.value
          ? Icons.airline_seat_flat
          : Icons.airline_seat_flat_angled,
    );

    Timer.periodic(
      const Duration(seconds: 1),
      (_) => textNotifier.value = Text(
        'Widget text',
        style: TextStyle(
          color: Colors.black,
          fontSize: 16,
          fontWeight: boolNotifier.value ? FontWeight.bold : FontWeight.normal,
          fontFamily: 'Epilogue',
        ),
      ),
    );

    /// 如果未定义小部件,则使用名称和值。
    CRLoggerInitializer.instance.addValueNotifier(
      widget: intWidget,
    );

    CRLoggerInitializer.instance.addValueNotifier(
      widget: boolWidget,
    );

    CRLoggerInitializer.instance.addValueNotifier(
      name: 'Double',
      notifier: doubleNotifier,
    );
    CRLoggerInitializer.instance.addValueNotifier(
      name: 'Bool',
      notifier: boolNotifier,
    );
    CRLoggerInitializer.instance.addValueNotifier(
      name: 'String',
      notifier: stringNotifier,
    );
    CRLoggerInitializer.instance.addValueNotifier(
      widget: iconWidget,
    );
    CRLoggerInitializer.instance.addValueNotifier(
      widget: textWidget,
    );

    /// 动作
    CRLoggerInitializer.instance.addActionButton('Log Hi', () => log.i('Hi'));
    CRLoggerInitializer.instance.addActionButton('Log By', () => log.i('By'));
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF3F5F6),
      body: SafeArea(
        child: Column(
          children: <Widget>[
            const SizedBox(height: 20),
            const Text(
              'CR Logger example app',
              style: TextStyle(
                fontSize: 17,
                fontWeight: FontWeight.w600,
              ),
            ),
            const SizedBox(height: 28),
            Expanded(
              child: Center(
                child: SingleChildScrollView(
                  padding: const EdgeInsets.symmetric(horizontal: 16),
                  child: ConstrainedBox(
                    constraints: const BoxConstraints(maxWidth: 592),
                    child: Column(
                      children: [
                        if (kIsWeb) ...[
                          Container(
                            height: 200,
                            decoration: BoxDecoration(
                              color: _dragging
                                  ? Colors.white.withOpacity(0.4)
                                  : Colors.white,
                              borderRadius: BorderRadius.circular(10),
                            ),
                            child: Stack(
                              children: [
                                const Center(
                                  child: Text(
                                    'drop logs here',
                                    style: TextStyle(
                                      fontWeight: FontWeight.w500,
                                      fontSize: 14,
                                      color: Colors.black,
                                    ),
                                  ),
                                ),
                                DropzoneView(
                                  key: UniqueKey(),
                                  operation: DragOperation.move,
                                  onCreated: (ctrl) => _dropCtrl = ctrl,
                                  onDrop: _onDrop,
                                  onHover: _onHover,
                                  onLeave: _onLeave,
                                ),
                              ],
                            ),
                          ),
                          const SizedBox(height: 12),
                        ],
                        Row(
                          children: [
                            Expanded(
                              child: ExampleBtn(
                                text: 'Make HTTP request',
                                assetName: Assets.assetsIcHttp,
                                onTap: _makeHttpRequest,
                              ),
                            ),
                            const SizedBox(width: 12),
                            Expanded(
                              child: ExampleBtn(
                                text: 'Make JSON log',
                                assetName: Assets.assetsIcJson,
                                onTap: _makeLogJson,
                              ),
                            ),
                          ],
                        ),
                        const SizedBox(height: 12),
                        Row(
                          children: [
                            Expanded(
                              child: ExampleBtn(
                                text: 'Log debug',
                                assetName: Assets.assetsIcDebug,
                                onTap: _makeLogDebug,
                              ),
                            ),
                            const SizedBox(width: 12),
                            Expanded(
                              child: ExampleBtn(
                                text: 'Log debug native',
                                assetName: Assets.assetsIcDebugNative,
                                onTap: kIsWeb ? null : _makeLogDebugNative,
                              ),
                            ),
                          ],
                        ),
                        const SizedBox(height: 12),
                        Row(
                          children: [
                            Expanded(
                              child: ExampleBtn(
                                text: 'Log warning',
                                assetName: Assets.assetsIcWarning,
                                onTap: _makeLogWarning,
                              ),
                            ),
                            const SizedBox(width: 12),
                            Expanded(
                              child: ExampleBtn(
                                text: 'Log warning native',
                                assetName: _getWarningNativeAsset(),
                                onTap: kIsWeb ? null : _makeLogWarningNative,
                              ),
                            ),
                          ],
                        ),
                        const SizedBox(height: 12),
                        Row(
                          children: [
                            Expanded(
                              child: ExampleBtn(
                                text: 'Log error',
                                assetName: Assets.assetsIcError,
                                onTap: _makeLogError,
                              ),
                            ),
                            const SizedBox(width: 12),
                            Expanded(
                              child: ExampleBtn(
                                text: 'Log error native',
                                assetName: _getErrorNativeAsset(),
                                onTap: kIsWeb ? null : _makeLogErrorNative,
                              ),
                            ),
                          ],
                        ),
                        const SizedBox(height: 12),
                        Row(
                          children: [
                            Expanded(
                              child: ExampleBtn(
                                text: 'Log debug with params',
                                assetName: Assets.assetsIcDebug,
                                onTap: _makeLogDebugWithParam,
                              ),
                            ),
                            const SizedBox(width: 12),
                            Expanded(
                              child: ExampleBtn(
                                text: 'Make native JSON log',
                                assetName: _getErrorNativeAsset(),
                                onTap: kIsWeb ? null : _makeNativeLogJson,
                              ),
                            ),
                          ],
                        ),
                        const SizedBox(height: 12),
                        Row(
                          children: [
                            Expanded(
                              child: ExampleBtn(
                                text: 'Log debug with toast',
                                assetName: Assets.assetsIcDebug,
                                onTap: _makeLogDebugWithToast,
                              ),
                            ),
                            const SizedBox(width: 12),
                            const Expanded(child: SizedBox()),
                          ],
                        ),
                        const SizedBox(height: 12),
                        ElevatedButton(
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.blueGrey,
                          ),
                          onPressed: () => _toggleDebugButton(context),
                          child: const Text(
                            'Toggle debug button',
                            style: TextStyle(
                              fontWeight: FontWeight.w500,
                              fontSize: 14,
                              color: Colors.white,
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  void _onHover() {
    _debouncer.dispose();
    if (!_dragging) {
      setState(() => _dragging = true);
    }
  }

  void _onLeave() {
    _debouncer.run(() {
      setState(() => _dragging = false);
    });
  }

  void _toggleDebugButton(BuildContext context) {
    if (CRLoggerInitializer.instance.isDebugButtonDisplayed) {
      CRLoggerInitializer.instance.dismissDebugButton();
    } else {
      CRLoggerInitializer.instance.showDebugButton(
        context,
        left: 16,
      );
    }
  }

  /// HTTP请求示例
  Future<void> _makeHttpRequest() async {
    /// Dio包的请求示例
    await _makeDioHttpRequest();

    /// Chopper包的请求示例
    //await _makeChopperHttpRequest();

    /// Http包的请求示例
    //await _makeRegularHttpRequest();

    /// dart:io库的请求示例
    //await _makeHttpClientRequest();
  }

  Future<void> _makeDioHttpRequest() async {
    final queryParameters = <String, dynamic>{
      'freeform': 'test',
      'testParameter': 'test',
    };

    await RestClient.instance.dio.post(
      'https://httpbin.org/anything',
      queryParameters: queryParameters,
      data: {
        'Test': '1',
        'Test2': {
          'Test': [
            {'Test': 'qwe'},
            {'Test': 'qwe'},
          ],
        },
        'Test5': {
          'Test9': [
            {'name': 'qwe'},
            {'Test7': 'qwe'},
          ],
        },
        'Test3': {'qwe': 1},
        'Test4': {'qwe': 1},
      },
      options: dio.Options(
        headers: {
          'Authorization': 'qwewrrq',
          'Test3': 'qwewrrq',
        },
      ),
    );
  }

  //忽略:未使用的元素
  Future<void> _makeRegularHttpRequest() async {
    final url = Uri.parse('https://httpbin.org/anything');
    const body = {'Test': '1', 'Test2': 'qwe'};

    /// 如果没有网络连接,http请求将抛出SocketException并且日志不会记录任何内容。
    /// 您可以在try catch块中包装请求并自行记录错误
    try {
      final response = await http.post(url, body: body);
      CRLoggerInitializer.instance.onHttpResponse(response, body);
    } on SocketException catch (error) {
      log.e(error.message);
    }
  }

  //忽略:未使用的元素
  Future<void> _makeHttpClientRequest() async {
    final url = Uri.parse('https://httpbin.org/anything');
    const body = {'Test': '1', 'Test2': 'qwe'};

    /// 如果没有网络连接,http请求将抛出SocketException并且日志不会记录任何内容。
    /// 您可以在try catch块中包装请求并自行记录错误
    final client = HttpClient();
    try {
      final request = await client.postUrl(url);
      request.headers.set(
        HttpHeaders.contentTypeHeader,
        'application/json; charset=UTF-8',
      );
      request.write('{"title": "Foo","body": "Bar", "userId": 99}');
      CRLoggerInitializer.instance.onHttpClientRequest(request, body);

      final response = await request.close();
      CRLoggerInitializer.instance.onHttpClientResponse(
        response,
        request,
        body,
      );
    } on SocketException catch (error) {
      log.e(error.message);
    }
  }

  //忽略:未使用的元素
  Future<void> _makeChopperHttpRequest() async {
    /// 如果没有网络连接,Chopper将记录请求,然后会收到SocketException并且不会记录响应。
    /// 这会导致请求在日志中保持“正在发送”状态。
    await RestClient.instance.chopper.send(chopper.Request(
      'POST',
      Uri.parse('https://httpbin.org/anything'),
      Uri.parse(''),
      headers: {
        'Authorization': 'qwewrrq',
        'Test3': 'qwewrrq',
      },
      body: {'Test': '1', 'Test2': 'qwe'},
    ));
  }

  String _getWarningNativeAsset() {
    if (kIsWeb) {
      return Assets.assetsIcWarning;
    } else if (Platform.isIOS) {
      return Assets.assetsIcWarningIos;
    } else {
      return Assets.assetsIcWarningAndroid;
    }
  }

  String _getErrorNativeAsset() {
    if (kIsWeb) {
      return Assets.assetsIcError;
    } else if (Platform.isIOS) {
      return Assets.assetsIcErrorIos;
    } else {
      return Assets.assetsIcErrorAndroid;
    }
  }

  void _makeLogDebug() {
    log
      ..d('Debug message at ${DateTime.now().toIso8601String()}')
      ..t('Trace message at ${DateTime.now().toIso8601String()}');
  }

  void _makeLogDebugWithToast() {
    log.d(
      'Debug message at ${DateTime.now().toIso8601String()}',
      showToast: true,
    );
  }

  void _makeLogDebugWithParam() {
    const token =
        'e9U_NJKzXUtNkom7BTlOSn:APA91bF4vFgc8nsFE2SKt7XLDTdpvPPf6xlGicRXR9sxyu6Wfd48Xm00oh-r3TqGaKyEUixlfE7HYfE62V83skQYwzcvxAi34Lp7a9IxCuVBB9Ovxj-xZm5T_RFbtn_7di7v_dTU0fLD';

    log
      ..d('Debug message with param: {{$token}}')
      ..t('Trace message with param: {{$token}}');
  }

  void _makeLogJson() {
    final data = {
      'a': 1,
      'b': 2,
      'c': 3,
      'str1': 'string1',
      'str2': 'string2',
      'tempData': {
        'a': 1,
        'b': 2,
        'c': 3,
        'str1': 'string1',
        'str2': 'string2',
      },
    };

    log
      ..d(data)
      ..i(data)
      ..e(data);
  }

  void _makeLogWarning() {
    log
      ..w('Warning message at ${DateTime.now().toIso8601String()}')
      ..i('Info message at ${DateTime.now().toIso8601String()}');
  }

  void _makeLogError() {
    log
      ..e(
        'Error message at ${DateTime.now().toIso8601String()}',
        error: const HttpException('message'),
      )
      ..f('Fatal message at ${DateTime.now().toIso8601String()}');
  }

  void _makeLogDebugNative() {
    platform.invokeMethod('debug');
  }

  void _makeLogWarningNative() {
    platform.invokeMethod('info');
  }

  void _makeLogErrorNative() {
    platform.invokeMethod('error');
  }

  void _makeNativeLogJson() {
    platform.invokeMethod('logJson');
  }

  Future<void> _onDrop(dynamic value) async {
    final bytes = await _dropCtrl.getFileData(value);
    try {
      final json = await compute(jsonDecode, utf8.decode(bytes));
      await CRLoggerInitializer.instance.createLogsFromJson(json);
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Logs successfully imported'),
        ),
      );
    } catch (ex) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Unsupported file'),
        ),
      );
    }
    setState(() {
      _dragging = false;
    });
  }
}

class Debouncer {
  Debouncer(this.milliseconds);

  final int milliseconds;
  Timer? _timer;

  void run(VoidCallback action) {
    dispose();
    _timer = Timer(Duration(milliseconds: milliseconds), action);
  }

  void dispose() {
    _timer?.cancel();
  }
}

更多关于Flutter日志记录插件cr_logger的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter日志记录插件cr_logger的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用cr_logger插件进行日志记录的示例代码。cr_logger是一个流行的Flutter日志记录库,它提供了灵活的日志记录功能,支持不同的日志级别(如INFO、DEBUG、ERROR等),并可以将日志输出到控制台、文件或其他目标。

步骤 1: 添加依赖

首先,在你的pubspec.yaml文件中添加cr_logger依赖:

dependencies:
  flutter:
    sdk: flutter
  cr_logger: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

步骤 2: 配置Logger

在你的Flutter应用的入口文件(通常是main.dart)中配置Logger。以下是一个简单的配置示例:

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

void main() {
  // 配置Logger
  final logger = Logger(
    printers: [
      // 将日志输出到控制台
      PrettyPrinter(),
      // 将日志输出到文件(可选)
      // FilePrinter(outputFilePath: 'app_logs.txt'),
    ],
    level: LogLevel.verbose, // 设置日志级别
  );

  // 使用Logger记录一条信息
  logger.i('应用启动');

  runApp(MyApp(logger: logger));
}

class MyApp extends StatelessWidget {
  final Logger logger;

  MyApp({required this.logger});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(logger: logger),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final Logger logger;

  MyHomePage({required this.logger});

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo Home Page'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // 在按钮点击时记录日志
            widget.logger.d('按钮被点击');
            widget.logger.w('这是一个警告');
            widget.logger.e('这是一个错误');
          },
          child: Text('记录日志'),
        ),
      ),
    );
  }
}

解释

  1. 配置Logger:在main函数中,我们创建了一个Logger实例,并配置了两个printers:一个PrettyPrinter用于将日志输出到控制台,另一个可选的FilePrinter用于将日志输出到文件。你可以根据需要添加或移除printers。我们还设置了日志级别为LogLevel.verbose,这意味着将记录所有级别的日志。

  2. 使用Logger:在应用的各个部分,我们通过传递Logger实例来使用它。例如,在MyAppMyHomePage构造函数中接收Logger实例,并在需要记录日志的地方调用相应的日志方法(如logger.ilogger.dlogger.wlogger.e等)。

日志级别

  • logger.vlogger.verbose:详细日志,通常用于调试。
  • logger.dlogger.debug:调试日志,用于开发过程中的信息记录。
  • logger.ilogger.info:信息日志,用于记录一般信息。
  • logger.wlogger.warn:警告日志,用于记录潜在的有害情况。
  • logger.elogger.error:错误日志,用于记录错误或异常。

通过上述代码,你可以轻松地在Flutter应用中使用cr_logger进行日志记录。根据你的需求,你可以进一步自定义Logger的配置,如添加更多的printers或调整日志级别。

回到顶部