SELECTSELECT

SELECT

Snowflakeで実現する構造化ログとトレース

By Tomáš SobotíkMay 19, 20246 min read

このページはEnglishDeutschEspañolFrançaisItalianoPortuguêsでもご覧いただけます。

データパイプラインやアプリケーションの動作を追跡することは、開発において欠かせません。適切なログとトレースの仕組みがなければ、安定運用を維持するのは困難です。障害発生時には、状況を把握するためのアラートが必要になります。データに不整合が見つかれば、変換ロジックをたどって原因を突き止めなければなりません。デバッグでは、コードの挙動を可視化することが極めて重要です。たとえば変数の値、出力メッセージ、計算結果といったコンテキスト情報が必要となり、これらは通常さまざまな形式でログに記録されます。Snowflakeは業界標準のライブラリを使ったログとトレースのネイティブ機能を備えており、開発者がすぐに活用できます。本記事では、これらの機能を使ってSnowflakeでイベントログとトレースを設定する方法を見ていきましょう。

イベントテーブルとは

イベントテーブルは2023年5月にパブリックプレビューとして公開されました。Snowflakeの特殊なテーブルで、標準のテーブルとは次のような違いがあります。

  • カラム構成があらかじめ定義されており、変更できない
  • ログとトレースデータの記録専用
  • アカウントに紐づけられる有効なイベントテーブルは1つだけ

代表的なユースケースは、ストアドプロシージャやUDFのハンドラーコードからログ情報を取得したり、ネイティブアプリからトレースデータを収集したりするケースです。

イベントテーブルの使い方

アカウントでイベントテーブルを有効化するには、次の図のようにいくつかのステップが必要です。

1. EVENT TABLEを作成する

まずイベントテーブルを作成します。専用のCREATE TABLE文を使います。

CREATE EVENT TABLE my_db.logging.my_event_table;

カラムはあらかじめ定義されているため、指定する必要はありません。現時点では、アカウントごとに有効なイベントテーブルは1つだけです。

2. イベントテーブルをアカウントに割り当てる

作成したイベントテーブルを使うには、アカウントに関連付ける必要があります。これにはALTER ACCOUNT文を使うため、ACCOUNTADMINロールが必要です。加えて、対象のイベントテーブルに対するOWNERSHIPまたはINSERT権限も必要です。

ALTER ACCOUNT SET EVENT_TABLE = my_db.logging.my_event_table;

3. ログイベントの収集を始める

これでUDF/UDTFやストアドプロシージャにロギングコードを組み込めるようになりました。ハンドラーの言語に応じて、ネイティブのロギングAPIやライブラリを利用できます。

言語 ロギングライブラリ
Java SLF4J API
JavaScript Snowflake JavaScript API
Python logging モジュール
Scala SLF4J API
Snowflake Scripting Snowflake SYSTEM$LOG 関数

ここではPythonを例に見ていきます。

CREATE OR REPLACE FUNCTION my_UDF()
RETURNS VARCHAR
RUNTIME_VERSION = 3.8
HANDLER = 'run'
AS $$
import logging
logger = logging.getLogger("my_logger")

def run():
        logger.info("Processing start")
        ...
        ...
        logger.error("Some error in your code")
        return value

ロギングを始めるには、loggingモジュールをインポートしてloggerオブジェクトを生成します。あとは通常のPythonアプリと同じようにINFOWARNINGERRORといったレベルでログを出力できます。

もう一つ、SQLでハンドラーを書く場合のログ追加例を見てみましょう。Snowflake ScriptingではSYSTEM$LOG関数を使います。こちらもinfowarningerrorなどのログレベルに対応しています。

たとえば、テーブルを返すシンプルなストアドプロシージャに情報レベルのログを追加するには、次のように書きます。

create or replace procedure returning_table()
returns table(id number, name varchar)
language sql
as
declare
    result RESULTSET DEFAULT (SELECT 1 id, 'test value' name);
begin
    SYSTEM$LOG('info', 'Returning a table');
        return table(result);
end;

4. イベントテーブルへのクエリ

ハンドラーコードへのロギング追加が済んだら、次は記録されたイベントを確認してみましょう。いよいよイベントテーブルにクエリを実行します。記録される各メッセージには次の情報が含まれます。

  • タイムスタンプ - イベントが記録された時刻
  • スコープ - 例: ログイベントが生成されたクラス名
  • ログの重大度レベル - 例: info、warning、error
  • ログメッセージ本体

イベントテーブルのカラム一覧についてはドキュメントを参照してください。一部のカラムは複数の属性を格納するキーバリュー形式になっており、次のようなクエリで取り出せます。

create or replace procedure returning_table()
returns table(id number, name varchar)
language sql
as
declare
    result RESULTSET DEFAULT (SELECT 1 id, 'test value' name);
begin
    SYSTEM$LOG('info', 'Returning a table');
        return table(result);
end;

イベントテーブルの出力は次のようになります。

トレースで構造化データを記録する

