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

在Puppeteer中使用Strapi日志时,evaluate内日志无法显示的问题排查

在Puppeteer中使用Strapi日志时,evaluate内日志无法显示的问题排查

我来帮你拆解并解决这个问题,你遇到的核心问题是Puppeteer的page.evaluate()运行在浏览器沙箱环境中,和Node.js(也就是Strapi日志所在的后端环境)完全隔离,再加上当前日志监听逻辑的几个小问题,导致evaluate里的日志传不出来,甚至现有监听也只输出空内容。下面一步步讲清楚问题和修复方案:


一、先搞懂核心问题:两个环境的隔离性

page.evaluate()里的代码是在Chromium浏览器的上下文里跑的,这里的console.log、console.error都是浏览器自带的控制台方法,和Node.js环境的console(以及Strapi的log)完全不是一回事。要让浏览器里的日志传到Strapi,必须通过Puppeteer提供的page.on('console')这个“桥梁”来转发,但你当前的转发逻辑有bug,导致消息没正确传过来。

二、现有日志监听的两个问题

  1. msg.text()无法正确获取日志内容
    Puppeteer的ConsoleMessage.text()方法在处理多参数console调用(比如console.log('a', 'b', 123))或非字符串参数时,可能返回空或不完整的内容。你看到的空[Log]:就是因为这个原因。

  2. 未处理重复监听问题
    如果你的Puppeteer Cluster开启了页面复用,每次cluster.execute都会给同一个page添加新的console监听,会导致同一条日志被重复输出,甚至出现异常。


三、完整修复方案

1. 修复日志转发逻辑

把原来的page.on('console')替换成能正确解析所有console参数的逻辑,同时避免重复监听:

// 修改后的puppeteer code
export const test = async (log?) => {
  const cluster = getCluster();

  const jobData = {
    url: 'https://google.com',
  };

  const data = await cluster.execute(jobData,
    async ({ page, data }) => {
      const { url } = data;
      page.setDefaultNavigationTimeout(0);
      
      // 修复console监听:先移除旧监听,再添加新的,确保参数正确解析
      if (log) {
        // 先移除之前的监听,避免页面复用导致重复输出
        page.off('console');
        page.on('console', async (msg) => {
          try {
            // 解析console的所有参数(支持多参数、对象类型)
            const args = await Promise.all(
              msg.args().map(async (arg) => {
                try {
                  return await arg.jsonValue();
                } catch (e) {
                  // 对无法序列化的对象,返回toString结果
                  return await arg.toString();
                }
              })
            );
            // 把参数展开传给Strapi的log
            log('[Log]:', ...args);
          } catch (err) {
            log('[Console解析错误]:', err.message);
          }
        });
      }

      await page.goto(url, { waitUntil: 'load' });

      const results = await page.evaluate(async () => {
        const tic = Date.now();
        try {
          // 这里写你的业务逻辑
        } catch (err) {
          // 这个error会被page.on('console')捕获并转发到Strapi
          console.error('Detail fetch error:', err);
        }

        // 这条日志现在能正常传到Strapi了
        console.log(`Elapsed time: ${Date.now() - tic}ms`);

        return []; // 替换成你实际要返回的数据
      });

      return results;
    });
  return data;
};

2. (可选)按日志级别转发到Strapi对应方法

如果想让浏览器的console.error对应Strapi的log.error,console.log对应log.info,可以调整调用方式和监听逻辑:

  • 调用时传整个Strapi log对象:
    // call puppeteer function in cron tab
    await test(strapi.log); // 不需要bind,因为我们会直接调用对应级别方法
    
  • 调整监听逻辑:
    if (log) {
      page.off('console');
      page.on('console', async (msg) => {
        try {
          const args = await Promise.all(
            msg.args().map(async (arg) => {
              try {
                return await arg.jsonValue();
              } catch (e) {
                return await arg.toString();
              }
            })
          );
          const logLevel = msg.type(); // 拿到日志级别:log/error/warn/info等
          // 调用Strapi对应级别的日志方法,兜底用debug
          if (typeof log[logLevel] === 'function') {
            log[logLevel](`[${logLevel.toUpperCase()}]:`, ...args);
          } else {
            log.debug(`[${logLevel.toUpperCase()}]:`, ...args);
          }
        } catch (err) {
          log.error('[Console解析错误]:', err.message);
        }
      });
    }
    

四、为什么这样能解决问题?

  • 参数解析逻辑:通过msg.args()获取浏览器console调用的所有参数,并转换为JSON值,确保能拿到evaluate里的Elapsed time日志,同时处理了对象类型的日志参数。
  • 重复监听处理:用page.off('console')先移除旧监听,避免Cluster复用页面时,同一条日志被多次输出。
  • 环境桥接:通过page.on('console')把浏览器上下文的日志事件转发到Node.js环境的Strapi log,彻底解决两个环境的隔离问题。

备注:内容来源于stack exchange,提问作者bgyooPtr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 13:34:31