flutter 客户端日志上传定位错误信息

背景

flutter 开发的app 安装到真机上 无法定位报错信息,只能使用usb连接电脑 使用adb logcat来查看日志效率低下。

想法

如果将flutter 开发的app 运行的时候 将日志写进一个日志文件里面去,然后给flutter app搭建一个http服务器,后端知道对应app的ip后,直连对应app 获取日志文件。

效果如下

第一步

获取本机的ip地址 放在个人中心里面

如:

flutter 获取本机ip

方式一:缺点(模拟器获取有时会获取不到) 不推荐 但是必须使用

  for (var interface in await NetworkInterface.list()) {
      for (var addr in interface.addresses) {
        if (addr.type == InternetAddressType.IPv4) {
          address = addr.address.toString();
          print('IPv4 地址: ${addr.address}');
        }
        // else if (addr.type == InternetAddressType.IPv6) {
        //   print('IPv6 地址: ${addr.address}');
        //   // address = addr.address.toString();
        // }
      }
    }

方式二:

dart_ipify dart_ipify | Dart Package 这里是外网的IP(不适用)

import 'package:dart_ipify/dart_ipify.dart';
 final ipv4 = await Ipify.ipv4();
 print(ipv4); // 98.207.254.136

这里声明一下  客户端日志上传只限于局域网  如果是外网 手机的网络ip app 内部是局域网 是访问不到的 

如果是外网 采用用户 手动触发一些列操作  如 去个人中心 点击上报日志 (这个功能需要实现)

第二步

利用shelf  flutter 的插件搭建 app内的服务器

shelf | Dart PackageA model for web server middleware that encourages composition and easy reuse.icon-default.png?t=N7T8https://pub-web.flutter-io.cn/packages/shelf注意端口不能为 80

import 'package:LS/common/extension/custom_ext.dart';
import 'package:LS/common/utils/log.dart';
import 'package:dart_ipify/dart_ipify.dart';
import 'package:flutter/foundation.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;

class Server {
  static String address = "";
  static init() async {
    if (kIsWeb) {
      return;
    }
    for (var interface in await NetworkInterface.list()) {
      for (var addr in interface.addresses) {
        if (addr.type == InternetAddressType.IPv4) {
          address = addr.address.toString();
          print('IPv4 地址: ${addr.address}');
        }
        // else if (addr.type == InternetAddressType.IPv6) {
        //   print('IPv6 地址: ${addr.address}');
        //   // address = addr.address.toString();
        // }
      }
    }

    // String ipv4 = await Ipify.ipv4();
    "当前ip i $address".w();
    // address = ipv4;
    var handler =
        const Pipeline().addMiddleware(logRequests()).addHandler(_echoRequest);

    var server = await shelf_io.serve(handler, address, 8080);

    // Enable content compression
    server.autoCompress = true;

    print('Serving at http://${server.address.host}:${server.port}');
  }

  static Future<Response> _echoRequest(Request request) async {
    switch (request.requestedUri.path) {
      case '/':
        return Response.ok('Hello, World!');
      case "/log":
        var headers = {
          'content-type': 'text/plain',
          'content-disposition': 'attachment; filename="log.txt"'
        };
        String log = await Console.getLogLocalFile();
        return Response.ok(log, headers: headers);
    }

    return Response.ok('Request for "${request.url}"');
  }
}

核心代码就是

import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;

void main() async {
  var handler =
      const Pipeline().addMiddleware(logRequests()).addHandler(_echoRequest);

  var server = await shelf_io.serve(handler, 'localhost', 8080);

  // Enable content compression
  server.autoCompress = true;

  print('Serving at http://${server.address.host}:${server.port}');
}

Response _echoRequest(Request request) =>
    Response.ok('Request for "${request.url}"');

 main中 运行服务器

  if (kDebugMode) {
    Server.init();
  }

日志是 再打印的时候 就会自动进行写入

日志输出 同时写入app文件夹制定文件里面

///输出日志工具类
class Console {
  static const bool _isLog = true; // 是否输出日志标识
  static const bool _isDebug = kDebugMode; // 是否为debug模式
  static const String _logFlag = "app输出日志"; // 输出日志前缀

  ///日志输出,仅dubug模式下生效
  static void d(Object? object) {
    if (_isLog && _isDebug) {
      // print("$_logFlag | $object");
      // developer.log();
      Global.logger.i("$_logFlag | $object");
      if (!kIsWeb) {
        writeLogsToFile("$_logFlag | $object");
      }
    }
  }

  static log(List args) {
    if (_isLog && _isDebug) {
      // print("$_logFlag | ${args.toString()}");
      // developer.log("$_logFlag | ${args.toString()}");
      Global.logger.i("$_logFlag | ${args.toString()}");
      if (!kIsWeb) {
        writeLogsToFile("$_logFlag | ${args.toString()}");
      }
    }
  }

