uniapp本地存储日志

uniapp本地存储日志

  • 背景
  • 实现
    • 代码实现
    • 使用
    • 查看生成log
    • 读取
  • 注意事项
  • 尾巴

背景

我们的APP开发完成之后,在我们测试环境或者自测的时候都好好的,但是发布到生产环境客户使用总会出现一些奇奇怪怪的问题。这时候因为没在开发环境,我们无法查看到日志,所以我们需要手机到用户的操作日志然后上传,方便我们排查出问题。

实现

我们通过5+API提供的IO模块来进行文件的读写操作。主要实现如下功能:
1、日志打印
2、日志按天存储(同一天的日志会自动续写到文件)
3、日志删除
4、日志压缩

代码实现

主要通过plus.io.requestFileSystem对应用私有目录进行读写,完整代码如下:

// 新建logger.js文件
// 日志存放的文件夹目录
const LOG_DIR = '_doc/logs';
/**
 * 获取当前时间
 */
function getDayStr() {
	var y, m, d, h, mm, s;
	var date = new Date();
	y = date.getFullYear();
	m = date.getMonth() + 1;
	d = date.getDate();
	m = m < 10 ? '0' + m : m;
	d = d < 10 ? '0' + d : d;
	// //console.log('日期:',y,m,d)
	// return '20220607' // 生成指定日期
	return '' + y + m + d;
}
/**
 * 获取当前时间,yyyy-mm-dd hh:mm:ss
 * 用于记录日志的时间信息
 */
function getTimeStr() {
	var y, m, d, h, mm, s;
	var date = new Date();
	y = date.getFullYear();
	m = date.getMonth() + 1;
	d = date.getDate();
	h = date.getHours();
	mm = date.getMinutes();
	s = date.getSeconds();
	m = m < 10 ? '0' + m : m;
	d = d < 10 ? '0' + d : d;
	h = h < 10 ? '0' + h : h;
	mm = mm < 10 ? '0' + mm : mm;
	s = s < 10 ? '0' + s : s;
	var timeStr = y + '-' + m + '-' + d + ' ' + h + ':' + mm + ':' + s;
	return timeStr;
}
/**
 * 日志TXT的名称
 */
function getLogFileName() {
	const txt = LOG_DIR + '/' + getDayStr() + '.txt';
	// //console.log('TXT文件名称:',txt)
	return txt;
}
let tasks = [];
/**
 * @param {Object} tag 标识
 * @param {Object} msg 空格
 */
function writeToTxt(tag) {
	return new Promise((resolve, reject) => {
		let msgs = '';
		for (var i = 1; i < arguments.length; i++) {
			const item = arguments[i];
			if (
				typeof item == 'string' ||
				typeof item == 'number' ||
				typeof item == 'boolean'
			) {
				msgs = msgs + '\t' + item;
			} else {
				msgs = msgs + '\t' + JSON.stringify(item);
			}
		}
		// 获取当前时间
		let txt_msg = getTimeStr() + '\t[' + tag + ']\t' + msgs + '\n';
		tasks.push(txt_msg);
		resolve(true);
	}).then(() => {
		clearTask();
	});
}
// 清空日志到日志文件
function clearTask() {
	// #ifdef APP-PLUS
	if (tasks.length === 0) {
		return;
	}
	const txt_msg = tasks.join('');
	tasks = [];
	const fileName = getLogFileName();
	plus.io.requestFileSystem(
		plus.io.PRIVATE_DOC,
		fs => {
			fs.root.getFile(
				fileName, {
					create: true,
				},
				function(entry) {
					// 写入到本地
					entry.createWriter(
						function(writer) {
							writer.onwrite = function(e) {
								// console.log("Write data success!");
								//console.log('写入本地日志 >>>> ', txt_msg);
							};
							writer.onerror = function(e) {
								if (process.env.NODE_ENV === 'development'){
									console.eror(
										'写入本地日志失败 >>>> ',
										JSON.stringify(e),
										txt_msg
									);
								}
							};
							// Write data to the end of file.
							writer.seek(writer.length);
							writer.write(txt_msg);
						},
						function(e) {
							if (process.env.NODE_ENV === 'development') {
								console.log(e.message);
							}
						}
					);
				}
			);
		},
		function(e) {
			if (process.env.NODE_ENV === 'development') {
				console.log('Request file system failed: ' + JSON.stringify(e));
			}
		}
	);
	// #endif
}

