サンプル SQL クエリ

このドキュメントには、ログデータとトレースデータのクエリに使用できるサンプルクエリが含まれています。

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)

トレースデータ

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

-- Matches trace spans whose start_time is within the most recent 1 hour.
WHERE start_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)

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

始める前に

  1. Google Cloud アカウントにログインします。 Google Cloudを初めて使用する場合は、 アカウントを作成して、実際のシナリオでの Google プロダクトのパフォーマンスを評価してください。新規のお客様には、ワークロードの実行、テスト、デプロイができる無料クレジット $300 分を差し上げます。
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. Observability API を有効にします。

    API を有効にするために必要なロール

    API を有効にするには、serviceusage.services.enable 権限を含む Service Usage 管理者 IAM ロール(roles/serviceusage.serviceUsageAdmin)が必要です。ロールを付与する方法を確認する

    API の有効化

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  6. Verify that billing is enabled for your Google Cloud project.

  7. Observability API を有効にします。

    API を有効にするために必要なロール

    API を有効にするには、serviceusage.services.enable 権限を含む Service Usage 管理者 IAM ロール(roles/serviceusage.serviceUsageAdmin)が必要です。ロールを付与する方法を確認する

    API の有効化

  8. [ログ分析] ページの読み込み、トレースデータに対する非公開クエリの作成、実行、保存に必要な権限を取得するには、次の IAM ロールを付与するよう管理者に依頼してください。

    • クエリを実行するオブザーバビリティ ビューに対するオブザーバビリティ ビュー アクセサー roles/observability.viewAccessor)。このロールは IAM 条件をサポートしています。これにより、付与を特定のビューに制限できます。ロール付与に条件を関連付けない場合、プリンシパルはすべてのオブザーバビリティ ビューにアクセスできます。オブザーバビリティ ビューはパブリック プレビュー版です。
    • プロジェクトに対する オブザーバビリティ分析ユーザー roles/observability.analyticsUser)。このロールには、非公開クエリの保存と実行、共有クエリの実行に必要な権限が含まれています。
    • プロジェクトに対するログビューア roles/logging.viewer)。
    • クエリするログビューを保存するプロジェクトに対するログ表示アクセス者 roles/logging.viewAccessor)。

    ロールの付与については、プロジェクト、フォルダ、組織へのアクセス権の管理をご覧ください。

    必要な権限は、カスタムロールや他の事前定義ロールから取得することもできます。

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

  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 は分析ビューのロケーションと名前です。

    トレースデータ

    次に、_AllSpans ビューをクエリするための FROM 句の形式を示します。

    FROM `PROJECT_ID.LOCATION._Trace.Spans._AllSpans`
    

    FROM 句には次のフィールドが含まれます。

このドキュメントに示すクエリを [BigQuery Studio] ページで使用する場合や、bq コマンドライン ツールを使用する場合は、FROM 句を編集して、リンクされたデータセットへのパスを入力します。

ログデータ

たとえば、プロジェクト myproject にある mydataset という名前のリンクされたデータセットで _AllLogs ビューをクエリする場合、パスは myproject.mydataset._AllLogs です。

トレースデータ

たとえば、プロジェクト myproject にある my_linked_dataset という名前のリンクされたデータセットで _AllSpans ビューをクエリする場合、パスは `myproject.my_linked_dataset._AllSpans` です。

一般的なユースケース

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

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

_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 関数を使用すると、クエリの効率が向上します。

すべてのトレースデータを表示する

_AllSpans ビューをクエリするには、次のクエリを実行します。

-- Display all data.
SELECT *
FROM `PROJECT_ID.LOCATION._Trace.Spans._AllSpans`
-- Limit to 10 entries.
LIMIT 10

共通のスパン情報を表示する

開始時間や期間などの一般的なスパン情報を表示するには、次のクエリを実行します。

