サンプル SQL クエリ

このドキュメントでは、ログ分析を使用するようにアップグレードされたログバケットに保存されているログエントリに対するサンプルクエリについて説明します。これらのバケットでは、 Google Cloud コンソールの [ログ分析] ページから SQL クエリを実行できます。その他のサンプルについては、logging-analytics-samplessecurity-analytics の GitHub リポジトリをご覧ください。

このドキュメントでは、SQL やログエントリのルーティングと保存の方法については説明しません。これらのトピックについては、次のステップをご覧ください。

このページの例では、ログビューに対してクエリを実行します。アナリティクス ビューをクエリするには、次のパス形式を使用します。 `analytics_view.PROJECT_ID.LOCATION.ANALYTICS_VIEW_ID`。前の式で、PROJECT_ID はプロジェクトの ID、LOCATIONANALYTICS_VIEW_ID は分析ビューのロケーションと名前です。

SQL 言語のサポート

[ログ分析] ページで使用されるクエリは、一部の例外を除き、GoogleSQL 関数をサポートしています。

[ログ分析] ページを使用して発行された SQL クエリでは、次の SQL コマンドはサポートされていません。

  • DDL コマンドと DML コマンド
  • JavaScript ユーザー定義関数
  • BigQuery ML 関数
  • SQL 変数

以下は、[BigQuery Studio] ページ、[Looker Studio] ページ、または bq コマンドライン ツールを使用してリンク済みデータセットをクエリする場合にのみサポートされます。

  • JavaScript ユーザー定義関数
  • BigQuery ML 関数
  • SQL 変数

ベスト プラクティス

クエリの期間を設定するには、期間セレクタを使用することをおすすめします。たとえば、過去 1 週間のデータを表示するには、期間セレクタで [過去 7 日間] を選択します。また、期間セレクタを使用して、開始時刻と終了時刻の指定、表示時間の指定、タイムゾーンの変更を行うこともできます。

WHERE 句に timestamp フィールドを含めると、期間セレクタの設定は使用されません。次の例は、タイムスタンプでフィルタする方法を示しています。

-- Matches log entries whose timestamp is within the most recent 1 hour.
WHERE timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)

時間でフィルタする方法の詳細については、時間関数タイムスタンプ関数をご覧ください。

始める前に

このセクションでは、ログ分析を使用する前に完了しておく必要のある手順について説明します。

ログバケットを構成する

ログ分析を使用できるようにログバケットがアップグレードされていることを確認します。

  1. Google Cloud コンソールで、[ログストレージ] ページに移動します。

    [ログストレージ] に移動

    このページを検索バーで検索する場合は、小見出しが「Logging」の結果を選択します。

  2. クエリを実行するログビューを含む各ログバケットで、[Log Analytics を使用可能] 列に [開く] が表示されていることを確認します。[アップグレード] が表示されている場合は、[アップグレード] をクリックしてダイアログを完了します。

IAM のロールと権限を構成する

このセクションでは、ログ分析の使用に必要な IAM ロールまたは権限について説明します。

  • ログ分析の使用とログビューへのクエリ実行に必要な権限を取得するには、プロジェクトに対する次の IAM ロールを付与するよう管理者に依頼してください。

    • _Required および _Default のログバケットに対してクエリを実行する: ログ閲覧者roles/logging.viewer
    • プロジェクトのすべてのログビューに対してクエリを実行する: ログ表示アクセス者roles/logging.viewAccessor

    プリンシパルを特定のログビューに制限するには、プロジェクト レベルで付与されたログ表示アクセス者ロールに IAM 条件を追加するか、ログビューのポリシー ファイルに IAM バインディングを追加します。詳細については、ログビューへのアクセスを制御するをご覧ください。

    これらの権限は、[ログ エクスプローラ] ページでログエントリを表示するために必要な権限と同じです。ユーザー定義バケットでのビューに対するクエリ実行、または _Default ログバケットの _AllLogs ビューに対するクエリ実行に必要な追加ロールについて詳しくは、Cloud Logging のロールをご覧ください。

  • 分析ビューへのクエリ実行に必要な権限を取得するには、プロジェクトに対する Observability Analytics ユーザーroles/observability.analyticsUser)IAM ロールを付与するよう管理者に依頼してください。