/**
 * 压缩所有的日志为zip
 */
function zipLogDir(callback) {
	// #ifdef APP-PLUS
	var zipFile = '_doc/logs.zip';
	var targetPath = LOG_DIR;

	// 开始压缩文件
	if (process.env.NODE_ENV === 'development') {
		console.log('开始压缩', targetPath, zipFile);
	}
	plus.zip.compress(
		targetPath,
		zipFile,
		function(res) {
			if (process.env.NODE_ENV === 'development') {
				console.log('开始压缩 Compress success!', res);
			}
			if (callback) {
				callback({
					success: true,
					res,
					zipPath: zipFile,
				});
			}
		},
		function(error) {
			if (process.env.NODE_ENV === 'development') {
				console.error('开始压缩 Compress error!', error);
			}
			if (callback) {
				callback({
					success: false,
					error,
				});
			}
		}
	);
	// #endif
}
/**
 * 删除多少天之前的日志文件
 */
function removeFile(durationDay) {
	// #ifdef APP-PLUS
	return new Promise((resolve, reject) => {
		if (!durationDay || durationDay <= 0) {
			durationDay = 10;
		}
		var dirPath = LOG_DIR;
		plus.io.resolveLocalFileSystemURL(
			dirPath,
			function(entry) {
				//读取这个目录对象
				var directoryReader = entry.createReader();
				//读取这个目录下的所有文件
				directoryReader.readEntries(
					function(entries) {
						console.log('日志文件数量', entries.length);
						//如果有才操作
						if (entries.length > 0) {
							let now = getDayStr();
							for (let file of entries) {
								// 判断需要保留的日志
								let day = file.name.replace('.txt', '');
								if (parseInt(day) + parseInt(durationDay) < parseInt(now)) {
									console.log('需要删除的日志是', file.name);
									try {
										file.remove(
											function() {
												if (process.env.NODE_ENV === 'development') {
													console.error('删除日志成功', file.name);
												}
											},
											function(e) {
												if (process.env.NODE_ENV === 'development') {
													console.error('删除日志失败', file.name, e);
												}
											}
										);
									} catch (e) {
										if (process.env.NODE_ENV === 'development') {
											console.error('删除日志失败', file.name, e);
										}
									}
								} else {
									if (process.env.NODE_ENV === 'development') {
										console.log('保留的日志是', file.name);
									}
								}
							} // for
						} // if
						resolve();
					},
					function(e) {
						if (process.env.NODE_ENV === 'development') {
							console.log('读取文件失败:' + e.message);
						}

						resolve();
					}
				);
			},
			function(e) {
				if (process.env.NODE_ENV === 'development') {
					console.log('读取目录失败:' + e.message);
				}
				resolve();
			}
		);
	});
	// #endif
}

/**
 * 自定义TXT日志
 */
const logger = {
	/**
	 * @param {Object} msg 日志信息的字符串信息
	 */
	debug: function() {
		writeToTxt('DEBUG', ...arguments);
		//production
		if (process.env.NODE_ENV === 'development') {
			console.debug(...arguments);
		}
	},
	log: function() {
		writeToTxt('LOG', ...arguments);
		if (process.env.NODE_ENV === 'development') {
			console.log(...arguments);
		}
	},
	info: function() {
		writeToTxt('INFO', ...arguments);
		if (process.env.NODE_ENV === 'development') {
			console.info(...arguments);
		}
	},
	warn: function() {
		writeToTxt('WARN', ...arguments);
		if (process.env.NODE_ENV === 'development') {
			console.warn(...arguments);
		}
	},
	error: function() {
		writeToTxt('ERROR', ...arguments);
		if (process.env.NODE_ENV === 'development') {
			console.error(...arguments);
		}
	},
	/**
	 * @param {String} tag 日志信息的自定义信息
	 */
	tag: function(tag) {
		writeToTxt(tag, ...arguments);
		if (process.env.NODE_ENV === 'development') {
			console.log(...arguments);
		}
	},
	/**
	 * @param {Object} msg 日志信息的字符串信息
	 */
	network: function() {
		writeToTxt('NETWORK', ...arguments);
		if (process.env.NODE_ENV === 'development') {
			console.log(...arguments);
		}
	},
	/**
	 * @param {Object} msg 日志信息的字符串信息
	 */
	logIpExchange: function(msg) {
		writeToTxt('IpExchange', ...arguments);
		if (process.env.NODE_ENV === 'development') {
			console.log(...arguments);
		}
	},
	/**
	 * 压缩成zip,并返回路径
	 * @param {Object} callback
	 */
	zipLogDir,
	/**
	 * 删除多少${durationDay}天之前的日志文件
	 * @param {Object} durationDay 默认是10天
	 */
	removeFile,
	/**
	 * 主要使用方法。先移除
	 * @param {Object} callback
	 */
	removeFileAndZipLogDir(callback) {
		removeFile().then(() => {
			zipLogDir(callback);
		});
	},
};

