You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

脚本崩溃时Winston无法写入日志文件的问题排查与解决求助

Winston日志在程序崩溃时未写入文件的问题

问题场景

我有一个需要日志文件的项目,因此选择使用Winston。但项目运行期间可能会崩溃,于是我进行了测试:

const winston = require('winston');

const logger = winston.createLogger({
    transports: [
        new winston.transports.Console(),
        new winston.transports.File({ filename: 'combined.log' })
    ]
});

let i=100000;
while(i-->0){
    logger.info('Hello world');
}

throw new Error('Error');

该代码会打印100000次Hello world后抛出错误。问题在于只有程序不崩溃时,combined.log才会被写入。为何Winston仅在程序运行结束后才写入日志?如何让它在发生异常时也能写入文件?

补充测试

有趣的是,在抛出错误前添加1秒延迟就能正常写入:

const winston = require('winston');
const fsp = require('fs').promises;

let a=async ()=>{

    try{
        await fsp.unlink('combined.log');
    }catch(e){

    }

const logger = winston.createLogger({
    transports: [
        new winston.transports.Console(),
        new winston.transports.File({ filename: 'combined.log' })
    ]
});

    let i=100000;
    while(i-->0){
        logger.info('Hello world');
    }

    //等待1秒
    await new Promise((resolve)=>setTimeout(resolve,1000));
//  await new Promise((resolve,reject)=>{resolve()});

    throw new Error('Error');
}

a()

原因分析

Winston的File传输默认采用异步写入机制:它会将日志消息暂存到内存队列中,后台分批执行文件IO操作,以此避免频繁IO阻塞主线程,提升整体性能。

在第一个测试代码中,同步的while循环会持续占用主线程,Node.js事件循环没有机会处理Winston队列中的异步写入任务,紧接着程序就抛出错误崩溃,导致队列里的日志还没来得及写入文件,最终combined.log为空。

而补充测试中的1秒延迟,给了事件循环足够的时间去处理Winston的异步写入任务,所以日志能正常落地到文件。

解决方法

1. 手动触发日志刷新

利用Winston File传输的flush方法,在崩溃前等待所有待写入的日志完成:

const winston = require('winston');

const logger = winston.createLogger({
    transports: [
        new winston.transports.Console(),
        new winston.transports.File({ filename: 'combined.log' })
    ]
});

async function run() {
    let i=100000;
    while(i-->0){
        logger.info('Hello world');
    }
    // 等待文件传输完成所有日志写入
    await new Promise(resolve => logger.transports[1].flush(resolve));
    throw new Error('Error');
}

run().catch(err => console.error(err));

2. 配置同步写入(不推荐)

给File传输添加sync: true配置,强制同步写入日志,但会严重降低性能,因为每次日志都会阻塞主线程等待IO完成:

const logger = winston.createLogger({
    transports: [
        new winston.transports.Console(),
        new winston.transports.File({ 
            filename: 'combined.log',
            sync: true // 开启同步写入
        })
    ]
});

3. 监听进程崩溃事件

在进程的uncaughtException或unhandledRejection事件中,先完成日志刷新再退出进程:

const winston = require('winston');

const logger = winston.createLogger({
    transports: [
        new winston.transports.Console(),
        new winston.transports.File({ filename: 'combined.log' })
    ]
});

// 处理未捕获的异常
process.on('uncaughtException', async (err) => {
    logger.error('未捕获的异常:', err);
    // 等待日志写入完成后退出
    await new Promise(resolve => logger.transports[1].flush(resolve));
    process.exit(1);
});

let i=100000;
while(i-->0){
    logger.info('Hello world');
}

throw new Error('Error');

内容的提问来源于stack exchange,提问作者user2741831

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 03:51:07