このページのクエリの使用方法

  1. Google Cloud コンソールで、[ログ分析] ページに移動します。

    [ログ分析] に移動

    検索バーを使用してこのページを検索する場合は、小見出しが [Logging] の結果を選択します。

  2. [クエリ] ペインで、[ SQL] をクリックし、クエリをコピーして SQL クエリ ペインに貼り付けます。

    クエリをコピーする前に、FROM 句で次のフィールドを置き換えます。

    • PROJECT_ID: プロジェクトの ID。
    • LOCATION: ログビューまたは分析ビューのロケーション。
    • BUCKET_ID: ログバケットの名前または ID。
    • LOG_VIEW_ID: ログビューの ID。100 文字に制限され、英字、数字、アンダースコア、ハイフンのみを使用できます。

    次に、ログビューFROM 句の形式を示します。

    FROM `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_ID`
    

    このページのログサンプルは、ログビューをクエリします。アナリティクス ビューをクエリするには、次のパス形式を使用します。 `analytics_view.PROJECT_ID.LOCATION.ANALYTICS_VIEW_ID`前の式で、PROJECT_ID はプロジェクトの ID、LOCATIONANALYTICS_VIEW_ID は分析ビューのロケーションと名前です。

このドキュメントに示すクエリを [BigQuery Studio] ページで使用する場合や、bq コマンドライン ツールを使用する場合は、FROM 句を編集して、リンクされたデータセットへのパスを入力します。たとえば、プロジェクト myproject にある mydataset という名前のリンクされたデータセットで _AllLogs ビューをクエリする場合、パスは myproject.mydataset._AllLogs です。

一般的なユースケース

このセクションでは、カスタムクエリの作成に役立つ一般的なユースケースをいくつか紹介します。

デフォルトのログバケットにログエントリを表示する

_Default バケットに対してクエリを実行するには、次のクエリを実行します。

SELECT
  timestamp, severity, resource.type, log_name, text_payload, proto_payload, json_payload
FROM
  `PROJECT_ID.LOCATION._Default._AllLogs`
-- Limit to 1000 entries
LIMIT 1000

正規表現でフィールド値を抽出する

正規表現を使用して文字列から値を抽出するには、関数 REGEXP_EXTRACT を使用します。

SELECT
  -- Display the timestamp, and the part of the name that begins with test.
  timestamp, REGEXP_EXTRACT(JSON_VALUE(json_payload.jobName), r".*(test.*)$") AS name,
FROM
  `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_ID`
WHERE
  -- Get the value of jobName, which is a subfield in a JSON structure.
  JSON_VALUE(json_payload.jobName) IS NOT NULL
ORDER BY timestamp DESC
LIMIT 20

詳細については、REGEXP_EXTRACT のドキュメントをご覧ください。

前のクエリのような部分文字列の一致の場合、CONTAINS_SUBSTR 関数を使用すると、クエリの効率が向上します。

ログエントリのフィルタリング

クエリにフィルタを適用するには、WHERE 句を追加します。この句で使用する構文は、フィールドのデータ型によって異なります。このセクションでは、さまざまなデータ型の例をいくつか示します。

ログエントリをペイロード タイプでフィルタする

ログエントリには、3 種類のペイロード タイプのいずれかを含めることができます。ペイロード タイプでログエントリをフィルタするには、次のいずれかの句を使用します。

  • テキスト ペイロード

    -- Matches log entries that have a text payload.
    WHERE text_payload IS NOT NULL
    
  • JSON ペイロード

    -- Matches log entries that have a JSON payload.
    WHERE json_payload IS NOT NULL
    
  • proto ペイロード

    -- Matches log entries that have a proto payload.
    -- Because proto_payload has a data type of RECORD, this statement tests
    -- whether a mandatory subfield exits.
    WHERE proto_payload.type IS NOT NULL
    