SELECT
  start_time,
  -- Set the value of service name based on the first non-null value in the list.
  COALESCE(
    JSON_VALUE(resource.attributes, '$."service.name"'),
    JSON_VALUE(attributes, '$."service.name"'),
    JSON_VALUE(attributes, '$."g.co/gae/app/module"')) AS service_name,
  name AS span_name,
  duration_nano,
  status.code AS status,
  trace_id,
  span_id
FROM
  `PROJECT_ID.LOCATION._Trace.Spans._AllSpans`
LIMIT 10

詳細については、条件式をご覧ください。

スパン レイテンシの 50 パーセンタイルと 99 パーセンタイルを表示する

各 RPC サービスのレイテンシの 50 パーセンタイルと 99 パーセンタイルを表示するには、次のクエリを実行します。

SELECT
  -- Compute 50th and 99th percentiles for each service
  STRING(attributes['rpc.service']) || '/' || STRING(attributes['rpc.method']) AS rpc_service_method,
  APPROX_QUANTILES(duration_nano, 100)[OFFSET(50)] AS duration_nano_p50,
  APPROX_QUANTILES(duration_nano, 100)[OFFSET(99)] AS duration_nano_p99
FROM
  `PROJECT_ID.LOCATION._Trace.Spans._AllSpans`
WHERE
  -- Matches spans whose kind field has a value of 2 (SPAN_KIND_SERVER).
  kind = 2
GROUP BY rpc_service_method

列挙型の詳細については、OpenTelemetry: SpanKind のドキュメントをご覧ください。

結果をグラフで表示するには、ディメンションを rpc_service_method に設定したグラフを作成します。たとえば、duration_nano_p50 値の平均値と duration_nano_p99 フィールドの平均値の 2 つの指標を追加できます。

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

クエリにフィルタを適用するには、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 番目のログエントリにのみ一致します。

トレース エントリをフィルタする

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

文字列データ型でフィルタする

フィールド nameString として保存されます。

  • name が指定されているスパンのみを分析するには、次の句を使用します。

    -- Matches spans that have a name field.
    WHERE name IS NOT NULL
    
  • name の値が "POST" のスパンのみを分析するには、次の句を使用します。

    -- Matches spans whose name is POST.
    WHERE STRPOS(name, "POST") > 0
    
  • name に値 "POST" が含まれているスパンのみを分析するには、LIKE 演算子とワイルドカードを使用します。

    -- Matches spans whose name contains POST.
    WHERE name LIKE "%POST%"
    

整数データ型でフィルタする

フィールド kind は 0 ~ 5 の値をとる整数です。

  • kind が指定されているスパンのみを分析するには、次の句を使用します。

    -- Matches spans that have field named kind.
    WHERE kind IS NOT NULL
    
  • kind 値が 1 または 2 のスパンを分析するには、次の句を使用します。

    -- Matches spans whose kind value is 1 or 2.
    WHERE kind IN (1, 2)
    

RECORD データ型でフィルタする

トレース スキーマの一部のフィールドのデータ型は RECORD です。これらのフィールドには、1 つ以上のデータ構造を保存することも、同じデータ構造の繰り返しエントリを保存することもできます。

ステータスまたはステータス コードでフィルタする

status フィールドは、データ型が RECORD のフィールドの例です。このフィールドには、codemessage というラベルの付いたメンバーを含む 1 つのデータ構造が格納されます。

  • status.code フィールドの値が 1 の場合にのみスパンを分析するには、次の句を追加します。

    -- Matches spans that have a status.code field that has a value of 1.
    WHERE status.code = 1
    

    status.code フィールドは整数として保存されます。

  • status フィールドが EMPTY ではないスパンを分析するには、次の句を追加します。

    -- Matches spans that have status field. When the status field exists, it
    -- must contain a subfield named code.
    -- Don't compare status to NULL, because this field has a data type of RECORD.
    WHERE status.code IS NOT NULL
    

