本文档介绍了 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"]。第一个列表是 split.index
为 0
的条目,第二个列表位于 split.index
为 1
的条目中。第二个列表以空字符串开头,用来保持列表中元素的位置,并指示不同列表中相同位置的元素必须连接在一起。在本例中,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"},
]
}
}
}