データパイプラインやアプリケーションの動作を追跡することは、開発において欠かせません。適切なログとトレースの仕組みがなければ、安定運用を維持するのは困難です。障害発生時には、状況を把握するためのアラートが必要になります。データに不整合が見つかれば、変換ロジックをたどって原因を突き止めなければなりません。デバッグでは、コードの挙動を可視化することが極めて重要です。たとえば変数の値、出力メッセージ、計算結果といったコンテキスト情報が必要となり、これらは通常さまざまな形式でログに記録されます。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アプリと同じようにINFO、WARNING、ERRORといったレベルでログを出力できます。
もう一つ、SQLでハンドラーを書く場合のログ追加例を見てみましょう。Snowflake ScriptingではSYSTEM$LOG関数を使います。こちらも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;
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がトレースイベントをどう表現しているかをさらに詳しく知りたい場合は、ドキュメントをご覧ください。
ネイティブアプリでのイベントテーブル
イベントテーブルは、ネイティブアプリのログイベントやテレメトリデータの収集にも利用できます。ネイティブアプリのコードはコンシューマーアカウント上で動作し、そこでイベントが収集されるため、追加の構成が必要です。設定はプロバイダー側とコンシューマー側の両方で行います。
プロバイダー側の設定の概要
- マニフェストファイルでログとイベントのレベルを設定する
- イベントを保存するアカウントを設定する
コンシューマー側の設定の概要
- イベントテーブルをセットアップする
- イベントテーブルでイベントを確認する
- ロギングを有効化し、プロバイダーとイベントを共有する
イベントテーブルと記録されたエントリには、コンシューマーとプロバイダーの双方がアクセスできます。そのためコンシューマーは、有効化する前にどのような情報が記録され、プロバイダーと共有されるのかをしっかり確認することが重要です。
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のインストラクターとして、オンラインのライブトレーニングも担当しています。