イベントテーブルのもう一つの使い道は、コードからのトレースデータ収集です。トレースデータはキーバリュー形式の構造化ログで、通常のログよりもコードの挙動を詳細に把握できます。ここではPythonで書かれたUDFからトレースデータを収集する例を見ていきましょう。

select
    resource_attributes:"snow.database.id"::number,
    resource_attributes:"snow.database.name"::varchar,
    resource_attributes:"snow.executable.name"::varchar,
    resource_attributes:"snow.executable.type"::varchar,
    resource_attributes:"snow.owner.name"::varchar,
    resource_attributes:"snow.query.id"::varchar,
    resource_attributes:"snow.warehouse.name"::varchar,
    resource_attributes:"telemetry.sdk.language"::varchar,
    record,
    value
from my_event_table;

必要なメソッドを含むsnowflake-telemetry-packageをインポートします。

set_span_attributeメソッドを使うと、spanオブジェクトにキーバリューのペアを設定できます。spanオブジェクトは、関数やプロシージャの実行が成功した後に生成されるテレメトリデータを保持するもので、UDFやストアドプロシージャの実行単位を表します。add_eventメソッドを使えば、その実行単位に複数のイベントを追加できます。

Snowflakeがトレースイベントをどう表現しているかをさらに詳しく知りたい場合は、ドキュメントをご覧ください。

ネイティブアプリでのイベントテーブル

イベントテーブルは、ネイティブアプリのログイベントやテレメトリデータの収集にも利用できます。ネイティブアプリのコードはコンシューマーアカウント上で動作し、そこでイベントが収集されるため、追加の構成が必要です。設定はプロバイダー側とコンシューマー側の両方で行います。

プロバイダー側の設定の概要

  1. マニフェストファイルでログとイベントのレベルを設定する
  2. イベントを保存するアカウントを設定する

コンシューマー側の設定の概要

  1. イベントテーブルをセットアップする
  2. イベントテーブルでイベントを確認する
  3. ロギングを有効化し、プロバイダーとイベントを共有する

イベントテーブルと記録されたエントリには、コンシューマーとプロバイダーの双方がアクセスできます。そのためコンシューマーは、有効化する前にどのような情報が記録され、プロバイダーと共有されるのかをしっかり確認することが重要です。

Snowflakeはプロバイダーとコンシューマーの双方に向けて、ステップごとの詳細な手順を公開しています。全体的なセットアップについて詳しく知りたい場合は、ドキュメントをご確認ください。

イベントに基づくアラート

イベントテーブルはSnowflakeアラートと組み合わせると、記録されたイベントに応じた通知を簡単に自動化できます。試しに、1時間ごとに実行され、エラーが記録されていればメールを送信するアラートを作ってみましょう。

CREATE OR REPLACE ALERT alert_logged_errors
  WAREHOUSE = my_warehouse
  SCHEDULE = '60 MINUTE'
  IF (EXISTS (
      SELECT *
      FROM my_event_table
      WHERE record:"severity_text"::VARCHAR == 'ERROR' and timestamp BETWEEN                SNOWFLAKE.ALERT.LAST_SUCCESSFUL_SCHEDULED_TIME()
       AND SNOWFLAKE.ALERT.SCHEDULED_TIME()
  ))
  THEN CALL SYSTEM$SEND_EMAIL(...);

イベントテーブルの制限と注意点

ログとトレースのペイロードサイズには上限があり、1MBを超えることはできません。

イベントテーブルはACCOUNT_USAGEビューで、アカウント内の全テーブル一覧の中から確認できます。TABLE_TYPEカラムの値が「EVENT TABLE」になっているのが目印です。

select *
from snowflake.account_usage.tables
where table_name = 'MY_EVENT_TABLE'
;

イベントテーブルは更新できません。UPDATE文を実行しようとするとUPDATE statement's target must be a tableというエラーになります。EVENT TABLEでサポートされている操作は以下のとおりです。

  • SHOW EVENT TABLE
  • DESCRIBE EVENT TABLE
  • DROP TABLE
  • UNDROP TABLE
  • TRUNCATE TABLE
  • DELETE
  • ALTER TABLE

イベントテーブルの料金と課金

ログイベントの収集はサーバーレス機能として課金されます。SnowflakeはイベントテーブルにSnowflake側が管理するリソースを使うため、ユーザーの仮想ウェアハウスは不要です。この機能でどれだけ課金されたかを確認したい場合は、EVENT_USAGE_HISTORYビューにクエリを実行します。

select
    start_time,
    end_time,
    credits_used,
    bytes_ingested
from snowflake.account_usage.EVENT_USAGE_HISTORY
order by start_time desc;

Tomáš Sobotík・Senior Data Engineer & Snowflake SME at Norlys

TomasはSnowflake Data SuperHeroとして長年活躍する、Snowflake全般のエキスパートです。データ分野での経験は10年以上におよび、多様な業界・技術のプロジェクトでSnowflakeのデータエンジニア、アーキテクト、管理者として携わってきました。コミュニティの中核メンバーとして積極的に知見を共有し、多くの人に刺激を与えています。またO'Reillyのインストラクターとして、オンラインのライブトレーニングも担当しています。