events フィールドと links フィールドは RECORD のデータ型で保存されますが、これらは繰り返しフィールドです。

  • 少なくとも 1 つのイベントを含むスパンを照合するには、次の句を使用します。

    -- Matches spans that have at least one event. Don't compare events to NULL.
    -- The events field has data type of RECORD and contains a repeated fields.
    WHERE ARRAY_LENGTH(events) > 0
    
  • name フィールドの値が message のイベントを含むスパンを照合するには、次の句を使用します。

    WHERE
      -- Exists is true when any event in the array has a name field with the
      -- value of message.
      EXISTS(
        SELECT 1
        FROM UNNEST(events) AS ev
        WHERE ev.name = 'message'
      )
    

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

attributes フィールドの型は JSON です。個々の属性は Key-Value ペアです。

  • attributes が指定されているスパンのみを分析するには、次の句を使用します。

    -- Matches spans where at least one attribute is specified.
    WHERE attributes IS NOT NULL
    
  • 属性キー component の値が "proxy" であるスパンのみを分析するには、次の句を使用します。

    -- Matches spans that have an attribute named component with a value of proxy.
    WHERE attributes IS NOT NULL
          AND JSON_VALUE(attributes, '$.component') = 'proxy'
    

    LIKE ステートメントとワイルドカードを使用して、包含テストを行うこともできます。

    -- Matches spans that have an attribute named component whose value contains proxy.
    WHERE attributes IS NOT NULL
          AND JSON_VALUE(attributes, '$.component') LIKE '%proxy%'
    

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

このセクションは、前述のサンプルに基づき、ログエントリをグループ化して集計する方法について説明します。グループ化を指定せずに集計を指定する場合は、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 のドキュメントをご覧ください。

トレースデータをグループ化して集計する

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

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

スパンを開始時刻でグループ化する

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

SELECT
  -- Truncate the start time to the hour. Count the number of spans per group.
  TIMESTAMP_TRUNC(start_time, HOUR) AS hour,
  status.code AS code,
  COUNT(*) AS count
FROM
  `PROJECT_ID.LOCATION._Trace.Spans._AllSpans`
WHERE
  -- Matches spans shows start time is within the previous 12 hours.
  start_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 12 HOUR)
GROUP BY
  -- Group by hour and status code.
  hour, code
ORDER BY hour DESC

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

ステータス コード別にスパンをカウントする

特定のステータス コードを持つスパンの数を表示するには、次のクエリを実行します。

SELECT
  -- Count the number of spans for each status code.
  status.code,
  COUNT(*) AS count
FROM
  `PROJECT_ID.LOCATION._Trace.Spans._AllSpans`
WHERE status.code IS NOT NULL
GROUP BY status.code

status.codekind に置き換えると、前のクエリは kind 列挙型の各値のスパン数をレポートします。同様に、status.codename に置き換えると、クエリ結果には各スパン名のエントリ数が表示されます。

すべてのスパンの平均期間を計算する

スパンデータをスパン名でグループ化した後の平均期間を表示するには、次のクエリを実行します。

SELECT
  -- Group by name, and then compute the average duration for each group.
  name,
  AVG(duration_nano) AS nanosecs,
FROM
  `PROJECT_ID.LOCATION._Trace.Spans._AllSpans`
GROUP BY name
ORDER BY nanosecs DESC

サービス名ごとに平均期間とパーセンタイルを計算する

次のクエリは、各サービスのスパンス数とさまざまな統計情報を計算します。

SELECT
  -- Set the service name by the first non-null value.
  COALESCE(
    JSON_VALUE(resource.attributes, '$."service.name"'),
    JSON_VALUE(attributes, '$."service.name"'),
    JSON_VALUE(attributes, '$."g.co/gae/app/module"')) AS service_name,

  -- Count the number spans for each service name. Also compute statistics.
  COUNT(*) AS span_count,
  AVG(duration_nano) AS avg_duration_nano,
  MIN(duration_nano) AS min_duration_nano,
  MAX(duration_nano) AS max_duration_nano,

  -- Calculate percentiles for duration
  APPROX_QUANTILES(duration_nano, 100)[OFFSET(50)] AS p50_duration_nano,
  APPROX_QUANTILES(duration_nano, 100)[OFFSET(95)] AS p95_duration_nano,
  APPROX_QUANTILES(duration_nano, 100)[OFFSET(99)] AS p99_duration_nano,

  -- Count the number of unique trace IDs. Also, collect up to 5 unique
  -- span names and status codes.
  COUNT(DISTINCT trace_id) AS distinct_trace_count,
  ARRAY_AGG(DISTINCT name IGNORE NULLS LIMIT 5) AS sample_span_names,
  ARRAY_AGG(DISTINCT status.code IGNORE NULLS LIMIT 5) AS sample_status_codes
