在 Cloud Run 中记录和查看日志

本页面介绍使用 Cloud Run 时可用的日志,以及如何查看和写入日志。

Cloud Run 提供三种类型的日志,这些日志会自动发送到 Cloud Logging

  • 请求日志(仅限服务):发送到 Cloud Run 服务的请求日志。这些日志是系统自动创建的。
  • 容器日志(服务和作业):这些日志由实例发出(通常来自您自己的代码),并将写入受支持的位置(请参阅写入容器日志)。
  • 系统日志(服务和作业):平台生成的日志,其中包含有关服务和作业的信息。这些日志会写入 varlog/system

查看日志

您可以通过多种方式查看服务或作业的日志:

查看日志的两种控制台方法都会检查 Cloud Logging 中存储的相同日志,而 Cloud Logging Logs Explorer 提供了更多详细信息和更多过滤功能。

在 Cloud Run 中查看日志

您可以在相应的服务和作业页面中查看服务和作业的日志。

查看服务的日志

如需使用 Cloud Run 页面查看服务日志,请执行以下操作:

  1. 转到 Cloud Run

  2. 在显示的列表中点击所需的服务。

  3. 点击日志标签页,以获取此服务的所有修订版本的请求日志及容器日志。您可以按日志严重级别进行过滤。

查看作业的日志

如需使用 Cloud Run 页面查看作业日志,请执行以下操作:

  1. 转到 Cloud Run

  2. 点击作业标签页。

  3. 在作业列表中找到作业,然后点击它。

  4. 点击日志标签页,以获取此作业的所有执行的容器日志。您可以按日志严重级别进行过滤。

  5. 或者,如果要查看特定作业执行预先过滤的日志,请点击作业执行,然后点击日志标签页。

使用 Google Cloud CLI 查看服务日志

您可以在命令行中使用 Google Cloud CLI 查看 Cloud Run 服务的尾随日志或读取现有日志。默认情况下,日志采用针对控制台进行了优化的单行格式。

如需跟踪日志,您需要在 Google Cloud CLI 中安装 log-streaming 组件。如果未安装该组件,系统将在需要时提示您安装。

在命令行中查看跟踪日志

对于 Cloud Run 服务,您可以直接在命令行中从 Cloud Run 服务实时跟踪日志:

gcloud beta run services logs tail SERVICE --project PROJECT-ID

您需要进行如下替换

  • SERVICE 替换为 Cloud Run 服务的名称。
  • PROJECT-ID 替换为 Google Cloud 项目 ID。您可以通过运行 gcloud config get-value project 命令来查看项目 ID。

在命令行中读取日志

对于 Cloud Run 服务,您可以通过以下任一方式读取现有日志:

  • 以针对控制台优化的格式:
    gcloud run services logs read SERVICE --limit=10 --project PROJECT-ID
  • 直接从 Cloud Logging:
    gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=SERVICE" --project PROJECT-ID --limit 10

您需要进行如下替换

  • SERVICE 替换为 Cloud Run 服务的名称。
  • PROJECT-ID 替换为 Google Cloud 项目 ID。您可以通过运行 gcloud config get-value project 命令来查看项目 ID。

在 Cloud Logging 中查看日志

如需在 Cloud Logging Logs Explorer 中查看 Cloud Run 日志,请执行以下操作:

  1. 转到 Google Cloud 控制台中的“Logs Explorer”页面:

    转到“Logs Explorer”页面

  2. 在页面顶部选择一个现有 Google Cloud 项目,或者创建一个新项目。

  3. 使用下拉菜单,选择服务的 Cloud Run 修订版本资源或选择作业的 Cloud Run 作业

如需了解详情,请参阅使用 Logs Explorer

在 Cloud Code 中查看服务日志

如需在 Cloud Code 中查看日志,请阅读 IntelliJVisual Studio Code 指南。

以编程方式读取日志

如果要以编程方式读取日志,可以使用以下方法之一:

了解实例扩缩日志