export default logger;

使用

引入logger.js文件

//这里根据你自己路径来
import logger from '@/common/js/logger.js'

删除5天前的日志

logger.removeFile(5)

打印

logger.log('test')

查看生成log

我们这里使用的是hbx基座调试,这里的plus.io.PRIVATE_DOC对应的手机端文件管理器中Android/data/io.dcloud.HBuilder/apps/Hbuilder/doc/logs,其中doc/logs对应代码中的LOG_DIR变量。
在这里插入图片描述
在这里插入图片描述

到这里log已经成功存储到了手机中。

读取

我们保存log到本地之后,我们希望读取然后上传,依然使用的是plus.io.requestFileSystem这个API,参考上文的写法。遍历文件夹后可以在界面显示让用户手动上传,或者启动应用静默上传。

注意事项

日志本地存储只在APP平台生效,演示为Android平台,iOS平台未做测试。

尾巴

今天的文章就到这里了,希望能给大家帮助,如果喜欢我的文章,欢迎给我点赞,评论,关注,谢谢大家!

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

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

相关文章

力扣hot100 买卖股票的最佳时机 贪心 经典题

Problem: 121. 买卖股票的最佳时机 文章目录 思路复杂度Code 思路 假设今天卖出&#xff0c;那怎么样收益最大呢&#xff1f;之前买入价是最低的 复杂度 ⏰ 时间复杂度: &#xff1a; O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( 1 ) O(1) O(1) Code class Solut…

c++之IO流

1.C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键 盘)读取数据&#xff0c;并将值存放在变量中。printf(): 将指定的文字/字符串输出到标准输出设备(屏幕)。 注意宽度输出和精度输出控制。C语言借助了相应的缓…

tsmc12: m0po max length问题(H384.M0_PO.L.1)

更多学习内容请关注「拾陆楼」知识星球 拾陆楼知识星球入口 在pt eco之后会有一些m0po max length问题出现,大部分问题都可以通过替换decap来解决,少部分由于局部density过高,需要手动调整。

2023强网杯复现

强网先锋 SpeedUp 要求2的27次方的阶乘的逐位之和 在A244060 - OEIS 然后我们将4495662081进行sha256加密 就得到了flag flag{bbdee5c548fddfc76617c562952a3a3b03d423985c095521a8661d248fad3797} MISC easyfuzz 通过尝试输入字符串判断该程序对输入字符的验证规则为9…

【C++】类和对象之构造函数、析构函数、拷贝构造函数(二)

前言&#xff1a;在上一篇我们对于C中类和对象有了一个初步的了解&#xff0c;今天我们将进一步的学习&#xff0c;今天我们目标是对构造函数、析构函数、拷贝构造函数进行一个初步学习在后面也会进一步的学习&#xff0c;一起加油呐&#xff01; &#x1f496; 博主CSDN主页:卫…

代码随想录 Leetcode669. 修剪二叉搜索树

题目&#xff1a; 代码(首刷看解析 2024年1月31日&#xff09;&#xff1a; class Solution { public:TreeNode* trimBST(TreeNode* root, int low, int high) {if (!root) return root;if (root->val < low) {TreeNode* node trimBST(root->right,low,high);return…

Flutter canvas 画一条波浪线 进度条

之前用 Flutter Canvas 画过一个三角三角形&#xff0c;html 的 Canvas 也画过一次类似的&#xff0c; 今天用 Flutter Canvas 试了下 感觉差不多&#xff1a; html 版本 大致效果如下&#xff1a; 思路和 html 实现的类似&#xff1a; 也就是找出点的位置&#xff0c;使用二阶…

山东第四次文物普查启动,全面开展不可移动文物预防性保护

