结构化日志记录

本文档讨论了结构化日志记录的概念以及 向日志条目载荷字段添加结构。何时为日志载荷设置格式 作为 JSON 对象,并且该对象存储在 jsonPayload 字段中,则日志 条目称为“结构化日志”。对于这些日志,您可以构建查询, 可搜索特定 JSON 路径,并且可以将 日志载荷。相反,如果日志载荷的格式设置为字符串, 存储在 textPayload 字段中,则日志条目是非结构化的。 您可以搜索文本字段,但无法将其内容编入索引。

如需创建结构化日志条目,请执行以下任一操作:

  • 调用 entries.write API 方法并提供 完整格式的 LogEntry
  • 使用 gcloud logging write 命令。
  • 使用可写入结构化日志的 Cloud Logging 客户端库。
  • 使用 BindPlane 服务。
  • 使用代理写入日志

    • 有些 Google Cloud 服务包含集成式日志记录代理,该代理 作为日志写入 stdoutstderr 的数据写入 Cloud Logging。 您可以将此方法用于 Google Cloud 服务,例如 Google Kubernetes Engine、 App Engine 柔性环境和 Cloud Run 函数

    • 对于 Compute Engine 虚拟机 (VM),您可以安装和配置 Ops Agent 或旧版 Logging 代理,然后使用 已安装的代理,以将日志发送到 Cloud Logging。

如需详细了解这些方法,请参阅以下部分。

使用客户端库或 API 写入日志

您可以使用 Cloud Logging 客户端库 调用 Cloud Logging API,或直接调用 Cloud Logging API。 客户端库可简化特殊 JSON 字段的填充, 自动捕获一些信息,并提供接口 正确填充这些字段不过,为了全面控制 载荷结构,请直接调用 Cloud Logging API 并将 完整 LogEntry 结构添加到 Cloud Logging API。

如需了解详情,请参阅 entries.write 参考文档。

如需查看代码示例,请参阅 写入结构化日志

使用 gcloud CLI 写入日志

您可以使用 gcloud CLI 写入日志数据。界面 支持非结构化日志和结构化日志。当您需要编写 结构化日志,请为命令提供序列化 JSON 对象。

有关快速入门,请参阅 使用 Google Cloud CLI 写入和查询日志条目

如需查看代码示例,请参阅 gcloud logging write 参考文档。

使用 BindPlane 写入日志

您可以使用 BindPlane 服务将日志发送到 Logging。 这些日志的载荷采用 JSON 格式 格式,并根据源系统进行结构化处理。如需了解 查找和查看使用 BindPlane 注入的日志,请参阅 BindPlane 快速入门指南

使用代理写入日志

如需从 Compute Engine 实例获取日志,请执行以下操作: 您可以使用 Ops Agent旧版 Cloud Logging 代理。 这两个代理都可以从第三方应用收集指标,并且 两者都支持结构化日志记录:

  • Ops Agent 是推荐的代理 收集 来自 Compute Engine 实例的遥测数据。此代理将 可将日志记录和指标整合到单个代理中,从而提供基于 YAML 的 以及高吞吐量日志记录功能

    有关如何配置 Ops Agent 以支持 结构化日志记录或自定义结构化日志的形式,请参阅 配置 Ops Agent

  • 旧版 Cloud Logging 代理会收集 日志。此代理不会收集其他形式的遥测数据。

本部分的其余内容专门介绍了 旧版 Logging 代理。

Logging 代理:特殊 JSON 字段

JSON 对象中的一些字段被 旧版 Logging 代理,并将其提取到 LogEntry 结构。这些特殊的 JSON 字段可用于设置 LogEntry 中的以下字段:

  • severity
  • spanId
  • labels 由用户定义
  • httpRequest

由于 JSON 比文本行更精确且更全能,因此您可以使用 JSON 对象编写多行消息并添加元数据。

如需使用简化格式为您的应用创建结构化日志条目,请参阅下表,其中列出了 JSON 格式的字段及其值:

JSON 日志字段 LogEntry 字段 Cloud Logging 代理函数 示例值
severity severity Logging 代理尝试匹配各种常见严重性字符串,其中包括 Logging API 识别的 LogSeverity 字符串列表。 "severity":"ERROR"
message textPayload(或 jsonPayload 的一部分) 日志浏览器中的日志条目行显示的消息。 "message":"There was an error in the application."