为您的服务启动新实例后,Cloud Logging 会在 varlog/system 日志名称下添加日志条目,以反映创建每个实例的原因。日志条目遵循以下格式:

Starting new instance. Reason: REASON - DESCRIPTION

下表详细介绍了实例说明:

原因 说明
CUSTOMER_MIN_INSTANCE 客户为服务配置的实例数下限
SCHEDULED 由于配置的扩缩因子(例如 CPU 利用率、请求吞吐量等)及其目标而启动实例。
OVERFLOW 由于未找到适合当前流量的现有容量而启动实例。

写入容器日志

从服务或作业中写入日志时,只要将日志写入以下任何位置,Cloud Logging 就会自动获取这些日志:

大多数开发者都应该使用标准输出和标准错误来写入日志。

写入这些受支持位置的容器日志会自动关联至 Cloud Run 服务、修订版本和位置,或与 Cloud Run 作业关联。Error Reporting 会捕获并报告这些日志中包含的异常。

集成式日志记录功能平衡了可靠性和资源使用情况,适用于大多数应用。如果您的应用有更高的数据量或可靠性要求,我们建议您直接使用 Cloud Logging API,将其作为应用中的库或作为单独的边车容器

在日志中使用简单文本和结构化 JSON

写入日志时,您可以发送一个简单文本字符串,也可以发送一行序列化 JSON(也称为“结构化”数据)。它由 Cloud Logging 获取并解析,并放入 jsonPayload 中。相反,简单文本消息会放在 textPayload 中。

写入结构化日志

以下代码段展示了如何写入结构化日志条目。还介绍了如何将日志消息与相应的请求日志相关联。

Node.js


// Uncomment and populate this variable in your code:
// const project = 'The project ID of your function or Cloud Run service';

// Build structured log messages as an object.
const globalLogFields = {};

// Add log correlation to nest all log messages beneath request log in Log Viewer.
// (This only works for HTTP-based invocations where `req` is defined.)
if (typeof req !== 'undefined') {
  const traceHeader = req.header('X-Cloud-Trace-Context');
  if (traceHeader && project) {
    const [trace] = traceHeader.split('/');
    globalLogFields['logging.googleapis.com/trace'] =
      `projects/${project}/traces/${trace}`;
  }
}

// Complete a structured log entry.
const entry = Object.assign(
  {
    severity: 'NOTICE',
    message: 'This is the default display field.',
    // Log viewer accesses 'component' as 'jsonPayload.component'.
    component: 'arbitrary-property',
  },
  globalLogFields
);

// Serialize to a JSON string and output.
console.log(JSON.stringify(entry));

Python

# Uncomment and populate this variable in your code:
# PROJECT = 'The project ID of your Cloud Run service';

# Build structured log messages as an object.
global_log_fields = {}

# Add log correlation to nest all log messages.
# This is only relevant in HTTP-based contexts, and is ignored elsewhere.
# (In particular, non-HTTP-based Cloud Functions.)
request_is_defined = "request" in globals() or "request" in locals()
if request_is_defined and request:
    trace_header = request.headers.get("X-Cloud-Trace-Context")

    if trace_header and PROJECT:
        trace = trace_header.split("/")
        global_log_fields[
            "logging.googleapis.com/trace"
        ] = f"projects/{PROJECT}/traces/{trace[0]}"

# Complete a structured log entry.
entry = dict(
    severity="NOTICE",
    message="This is the default display field.",
    # Log viewer accesses 'component' as jsonPayload.component'.
    component="arbitrary-property",
    **global_log_fields,
)

print(json.dumps(entry))

Go

每个日志条目的结构由 Entry 类型提供:


// Entry defines a log entry.
type Entry struct {
	Message  string `json:"message"`
	Severity string `json:"severity,omitempty"`
	Trace    string `json:"logging.googleapis.com/trace,omitempty"`

	// Logs Explorer allows filtering and display of this as `jsonPayload.component`.
	Component string `json:"component,omitempty"`
}

