イベントテーブルのデータベースへの関連付けを試してみた #SnowflakeDB

イベントテーブルのデータベースへの関連付けを試してみた #SnowflakeDB

Clock Icon2025.03.05

はじめに

2025年2月のアップデートでイベント テーブルをデータベースに関連付ける機能が一般提供となりました。こちらの機能を試してみましたので、本記事で内容をまとめてみます。

https://docs.snowflake.com/en/release-notes/2025/9_04#support-for-associating-an-event-table-with-a-database-general-availability

アップデートの概要

Snowflake ではイベントテーブルとして、ストアドプロシージャや UDF などの Snowflake オブジェクトがテレメトリ データを出力すると、自動的にデータを収集できる特殊な種類のデータベース テーブルが提供されています。

ログの収集の場合、イベントテーブルを設定し、アカウントや対象のデータレベル等でログレベルの設定を行うことで、ログの収集が開始されます。

イベントテーブルについては以下でも紹介していますので、あわせてご参照ください。

https://dev.classmethod.jp/articles/collect-logs-account-default-event-table-snowflakedb/

これまではイベントテーブルをアカウントレベルで設定することができました。つまり、異なるデータベース内のプロシージャであっても、収集先のイベントテーブルは一つ(共有)でした。そのため、アクセス制御はレコードレベルで行う必要がありました。

今回のアップデートで、データベースレベルでイベントテーブルを関連付けることができるようになりました。アカウント内でイベントテーブルは複数作成できるため、これによりログの取得先となるイベントテーブルを分離できるようになります。

前提条件

以下の環境で検証しています。

  • 本機能の使用には、Enterprise 以上のエディションが必要です
  • イベントテーブル
    • カスタムイベントテーブルを使用
    • ※アカウントのデフォルトのイベントテーブル( SNOWFLAKE.TELEMETRY.EVENTS)は使用しない

検証手順

本機能については、以下に記載があります。
ここでは、異なるデータベースにイベントテーブルとストアドプロシージャを作成し、各イベントテーブルに対応するストアドプロシージャのログを取得してみます。

https://docs.snowflake.com/en/developer-guide/logging-tracing/event-table-setting-up#associate-an-event-table-with-an-object

検証用のサンプルオブジェクトを作成

はじめに以下のコマンドで、同様の環境を2つ用意しました。

--sysadminで作成
USE ROLE sysadmin;

--変数を定義
----環境名
SET env_name = 'test1';
--SET env_name = 'test2'; 

----データベース名
SET db_name = concat($env_name,'_db');
----イベントテーブル名
SET event_table_name = concat($env_name,'_events');

--イベントテーブルとストアドプロシージャの作成先となるデータベースを作成
CREATE OR REPLACE DATABASE identifier($db_name);

--スキーマを作成
USE DATABASE identifier($db_name);
----イベントテーブルの作成
CREATE OR REPLACE SCHEMA event_tables;
----ストアドプロシージャの作成
CREATE OR REPLACE SCHEMA procedures;

--イベントテーブルを作成
USE DATABASE identifier($db_name);
USE SCHEMA event_tables;
CREATE OR REPLACE EVENT TABLE identifier($event_table_name);

--ストアドプロシージャの作成
USE DATABASE identifier($db_name);
USE SCHEMA procedures;

----サンプルテーブルを作成
CREATE OR REPLACE TABLE parent (ID INTEGER);
CREATE OR REPLACE TABLE child (ID INTEGER);

----Snowflakeスクリプトでストアドプロシージャを定義
CREATE OR REPLACE PROCEDURE insert_data()
RETURNS VARCHAR NOT NULL
LANGUAGE SQL
AS
$$
BEGIN
    BEGIN TRANSACTION;
    INSERT INTO procedures.parent VALUES (1);
    SELECT 1/0; --エラーを発生させる
    INSERT INTO procedures.child VALUES (1);
    COMMIT;
    RETURN 'complete';
EXCEPTION
  WHEN OTHER THEN
  ROLLBACK;
  RAISE;
END;
$$
;

各環境ごとに実行後、以下のオブジェクトが作成されます。

image

データベースにイベントテーブルを関連付け

イベントテーブルを作成後、各データベースに紐づけます。この際、以下の権限が必要です。

  • ACCOUNTADMIN
  • イベントテーブルに対する所有者権限または INSERT 権限

https://docs.snowflake.com/en/developer-guide/logging-tracing/event-table-setting-up#supported-objects

SYSADMIN でイベントテーブルを作成したので、所有者権限は ACCOUNTADMIN にも継承されます。そのため、以下のコマンドで関連付けを行いました。

--データベースにイベントテーブルを関連付け
USE ROLE ACCOUNTADMIN;
----環境1
ALTER DATABASE test1_db SET EVENT_TABLE = test1_db.event_tables.test1_events;
----環境2
ALTER DATABASE test2_db SET EVENT_TABLE = test2_db.event_tables.test2_events;

--設定値を確認:環境1
>SHOW PARAMETERS LIKE 'event_table' IN DATABASE test1_db;
+-------------+------------------------------------+----------------------------+----------+-----------------------------------------+--------+
| key         | value                              | default                    | level    | description                             | type   |
|-------------+------------------------------------+----------------------------+----------+-----------------------------------------+--------|
| EVENT_TABLE | test1_db.event_tables.test1_events | snowflake.telemetry.events | DATABASE | Event destination for the given target. | STRING |
+-------------+------------------------------------+----------------------------+----------+-----------------------------------------+--------+

データベースレベルでログレベルを設定

この状態では、まだログは取得されません。続けてログレベルの設定を行います。ログレベルはアカウントやデータベースなど各レベルで設定可能です。