クエリ結果では、json_payload フィールドと proto_payload フィールドの両方が JSON でレンダリングされ、ナビゲートできます。

タイムスタンプでログデータをフィルタする

タイムスタンプでログエントリをフィルタリングするには、期間セレクタを使用することをおすすめします。ただし、WHERE 句で timestamp を指定することもできます。

-- Matches log entries whose timestamp is within the most recent hour
WHERE timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)

時間でフィルタする方法の詳細については、時間関数タイムスタンプ関数をご覧ください。

リソースでフィルタする

ログデータをリソースでフィルタするには、WHERE 句に resource.type ステートメントを追加します。

-- Matches log entries whose resource type is gce_instance
WHERE resource.type = "gce_instance"

重大度でフィルタする

重大度でログデータをフィルタするには、WHERE 句に severity ステートメントを追加します。

-- Matches log entries whose severity is INFO or ERROR
WHERE severity IS NOT NULL AND severity IN ('INFO', 'ERROR')

ログエントリは、整数である severity_number でフィルタすることもできます。たとえば、次の句は、重大度が NOTICE 以上のすべてのログエントリに一致します。

-- Matches log entries whose severity level is at least NOTICE
WHERE severity_number IS NOT NULL AND severity_number > 200

列挙される値の詳細については、LogSeverity をご覧ください。

ログ名でフィルタする

ログ名でログデータをフィルタするには、WHERE 句に log_name ステートメントまたは log_id ステートメントを追加します。

  • ログ名でリソースパスを指定します。

    -- Matches log entries that have the following log ID.
    WHERE log_name="projects/cloud-logs-test-project/logs/cloudaudit.googleapis.com%2Factivity"
    
  • ログ ID にはリソースパスが含まれていません。

    -- Matches log entries that have the following log id.
    WHERE log_id = "cloudaudit.googleapis.com/data_access"
    

ログエントリをリソースラベルでフィルタする

リソースラベルは JSON 構造として保存されます。JSON 構造内のフィールドの値でフィルタするには、関数 JSON_VALUE を使用します。

SELECT
  timestamp, JSON_VALUE(resource.labels.zone) AS zone, json_payload, resource, labels
FROM
  `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_ID`
WHERE
  -- Matches log entries whose resource type is gce_instance and whose zone is
  -- us-central1-f. Because resource has data type JSON, you must use JSON_VALUE
  -- to get the value for subfields, like zone.
  resource.type = "gce_instance" AND
  JSON_VALUE(resource.labels.zone) = "us-central1-f"
ORDER BY timestamp ASC

上記のクエリは、ログエントリに保存されているリソースラベルの形式に依存しています。リソース フィールドの例を次に示します。

{
   type: "gce_instance"
   labels: {
      instance_id: "1234512345123451"
      project_id: "my-project"
      zone: "us-central1-f"
   }
}

JSON データを取得して変換できるすべての関数の詳細については、JSON 関数をご覧ください。

HTTP リクエストでフィルタする

HTTP リクエスト フィールドを含むログエントリのみをクエリするには、次の句を使用します。

-- Matches log entries that have a HTTP request_method field.
-- Don't compare http_request to NULL. This field has a data type of RECORD.
WHERE http_request.request_method IS NOT NULL

また、IN ステートメントを使用することもできます。

-- Matches log entries whose HTTP request_method is GET or POST.
WHERE http_request.request_method IN ('GET', 'POST')

HTTP ステータスでフィルタする

HTTP ステータスを含むログエントリのみをクエリするには、次の句を使用します。

-- Matches log entries that have an http_request.status field.
WHERE http_request.status IS NOT NULL

JSON データ型のフィールドでフィルタする

JSON データ型のフィールドのサブフィールドに特定の値がある場合にのみログエントリをクエリするには、関数 JSON_VALUE を使用して値を抽出します。

-- Compare the value of the status field to NULL.
WHERE JSON_VALUE(json_payload.status) IS NOT NULL

