当前位置: 首页 > news >正文

uniApp App 端日志本地存储方案:实现可靠的日志记录功能

在移动应用开发过程中,日志记录是排查问题、分析用户行为的重要手段。对于 UniApp 开发的 App 来说,实现日志本地存储并在需要时导出,能极大地方便问题定位。本文将介绍如何在 UniApp 的 App 端实现日志的本地文件存储功能。

功能需求分析

一个完善的本地日志存储方案应具备以下功能:

  • 支持写入不同级别日志(info、warn、error 等)
  • 自动记录日志时间和级别
  • 按日期分割日志文件,避免单个文件过大
  • 支持设置日志文件最大保存天数
  • 提供日志文件清理功能
  • 确保在 App 重启后日志不会丢失

实现方案

下面是一个完整的日志工具类实现,基于 UniApp 的文件系统 API:

// logger.js
const LOG_LEVELS = {DEBUG: 'DEBUG',INFO: 'INFO',WARN: 'WARN',ERROR: 'ERROR'
}class Logger {constructor() {this.maxLogDays = 7 // 默认保存7天日志this.logDir = 'logs' // 日志目录this.init()}async init() {// 确保日志目录存在try {const dirInfo = await this.getDirInfo(this.logDir)if (!dirInfo) {await this.createDir(this.logDir)}} catch (e) {console.error('初始化日志目录失败:', e)}// 启动时清理过期日志this.cleanOldLogs()}// 设置日志最大保存天数setMaxLogDays(days) {if (days > 0) {this.maxLogDays = days}}// 获取当前日期字符串 (格式: YYYY-MM-DD)getCurrentDateStr() {const date = new Date()return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`}// 获取当前时间字符串getCurrentTimeStr() {const date = new Date()return `${this.getCurrentDateStr()} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}.${date.getMilliseconds().toString().padStart(3, '0')}`}// 获取日志文件名getLogFileName() {return `app_${this.getCurrentDateStr()}.log`}// 写入日志async log(level, message, data = null) {try {const time = this.getCurrentTimeStr()let logContent = `[${time}] [${level}] ${message}`if (data) {logContent += ` | ${typeof data === 'object' ? JSON.stringify(data) : data}`}logContent += '\n'const fileName = this.getLogFileName()const filePath = `${this.logDir}/${fileName}`// 检查文件是否存在const fileInfo = await this.getFileInfo(filePath)if (fileInfo) {// 文件存在,追加内容await this.appendFile(filePath, logContent)} else {// 文件不存在,创建新文件await this.writeFile(filePath, logContent)}} catch (e) {console.error('写入日志失败:', e)}}// 快捷方法debug(message, data) {this.log(LOG_LEVELS.DEBUG, message, data)}info(message, data) {this.log(LOG_LEVELS.INFO, message, data)}warn(message, data) {this.log(LOG_LEVELS.WARN, message, data)}error(message, data) {this.log(LOG_LEVELS.ERROR, message, data)}// 清理过期日志async cleanOldLogs() {try {const now = new Date()const threshold = now.setDate(now.getDate() - this.maxLogDays)const dirInfo = await this.getDirInfo(this.logDir)if (!dirInfo) returnconst files = await this.readDir(this.logDir)for (const file of files) {const fileName = file.name// 解析文件名中的日期const dateStr = fileName.match(/app_(\d{4}-\d{2}-\d{2})\.log/)?.[1]if (dateStr) {const fileDate = new Date(dateStr)if (fileDate.getTime() < threshold) {// 文件日期早于阈值,删除await this.deleteFile(`${this.logDir}/${fileName}`)}}}} catch (e) {console.error('清理日志失败:', e)}}// 手动清理所有日志async clearAllLogs() {try {const files = await this.readDir(this.logDir)for (const file of files) {await this.deleteFile(`${this.logDir}/${file.name}`)}} catch (e) {console.error('清除所有日志失败:', e)}}// 获取所有日志文件列表async getLogFiles() {try {const files = await this.readDir(this.logDir)return files.filter(file => file.name.endsWith('.log'))} catch (e) {console.error('获取日志文件列表失败:', e)return []}}// 读取日志文件内容async readLogFile(fileName) {try {const filePath = `${this.logDir}/${fileName}`const content = await this.readFile(filePath)return content} catch (e) {console.error('读取日志文件失败:', e)return null}}// ========== 文件系统操作封装 ==========// 获取目录信息async getDirInfo(dirPath) {return new Promise((resolve, reject) => {plus.io.resolveLocalFileSystemURL(`_doc/${dirPath}`,entry => resolve(entry),error => resolve(null))})}// 创建目录async createDir(dirPath) {return new Promise((resolve, reject) => {plus.io.resolveLocalFileSystemURL('_doc',rootEntry => {rootEntry.getDirectory(dirPath,{ create: true, exclusive: false },dirEntry => resolve(dirEntry),error => reject(error))},error => reject(error))})}// 获取文件信息async getFileInfo(filePath) {return new Promise((resolve, reject) => {plus.io.resolveLocalFileSystemURL(`_doc/${filePath}`,entry => resolve(entry),error => resolve(null))})}// 写入文件async writeFile(filePath, content) {return new Promise((resolve, reject) => {plus.io.resolveLocalFileSystemURL('_doc',rootEntry => {rootEntry.getFile(filePath,{ create: true, exclusive: false },fileEntry => {fileEntry.createWriter(writer => {writer.onwriteend = () => resolve()writer.onerror = e => reject(e)writer.write(content)},error => reject(error))},error => reject(error))},error => reject(error))})}// 追加内容到文件async appendFile(filePath, content) {return new Promise((resolve, reject) => {plus.io.resolveLocalFileSystemURL(`_doc/${filePath}`,fileEntry => {fileEntry.createWriter(writer => {writer.onwriteend = () => resolve()writer.onerror = e => reject(e)writer.seek(writer.length)writer.write(content)},error => reject(error))},error => reject(error))})}// 读取文件内容async readFile(filePath) {return new Promise((resolve, reject) => {plus.io.resolveLocalFileSystemURL(`_doc/${filePath}`,fileEntry => {fileEntry.file(file => {const reader = new plus.io.FileReader()reader.onloadend = e => resolve(e.target.result)reader.onerror = e => reject(e)reader.readAsText(file)},error => reject(error))},error => reject(error))})}// 读取目录内容async readDir(dirPath) {return new Promise((resolve, reject) => {plus.io.resolveLocalFileSystemURL(`_doc/${dirPath}`,dirEntry => {const reader = dirEntry.createReader()reader.readEntries(entries => resolve(entries),error => reject(error))},error => reject(error))})}// 删除文件async deleteFile(filePath) {return new Promise((resolve, reject) => {plus.io.resolveLocalFileSystemURL(`_doc/${filePath}`,entry => {entry.remove(() => resolve(),error => reject(error))},error => reject(error))})}
}// 创建全局单例
const logger = new Logger()export default logger

使用方法

在 UniApp 项目中使用该日志工具非常简单,以下是使用示例:

import logger from './logger.js'// 记录不同级别日志
logger.debug('这是一条调试信息', { key: 'value' })
logger.info('这是一条普通信息')
logger.warn('这是一条警告信息')
logger.error('这是一条错误信息', new Error('示例错误'))// 设置日志最大保存天数
logger.setMaxLogDays(30) // 保存30天日志// 手动清理日志
logger.cleanOldLogs()// 清除所有日志
logger.clearAllLogs()

使用时,只需在项目中引入该方法,即可在任何需要记录日志的地方调用相应方法。

http://www.lryc.cn/news/621935.html

相关文章:

  • Flutter 自定义组件开发指南
  • Wi-Fi 与蜂窝网络(手机网络)的核心区别,以及 Wi-Fi 技术未来的发展方向
  • css变量的妙用(setProperty()的使用)
  • MySQL的学习笔记
  • 前端性能优化工具Performance面板实战指南
  • w484扶贫助农系统设计与实现
  • Android项目中Ktor的引入与使用实践
  • @[TOC](计算机是如何⼯作的) JavaEE==网站开发
  • 从理论到实战:KNN 算法与鸢尾花分类全解析
  • Python基础(Flask①)
  • Sklearn 机器学习 手写数字识别 使用K近邻算法做分类
  • DAY41打卡
  • IO多路复用底层原理
  • TDengine IDMP 高级功能(1. 元素模板)
  • frp踩坑 以及进阶教程
  • Floyd 判圈算法(龟兔赛跑算法)
  • Linux运维新手的修炼手扎之第29天
  • 【网络】IP总结复盘
  • Claude Opus 4.1深度解析:抢先GPT5发布,AI编程之王主动出击?
  • day31 UDP通信
  • Ansible 学习笔记:变量事实管理、任务控制与文件部署
  • 计算机视觉(opencv)实战四——图片阈值处理cv2.threshold()
  • Android RxJava变换操作符详解
  • 从0开始学习Java+AI知识点总结-15.后端web基础(Maven基础)
  • 使用 PyQt5 构建 Python 人脸采集系统实战指南
  • 16进制pcm数据转py波形脚本
  • 来火山引擎「算子广场」,一键处理多模态数据
  • 标题:移动端安全加固:发散创新,筑牢安全防线引言:随着移动互联网
  • OpenCV Python——VSCode编写第一个OpenCV-Python程序 ,图像读取及翻转cv2.flip(上下、左右、上下左右一起翻转)
  • 【数据结构初阶】--排序(三):冒泡排序、快速排序