ここでは、データベース単位でログレベルの設定を行います。データベースに対してログレベルの設定を行うには対象のデータベースに対する MODIFY 権限が必要です。

--データベースレベルのログレベル設定変更
----データベース所有者のSYSADMINを使用
USE ROLE SYSADMIN;
--環境1:ログレベルを「WARN」に設定
ALTER DATABASE test1_db SET LOG_LEVEL = WARN;
--環境2:ログレベルを「ERROR」に設定
ALTER DATABASE test2_db SET LOG_LEVEL = ERROR;

--確認:環境1
>SHOW PARAMETERS LIKE '%LOG_LEVEL%' IN DATABASE test1_db;
+-----------+-------+---------+----------+----------------------------------------+--------+
| key       | value | default | level    | description                            | type   |
|-----------+-------+---------+----------+----------------------------------------+--------|
| LOG_LEVEL | WARN  | OFF     | DATABASE | LOG_LEVEL to use when filtering events | STRING |
+-----------+-------+---------+----------+----------------------------------------+--------+

イベントテーブルにログを取得する

イベントテーブルの設定と、データベースのログレベルを設定したので、この状態でプロシージャを実行します。

--各環境でストアドプロシージャを作成
USE ROLE SYSADMIN;
--環境1
USE SCHEMA test1_db.procedures;
CALL insert_data();
--環境2
USE SCHEMA test2_db.procedures;
CALL insert_data();

実行結果は下図のようにエラーとなります。

image 1

実行から1分程度したら、各イベントテーブルを確認します。下図の通りほぼ同じ時間にログが記録されています。[RESOURCE_ATTRIBUTES] を見るとどこで発生したログか確認できます。

  • 環境1

image 2

RESOURCE_ATTRIBUTES
{
  "db.user": "<ユーザー名>",
  "snow.database.id": 11,
  "snow.database.name": "TEST1_DB",
  "snow.executable.id": 513,
  "snow.executable.name": "INSERT_DATA():VARCHAR",
  "snow.executable.type": "PROCEDURE",
  "snow.owner.id": 2,
  "snow.owner.name": "SYSADMIN",
  "snow.query.id": "01bacb2a-0002-274f-0000-000223724785",
  "snow.schema.id": 33,
  "snow.schema.name": "PROCEDURES",
  "snow.session.id": 601922486771722,
  "snow.session.role.primary.id": 2,
  "snow.session.role.primary.name": "SYSADMIN",
  "snow.user.id": 1,
  "snow.warehouse.id": 4,
  "snow.warehouse.name": "COMPUTE_WH",
  "telemetry.sdk.language": "sql"
}
  • 環境2

image 3

RESOURCE_ATTRIBUTES
{
  "db.user": "<ユーザー名>",
  "snow.database.id": 12,
  "snow.database.name": "TEST2_DB",
  "snow.executable.id": 258,
  "snow.executable.name": "INSERT_DATA():VARCHAR",
  "snow.executable.type": "PROCEDURE",
  "snow.owner.id": 2,
  "snow.owner.name": "SYSADMIN",
  "snow.query.id": "01bacb2a-0002-27fe-0002-23720001702e",
  "snow.schema.id": 36,
  "snow.schema.name": "PROCEDURES",
  "snow.session.id": 601922486771722,
  "snow.session.role.primary.id": 2,
  "snow.session.role.primary.name": "SYSADMIN",
  "snow.user.id": 1,
  "snow.warehouse.id": 4,
  "snow.warehouse.name": "COMPUTE_WH",
  "telemetry.sdk.language": "sql"
}

内容からも各環境(データベース)で発生したログを対応するイベントテーブルに出力することができました。

デフォルトのイベントテーブルを使用する

さいごにこれまで通りの設定で試してみます。これまではアカウントレベルで一つのイベントテーブルを設定できました。デフォルトでは、以下のイベントテーブルが設定されています。

--デフォルトのイベントテーブルを確認
USE ROLE ACCOUNTADMIN;
SELECT * FROM SNOWFLAKE.TELEMETRY.EVENTS;

上記の手順では、各データベースに異なるイベントテーブルを設定していたので、これをデフォルトのイベントテーブルに変更します。

----環境1
ALTER DATABASE test1_db UNSET EVENT_TABLE;
----環境2
ALTER DATABASE test2_db UNSET EVENT_TABLE;

--確認:環境1
>SHOW PARAMETERS LIKE 'event_table' IN DATABASE test1_db;
+-------------+----------------------------+----------------------------+-------+-----------------------------------------+--------+
| key         | value                      | default                    | level | description                             | type   |
|-------------+----------------------------+----------------------------+-------+-----------------------------------------+--------|
| EVENT_TABLE | snowflake.telemetry.events | snowflake.telemetry.events |       | Event destination for the given target. | STRING |
+-------------+----------------------------+----------------------------+-------+-----------------------------------------+--------+

データベースレベルでログレベルは設定済みなので、プロシージャを実行します。

USE SCHEMA test1_db.procedures;
CALL insert_data();

USE SCHEMA test2_db.procedures;
CALL insert_data();

イベントテーブルを確認すると、先ほど設定した各データベースのイベントテーブルにはログは取得されず、デフォルトのイベントテーブルにログが取得されます。

--デフォルトのイベントテーブルを確認
USE ROLE ACCOUNTADMIN;
SELECT * FROM SNOWFLAKE.TELEMETRY.EVENTS;

image 4

さいごに

イベント テーブルのデータベースへの関連付けを試してみました。
明確にログの取得先を分離できる点がこのアップデートの強みと思いました。これまではログの取得先が共有され、ユーザー権限によって特定のイベントテーブル レコードのみを返したい場合に、行アクセスポリシーを使用する必要がありましたが、この点が構成しやすくなったと思います。
こちらの内容が何かの参考になれば幸いです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.