上記の句は、次の句と微妙に異なります。

-- Compare the status field to NULL.
WHERE json_payload.status IS NOT NULL

最初の句は、ステータス フィールドの値が NULL かどうかをテストします。2 番目の句は、ステータス フィールドが存在するかどうかをテストします。ログビューに 2 つのログエントリが含まれているとします。1 つのログエントリの場合、json_payload フィールドの形式は次のとおりです。

{
    status: {
        measureTime: "1661517845"
    }
}

他のログエントリでは、json_payload フィールドの構造が異なります。

{
    @type: "type.googleapis.com/google.cloud.scheduler.logging.AttemptFinished"
    jobName: "projects/my-project/locations/us-central1/jobs/test1"
    relativeUrl: "/food=cake"
    status: "NOT_FOUND"
    targetType: "APP_ENGINE_HTTP"
}

WHERE json_payload.status IS NOT NULL 句は両方のログエントリと一致します。ただし、WHERE JSON_VALUE(json_payload.status) IS NOT NULL 句は 2 番目のログエントリにのみ一致します。

ログエントリをグループ化して集計する

このセクションは、前述のサンプルに基づき、ログエントリをグループ化して集計する方法について説明します。グループ化を指定せずに集計を指定する場合は、SQL が WHERE 句を満たすすべての行を 1 つのグループとして扱うため、1 つの結果が出力されます。

すべての SELECT 式は、グループ フィールドに含めるか、集計する必要があります。

ログエントリをタイムスタンプでグループ化する

タイムスタンプでデータをグループ化するには、関数 TIMESTAMP_TRUNC を使用します。この関数は、HOUR のような指定した粒度までタイムスタンプを切り詰めます。

SELECT
  -- Truncate the timestamp by hour.
  TIMESTAMP_TRUNC(timestamp, HOUR) AS hour,
  JSON_VALUE(json_payload.status) AS status,
  -- Count the number log entries in each group.
  COUNT(*) AS count
FROM
  `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_ID`
WHERE
  -- Matches log entries that have a status field whose value isn't NULL.
  json_payload IS NOT NULL AND JSON_VALUE(json_payload.status) IS NOT NULL
GROUP BY
  -- Group by hour and status
  hour,status
ORDER BY hour ASC

詳細については、TIMESTAMP_TRUNC のドキュメント日時関数をご覧ください。

ログエントリをリソース別にグループ化する

次のクエリは、ログエントリをリソースタイプでグループ化し、各グループのログエントリ数をカウントする方法を示しています。

SELECT
   -- Count the number of log entries for each resource type
   resource.type, COUNT(*) AS count
FROM
  `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_ID`
GROUP BY resource.type
LIMIT 100

ログエントリを重大度でグループ化する

次のクエリは、ログエントリを重大度でグループ化し、各グループのログエントリ数をカウントする方法を示しています。

SELECT
  -- Count the number of log entries for each severity.
  severity, COUNT(*) AS count
FROM
  `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_ID`
WHERE
  severity IS NOT NULL
GROUP BY severity
ORDER BY severity
LIMIT 100

ログエントリを log_id でグループ化する

次のクエリは、ログエントリをログ ID でグループ化し、各グループのログエントリ数をカウントする方法を示しています。

SELECT
  -- Count the number of log entries for each log ID.
  log_id, COUNT(*) AS count
FROM
  `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_ID`
GROUP BY log_id
ORDER BY count DESC
LIMIT 100

URL ごとの HTTP リクエストの平均レイテンシを計算する

次のクエリは、ログエントリを HTTP リクエスト URL とロケーションでグループ化し、各グループのログエントリ数をカウントする方法を示しています。

SELECT
  -- Compute the average latency for each group. Because the labels field has a
  -- data type of JSON, use JSON_VALUE to get the value of checker_location.
  JSON_VALUE(labels.checker_location) AS location,
  AVG(http_request.latency.seconds) AS secs, http_request.request_url
FROM
  `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_ID`