注意:如果在 Logging 代理移动其他特殊用途字段之后只留下了一个 message 字段并且 detect_json 未启用,则该 message 将保存为 textPayload,否则 message 仍保留在 jsonPayload 中。detect_json 不适用于 Google Kubernetes Engine 等代管式日志记录环境。如果您的日志条目包含异常堆栈轨迹,则应在此 message JSON 日志字段中设置异常堆栈轨迹,以便系统可以解析该异常堆栈跟踪并将其保存到 Error Reporting 之中。
log(仅限旧版 Google Kubernetes Engine) textPayload 仅适用于旧版 Google Kubernetes Engine:如果在移动特殊用途字段后,只留下一个 log 字段,则该字段将保存为 textPayload
httpRequest httpRequest 采用 LogEntry HttpRequest 字段格式的结构化记录。 "httpRequest":{"requestMethod":"GET"}
与时间相关的字段 timestamp 如需了解详情,请参阅与时间相关的字段 "time":"2020-10-12T07:20:50.52Z"
logging.googleapis.com/insertId insertId 如需了解详情,请参阅 LogEntry 页面上的 insertId "logging.googleapis.com/insertId":"42"
logging.googleapis.com/labels labels 此字段的值必须为结构化记录。如需了解详情,请参阅 LogEntry 页面上的 labels "logging.googleapis.com/labels": {"user_label_1":"value_1","user_label_2":"value_2"}
logging.googleapis.com/operation operation 此字段的值也可用于日志浏览器对相关日志条目进行分组。如需了解详情,请参阅 LogEntry 页面上的 operation "logging.googleapis.com/operation": {"id":"get_data","producer":"github.com/MyProject/MyApplication", "first":"true"}
logging.googleapis.com/sourceLocation sourceLocation 与日志条目关联的源代码位置信息(如果有)。如需了解详情,请参阅 LogEntry 页面上的 LogEntrySourceLocation "logging.googleapis.com/sourceLocation": {"file":"get_data.py","line":"142","function":"getData"}
logging.googleapis.com/spanId spanId 与日志条目相关联的跟踪记录内的 Span ID。如需了解详情,请参阅 LogEntry 页面上的 spanId "logging.googleapis.com/spanId":"000000000000004a"
logging.googleapis.com/trace trace 与日志条目关联的跟踪记录的资源名称(如果有)。如需了解详情,请参阅 LogEntry 页面上的 trace "logging.googleapis.com/trace":"projects/my-projectid/traces/0679686673a"

注意:如果未向 stdoutstderr 写入数据,则此字段的值应设置为 projects/[PROJECT-ID]/traces/[TRACE-ID] 格式,以便 Logs Explorer 和 Trace Viewer 可以使用此字段对日志条目进行分组,并且将日志条目与跟踪记录一起显示。 如果 autoformat_stackdriver_trace 为 true,并且 [V]ResourceTrace traceId 的格式相匹配,则 LogEntry trace 字段的值将是 projects/[PROJECT-ID]/traces/[V]
logging.googleapis.com/trace_sampled traceSampled 此字段的值必须是 truefalse。如需了解详情,请参阅 LogEntry 页面上的 traceSampled "logging.googleapis.com/trace_sampled": false

如需创建简化格式的日志条目,请使用字段创建条目的 JSON 表示。所有字段均为可选字段。

以下是简化版 JSON 日志条目的示例:

{
  "severity":"ERROR",
  "message":"There was an error in the application.",
  "httpRequest":{
    "requestMethod":"GET"
  },
  "times":"2020-10-12T07:20:50.52Z",
  "logging.googleapis.com/insertId":"42",
  "logging.googleapis.com/labels":{
    "user_label_1":"value_1",
    "user_label_2":"value_2"
  },
  "logging.googleapis.com/operation":{
    "id":"get_data",
    "producer":"github.com/MyProject/MyApplication",
     "first":"true"
  },
  "logging.googleapis.com/sourceLocation":{
    "file":"get_data.py",
    "line":"142",
    "function":"getData"
  },
  "logging.googleapis.com/spanId":"000000000000004a",
  "logging.googleapis.com/trace":"projects/my-projectid/traces/06796866738c859f2f19b7cfb3214824",
  "logging.googleapis.com/trace_sampled":false
}

以下是生成的日志条目的示例:

