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

能否通过Node.js库流式传输Cloud Build运行中的构建日志?

Cloud Build Node.js工具日志流式输出方案

问题背景

我们的Cloud Build流程依赖手动触发器和约8个substitutions变量,用来定制部署到不同Firebase项目、托管站点和预览渠道。之前采用bash脚本结合gcloud工具实现自动化:选择substitution选项、通过gcloud beta builds triggers import更新触发器(因复杂业务限制只能用单个触发器)、运行触发器。但bash脚本维护成本高,且“导入触发器后立即运行”的逻辑导致多起错误部署,效果不佳。

后来发现Cloud Build Node.js库的runTrigger接口可直接传递substitution变量,于是正在将构建工具迁移到Node.js版本。但当前遇到核心问题:没有原生方法在控制台流式输出运行中构建的日志(虽然可以用exec调用gcloud命令,但不够优雅)。已查阅Cloud Build REST API和Node.js客户端库文档,未找到明确解决方案,希望得到可行方案。

可行解决方案

1. 基于Cloud Build API轮询拉取日志

每个Cloud Build构建实例都有独立的日志入口,可通过API轮询构建状态,在运行期间定期拉取日志并输出到控制台。

示例代码:

const {CloudBuildClient} = require('@google-cloud/cloudbuild');
const cloudbuild = new CloudBuildClient();

// 流式输出指定构建的日志
async function streamBuildLogs(buildId, projectId) {
  const buildName = `projects/${projectId}/builds/${buildId}`;

  // 检查构建状态
  const getBuildStatus = async () => {
    const [build] = await cloudbuild.getBuild({name: buildName});
    return build.status;
  };

  // 拉取并打印日志
  const fetchLogs = async () => {
    const [response] = await cloudbuild.getBuildLog({name: buildName});
    const logContent = await response.text();
    console.log(logContent);
  };

  // 轮询直到构建完成
  let status = await getBuildStatus();
  while (status === 'WORKING') {
    await fetchLogs();
    await new Promise(resolve => setTimeout(resolve, 2000)); // 2秒间隔可调整
    status = await getBuildStatus();
  }
  // 输出最终日志
  await fetchLogs();
}

// 调用runTrigger并启动日志流式输出
async function runTriggerAndStreamLogs(projectId, triggerId, substitutions) {
  const [operation] = await cloudbuild.runTrigger({
    projectId,
    triggerId,
    substitutions
  });
  const [build] = await operation.promise();
  await streamBuildLogs(build.id, projectId);
}

// 示例调用
runTriggerAndStreamLogs('your-project-id', 'your-trigger-id', {
  '_FIREBASE_PROJECT': 'prod-project',
  '_HOSTING_SITE': 'prod-site'
}).catch(console.error);

2. 利用Cloud Logging库实时订阅日志

通过Google Cloud Logging的订阅功能,过滤指定构建ID的日志条目,实时接收并输出到控制台,这是更优雅的长期方案。

首先安装依赖:

npm install @google-cloud/logging

示例代码:

const {CloudBuildClient} = require('@google-cloud/cloudbuild');
const {Logging} = require('@google-cloud/logging');
const cloudbuild = new CloudBuildClient();
const logging = new Logging({projectId: 'your-project-id'});

// 订阅指定构建的实时日志
async function subscribeBuildLogs(buildId, projectId) {
  // 日志过滤器:匹配指定构建ID的Cloud Build日志
  const logFilter = `resource.type="cloud_build" AND labels.build_id="${buildId}"`;
  const streamOptions = {filter: logFilter};

  // 先输出已生成的日志
  const [existingEntries] = await logging.getEntries(streamOptions);
  existingEntries.forEach(entry => console.log(entry.data.message));

  // 创建实时日志流
  const logStream = logging.streamEntries(streamOptions);
  logStream.on('data', entry => {
    console.log(entry.data.message);
  });
  logStream.on('error', err => {
    console.error('日志流异常:', err);
    logStream.destroy();
  });

  // 监听构建完成后关闭流
  const waitForBuildComplete = async () => {
    const buildName = `projects/${projectId}/builds/${buildId}`;
    let status = (await cloudbuild.getBuild({name: buildName}))[0].status;
    while (status === 'WORKING') {
      await new Promise(resolve => setTimeout(resolve, 5000));
      status = (await cloudbuild.getBuild({name: buildName}))[0].status;
    }
    logStream.destroy();
    console.log('构建完成,日志流已关闭');
  };

  waitForBuildComplete().catch(console.error);
}

// 结合runTrigger使用
async function runTriggerWithLogging(projectId, triggerId, substitutions) {
  const [operation] = await cloudbuild.runTrigger({
    projectId,
    triggerId,
    substitutions
  });
  const [build] = await operation.promise();
  await subscribeBuildLogs(build.id, projectId);
}

// 示例调用
runTriggerWithLogging('your-project-id', 'your-trigger-id', {
  '_FIREBASE_PROJECT': 'dev-project',
  '_HOSTING_SITE': 'dev-site'
}).catch(console.error);

3. 封装gcloud builds log命令(简便方案)

如果觉得API方式过于繁琐,可通过Node.js的child_process模块封装gcloud builds log --stream命令,稳定性强且实现简单。

示例代码:

const {spawn} = require('child_process');
const {CloudBuildClient} = require('@google-cloud/cloudbuild');
const cloudbuild = new CloudBuildClient();

// 用gcloud命令流式输出日志
function streamLogsWithGcloud(buildId) {
  const gcloudProcess = spawn('gcloud', ['builds', 'log', '--stream', buildId]);

  gcloudProcess.stdout.on('data', data => {
    console.log(data.toString().trim());
  });

  gcloudProcess.stderr.on('data', data => {
    console.error(data.toString().trim());
  });

  gcloudProcess.on('close', code => {
    console.log(`日志流结束,退出码: ${code}`);
  });
}

// 调用runTrigger后启动日志流
async function runTriggerWithGcloudLogs(projectId, triggerId, substitutions) {
  const [operation] = await cloudbuild.runTrigger({
    projectId,
    triggerId,
    substitutions
  });
  const [build] = await operation.promise();
  streamLogsWithGcloud(build.id);
}

// 示例调用
runTriggerWithGcloudLogs('your-project-id', 'your-trigger-id', {
  '_PREVIEW_CHANNEL': 'pr-123'
}).catch(console.error);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 03:05:26