WHERE
  -- Matches log entries when the request_method field is GET.
  http_request IS NOT NULL AND http_request.request_method IN ('GET')
GROUP BY
  -- Group by request URL and location
  http_request.request_url, location
ORDER BY location
LIMIT 100

サブネットワーク テストの平均送信バイト数を計算する

次のクエリは、リソースラベルで指定されたロケーションでログエントリをグループ化し、各グループのログエントリ数を計算する方法を示しています。

SELECT
  -- Compute the average number of bytes sent per location. Because labels has
  -- a data type of JSON, use JSON_VALUE to get the value of the location field.
  -- bytes_sent is a string. Must cast to a FLOAT64 before computing average.
  JSON_VALUE(resource.labels.location) AS location,
  AVG(CAST(JSON_VALUE(json_payload.bytes_sent) AS FLOAT64)) AS bytes
FROM
  `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_ID`
WHERE
  resource.type = "gce_subnetwork" AND json_payload IS NOT NULL
GROUP BY
  -- Group by location
  location
LIMIT 100

詳細については、JSON 関数変換関数をご覧ください。

パターンに一致するフィールドがあるログエントリをカウントする

正規表現と一致する部分文字列を返すには、関数 REGEXP_EXTRACT を使用します。

SELECT
  -- Extract the value that begins with test.
  -- Count the number of log entries for each name.
  REGEXP_EXTRACT(JSON_VALUE(json_payload.jobName), r".*(test.*)$") AS name,
  COUNT(*) AS count
FROM
  `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_ID`
WHERE
  json_payload.jobName IS NOT NULL
GROUP BY name
ORDER BY count
LIMIT 20

他の例については、REGEXP_EXTRACT のドキュメントをご覧ください。

このセクションでは、クエリを実行するビューの複数の列を検索するために使用できる 2 つの方法について説明します。

  • トークンベース検索: 検索場所と検索クエリを指定し、SEARCH 関数を使用します。SEARCH 関数にはデータの検索方法に関する特定のルールがあるため、SEARCH のドキュメントを一読することをおすすめします。

  • 部分文字列ベースの検索: 検索場所と文字列リテラルを指定し、CONTAINS_SUBSTR 関数を使用します。システムは、大文字と小文字を区別しないテストを実行して、文字列リテラルが式に存在するかどうかを判断します。CONTAINS_SUBSTR 関数は、文字列リテラルが存在する場合は TRUE を返し、それ以外の場合は FALSE を返します。検索値は STRING リテラルである必要があり、リテラル NULL ではありません。

次のクエリは、「35.193.12.15」に完全に一致するフィールドを持つ行のみを保持します。

SELECT
  timestamp, log_id, proto_payload, severity, resource.type, resource, labels
FROM
  `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_ID` AS t
WHERE
  -- Search data access audit logs for the IP address that matches 35.193.12.15.
  -- The use of backticks prevents the string from being tokenized.
  proto_payload IS NOT NULL AND
  log_id = "cloudaudit.googleapis.com/data_access" AND
  SEARCH(t,"`35.193.12.15`")
ORDER BY timestamp ASC
LIMIT 20

クエリ文字列でバッククォートが省略されている場合、クエリ文字列は SEARCH のドキュメントで定義されているルールに基づいて分割されます。たとえば、次のステートメントを実行すると、クエリ文字列は「35」、「193」、「12」、「15」の 4 つのトークンに分割されます。

  SEARCH(t,"35.193.12.15")

上記の SEARCH ステートメントでは、1 つのフィールドが 4 つのトークンすべてに一致する場合、行と一致します。トークンの順序は関係ありません。

クエリには、複数の SEARCH ステートメントを含めることが可能です。たとえば、上記のクエリでは、ログ ID のフィルタを次のようなステートメントに置き換えることが可能です。

  SEARCH(t,"`cloudaudit.googleapis.com/data_access`")

上記のステートメントはログビューのログエントリのすべてのフィールドを検索しますが、元のステートメントはログエントリの log_id フィールドのみを検索します。