{
  "insertId": "42",
  "jsonPayload": {
    "message": "There was an error in the application",
    "times": "2020-10-12T07:20:50.52Z"
  },
  "httpRequest": {
    "requestMethod": "GET"
  },
  "resource": {
    "type": "k8s_container",
    "labels": {
      "container_name": "hello-app",
      "pod_name": "helloworld-gke-6cfd6f4599-9wff8",
      "project_id": "stackdriver-sandbox-92334288",
      "namespace_name": "default",
      "location": "us-west4",
      "cluster_name": "helloworld-gke"
    }
  },
  "timestamp": "2020-11-07T15:57:35.945508391Z",
  "severity": "ERROR",
  "labels": {
    "user_label_2": "value_2",
    "user_label_1": "value_1"
  },
  "logName": "projects/stackdriver-sandbox-92334288/logs/stdout",
  "operation": {
    "id": "get_data",
    "producer": "github.com/MyProject/MyApplication",
    "first": true
  },
  "trace": "projects/my-projectid/traces/06796866738c859f2f19b7cfb3214824",
  "sourceLocation": {
    "file": "get_data.py",
    "line": "142",
    "function": "getData"
  },
  "receiveTimestamp": "2020-11-07T15:57:42.411414059Z",
  "spanId": "000000000000004a"
}

Logging 代理:配置

旧版 Logging 代理 google-fluentd Cloud Logging 专有的 Fluentd 日志数据收集器。 Logging 代理默认采用 Fluentd 配置,并使用 Fluentd 输入插件从外部源(例如磁盘上的文件)拉取事件日志,或者使用该插件解析传入的日志记录。

Fluentd 支持多种解析器,这些解析器可提取日志并将日志转换为结构化 (JSON) 负载。

通过使用 format [PARSER_NAME] 配置日志源,您可以在 内置解析器。有关如何配置 旧版 Logging 代理,请参阅 配置 Logging 代理

以下代码示例展示了 Fluentd 配置、输入日志记录,以及属于 Cloud Logging 日志条目一部分的输出结构化载荷:

  • Fluentd 配置:

      <source>
        @type tail
    
        format syslog # This uses a predefined log format regex named
                      # `syslog`. See details at https://docs.fluentd.org/parser/syslog.
    
        path /var/log/syslog
        pos_file /var/lib/google-fluentd/pos/syslog.pos
        read_from_head true
        tag syslog
      </source>
    
  • 日志记录(输入):

      <6>Feb 28 12:00:00 192.168.0.1 fluentd[11111]: [error] Syslog test
    
  • 结构化负载(输出):

      jsonPayload: {
          "pri": "6",
          "host": "192.168.0.1",
          "ident": "fluentd",
          "pid": "11111",
          "message": "[error] Syslog test"
      }
    

如需详细了解 syslog 解析器的工作原理,请参阅详细的 Fluentd 文档

Logging 代理:默认启用标准解析器

下表列出了启用结构化日志记录后代理中包含的标准解析器:

解析器名称 配置文件
syslog /etc/google-fluentd/config.d/syslog.conf
nginx /etc/google-fluentd/config.d/nginx.conf
apache2 /etc/google-fluentd/config.d/apache.conf
apache_error /etc/google-fluentd/config.d/apache.conf

有关在安装 旧版 Logging 代理,请参阅 安装部分。

Logging 代理:安装

要启用结构化日志记录,您必须更改 旧版 Logging 代理。 启用结构化日志记录会替换之前列出的配置文件 但不会更改代理本身的运行。

启用结构化日志记录后,列出的日志将转换为与启用结构化日志之前的格式不同的日志条目。如果正在将日志路由到 Logging 之外的目标位置,发生的更改可能会影响所有后处理应用。例如,如果将日志路由到 BigQuery,则 BigQuery 会因架构错误拒绝当天剩余时间内的新日志条目。

有关如何安装旧版 Logging 代理和 请参阅安装 Logging 代理

您可以在以下位置找到旧版 Logging 代理配置文件: /etc/google-fluentd/config.d/,其中现在应包含 默认启用的标准解析器

Logging 代理:配置 Apache 访问日志格式

默认情况下,旧版 Logging 代理会存储 Apache 访问权限 日志数据。jsonPayload例如:

{
  "logName": ...,
  "resource": ...,
  "httpRequest": ...,
  "jsonPayload": {
    "user"   : "some-user",
    "method" : "GET",
    "code"   : 200,
    "size"   : 777,
    "host"   : "192.168.0.1",
    "path"   : "/some-path",
    "referer": "some-referer",
    "agent"  : "Opera/12.0"
  },
  ...
}

或者,您也可以将旧版 Logging 代理配置为 将某些字段提取到 httpRequest 字段中。例如:

{
  "logName": ...,
  "resource": ...,
  "httpRequest": {
    "requestMethod": "GET",
    "requestUrl": "/some-path",
    "requestSize": "777",
    "status": "200",
    "userAgent": "Opera/12.0",
    "serverIp": "192.168.0.1",
    "referrer":"some-referrer",
  },
  "jsonPayload": {
    "user":"some-user"
  },
  ...
}

配置 httpRequest 字段(如前面的示例所示)有助于 跟踪:Google Cloud 控制台显示给定 HTTP 请求的所有日志 处于父级-子级层次结构中