// String renders an entry structure to the JSON format expected by Cloud Logging.
func (e Entry) String() string {
	if e.Severity == "" {
		e.Severity = "INFO"
	}
	out, err := json.Marshal(e)
	if err != nil {
		log.Printf("json.Marshal: %v", err)
	}
	return string(out)
}

记录 Entry struct 时,调用 String 方法以将其编组为 Cloud Logging 预期的 JSON 格式:


func init() {
	// Disable log prefixes such as the default timestamp.
	// Prefix text prevents the message from being parsed as JSON.
	// A timestamp is added when shipping logs to Cloud Logging.
	log.SetFlags(0)
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	// Uncomment and populate this variable in your code:
	// projectID = "The project ID of your Cloud Run service"

	// Derive the traceID associated with the current request.
	var trace string
	if projectID != "" {
		traceHeader := r.Header.Get("X-Cloud-Trace-Context")
		traceParts := strings.Split(traceHeader, "/")
		if len(traceParts) > 0 && len(traceParts[0]) > 0 {
			trace = fmt.Sprintf("projects/%s/traces/%s", projectID, traceParts[0])
		}
	}

	log.Println(Entry{
		Severity:  "NOTICE",
		Message:   "This is the default display field.",
		Component: "arbitrary-property",
		Trace:     trace,
	})

	fmt.Fprintln(w, "Hello Logger!")
}

Java

通过在 logback.xml 配置中启用 Logstash JSON 编码器,以使用 LogbackSLF4J 启用 JSON 日志记录。

// Build structured log messages as an object.
Object globalLogFields = null;

// Add log correlation to nest all log messages beneath request log in Log Viewer.
// TODO(developer): delete this code if you're creating a Cloud
//                  Function and it is *NOT* triggered by HTTP.
String traceHeader = req.headers("x-cloud-trace-context");
if (traceHeader != null && project != null) {
  String trace = traceHeader.split("/")[0];
  globalLogFields =
      kv(
          "logging.googleapis.com/trace",
          String.format("projects/%s/traces/%s", project, trace));
}
// -- End log correlation code --

// Create a structured log entry using key value pairs.
// For instantiating the "logger" variable, see
// https://cloud.google.com/run/docs/logging#run_manual_logging-java
logger.error(
    "This is the default display field.",
    kv("component", "arbitrary-property"),
    kv("severity", "NOTICE"),
    globalLogFields);

自定义标准字段名称,以将不需要的内容从日志负载中排除。如需查看字段名称和预期数据格式的列表,请参阅使用 Logging 代理

<configuration>
  <appender name="jsonConsoleAppender" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="net.logstash.logback.encoder.LogstashEncoder">
      <!-- Ignore default logging fields -->
      <fieldNames>
        <timestamp>[ignore]</timestamp>
        <version>[ignore]</version>
        <logger>[ignore]</logger>
        <thread>[ignore]</thread>
        <level>[ignore]</level>
        <levelValue>[ignore]</levelValue>
      </fieldNames>
    </encoder>
  </appender>
  <root level="INFO">
    <appender-ref ref="jsonConsoleAppender"/>
  </root>
</configuration>

消息中的特殊 JSON 字段

如果您以 JSON 字典形式提供结构化日志,则系统会从 jsonPayload 中删除某些特殊字段,并将这些字段写入所生成 LogEntry 中的相应字段(具体说明请参阅特殊字段文档)。

例如,如果您的 JSON 包含 severity 属性,则系统会将该属性从 jsonPayload 中移除,并将其显示为日志条目的 severitymessage 属性用作日志条目的主显示文本(如果存在)。 如需详细了解特殊属性,请参阅下面的 Logging 资源部分。

将容器日志与请求日志相关联(仅限服务)

在 Logs Explorer 中,以“父级-子级”格式可以查看由相同 trace 关联的日志:当您点击请求日志条目左侧的三角形图标时,与该请求相关的容器日志即会嵌套显示在请求日志下方。