複数のフィールドに対して複数の検索を実行するには、個々の文字列をスペースで区切ります。たとえば、次のステートメントは、フィールドに「Hello World」、「happy」、「days」が含まれている行に一致します。

  SEARCH(t,"`Hello World` happy days")

最後に、テーブル全体ではなく、特定のフィールドを検索することもできます。たとえば、次のステートメントは、text_payloadjson_payload という名前の列のみを検索します。

   SEARCH((text_payload, json_payload) ,"`35.222.132.245`")

SEARCH 関数のパラメータの処理方法については、BigQuery リファレンス ページの検索関数をご覧ください。

たとえば、次のクエリは、タイムスタンプが特定の時間範囲内にある特定の IP アドレスを持つすべてのデータアクセス監査ログエントリを取得します。最後に、クエリ結果を並べ替えて、古いものから 20 件の結果を表示します。

SELECT
  timestamp, log_id, proto_payload, severity, resource.type, resource, labels
FROM
  `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_ID` AS t
WHERE
  -- Search data access audit logs for the IP address that matches 35.193.12.15.
  -- CONTAINS_SUBSTR performs a contains-test.
  proto_payload IS NOT NULL AND
  log_id = "cloudaudit.googleapis.com/data_access" AND
  CONTAINS_SUBSTR(t,"35.193.12.15")
ORDER BY timestamp ASC
LIMIT 20

複数のビューをクエリする

クエリ ステートメントは、1 つ以上のテーブルまたは式をスキャンし、計算結果の行を返します。たとえば、クエリ ステートメントを使用して、さまざまなテーブルやデータセットの SELECT ステートメントの結果をマージし、結合データから列を選択できます。

複数のビューをクエリする場合、これらのビューは同じロケーションに存在する必要があります。たとえば、2 つのビューが us-east1 ロケーションにある場合、1 つのクエリで両方のビューをクエリできます。us マルチリージョンにある 2 つのビューをクエリすることもできます。ただし、ビューのロケーションが global の場合、そのビューは任意の物理ロケーションに存在できます。したがって、ロケーションが global の 2 つのビュー間の結合が失敗する可能性があります。

トレース ID で 2 つのログビューを結合する

2 つのテーブルの情報を組み合わせるには、いずれかの JOIN 演算子を使用します。

SELECT
  -- Do an inner join on two tables by using the span ID and trace ID.
  -- Don't join only by span ID, as this field isn't globally unique.
  -- From the first view, show the timestamp, severity, and JSON payload.
  -- From the second view, show the JSON payload.
  a.timestamp, a.severity, a.json_payload, b.json_payload, a.span_id, a.trace
FROM  `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_1` a
JOIN  `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_2` b
ON
  a.span_id = b.span_id AND
  a.trace = b.trace
LIMIT 100

結合ステートメントを使用して 2 つのログビューをクエリする

2 つ以上の SELECT ステートメントの結果を結合して重複行を破棄するには、UNION 演算子を使用します。重複した行を保持するには、UNION ALL 演算子を使用します。

SELECT
  timestamp, log_name, severity, json_payload, resource, labels
-- Create a union of two log views
FROM(
  SELECT * FROM `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_1`
  UNION ALL
  SELECT * FROM `PROJECT_ID.LOCATION.BUCKET_ID.LOG_VIEW_2`
)
-- Sort the union by timestamp.
ORDER BY timestamp ASC
LIMIT 100

重複するログエントリを削除する

ログ分析では、クエリの実行前に重複するログエントリが削除されることはありません。これは、ログ エクスプローラを使用してログエントリをクエリする場合とは異なります。ログ エクスプローラでは、ログ名、タイムスタンプ、挿入 ID のフィールドを比較して重複エントリが削除されます。

行レベルの検証を使用して、重複するログエントリを削除できます。

詳細については、トラブルシューティング: ログ分析の結果に重複するログエントリがあるをご覧ください。

次のステップ

ログエントリを転送して保存する方法については、次のドキュメントをご覧ください。

SQL リファレンス ドキュメントについては、次のドキュメントをご覧ください。