在 Firebase Cloud Functions 中记录 'jsonPayload'

Log 'jsonPayload' in Firebase Cloud Functions

TL;DR;

有谁知道是否可以在 Firebase/Google 云函数中使用 console.log 来使用 jsonPayload 属性 将条目记录到堆栈驱动程序,这样我的日志就可以搜索到(目前我传递给 console.log 的任何内容都会被字符串化为 textPayload)。


我有一个多模块项目,其中一些代码 运行 在 Firebase Cloud Functions 上,一些 运行 在 Google Compute Engine 等其他环境中。稍微简化一下,我基本上有一个 'core' 模块,然后我将 'cloud-functions' 模块部署到 Cloud Functions,将 'backend-service' 部署到 GCE,它们都依赖于 'core' 等。

我在整个 'core' 模块中使用 bunyan 进行日志记录,当部署到 GCE 时,记录器是使用“@google-cloud/logging-bunyan”配置的,所以我的日志会转到 Stack Driver。

旁白:在 Google Cloud Functions 中使用此配置会导致 Error: Endpoint read failed 出现问题,我认为这是由于函数不会变冷并试图重用死连接,但是我不是 100% 确定真正的原因是什么。

所以现在我尝试使用 console.log(arg) 进行记录,其中 arg 是一个对象,而不是字符串。我希望此对象出现在 jsonPayload 下的 Stack Driver 中,但它正在被字符串化并放入 textPayload 字段中。

我花了一段时间,但我终于找到了 this example in firebase functions samples repository。最后我选择了这样的东西:

const Logging = require('@google-cloud/logging');
const logging = new Logging();
const log = logging.log('my-func-logger');
const logMetadata = {
  resource: {
    type: 'cloud_function',
    labels: {
      function_name: process.env.FUNCTION_NAME ,
      project: process.env.GCLOUD_PROJECT,
      region: process.env.FUNCTION_REGION
    },
  },
};
const logData = { id: 1, score: 100 };
const entry = log.entry(logMetaData, logData);
log.write(entry)

您可以将字符串 severity 属性 值添加到 logMetaData(例如 "INFO" 或 "ERROR")。 Here is the list 个可能值。


可用节点 10 环境变量的更新。这些似乎可以解决问题:

labels: {
  function_name: process.env.FUNCTION_TARGET,
  project: process.env.GCP_PROJECT,
  region: JSON.parse(process.env.FIREBASE_CONFIG).locationId
}

更新:对于 Node 10 运行时,他们希望您在部署期间 set env values explicitly。我想有一个宽限期,因为我部署的功能仍在运行。

我 运行 遇到了同样的问题,正如@wtk 的回答中的评论所述,我想添加复制我可以在下面的代码片段中找到的所有默认云函数日志记录行为,包括 execution_id.

至少在使用 Cloud FunctionsHTTP Trigger 选项时,以下为我生成了正确的日志。我还没有测试 Firebase Cloud Functions

// global
const { Logging } = require("@google-cloud/logging");
const logging = new Logging();
const Log = logging.log("cloudfunctions.googleapis.com%2Fcloud-functions");
const LogMetadata = {
  severity: "INFO",
  type: "cloud_function",
  labels: {
    function_name: process.env.FUNCTION_NAME,
    project: process.env.GCLOUD_PROJECT,
    region: process.env.FUNCTION_REGION
  }
};

// per request
const data = { foo: "bar" };
const traceId = req.get("x-cloud-trace-context").split("/")[0];
const metadata = {
  ...LogMetadata,
  severity: 'INFO',
  trace: `projects/${process.env.GCLOUD_PROJECT}/traces/${traceId}`,
  labels: {
    execution_id: req.get("function-execution-id")
  }
};
Log.write(Log.entry(metadata, data));

@wtk的回答中的github link应该更新为:

https://github.com/firebase/functions-samples/blob/2f678fb933e416fed9be93e290ae79f5ea463a2b/stripe/functions/index.js#L103

因为它指的是截至回答问题时的存储库,并且其中具有以下功能:

// To keep on top of errors, we should raise a verbose error report with Stackdriver rather
// than simply relying on console.error. This will calculate users affected + send you email
// alerts, if you've opted into receiving them.
// [START reporterror]
function reportError(err, context = {}) {
    // This is the name of the StackDriver log stream that will receive the log
    // entry. This name can be any valid log stream name, but must contain "err"
    // in order for the error to be picked up by StackDriver Error Reporting.
    const logName = 'errors';
    const log = logging.log(logName);

    // https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource
    const metadata = {
    resource: {
        type: 'cloud_function',
        labels: {function_name: process.env.FUNCTION_NAME},
    },
    };

    // https://cloud.google.com/error-reporting/reference/rest/v1beta1/ErrorEvent
    const errorEvent = {
    message: err.stack,
    serviceContext: {
        service: process.env.FUNCTION_NAME,
        resourceType: 'cloud_function',
    },
    context: context,
    };

    // Write the error log entry
    return new Promise((resolve, reject) => {
    log.write(log.entry(metadata, errorEvent), (error) => {
        if (error) {
        return reject(error);
        }
        resolve();
    });
    });
}
// [END reporterror]