要配置此提取,请将以下内容添加到 /etc/google-fluentd/config.d/apache.conf 的末尾:

<filter apache-access>
  @type record_transformer
  enable_ruby true
  <record>
    httpRequest ${ {"requestMethod" => record['method'], "requestUrl" => record['path'], "requestSize" => record['size'], "status" => record['code'], "userAgent" => record['agent'], "serverIp" => record['host'],
    "referer" => record['referer']} }
  </record>
  remove_keys method, path, size, code, agent, host, referer
</filter>

如需详细了解如何配置日志条目,请参阅修改日志记录

Logging 代理:配置 nginx 访问日志格式

默认情况下,旧版 Logging 代理会存储 nginx 访问权限 日志数据。jsonPayload例如:

{
  "logName": ...,
  "resource": ...,
  "httpRequest": ...,
  "jsonPayload": {
    "remote":"127.0.0.1",
    "host":"192.168.0.1",
    "user":"some-user",
    "method":"GET",
    "path":"/some-path",
    "code":"200",
    "size":"777",
    "referrer":"some-referrer",
    "agent":"Opera/12.0",
    "http_x_forwarded_for":"192.168.3.3"
  },
  ...
}

或者,您也可以将旧版 Logging 代理配置为 将某些字段提取到 httpRequest 字段中。例如:

{
  "logName": ...,
  "resource": ...,
  "httpRequest": {
    "requestMethod": "GET",
    "requestUrl": "/some-path",
    "requestSize": "777",
    "status": "200",
    "userAgent": "Opera/12.0",
    "remoteIp": "127.0.0.1",
    "serverIp": "192.168.0.1",
    "referrer":"some-referrer",
  },
  "jsonPayload": {
    "user":"some-user",
    "http_x_forwarded_for":"192.168.3.3"
  },
  ...
}

配置 httpRequest 字段(如前面的示例所示)有助于 跟踪:Google Cloud 控制台显示给定 HTTP 请求的所有日志 处于父级-子级层次结构中

要配置此提取,请将以下内容添加到 /etc/google-fluentd/config.d/nginx.conf 的末尾:

<filter nginx-access>
  @type record_transformer
  enable_ruby true
  <record>
    httpRequest ${ {"requestMethod" => record['method'], "requestUrl" => record['path'], "requestSize" => record['size'], "status" => record['code'], "userAgent" => record['agent'], "remoteIp" => record['remote'], "serverIp" => record['host'], "referer" => record['referer']} }
  </record>
  remove_keys method, path, size, code, agent, remote, host, referer
</filter>

如需详细了解如何配置日志条目,请参阅修改日志记录

编写您自己的解析器

如果标准解析器不支持您的日志,您可以编写自己的解析器。解析器包含一个用于匹配日志记录并将标签应用于各部分的正则表达式。

以下代码示例显示了日志记录中的日志行、配置 以及用于指明日志行格式的正则表达式 存储的日志条目:

  • 日志记录中的日志行:

    REPAIR CAR $500
    
  • 包含指示 日志行:

    $ sudo vim /etc/google-fluentd/config.d/test-structured-log.conf
    $ cat /etc/google-fluentd/config.d/test-structured-log.conf
    <source>
      @type tail
    
      # Format indicates the log should be translated from text to
      # structured (JSON) with three fields, "action", "thing" and "cost",
      # using the following regex:
      format /(?<action>\w+) (?<thing>\w+) \$(?<cost>\d+)/
      # The path of the log file.
      path /tmp/test-structured-log.log
      # The path of the position file that records where in the log file
      # we have processed already. This is useful when the agent
      # restarts.
      pos_file /var/lib/google-fluentd/pos/test-structured-log.pos
      read_from_head true
      # The log tag for this log input.
      tag structured-log
    </source>
    
  • 生成的日志条目:

    {
    insertId:  "eps2n7g1hq99qp"
    jsonPayload: {
      "action": "REPAIR"
      "thing": "CAR"
      "cost": "500"
    }
    labels: {
      compute.googleapis.com/resource_name:  "add-structured-log-resource"
    }
    logName:  "projects/my-sample-project-12345/logs/structured-log"
    receiveTimestamp:  "2023-03-21T01:47:11.475065313Z"
    resource: {
      labels: {
        instance_id:  "3914079432219560274"
        project_id:  "my-sample-project-12345"
        zone:  "us-central1-c"
      }
      type:  "gce_instance"
    }
    timestamp:  "2023-03-21T01:47:05.051902169Z"
    }
    

问题排查

排查在安装或与 旧版 Logging 代理,请参阅 排查代理问题

后续步骤