  // 只写日志 不输出
  static void staticWriteLogsToFile(String logs, String type) async {
    writeLogsToFile("$type | ${DateTime.now().toLocal()} | $logs}");
  }

  static Future<String> getLogsDirectory() async {
    Directory appDocDir = await getApplicationDocumentsDirectory();
    print("appDocDir==> $appDocDir");
    String logsDirectory = '${appDocDir.path}/log/api';
    Directory(logsDirectory).createSync(recursive: true);
    return logsDirectory;
  }

  static Future<void> writeLogsToFile(String logMessage) async {
    String logsDirectory = await getLogsDirectory();
    File logFile = File('$logsDirectory/app_logs.txt');
    await logFile.writeAsString('$logMessage\n', mode: FileMode.append);
  }

  static Future getLogLocalFile() async {
    String logsDirectory = await getLogsDirectory();
    File logFile = File('$logsDirectory/app_logs.txt');
    String log = await logFile.readAsString();
    return log;
  }
}

为字符串扩展 方法进行 打印  

extension LogExt on String {
  log() {
    Global.logger.d(this);
    Console.staticWriteLogsToFile(this, "log");
  }

  t() {
    Global.logger.t(this);
    Console.staticWriteLogsToFile(this, "stackTrace");
  }

  info() {
    Global.logger.i(this);
    Console.staticWriteLogsToFile(this, "info");
  }

  errorLog() {
    Global.logger.e("Error", error: this);
    Console.staticWriteLogsToFile(this, "error");
  }

  w() {
    Global.logger.w(this);
    Console.staticWriteLogsToFile(this, "warning");
  }
}

使用如:

"日志 啊啊啊".w();

获取日志

任何可以发送请求的 工具 ,我使用浏览器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/329766.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows 11 系统 安装MySQL

Windows 11 系统 安装MySQL 1.下载2.安装3.配置4.环境变量5.检验 1.下载 网址&#xff1a;https://www.mysql.com/ 2.安装 3.配置 4.环境变量 在电脑上找到系统属性 C:\Program Files\MySQL\MySQL Server 8.0\bin5.检验 Win R 键 mysql -uroot -p

c++多态与虚函数

多态是什么&#xff1f; 多态&#xff08;Polymorphism&#xff09;是面向对象编程中的一个核心概念&#xff0c;它来源于希腊语&#xff0c;意为“多种形态”。 从字面意思理解&#xff0c;多态是指函数有多种形态&#xff08;实现&#xff09;。换句话说&#xff0c;运行阶段…

