監査ログエントリの分割

このドキュメントでは、Cloud Logging がサイズ超過の監査ログエントリを分割する方法と、分割された監査ログを再構成する方法について説明します。

単一の監査ログエントリがサイズの上限を超えると、loud Logging がそのエントリを分割し、元の監査ログエントリに含まれるデータを複数のエントリに分散します。個別の分割監査ログエントリには元の監査ログのすべてのフィールドは含まれていないため、ユーザーは分割監査ログを再構築する場合があります。

分割監査ログエントリの認識

分割ログエントリには、分割元のエントリに関する情報が含まれています。ログエントリに split フィールドが含まれている場合、そのエントリは、元の大きなログエントリの分割の結果になります。split フィールドは LogSplit オブジェクトで、関連する分割ログエントリを識別するために必要な情報が含まれます。

各分割ログエントリには、次のフィールドが含まれています。

  • split.uid: 共通の元のログエントリから分割されたログエントリのグループの一意の識別子。このフィールドの値は、元のログエントリから分割されたすべてのエントリで同じです。

  • split.index: 分割エントリのシリーズ内でのこのエントリの位置。分割された最初のエントリのインデックスは 0 です。split.indexLogEntry.insertId フィールドに追加されます。

  • split.totalSplits: 元のログエントリが分割されたログエントリの数。このフィールドの値は、元のログエントリから分割されたすべてのエントリで同じです。

ログエントリの分割方法

サイズの大きい監査ログエントリが分割される場合、フィールドは結果の分割ログエントリ間で次のように分散されます。

  • protoPayload フィールドを除くすべてのフィールドが、各分割エントリに複製されます。

  • 次の protoPayload サブフィールドは、複数のエントリに分割できます。

    • protoPayload.metadata
    • protoPayload.request
    • protoPayload.response
  • 他のすべての protoPayload サブフィールドは、すべての分割エントリに含まれます。

  • protoPayload.metadataprotoPayload.requestprotoPayload.response サブフィールドでは、次のフィールド タイプを複数のエントリに分割できます。

    • Struct
    • Value
    • ListValue
    • 文字列
    • 任意の繰り返しフィールド(タイプに関係なく)

フィールドが分割可能フィールドのメンバーであるものの、分割可能なフィールド タイプのうちの一つでない場合、そのフィールドは分割ログの 1 つにのみ存在します。

たとえば、protoPayload.request サブフィールド内のブール値フィールドは 1 つの分割ログエントリにしか表示されませんが、protoPayload.request サブフィールド内の文字列フィールドの内容が複数の分割ログエントリに存在できます。

長いエントリの分割方法の例については、ログエントリの分割の例をご覧ください。

多数の値が含まれる繰り返しフィールド

protoPayload.metadataprotoPayload.request または protoPayload.response フィールドの値に繰り返し値のリストが含まれている場合、そのリストは分割され、複数の分割ログエントリに分散されることがあります。

たとえば、値「"foo"、"bar"、"baz"」のリストは、「"foo"、"ba"」と「""、"r"、"baz"」の 2 つのリストに分割できます。最初のリストは split.index0 のエントリで、2 番目のリストは split.index1 のエントリです。2 番目のリストは空の文字列で始まります。これは、リスト内の要素の位置を維持し、異なるリスト内の同じ位置にある要素を結合する必要があることを示します。この例では、ba はエントリ 0 の 2 番目のリスト要素で、r はエントリ 1 の 2 番目のリスト要素であるため、元のリストを再構成する場合、順序 bar で再結合されます。

大規模な、繰り返しでないフィールド

大規模な、繰り返しでない Struct フィールドと string フィールドが分割される場合、これらのフィールドは次のように処理されます。

  • string フィールドは、バイトレベルではなく、文字レベルで分割されます。したがって、マルチバイト文字は変更されません。

  • LogEntry.split.index は、分割の繰り返しでないフィールドの内容の順序を制御します。

分割ログエントリの再構成

一連の分割ログを再構成するには、次の手順を実行します。

  1. 分割された監査ログのセットを LogEntry.split.index で昇順に並べ替えます。

  2. 最初の分割ログのコピーを作成します(LogEntry.split.index == 0)。 このコピーは、再構成されたログの先頭です。

  3. 残りのログエントリについて、protoPayload のすべての分割可能フィールドを繰り返し、フィールドごとに次の手順を実行します。

    1. 再構成ログにフィールドがすでに存在する場合は、そのフィールドの内容を再構成されたログに追加します。

    2. 再構成されたログにフィールドが存在しない場合は、そのフィールドを再構成されたログにコピーします。

      分割される場合、繰り返しフィールドがその要素のインデックスが保持されるため、繰り返しフィールドを再構成するときは、これらのステップを要素レベルで適用できます。

  4. 分割可能なフィールドで反復処理を行ったら、再構成されたログの LogEntry.split をクリアします。

  5. 再構成されたログの LogEntry.insert_id から .0 接尾辞を削除します。

サンプルクエリ

同じ元のログエントリから分割されたすべてのログエントリを検索するには、ログ エクスプローラで次のクエリを実行します。UID 変数は目的の値に置き換えます。

split.uid="UID"

例:

split.uid="abc123"

任意の分割の一部であるすべてのログエントリを検索するには、次のクエリを実行します。

split:*

分割監査ログをフィルタする

次のフィルタを使用して、クエリからすべての分割監査ログを除外できます。

split.totalSplits = 0

次のフィルタを使用して、分割監査ログの最初のエントリのみを含め、残りのエントリを除外することもできます。

split.index = 0

ログエントリの分割の例

次の例は、4 つの新しいログエントリに分割される前の監査ログエントリを示しています。新しいエントリは、分割オペレーションでさまざまなフィールドがどのように処理されるかを示しています。

分割前のサイズの大きい監査ログエントリ

{
  "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"},
      ]
    }
  }
}

分割後のサイズの大きい監査ログエントリ

元のログエントリは次のエントリに分割されます。各エントリには、uidtotalSplits 値が 4split オブジェクトが含まれています。各エントリには、012、または 3split.index 値があり、これは分割ログエントリの順序を示します。

分割ログエントリ、インデックス 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"},
      ]
    }
  }
}