analytics-events について
Metabase のフロントエンド開発者向けに、Snowplow のプロダクト分析イベント追加手順をまとめたスキルです。型付きの `trackSimpleEvent` を使い、追跡関数やフィールド型を各機能内に置く方法を示します。
主な機能
- 01`trackSimpleEvent` によるイベント記録
- 02標準フィールドに基づくペイロード検証
- 03追跡コードと型の機能単位での管理
- 04命名規則とプライバシー規則の適用
- 05開発中のイベント発火確認
活用シーン
- 01UI 操作の分析イベント追加
- 02処理結果や所要時間の記録
- 03開発中の Snowplow イベント確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ metabase/ metabase --skill analytics-eventsインストール後、Claude Code で/analytics-eventsを入力して読み込まれたか確認します。
詳細
フロントエンド分析イベントスキル
このスキルは、Metabase フロントエンドコードベースでユーザーの操作をトラッキングするためのプロダクト分析(Snowplow)イベントを追加するのに役立ちます。
クイックリファレンス
Metabase の分析イベントは、型付きイベントスキーマを備えた Snowplow を使用します。シンプルなイベントは使用される場所で宣言します — trackSimpleEvent はジェネリックであり、呼び出し箇所でペイロードを検証します。
主要なファイル:
frontend/src/metabase/analytics/event.ts- コアのトラッキング関数、trackSimpleEvent/trackSchemaEvent(metabase/analyticsからインポート)frontend/src/metabase-types/analytics/event.ts- 共有のSimpleEventSchemaのみ。ここにイベント型を追加しないでください(以下を参照)frontend/src/metabase-types/analytics/schema.ts- スキーマレジストリ(カスタム/レガシースキーマのみ)- 機能固有の
analytics.tsファイル - トラッキング関数とローカル型が置かれる場所
クイックチェックリスト
新しい分析イベントを追加する場合:
- イベント名を選ぶ(snake_case、過去形)
- その機能の
analytics.tsファイルに、trackSimpleEvent()を呼び出すトラッキング関数を追加する - フィールドのユニオン(例:
"success" | "failure")は、同じファイル内のローカル型として保持する - 操作ポイントでトラッキング関数をインポートして呼び出す
-
metabase-types/analytics/event.tsやいずれかのユニオンにイベント型を追加しない
イベントスキーマの種類
1. シンプルイベント(最も一般的)
基本的なトラッキングには SimpleEventSchema を使用します。以下の標準フィールドをサポートしています:
type SimpleEventSchema = {
event: string; // Required: Event name (snake_case)
target_id?: number | null; // Optional: ID of affected entity
triggered_from?: string | null; // Optional: UI location/context
duration_ms?: number | null; // Optional: Duration in milliseconds
result?: string | null; // Optional: Outcome (e.g., "success", "failure")
event_detail?: string | null; // Optional: Additional detail/variant
};
使用する場合: イベントの 90% はこのスキーマに当てはまります。クリック、開く、閉じる、作成、削除などに使用します。
trackSimpleEvent はジェネリックであり、渡されたオブジェクトリテラルに対してこのスキーマを強制します:
// frontend/src/metabase/analytics/event.ts
export function trackSimpleEvent<
T extends SimpleEventSchema &
Record<Exclude<keyof T, keyof SimpleEventSchema>, never>,
>(event: T) {
trackSchemaEvent("simple_event", event);
}
つまり、event の欠落や SimpleEventSchema 以外のフィールドは、呼び出し箇所でコンパイルエラーになります。宣言すべき別個のイベント型はなく、追加すべき satisfies 句もありません — 以前の ValidateEvent<...> ヘルパーはエクスポートされなくなり、ワークフローの一部ではありません。
trackSchemaEvent もジェネリックです: スキーマ名とペイロード型を関連付けるため、simple_event スキーマの下でダッシュボードイベントを送信することはできません。
2. カスタムスキーマ(レガシー、イベントは追加されていません)
新しいイベントスキーマの追加は、ごく特殊なケースでのみ検討してください。
例: DashboardEventSchema、CleanupEventSchema、QuestionEventSchema
ステップバイステップ: シンプルイベントの追加
例: ユーザーがテーブルピッカーでフィルターを適用したときをトラッキングする
ステップ 1: トラッキング関数を作成する
その機能の analytics.ts ファイル(例: enterprise/frontend/src/metabase-enterprise/data-studio/analytics.ts)に記述します:
import { trackSimpleEvent } from "metabase/analytics";
export const trackDataStudioTablePickerFiltersApplied = () => {
trackSimpleEvent({
event: "data_studio_table_picker_filters_applied",
});
};
export const trackDataStudioTablePickerFiltersCleared = () => {
trackSimpleEvent({
event: "data_studio_table_picker_filters_cleared",
});
};
ステップ 2: コンポーネントで使用する
操作ポイントでトラッキング関数をインポートして呼び出します:
import {
trackDataStudioTablePickerFiltersApplied,
trackDataStudioTablePickerFiltersCleared,
} from "metabase-enterprise/data-studio/analytics";
function FilterPopover({ filters, onSubmit }) {
const handleReset = () => {
trackDataStudioTablePickerFiltersCleared(); // <- Track here
onSubmit(emptyFilters);
};
return (
<form
onSubmit={(event) => {
event.preventDefault();
trackDataStudioTablePickerFiltersApplied(); // <- Track here
onSubmit(form);
}}
>
{/* form content */}
</form>
);
}
SimpleEventSchema のフィールドの使用
以下のすべての例は、その機能自身の analytics.ts に置かれます — 中央に登録されるものはありません。
例: target_id を持つイベント
export const trackDataStudioLibraryCreated = (id: CollectionId) => {
trackSimpleEvent({
event: "data_studio_library_created",
target_id: Number(id),
});
};
// Usage
trackDataStudioLibraryCreated(newLibrary.id);
例: triggered_from を持つイベント
// Local union, exported only if another feature needs to pass the same value
export type NewButtonLocation = "app-bar" | "empty-collection";
export const trackNewButtonClicked = (location: NewButtonLocation) => {
trackSimpleEvent({
event: "new_button_clicked",
triggered_from: location,
});
};
// Usage
<Button onClick={() => {
trackNewButtonClicked("app-bar");
handleCreate();
}}>
New
</Button>
例: event_detail を持つイベント
実際の例 — frontend/src/metabase/metadata/pages/shared/analytics.ts:
export type MetadataEditEventDetail =
| "type_casting"
| "semantic_type_change"
| "visibility_change";
export const trackMetadataChange = (detail: MetadataEditEventDetail) => {
trackSimpleEvent({
event: "metadata_edited",
event_detail: detail,
triggered_from: "admin",
});
};
// Usage
trackMetadataChange("semantic_type_change");
例: result と duration を持つイベント
この実際のバージョンについては frontend/src/metabase/archive/analytics.ts を参照してください。
export const trackMoveToTrash = (params: {
targetId: number | null;
triggeredFrom: "collection" | "detail_page" | "cleanup_modal";
durationMs: number | null;
result: "success" | "failure";
itemType: "question" | "model" | "metric" | "dashboard";
}) => {
trackSimpleEvent({
event: "moved-to-trash",
target_id: params.targetId,
triggered_from: params.triggeredFrom,
duration_ms: params.durationMs,
result: params.result,
event_detail: params.itemType,
});
};
// Usage with timing
const startTime = Date.now();
try {
await moveToTrash(item);
trackMoveToTrash({
targetId: item.id,
triggeredFrom: "collection",
durationMs: Date.now() - startTime,
result: "success",
itemType: "question",
});
} catch (error) {
trackMoveToTrash({
targetId: item.id,
triggeredFrom: "collection",
durationMs: Date.now() - startTime,
result: "failure",
itemType: "question",
});
}
命名規則
イベント名(snake_case)
// Good
"data_studio_library_created"
"table_picker_filters_applied"
"metabot_chat_opened"
// Bad
"DataStudioLibraryCreated" // Wrong case
"tablePickerFiltersApplied" // Wrong case
"filters-applied" // Use underscore, not hyphen
ローカルフィールド型(PascalCase、フィールドにちなんで命名)
名前を付けるべき ...Event 型は、もはや通常は存在しません。フィールドにユニオンが必要な場合は、それが供給するフィールドにちなんで命名します:
// Good
type MetricDimensionResult = "success" | "failure"; // -> result
export type MetadataEditEventDetail = "type_casting"; // -> event_detail
type NewButtonLocation = "app-bar" | "empty-collection"; // -> triggered_from
トラッキング関数名(「track」プレフィックスを付けた camelCase)
// Good
trackDataStudioLibraryCreated
trackTablePickerFiltersApplied
trackMetabotChatOpened
// Bad
DataStudioLibraryCreated // Missing "track" prefix
track_library_created // Wrong case
logLibraryCreated // Use "track" prefix
一般的なパターン
パターン 1: 機能間でフィールド型を共有する
2 つの機能が異なる triggered_from で同じイベントを送信する場合、所有する機能の analytics.ts からフィールドユニオンをエクスポートしてインポートします — metabase-types に何も持ち上げないでください:
// frontend/src/metabase/data-studio/data-model/analytics.ts
import { trackSimpleEvent } from "metabase/analytics";
import type { MetadataEditEventDetail } from "metabase/metadata/pages/shared/analytics";
export function trackMetadataChange(detail: MetadataEditEventDetail) {
trackSimpleEvent({
event: "metadata_edited",
event_detail: detail,
triggered_from: "data_studio",
});
}
これが拡張可能イベント設計の要点です: エンタープライズおよび機能階層の型は、共有ユニオンにインポートされるのではなく、自身のモジュールに留まります。
パターン 2: 条件付きトラッキング
ユーザーのアクションに基づいて異なるイベントをトラッキングします。
const handleSave = async () => {
if (isNewItem) {
await createItem(data);
trackItemCreated(newItem.id);
} else {
await updateItem(id, data);
trackItemUpdated(id);
}
};
よくある落とし穴
やってはいけない: シンプルなイベントにカスタムフィールドを追加する
// WRONG - SimpleEventSchema doesn't support custom fields (this is a compile error)
export const trackFiltersApplied = (filters: FilterState) => {
trackSimpleEvent({
event: "filters_applied",
data_layer: filters.dataLayer, // ❌ Not in SimpleEventSchema
data_source: filters.dataSource, // ❌ Not in SimpleEventSchema
with_owner: filters.hasOwner, // ❌ Not in SimpleEventSchema
});
};
// RIGHT - Use only standard SimpleEventSchema fields
export const trackFiltersApplied = () => {
trackSimpleEvent({
event: "filters_applied",
});
};
// Or use event_detail for a single variant
export const trackFilterApplied = (filterType: string) => {
trackSimpleEvent({
event: "filter_applied",
event_detail: filterType, // ✓ "data_layer", "data_source", etc.
});
};
やってはいけない: metabase-types/analytics/event.ts にイベント型を追加する
中央の SimpleEvent ユニオンは削除されました — これにより、機能層の型を共有コードに
インポートせざるを得なくなり、モジュール境界の違反を引き起こしていました。trackSimpleEvent は
現在ジェネリックであるため、この型は重複以外の何ももたらしません。
// ❌ WRONG - central declaration + re-import for a `satisfies` clause
// frontend/src/metabase-types/analytics/event.ts
export type NewFeatureClickedEvent = ValidateEvent<{
event: "new_feature_clicked";
target_id: number;
}>;
// frontend/src/metabase/my-feature/analytics.ts
import type { NewFeatureClickedEvent } from "metabase-types/analytics";
export const trackNewFeatureClicked = (id: number) => {
trackSimpleEvent({
event: "new_feature_clicked",
target_id: id,
} satisfies NewFeatureClickedEvent);
};
// ✓ RIGHT - the object literal is already checked by the generic
// frontend/src/metabase/my-feature/analytics.ts
export const trackNewFeatureClicked = (id: number) => {
trackSimpleEvent({
event: "new_feature_clicked",
target_id: id,
});
};
いくつかの ...Event 型はまだ metabase-types/analytics/event.ts に残っています。これらは
リファクタリングの前後にマージされた PR の残骸です — コピーしないでください。また、これらに追加しないでください。
やってはいけない: イベント名の形式を混同する
// WRONG
event: "dataStudioLibraryCreated" // camelCase
event: "data-studio-library-created" // kebab-case
event: "Data_Studio_Library_Created" // Mixed case
// RIGHT
event: "data_studio_library_created" // snake_case
やってはいけない: PII や機密データをトラッキングする
// WRONG - Don't track user emails, names, or sensitive data
trackSimpleEvent({
event: "user_logged_in",
event_detail: user.email, // ❌ PII
});
// RIGHT - Track non-sensitive identifiers only
trackSimpleEvent({
event: "user_logged_in",
target_id: user.id, // ✓ Just the ID
});
やってはいけない: 成功と失敗の両方をトラッキングし忘れる
// WRONG - Only tracking success
try {
await saveData();
trackDataSaved();
} catch (error) {
// ❌ No tracking for failure case
}
// RIGHT - Track both outcomes
try {
await saveData();
trackDataSaved({ result: "success" });
} catch (error) {
trackDataSaved({ result: "failure" });
}
アナリティクスイベントのテスト
開発中は、イベントが発火していることを確認できます。
- ブラウザコンソールを確認する - 開発環境で
SNOWPLOW_ENABLED=trueの場合、イベントがログに記録されます - shouldLogAnalytics を使用する -
metabase/envで設定すると、すべてのアナリティクスがコンソールに表示されます - Snowplow デバッガーを確認する - Snowplow イベント用のブラウザ拡張機能です
コンソール出力の例:
[SNOWPLOW EVENT | event sent:true], data_studio_table_picker_filters_applied
ファイル構成
トラッキング関数を置く場所:
Tracking functions AND their local field types (this is where new events live):
frontend/src/metabase/{feature}/analytics.ts
enterprise/frontend/src/metabase-enterprise/{feature}/analytics.ts
Core tracking utilities:
frontend/src/metabase/analytics/ (import from `metabase/analytics`)
Shared SimpleEventSchema only — nothing new goes here:
frontend/src/metabase-types/analytics/event.ts
埋め込み SDK のコードでは、代わりに trackSdkSimpleEvent
(frontend/src/embedding-sdk-bundle/analytics/snowplow.ts) を使用してください — メインアプリの "sp" トラッカーは
顧客のページでは初期化されないため、trackSimpleEvent の Snowplow 部分はそこでは何も行いません。
実例
参考として以下のファイルを参照してください:
- シンプルなイベント + ローカルフィールドユニオン:
frontend/src/metabase/metadata/pages/shared/analytics.ts - 別の機能のフィールド型を再利用する:
frontend/src/metabase/data-studio/data-model/analytics.ts - 結果 + 所要時間の計測:
frontend/src/metabase/archive/analytics.ts - エンタープライズ機能のイベント:
enterprise/frontend/src/metabase-enterprise/google_drive/analytics.ts
ワークフローまとめ
- トラッキングするユーザーインタラクションを特定する
- イベント名を決める (snake_case、説明的なもの)
- 機能の
analytics.tsにトラッキング関数を作成する (trackSimpleEvent()を呼び出します) - フィールドが固定の値の集合を持つ場合は、同じファイルにローカルフィールドユニオンを追加する
- インタラクションの箇所でインポートして呼び出す
- イベントが正しく発火することをテストする
ヒント
- 具体的にする -
action_performedよりもfilters_appliedのほうがよい - 過去形を使う -
create_libraryではなくlibrary_created - 関連するイベントをグループ化する - 機能のトラッキング関数はその
analytics.tsにまとめておく - 意味のあるアクションをトラッキングする - すべてのクリックをトラッキングする必要はありません
- データを考慮する - 後で何を分析したいですか?
- 一貫性を保つ - コードベース内の既存の命名パターンに従う
- コンテキストを記録する -
triggered_fromを使ってアクションがどこで発生したかをトラッキングする