【项目实战】Postgresql数据库中出现锁表如何解决

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 系列专栏目录 [Java项目…

地震预测系统项目实现

整个项目思路即在一组观测数据中&#xff0c;地震专家&#xff08;即用户&#xff09;输入观测窗口的最小数量和最大数量&#xff0c;进行预测峰值点 数据文件如图所示&#xff1a; #define _CRT_SECURE_NO_WARNINGS #include<fstream> #include<string> #include&…

IOS-高德地图SDK接入-Swift

申请key 这个要前往高德开发平台注册成为个人开发者然后在控制台创建一个应用&#xff1a; 高德开发平台 注册步骤就不写了&#xff0c;写一下创建应用的步骤&#xff1a; 1、点击应用管理——>我的应用 2、点击右上角的创建新应用 3、输入内容&#xff1a; 4、点击添加ke…

在全志T113-i平台上实现H.265视频解码步骤详解

H.265&#xff0c;也被称为HEVC(HighEfficiency Video Coding)&#xff0c;作为H.264的继任者&#xff0c;提供了更好的视频压缩和更高的视频质。H.265通过引入更多先进的编码技术&#xff0c;如更强大的运动估计和更高效的变换编码&#xff0c;对比H.264进行了改进。这些改进使…

设计大师的秘密武器:色彩搭配的奇妙技巧

在设计中&#xff0c;色彩搭配扮演着至关重要的角色。色彩搭配的选择和设计是设计师创作过程中不可或缺的一部分。本文将介绍色彩搭配的重要性&#xff0c;如何设计出令人惊叹的色彩搭配以及色彩对设计师的作用。 色彩卡 | 一个覆盖广泛主题工具的高效在线平台(amd794.com) h…

月薪2W的软件测试工程师,到底是做什么的?

在生活中&#xff0c;我们常常会遇到以下几种窘迫时刻&#xff1a; 准备骑共享单车出行&#xff0c;却发现扫码开锁半天&#xff0c;车子都没有反应&#xff1b;手机导航打车&#xff0c;却发现地图定位偏差很大&#xff0c;司机总是跑错地方&#xff1b;买个水&#xff0c;却…

一个小程序跳转到另一个小程序中如何实现

小程序 保证两个小程序是一样的主体才可以跳转。怎么知道是不是同样的主体呢&#xff1f; 小程序的后台管理-设置-基本设置-基本信息。查看主体信息。 跳转 <button clicktoOtherMini()>跳转到另一个小程序</button> function toOtherMini(){wx.navigateToMini…

Revealing the Dark Secrets of MIM

论文名称&#xff1a; Revealing the Dark Secrets of Masked Image Modeling 发表时间&#xff1a;CVPR2022 作者及组织&#xff1a;Zhenda Xie, Zigang Geng, Hu Han等&#xff0c;来自清华&#xff0c;中科院&#xff0c;微软亚洲研究院。 前言 本文尝试探讨MIM为何有效的原…

数据库的安全管理

数据库的安全管理 一、实验目的 掌握用户账号的创建、查看、修改、删除的方法。掌握用户权限设置方法。掌握角色的创建、删除方法。 二、实验内容用户账号的创建、查看、修改、删除的SQL语句。用户权限设置SQL语句。角色的创建、删除SQL语句。 三、实验步骤在本地主机创建用户…

使用vite框架封装vue3插件,发布到npm

目录 一、vue环境搭建 1、创建App.vue 2、修改main.ts 3、修改vite.config.ts 二、插件配置 1、创建插件 2、开发调试 3、打包配置 4、package.json文件配置 5、执行打包命令 pnpm build 6、修改index.d.ts 目录 一、vue环境搭建 1、创建App.vue 2、修改main.ts 3…

NOIP2018提高组day2 - T1:旅行

题目链接 [NOIP2018 提高组] 旅行 题目描述 小 Y 是一个爱好旅行的 OIer。她来到 X 国&#xff0c;打算将各个城市都玩一遍。 小 Y 了解到&#xff0c;X 国的 n n n 个城市之间有 m m m 条双向道路。每条双向道路连接两个城市。 不存在两条连接同一对城市的道路&#xff…

企事业单位宣传任务的考核稿和投稿有哪些网站?

企事业单位在宣传任务方面扮演着重要角色&#xff0c;他们不仅要向公众展示自己的实力和影响力&#xff0c;也需要提高自己的知名度和形象。在这个信息化时代&#xff0c;涌现出了许多网络平台&#xff0c;为企事业单位提供了更多的宣传机会。本文将介绍一家被广泛认可的投稿平…

模型Model:文件系统模型QFileSystemModel

一、 1、常用函数 QFileSystemModel自带目录变化监听 1)、 QModelIndex setRootPath(const QString &path); 设置检索根目录 2)、 bool isDir(const QModelIndex &index) const; 选中索引是否为目录节点 3)、 QString filePath(const QModelIndex &index) const;…

算法和数据结构--树状数组

概念&#xff1a; 树状数组的初衷是解决状态压缩空间里的累积频率&#xff0c;现在多用于求前缀和与后缀和(方便计算)&#xff0c;它可以以 O(logN)的时间得到任意前缀和&#xff0c;并同时支持在 O(logN)时间内支持动态单点值的修改。空间复杂度 O(N)。 树状数组的引用&#…

如何根据自己的数据集微调一个 Transformer 模型

将通过 NLP 中最常见的文本分类任务来学习如何在自己的数据集上利用迁移学习&#xff08;transfer learning&#xff09;微调一个预训练的 Transformer 模型—— DistilBERT。DistilBERT 是 BERT 的一个衍生版本&#xff0c;它的优点在它的性能与 BERT 相当&#xff0c;但是体积…

Unity3d C#实现场景编辑/运行模式下3D模型XYZ轴混合一键排序功能(含源码工程)

前言 在部分场景搭建中需要整齐摆放一些物品&#xff08;如仓库中的货堆、货架等&#xff09;&#xff0c;因为有交互的操作在单个模型上&#xff0c;每次总是手动拖动模型操作起来也是繁琐和劳累。 在这背景下&#xff0c;我编写了一个在运行或者编辑状态下都可以进行一键排序…

【嘉立创EDA-PCB设计指南】3.网络表概念解读+板框绘制

前言&#xff1a;本文对网络表概念解读板框绘制&#xff08;确定PCB板子轮廓&#xff09; 网络表概念解读 在本专栏的上一篇文章【嘉立创EDA-PCB设计指南】2&#xff0c;将设计的原理图转为了PCB&#xff0c;在PCB界面下出现了所有的封装&#xff0c;以及所有的飞线属性&…

从0开始python学习-48.pytest框架之断言

目录 1. 响应进行断言 1.1 在yaml用例中写入断言内容 1.2 封装断言方法 1.3 在执行流程中加入断言判断内容 2. 数据库数据断言 2.1 在yaml用例中写入断言内容 2.2 连接数据库并封装执行sql的方法 2.3 封装后校验方法是否可执行 2.4 使用之前封装的断言方法&#xff0c…