本文档介绍了 Cloud Logging 如何拆分超大的审核日志条目,并提供了有关如何重新组合这些拆分审核日志的指导。
如果单个审核日志条目超出大小限制,则 Cloud Logging 会拆分该条目并将原始审核日志条目中包含的数据分散到多个条目中。用户可能需要重新组合拆分后的审核日志,因为各个拆分日志条目不包含原始审核日志中的所有字段。
识别拆分后的审核日志条目
拆分日志条目包含与其拆分自的原始条目相关的信息。如果日志条目包含 split
字段,则该条目是拆分较大的原始日志条目的结果。split
字段是一个 LogSplit
对象,其中包含标识相关拆分日志条目所需的信息。
每个分块日志条目都包含以下字段:
split.uid
:从一个共同的原始日志条目拆分出来的一组日志条目的唯一标识符。从原始日志条目拆分出的所有条目的此字段值都相同。split.index
:此条目在一系列分屏条目中的索引位置。分块中的第一个条目具有索引0
。split.index
也会附加到LogEntry.insertId
字段。split.totalSplits
:原始日志条目拆分成的日志条目数。从原始日志条目拆分出的所有条目的此字段值都相同。
日志条目的拆分方式
当过大的审核日志条目被拆分时,这些字段会按如下方式分布在生成的拆分日志条目中:
除
protoPayload
字段外,所有字段都会复制到每个分屏条目中。以下
protoPayload
子字段可以拆分到多个条目中:protoPayload.metadata
protoPayload.request
protoPayload.response
所有其他
protoPayload
子字段都包含在所有分屏条目中。对于
protoPayload.metadata
、protoPayload.request
和protoPayload.response
子字段,以下字段类型可拆分到多个条目中:
如果某个字段是可拆分字段的成员,但不是可拆分字段类型之一,则它只会出现在其中一个分块日志中。
例如,protoPayload.request
子字段中的布尔值字段只能出现在一个分屏日志条目中,但 protoPayload.request
子字段中的字符串字段的内容可以拆分到多个分屏日志条目中。
如需查看长条目拆分方式的示例,请参阅日志条目拆分示例。
包含多个值的重复字段
当 protoPayload.metadata
、protoPayload.request
或 protoPayload.response
字段的值包含重复值列表时,该列表可能会被拆分并分布在多个分屏日志条目中。
例如,值列表 ["foo", "bar", "baz"] 可能会拆分为 2 个列表:["foo", "ba"] 和 ["", "r", "baz"]。第一个列表是 0
的 split.index
条目,第二个列表是 1
的 split.index
条目。第二个列表以空字符串开头,以便维护列表中元素的位置,并指明不同列表中相同位置的元素必须联接在一起。在该示例中,ba
是条目 0
中的第二个列表元素,r
是条目 1
中的第二个列表元素,因此在重新组装原始列表时,它们会按 bar
的顺序重新组合。
大型非重复字段
当大型非重复 Struct
和 string
字段被拆分时,系统会按如下方式处理这些字段:
string
字段在字符级别拆分,而不是在字节级别拆分。因此,多字节字符不会发生更改。LogEntry.split.index
用于控制分屏非重复字段内容的排序。
重新组合拆分日志条目
如需重新组合一组拆分日志,请完成以下步骤:
按
LogEntry.split.index
对一组拆分后的审核日志进行升序排序。创建第一个分块日志的副本,其中
LogEntry.split.index == 0
。 此副本是重新组合日志的开头。对于其余日志条目,请迭代
protoPayload
的所有可拆分字段,并针对每个字段完成以下步骤:如果该字段已存在于重新组装的日志中,请将该字段的内容附加到重新组装的日志中。
如果该字段不存在于重新组合后的日志中,请将该字段复制到重新组合后的日志中
分割时,重复字段会保留其元素的索引,因此您可以在重新组合重复字段时在元素级别应用这些步骤。
迭代完可拆分字段后,清除重新组合日志的
LogEntry.split
。从重新组装的日志的
LogEntry.insert_id
中移除.0
后缀。
查询示例
如需查找从同一原始日志条目分离出来的所有日志条目,请在日志浏览器中运行以下查询,并将 UID 变量替换为所需值:
split.uid="UID"
例如:
split.uid="abc123"
如需查找任何分块中的所有日志条目,请运行以下查询:
split:*
滤除拆分后的审核日志
您可以使用以下过滤条件从查询中排除所有分屏审核日志:
split.totalSplits = 0
您还可以使用以下过滤条件,仅包含拆分审核日志的第一个条目,并排除其余条目:
split.index = 0
日志条目拆分示例
以下示例展示了某个审核日志条目在拆分为四个新日志条目之前的样子。新条目显示了分屏操作中如何处理不同的字段。
拆分前的过大审核日志条目
{
"insertId": "567",
"logName": "projects/1234/logs/cloudaudit.googleapis.com%2Fdata_access",
"resource": {
"type": "audited_resource"
},
"protoPayload": {
"serviceName": "example.googleapis.com",
"methodName": "google.cloud.example.ExampleMethod",
"resourceName": "projects/1234/resources/123",
"status": {
"code": 0
},
"authenticationInfo": {
"principalEmail": "user@example_company.com"
},
"authorizationInfo": [
{
"resource": "example.googleapis.com/projects/1234/resources/123",
"permission": "examples.get",
"granted": "true"
}
],
"request" {
"boolField": true,
"numberField": 123,
"stringField": "Very long string that needs 2 log entries.",
"structField": {
"nestedNumberField": 1337,
"nestedStringField": "Another long string that needs 2 log entries.",
},
"listField" [
{"value": "short 1"},
{"value": "Yet another long string."},
{"value": "short 2"},
{"value": "short 3"},
]
}
}
}
拆分后过大的审核日志条目
原始日志条目会拆分为以下条目。请注意,每个条目都包含 split
对象,其 uid
和 totalSplits
值均为 4
。每个条目的 split.index
值均为 0
、1
、2
或 3
,表示分屏日志条目的顺序。
拆分日志条目,编号为 0
以下是第一个分块日志条目,其 split.index
值为 0
。
{
"insertId": "567.0",
"logName": "projects/1234/logs/cloudaudit.googleapis.com%2Fdata_access",
"resource": {
"type": "audited_resource"
},
"split": {
"uid": "789+2022-02-22T12:22:22.22+05:00",
"index": 0,
"totalSplits": 4,
},
"protoPayload": {
// The following fields are included in all split entries
"serviceName": "example.googleapis.com",
"methodName": "google.cloud.example.ExampleMethod",
"resourceName": "projects/1234/resources/123",
"status": {
"code": 0
},
"authenticationInfo": { // small size; included in all split entries
"principalEmail": "user@example_company.com"
},
// The following field is included in this split entry only.
"authorizationInfo": [
{
"resource": "spanner.googleapis.com/projects/1234/datasets/123",
"permission": "databases.read",
"granted": "true"
}
],
// The following field is split across all the split entries
"request" {
// boolField and numberField can only be in one split.
"boolField": true,
"numberField": 123,
// Split with the next LogEntry.
"stringField": "Very long string that ",
}
}
}
拆分日志条目,索引 1
下面是下一个分块日志条目,其 split.index
值为 1
。
{
"insertId": "567.1",
"logName": "projects/1234/logs/cloudaudit.googleapis.com%2Fdata_access",
"resource": {
"type": "audited_resource"
},
"split": {
"uid": "567+2022-02-22T12:22:22.22+05:00",
"index": 1,
"totalSplits": 4,
},
"protoPayload": {
"serviceName": "example.googleapis.com",
"methodName": "google.cloud.example.ExampleMethod",
"resourceName": "projects/1234/resources/123",
"status": {
"code": 0
},
"authenticationInfo": {
"principalEmail": "user@example_company.com"
},
"request" {
// boolField and numberField aren't present
// Continued from the previous entry.
"stringField": "needs 2 log entries.",
"structField": {
"nestedNumberField": 1337,
// Split with the next LogEntry.
"nestedStringField": "Another long string ",
}
}
}
}
拆分日志条目,索引 2
下面是下一个分块日志条目,其 split.index
值为 2
。
{
"insertId": "567.2",
"logName": "projects/1234/logs/cloudaudit.googleapis.com%2Fdata_access",
"resource": {
"type": "audited_resource"
},
"split": {
"uid": "567+2022-02-22T12:22:22.22+05:00",
"index": 2,
"totalSplits": 4,
},
"protoPayload": {
"serviceName": "example.googleapis.com",
"methodName": "google.cloud.example.ExampleMethod",
"resourceName": "projects/1234/resources/123",
"status": {
"code": 0
},
"authenticationInfo": {
"principalEmail": "user@example_company.com"
},
request {
"structField": {
// Continued from the previous entry.
"nestedStringField": "that needs 2 log entries.",
}
"listField" [
{"value": "short 1"},
{"value": "Yet another "}, // Split with the next LogEntry.
// Missing two values, split with the next LogEntry.
]
}
}
}
拆分日志条目,编号为 3
以下是最终的分块日志条目,其 split.index
值为 3
。
{
"insertId": "567.3",
"logName": "projects/1234/logs/cloudaudit.googleapis.com%2Fdata_access",
"resource": {
"type": "audited_resource"
},
"split": {
"uid": "567+2022-02-22T12:22:22.22+05:00",
"index": 3,
"totalSplits": 4,
},
"protoPayload": {
"serviceName": "example.googleapis.com",
"methodName": "google.cloud.example.ExampleMethod",
"resourceName": "projects/1234/resources/123",
"status": {
"code": 0
},
"authenticationInfo": {
"principalEmail": "user@example_company.com"
},
"request" {
"listField" [
{}, // Padding to ensure correct positioning of elements in split repeated fields.
{"value": "long string."}, // Continuation of the second repeated field.
{"value": "short 2"},
{"value": "short 3"},
]
}
}
}