Winston日志Http传输:离线时日志缓冲的配置方案问询
解决Winston离线日志缓冲的几种简单方案
方案1:本地文件缓冲 + 定时同步
这是最易落地的方案,先将日志写入本地文件做缓冲,待网络恢复后批量同步到你的Express API:
- 安装依赖包:
npm install winston-daily-rotate-file node-schedule axios - 配置Winston同时启用本地文件传输和Http传输:
const winston = require('winston'); const DailyRotateFile = require('winston-daily-rotate-file'); const logger = winston.createLogger({ format: winston.format.json(), // 用JSON格式方便后续解析 transports: [ // 本地按天分割日志,作为离线缓冲 new DailyRotateFile({ filename: 'logs/%DATE%.log', datePattern: 'YYYY-MM-DD', maxSize: '20m', // 单文件最大20MB maxFiles: '14d' // 保留最近14天的日志 }), // 原有Http传输,基础重试配置 new winston.transports.Http({ host: '你的API域名/IP', path: '/logs', port: 80, retryDelay: 5000, // 重试间隔5秒 retries: 3 // 最多重试3次 }) ] }); - 添加定时同步任务,定期检查网络并上传本地日志:
const schedule = require('node-schedule'); const fs = require('fs').promises; const path = require('path'); const axios = require('axios'); // 每天凌晨2点执行同步(可根据需求调整时间) schedule.scheduleJob('0 0 2 * * *', async () => { try { // 先验证API是否可达 await axios.head('你的API健康检查接口'); const logDir = path.join(__dirname, 'logs'); const files = await fs.readdir(logDir); for (const file of files) { if (!file.endsWith('.log')) continue; const filePath = path.join(logDir, file); const logContent = await fs.readFile(filePath, 'utf8'); // 分割成单条日志(假设每行一条JSON) const logs = logContent.split('\n') .filter(line => line.trim()) .map(line => JSON.parse(line)); // 批量发送到API await axios.post('你的API日志接收接口', logs); // 发送成功后删除本地文件 await fs.unlink(filePath); } } catch (err) { console.error('日志同步失败,下次再试:', err.message); } });
方案2:自定义带本地队列的Http传输
如果不想生成独立日志文件,可以实现一个Winston自定义传输,把发送失败的日志存入本地队列,网络恢复后自动重试:
- 实现自定义传输类:
const { Transport } = require('winston'); const fs = require('fs').promises; const path = require('path'); const axios = require('axios'); class BufferedHttpTransport extends Transport { constructor(opts) { super(opts); this.apiUrl = opts.apiUrl; this.queuePath = path.join(__dirname, 'log-queue.json'); this.queue = []; // 初始化加载本地队列 this._loadQueue(); // 每10秒尝试发送队列中的日志 setInterval(() => this._processQueue(), 10000); } async _loadQueue() { try { const data = await fs.readFile(this.queuePath, 'utf8'); this.queue = JSON.parse(data); } catch { // 队列文件不存在则初始化空队列 this.queue = []; } } async _saveQueue() { await fs.writeFile(this.queuePath, JSON.stringify(this.queue)); } async _processQueue() { if (this.queue.length === 0) return; try { // 批量发送队列日志 await axios.post(this.apiUrl, this.queue); // 发送成功清空队列 this.queue = []; await this._saveQueue(); } catch (err) { console.error('队列日志发送失败,保留队列:', err.message); } } log(info, callback) { setImmediate(() => this.emit('logged', info)); // 优先尝试直接发送 axios.post(this.apiUrl, info) .catch(async () => { // 发送失败则加入队列并保存 this.queue.push(info); await this._saveQueue(); }); callback(); } } // 使用自定义传输 const logger = winston.createLogger({ format: winston.format.json(), transports: [ new BufferedHttpTransport({ apiUrl: '你的API日志接收接口' }) ] });
关键注意事项
- 树莓派的存储有限,方案1中一定要设置合理的
maxFiles和maxSize,避免日志占满磁盘。 - 方案2的队列文件可以改用SQLite等轻量数据库,比JSON文件更可靠,但JSON文件已经满足简单场景需求。
- 网络检查建议直接请求API的健康检查接口,而不是ping,确保API服务本身可用。
内容的提问来源于stack exchange,提问作者user26403533
相关产品推荐
相关产品推荐