如果不使用 Cloud Logging 客户端库,容器日志不会自动关联至请求日志。要在不使用客户端库的情况下将容器日志与请求日志相关联,您可以使用结构化 JSON 日志行,其包含 logging.googleapis.com/trace 字段,该字段具有从 X-Cloud-Trace-Context 标头中提取的跟踪标识符,如以上示例结构化日志记录所示。

控制请求日志资源的使用情况(仅限服务)

请求日志是系统自动创建的。虽然您无法直接通过 Cloud Run 控制请求日志量,但您可以使用 Cloud Logging 的日志排除功能。

有关 Logging 代理的说明

如果您已将 Cloud Logging 用于某些 Google Cloud 产品(例如 Compute Engine),则可能已使用 Cloud Logging 日志记录代理。Cloud Run 不使用 Logging 代理,因为它具有对日志收集的内置支持。

日志记录资源名称

Cloud Run 的日志记录资源名称如下:

日志记录资源

点击 Logs Explorer 中的日志条目会打开一条 JSON 格式的日志条目,因此您可以深入了解所需的详细信息。

日志条目中的所有字段(例如时间戳、严重性和 httpRequest)都是标准字段,日志条目文档中对其进行了说明。

Cloud Run 会添加额外的元数据,以便您可以确定日志的来源。这包括您在 Cloud Run 服务上设置的标签以及特定于 Cloud Run 的资源标签。

服务的日志条目字段

以下是可以在 Cloud Run 服务的日志条目中找到的字段列表:

字段 值和备注
LogEntry.labels.instanceId 处理请求的实例。
LogEntry.labels.mylabel
LogEntry.labels.mysecondlabel
您在服务上设置的标签
LogEntry.logName 用于标识日志,例如请求日志、标准错误、标准输出等。
LogEntry.resource.labels.location 标识服务的 Google Cloud 位置。
LogEntry.resource.labels.project_id 服务被部署到的项目。
LogEntry.resource.labels.revision_name 处理请求的修订版本。
LogEntry.resource.labels.service_name 处理请求的服务。
LogEntry.resource.type cloud_run_revision。Cloud Run 资源类型。

以下是 Cloud Run 服务的请求日志条目示例:

{
 httpRequest: {}
 insertId:  "5c82b3d1000ece0000000000"
 labels: {
  instanceId:  "00bf4bf00000fb59c906a00000c9e29c2c4e06dce91500000000056008d2b6460f163c0057b97b2345f2725fb2423ee5f0bafd36df887fdb1122371563cf1ff453717282afe000001"
  mylabel: "mylabelvalue"
  mysecondlabel: "mysecondlabelvalue"
 }
 logName:  "projects/my-project/logs/run.googleapis.com%2Frequests"
 receiveTimestamp:  "2019-03-08T18:26:25.981686167Z"
 resource: {
  labels: {
   configuration_name:  "myservice"
   location:  "us-central1"
   project_id:  "my-project"
   revision_name:  "myservice-00002"
   service_name:  "myservice"
  }
  type:  "cloud_run_revision"
 }
 severity:  "INFO"
 timestamp:  "2019-03-08T18:26:25.970397Z"
}

作业的日志条目字段

以下是可以在 Cloud Run 作业的日志条目中找到的字段列表:

字段 值和备注
LogEntry.labels.instanceId 实例。
LogEntry.labels.mylabel、

LogEntry.labels.mysecondlabel

您在作业上设置的标签
LogEntry.logName 用于标识日志,例如请求日志、标准错误、标准输出等。
LogEntry.resource.labels.location 标识服务的 Google Cloud 位置。
LogEntry.resource.labels.project_id 服务被部署到的项目。
LogEntry.resource.labels.job_name 作业的名称。
LogEntry.labels.execution_name 作业执行的名称。
LogEntry.labels.task_index 任务索引。
LogEntry.labels.task_attempt 此任务已经尝试的次数。
LogEntry.resource.type cloud_run_job。Cloud Run 资源类型。