FROM
  `PROJECT_ID.LOCATION._Trace.Spans._AllSpans`
GROUP BY service_name
ORDER BY span_count DESC

このセクションでは、クエリを実行しているビューの複数の列を検索するために使用できる 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 ステートメントの結果をマージし、結合データから列を選択できます。

ビューを結合するには、次の制限が適用されます。

  1. ビューの場所は次のいずれかを満たします。

    • すべてのビューが同じロケーションにある。
    • すべてのビューが global または us のいずれかのロケーションにある。
  2. ストレージ リソースが顧客管理の暗号鍵(CMEK)を使用している場合、次のいずれかが当てはまります。

    • CMEK を使用するストレージ リソースは、同じ Cloud KMS 鍵を使用します。
    • CMEK を使用するストレージ リソースには共通の祖先があり、その祖先はストレージ リソースと同じロケーションにあるデフォルトの Cloud KMS 鍵を指定します。

    1 つ以上のストレージ リソースで CMEK が使用されている場合、システムは共通の Cloud KMS 鍵または祖先のデフォルトの Cloud KMS 鍵を使用して、結合によって生成された一時データを暗号化します。

たとえば、同じロケーションに存在する 2 つのビューがあるとします。次のいずれかに該当する場合は、これらのビューを結合できます。

  • ストレージ リソースが CMEK を使用していない。
  • 一方のストレージ リソースは CMEK を使用し、もう一方のストレージ リソースは CMEK を使用していません。
  • 両方のストレージ リソースが CMEK を使用し、両方とも同じ Cloud KMS 鍵を使用します。
  • 両方のストレージ リソースで CMEK が使用されていますが、使用されている鍵は異なります。ただし、リソースは、ストレージ リソースと同じロケーションにあるデフォルトの Cloud KMS 鍵を指定する祖先を共有します。

    たとえば、ログバケットとオブザーバビリティ バケットのリソース階層に同じ組織が含まれているとします。組織で、ストレージ ロケーションに同じデフォルトの Cloud KMS 鍵を使用して Cloud Logging のデフォルトのリソース設定オブザーバビリティ バケットを構成している場合、これらのバケットのビューを結合できます。

トレース 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

union ステートメントを使用して 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 を使用してトレースデータとログデータを結合する

次のクエリは、スパン ID とトレース ID を使用してログデータとトレースデータを結合します。

SELECT
  T.trace_id,
  T.span_id,
  T.name,
  T.start_time,
  T.duration_nano,
  L.log_name,
  L.severity,
  L.json_payload
FROM
  `PROJECT_ID.LOCATION._Trace.Spans._AllSpans` AS T
JOIN
  `PROJECT_ID.LOCATION._Default._AllLogs` AS L
ON
  -- Join log and trace data by both the span ID and trace ID.
  -- Don't join only on span ID, this field isn't globally unique.
  T.span_id = L.span_id
  -- A regular expression is required because the storage format of the trace ID
  -- differs between a log view and a trace view.
  AND T.trace_id = REGEXP_EXTRACT(L.trace, r'/([^/]+)$')
WHERE T.duration_nano > 1000000
LIMIT 10

クエリのレスポンスにはトレース ID とスパン ID が一覧表示されます。これらの ID を個別にクエリして、詳細情報を収集できます。また、結果にはログエントリの重大度と JSON ペイロードが一覧表示されます。

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

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

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

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

次のステップ

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