一、山东进行全面普查不可移动文物资源 近日&#xff0c;山东省政府下发了《关于做好第四次全国文物普查工作通知》&#xff0c;根据调查充分摸清山东不可移动文物资源状况。这是我国时隔16年再次启动文物普查&#xff0c;也是“十四五”期内最大的文物保护行动&#xff0c;以…

Memcached缓存服务器

目录 简介 NoSQL的优点和缺点 下面扩展一下关系型数据库和非关系型数据库的区别 Memcached 特点 原理 配置与安装 Memcached我们直接使用yum安装就可以了 安装后启动 修改配置文件 测试 开发端安装telnet 进行远程连接memcached&#xff0c;端口探测 以下显示为连接…

香蕉派BPI-M7 瑞芯微RK3588 人工智能开源硬件开发板公开发售

香蕉派(Banana Pi) BPI-M7瑞芯微K3588开源硬件单板计算机公开销售&#xff0c;支持WiFi 6和BT5.2&#xff0c;硬件有3个版本:8G Ram64G eMMC, 16G Ram128 eMMC和32G Ram128 eMMC 香蕉派BPI-M7采用睿芯最新旗舰RK3588八核64位处理器&#xff0c;最高频率为2.4GHz, 6 TOPS NPU&…

106.乐理基础-五线谱-八度记号

内容参考于&#xff1a;三分钟音乐社 上一个内容&#xff1a;105.乐理基础-五线谱-谱号扩展-CSDN博客 上一个内容里的练习答案&#xff1a; 然后下图红圈位置的音&#xff0c;用高音谱号、低音谱号、中音谱号、次中音谱号&#xff0c;在加三根线的情况下无法表示&#xff0c;…

uniapp底部栏设置未读红点或角标

pages.json {... // 省略"tabBar": {"color": "#333333","selectedColor": "#3296fa","backgroundColor": "#ffffff","borderStyle": "white","list": [{"pagePat…

微信开放平台第三方授权(第四篇)-wechat发送客服消息

1.发送客服消息 上一张介绍了发送消息需要用到的authorizer_access_token,和发送消息的接口结合使用&#xff0c;上面直接上代码。 重写WechatMpService 获取token&#xff0c;这个发消息会用到 package com.test.wechat.service;import com.test.wechat.config.WechatMpConf…

Roxlabs全球IP代理服务:解锁高效数据采集与网络应用新境界

引言 在这个数字化迅速发展的时代&#xff0c;数据采集和网络应用的重要性显得愈发突出。江苏阿克索网络科技有限公司旗下的Roxlabs&#xff0c;以其卓越的全球IP代理服务&#xff0c;正引领着这一领域的创新和发展。Roxlabs不仅提供遍及200多个国家和地区的高质量动态住宅IP资…

10个最好的免费数据恢复软件工具分享【合集】

数据丢失经常发生。它可能是由于恶意软件、有缺陷的更新或疏忽造成的。无论哪种情况&#xff0c;这都是一个麻烦的事件。 许多人认为不可能恢复已删除的文件。这是一个错误的印象。 从回收站删除的文件很有可能仍然可以恢复。免费的开源数据恢复软件可以在这里为您提供帮助&a…

Linux下使用信号量实现PV操作

一.信号量与PV操作概述 在多道程序系统中&#xff0c;由于资源共享与进程合作&#xff0c;使各进程之间可能产生两种形式的制约关系&#xff0c;一种是间接相互制约&#xff0c;例如&#xff0c;在仅有一台打印机的系统&#xff0c;同一时刻只能有一个进程分配到到打印机&…

Python算法题集_最大子数组和

本文为Python算法题集之一的代码示例 题目53&#xff1a;最大子数组和 说明&#xff1a;给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。…

实战教程:使用Spring Boot和Vue.js开发社区团购管理系统

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

本地配置Joplin Server用于Joplin笔记同步并实现公网远程访问

文章目录 1. 安装Docker2. 自建Joplin服务器3. 搭建Joplin Sever4. 安装cpolar内网穿透5. 创建远程连接的固定公网地址 Joplin 是一个开源的笔记工具&#xff0c;拥有 Windows/macOS/Linux/iOS/Android/Terminal 版本的客户端。多端同步功能是笔记工具最重要的功能&#xff0c;…

关于JavaWeb的不使用模板创建方法

首先说一个特别重要的 , pom配置文件写完他不自动更新 就得这么办 , 不更新 idea就无法识别javaweb项目很烦