analytics-events

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Frontend Analytics Events Skill

前端分析事件实现指南

This skill helps you add product analytics (Snowplow) events to track user interactions in the Metabase frontend codebase.
本指南将帮助你在Metabase前端代码库中添加产品分析(Snowplow)事件,以追踪用户交互。

Quick Reference

快速参考

Analytics events in Metabase use Snowplow with typed event schemas. All events must be defined in TypeScript types before use.
Key Files:
  • frontend/src/metabase-types/analytics/event.ts
    - Event type definitions
  • frontend/src/metabase-types/analytics/schema.ts
    - Schema registry
  • frontend/src/metabase/lib/analytics.ts
    - Core tracking functions
  • Feature-specific
    analytics.ts
    files - Tracking function wrappers
Metabase中的分析事件使用Snowplow及类型化事件架构。所有事件在使用前必须先在TypeScript类型中定义。
关键文件:
  • frontend/src/metabase-types/analytics/event.ts
    - 事件类型定义
  • frontend/src/metabase-types/analytics/schema.ts
    - 架构注册表
  • frontend/src/metabase/lib/analytics.ts
    - 核心追踪函数
  • 功能专属
    analytics.ts
    文件 - 追踪函数封装

Quick Checklist

快速检查清单

When adding a new analytics event:
  • Define event type in
    frontend/src/metabase-types/analytics/event.ts
  • Add event to appropriate union type (e.g.,
    DataStudioEvent
    ,
    SimpleEvent
    )
  • Create tracking function in feature's
    analytics.ts
    file
  • Import and call tracking function at the interaction point
  • Use
    trackSimpleEvent()
    for basic events (most common)
添加新分析事件时:
  • frontend/src/metabase-types/analytics/event.ts
    中定义事件类型
  • 将事件添加到对应的联合类型中(如
    DataStudioEvent
    SimpleEvent
  • 在功能专属的
    analytics.ts
    文件中创建追踪函数
  • 在交互触发点导入并调用追踪函数
  • 基础事件使用
    trackSimpleEvent()
    (最常用)

Event Schema Types

事件架构类型

1. Simple Events (Most Common)

1. 简单事件(最常用)

Use
SimpleEventSchema
for straightforward tracking. It supports these standard fields:
typescript
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
};
When to use: 90% of events fit this schema. Use for clicks, opens, closes, creates, deletes, etc.
对于基础追踪场景,使用
SimpleEventSchema
。它支持以下标准字段:
typescript
type SimpleEventSchema = {
  event: string;                    // 必填:事件名称(蛇形命名法snake_case)
  target_id?: number | null;        // 可选:受影响实体的ID
  triggered_from?: string | null;   // 可选:UI触发位置/上下文
  duration_ms?: number | null;      // 可选:耗时(毫秒)
  result?: string | null;           // 可选:结果(如"success"、"failure")
  event_detail?: string | null;     // 可选:额外详情/变体
};
适用场景: 90%的事件都符合此架构,适用于点击、打开、关闭、创建、删除等操作。

2. Custom Schemas (legacy, no events are being added)

2. 自定义架构(旧版,不再新增事件)

Consider adding new event schema only in very special cases.
Examples:
DashboardEventSchema
,
CleanupEventSchema
,
QuestionEventSchema
仅在极特殊情况下才考虑添加新的自定义事件架构。
示例:
DashboardEventSchema
CleanupEventSchema
QuestionEventSchema

Step-by-Step: Adding a Simple Event

分步教程:添加简单事件

Example: Track when a user applies filters in a table picker

示例:追踪用户在表格选择器中应用筛选器的操作

Step 1: Define Event Types

步骤1:定义事件类型

Add event type definitions to
frontend/src/metabase-types/analytics/event.ts
:
typescript
export type DataStudioTablePickerFiltersAppliedEvent = ValidateEvent<{
  event: "data_studio_table_picker_filters_applied";
}>;

export type DataStudioTablePickerFiltersClearedEvent = ValidateEvent<{
  event: "data_studio_table_picker_filters_cleared";
}>;
frontend/src/metabase-types/analytics/event.ts
中添加事件类型定义:
typescript
export type DataStudioTablePickerFiltersAppliedEvent = ValidateEvent<{
  event: "data_studio_table_picker_filters_applied";
}>;

export type DataStudioTablePickerFiltersClearedEvent = ValidateEvent<{
  event: "data_studio_table_picker_filters_cleared";
}>;

Step 2: Add to Union Type

步骤2:添加到联合类型

Find or create the appropriate union type and add your events:
typescript
export type DataStudioEvent =
  | DataStudioLibraryCreatedEvent
  | DataStudioTablePublishedEvent
  | DataStudioGlossaryCreatedEvent
  | DataStudioGlossaryEditedEvent
  | DataStudioGlossaryDeletedEvent
  | DataStudioTablePickerFiltersAppliedEvent  // <- Add here
  | DataStudioTablePickerFiltersClearedEvent; // <- Add here
找到或创建对应的联合类型,并添加你的事件:
typescript
export type DataStudioEvent =
  | DataStudioLibraryCreatedEvent
  | DataStudioTablePublishedEvent
  | DataStudioGlossaryCreatedEvent
  | DataStudioGlossaryEditedEvent
  | DataStudioGlossaryDeletedEvent
  | DataStudioTablePickerFiltersAppliedEvent  // <- 在此处添加
  | DataStudioTablePickerFiltersClearedEvent; // <- 在此处添加

Step 3: Create Tracking Functions

步骤3:创建追踪函数

In your feature's
analytics.ts
file (e.g.,
enterprise/frontend/src/metabase-enterprise/data-studio/analytics.ts
):
typescript
import { trackSimpleEvent } from "metabase/lib/analytics";

export const trackDataStudioTablePickerFiltersApplied = () => {
  trackSimpleEvent({
    event: "data_studio_table_picker_filters_applied",
  });
};

export const trackDataStudioTablePickerFiltersCleared = () => {
  trackSimpleEvent({
    event: "data_studio_table_picker_filters_cleared",
  });
};
在功能专属的
analytics.ts
文件中(如
enterprise/frontend/src/metabase-enterprise/data-studio/analytics.ts
):
typescript
import { trackSimpleEvent } from "metabase/lib/analytics";

export const trackDataStudioTablePickerFiltersApplied = () => {
  trackSimpleEvent({
    event: "data_studio_table_picker_filters_applied",
  });
};

export const trackDataStudioTablePickerFiltersCleared = () => {
  trackSimpleEvent({
    event: "data_studio_table_picker_filters_cleared",
  });
};

Step 4: Use in Components

步骤4:在组件中使用

Import and call the tracking function at the interaction point:
typescript
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>
  );
}
在交互触发点导入并调用追踪函数:
typescript
import {
  trackDataStudioTablePickerFiltersApplied,
  trackDataStudioTablePickerFiltersCleared,
} from "metabase-enterprise/data-studio/analytics";

function FilterPopover({ filters, onSubmit }) {
  const handleReset = () => {
    trackDataStudioTablePickerFiltersCleared(); // <- 在此处追踪
    onSubmit(emptyFilters);
  };

  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        trackDataStudioTablePickerFiltersApplied(); // <- 在此处追踪
        onSubmit(form);
      }}
    >
      {/* 表单内容 */}
    </form>
  );
}

Using SimpleEventSchema Fields

使用SimpleEventSchema字段

Example: Event with target_id

示例:包含target_id的事件

typescript
// Type definition
export type DataStudioLibraryCreatedEvent = ValidateEvent<{
  event: "data_studio_library_created";
  target_id: number | null;
}>;

// Tracking function
export const trackDataStudioLibraryCreated = (id: CollectionId) => {
  trackSimpleEvent({
    event: "data_studio_library_created",
    target_id: Number(id),
  });
};

// Usage
trackDataStudioLibraryCreated(newLibrary.id);
typescript
// 类型定义
export type DataStudioLibraryCreatedEvent = ValidateEvent<{
  event: "data_studio_library_created";
  target_id: number | null;
}>;

// 追踪函数
export const trackDataStudioLibraryCreated = (id: CollectionId) => {
  trackSimpleEvent({
    event: "data_studio_library_created",
    target_id: Number(id),
  });
};

// 使用方法
trackDataStudioLibraryCreated(newLibrary.id);

Example: Event with triggered_from

示例:包含triggered_from的事件

typescript
// Type definition
export type NewButtonClickedEvent = ValidateEvent<{
  event: "new_button_clicked";
  triggered_from: "app-bar" | "empty-collection";
}>;

// Tracking function
export const trackNewButtonClicked = (location: "app-bar" | "empty-collection") => {
  trackSimpleEvent({
    event: "new_button_clicked",
    triggered_from: location,
  });
};

// Usage
<Button onClick={() => {
  trackNewButtonClicked("app-bar");
  handleCreate();
}}>
  New
</Button>
typescript
// 类型定义
export type NewButtonClickedEvent = ValidateEvent<{
  event: "new_button_clicked";
  triggered_from: "app-bar" | "empty-collection";
}>;

// 追踪函数
export const trackNewButtonClicked = (location: "app-bar" | "empty-collection") => {
  trackSimpleEvent({
    event: "new_button_clicked",
    triggered_from: location,
  });
};

// 使用方法
<Button onClick={() => {
  trackNewButtonClicked("app-bar");
  handleCreate();
}}>
  New
</Button>

Example: Event with event_detail

示例:包含event_detail的事件

typescript
// Type definition
export type MetadataEditEvent = ValidateEvent<{
  event: "metadata_edited";
  event_detail: "type_casting" | "semantic_type_change" | "visibility_change";
  triggered_from: "admin" | "data_studio";
}>;

// Tracking function
export const trackMetadataChange = (
  detail: "type_casting" | "semantic_type_change" | "visibility_change",
  location: "admin" | "data_studio"
) => {
  trackSimpleEvent({
    event: "metadata_edited",
    event_detail: detail,
    triggered_from: location,
  });
};

// Usage
trackMetadataChange("semantic_type_change", "data_studio");
typescript
// 类型定义
export type MetadataEditEvent = ValidateEvent<{
  event: "metadata_edited";
  event_detail: "type_casting" | "semantic_type_change" | "visibility_change";
  triggered_from: "admin" | "data_studio";
}>;

// 追踪函数
export const trackMetadataChange = (
  detail: "type_casting" | "semantic_type_change" | "visibility_change",
  location: "admin" | "data_studio"
) => {
  trackSimpleEvent({
    event: "metadata_edited",
    event_detail: detail,
    triggered_from: location,
  });
};

// 使用方法
trackMetadataChange("semantic_type_change", "data_studio");

Example: Event with result and duration

示例:包含result和duration的事件

typescript
// Type definition
export type MoveToTrashEvent = ValidateEvent<{
  event: "moved-to-trash";
  target_id: number | null;
  triggered_from: "collection" | "detail_page" | "cleanup_modal";
  duration_ms: number | null;
  result: "success" | "failure";
  event_detail: "question" | "model" | "metric" | "dashboard";
}>;

// Tracking function
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",
  });
}
typescript
// 类型定义
export type MoveToTrashEvent = ValidateEvent<{
  event: "moved-to-trash";
  target_id: number | null;
  triggered_from: "collection" | "detail_page" | "cleanup_modal";
  duration_ms: number | null;
  result: "success" | "failure";
  event_detail: "question" | "model" | "metric" | "dashboard";
}>;

// 追踪函数
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,
  });
};

// 带计时的使用方法
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",
  });
}

Naming Conventions

命名规范

Event Names (snake_case)

事件名称(蛇形命名法snake_case)

typescript
// 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
typescript
// 正确示例
"data_studio_library_created"
"table_picker_filters_applied"
"metabot_chat_opened"

// 错误示例
"DataStudioLibraryCreated"  // 错误的大小写
"tablePickerFiltersApplied" // 错误的大小写
"filters-applied"            // 使用下划线而非连字符

Event Type Names (PascalCase with "Event" suffix)

事件类型名称(帕斯卡命名法PascalCase,带"Event"后缀)

typescript
// Good
DataStudioLibraryCreatedEvent
TablePickerFiltersAppliedEvent
MetabotChatOpenedEvent

// Bad
dataStudioLibraryCreated      // Wrong case
DataStudioLibraryCreated      // Missing "Event" suffix
typescript
// 正确示例
DataStudioLibraryCreatedEvent
TablePickerFiltersAppliedEvent
MetabotChatOpenedEvent

// 错误示例
dataStudioLibraryCreated      // 错误的大小写
DataStudioLibraryCreated      // 缺少"Event"后缀

Tracking Function Names (camelCase with "track" prefix)

追踪函数名称(驼峰命名法camelCase,带"track"前缀)

typescript
// Good
trackDataStudioLibraryCreated
trackTablePickerFiltersApplied
trackMetabotChatOpened

// Bad
DataStudioLibraryCreated      // Missing "track" prefix
track_library_created         // Wrong case
logLibraryCreated             // Use "track" prefix
typescript
// 正确示例
trackDataStudioLibraryCreated
trackTablePickerFiltersApplied
trackMetabotChatOpened

// 错误示例
DataStudioLibraryCreated      // 缺少"track"前缀
track_library_created         // 错误的大小写
logLibraryCreated             // 使用"track"前缀而非"log"

Common Patterns

常见模式

Pattern 1: Feature-Specific Union Types

模式1:功能专属联合类型

Group related events together:
typescript
export type DataStudioEvent =
  | DataStudioLibraryCreatedEvent
  | DataStudioTablePublishedEvent
  | DataStudioGlossaryCreatedEvent;

export type MetabotEvent =
  | MetabotChatOpenedEvent
  | MetabotRequestSentEvent
  | MetabotFixQueryClickedEvent;

// Then add to SimpleEvent union
export type SimpleEvent =
  | /* other events */
  | DataStudioEvent
  | MetabotEvent
  | /* more events */;
将相关事件分组:
typescript
export type DataStudioEvent =
  | DataStudioLibraryCreatedEvent
  | DataStudioTablePublishedEvent
  | DataStudioGlossaryCreatedEvent;

export type MetabotEvent =
  | MetabotChatOpenedEvent
  | MetabotRequestSentEvent
  | MetabotFixQueryClickedEvent;

// 然后添加到SimpleEvent联合类型中
export type SimpleEvent =
  | /* 其他事件 */
  | DataStudioEvent
  | MetabotEvent
  | /* 更多事件 */;

Pattern 2: Conditional Tracking

模式2:条件追踪

Track different events based on user action:
typescript
const handleSave = async () => {
  if (isNewItem) {
    await createItem(data);
    trackItemCreated(newItem.id);
  } else {
    await updateItem(id, data);
    trackItemUpdated(id);
  }
};
根据用户操作追踪不同事件:
typescript
const handleSave = async () => {
  if (isNewItem) {
    await createItem(data);
    trackItemCreated(newItem.id);
  } else {
    await updateItem(id, data);
    trackItemUpdated(id);
  }
};

Common Pitfalls

常见陷阱

Don't: Add custom fields to SimpleEvent

错误做法:向SimpleEvent添加自定义字段

typescript
// WRONG - SimpleEvent doesn't support custom fields
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.
  });
};
typescript
// 错误 - SimpleEvent不支持自定义字段
export const trackFiltersApplied = (filters: FilterState) => {
  trackSimpleEvent({
    event: "filters_applied",
    data_layer: filters.dataLayer,      // ❌ 不在SimpleEventSchema中
    data_source: filters.dataSource,    // ❌ 不在SimpleEventSchema中
    with_owner: filters.hasOwner,       // ❌ 不在SimpleEventSchema中
  });
};

// 正确 - 仅使用SimpleEventSchema的标准字段
export const trackFiltersApplied = () => {
  trackSimpleEvent({
    event: "filters_applied",
  });
};

// 或使用event_detail传递单个变体
export const trackFilterApplied = (filterType: string) => {
  trackSimpleEvent({
    event: "filter_applied",
    event_detail: filterType,  // ✓ 可传递"data_layer"、"data_source"等
  });
};

Don't: Forget to add event to union type

错误做法:忘记将事件添加到联合类型

typescript
// Define the event
export type NewFeatureClickedEvent = ValidateEvent<{
  event: "new_feature_clicked";
}>;

// ❌ WRONG - Forgot to add to SimpleEvent union
// Event won't be recognized by TypeScript

// ✓ RIGHT - Add to appropriate union
export type SimpleEvent =
  | /* other events */
  | NewFeatureClickedEvent;
typescript
// 定义事件
export type NewFeatureClickedEvent = ValidateEvent<{
  event: "new_feature_clicked";
}>;

// ❌ 错误 - 忘记添加到SimpleEvent联合类型
// TypeScript将无法识别该事件

// ✓ 正确 - 添加到对应的联合类型
export type SimpleEvent =
  | /* 其他事件 */
  | NewFeatureClickedEvent;

Don't: Mix up event name formats

错误做法:混淆事件名称格式

typescript
// 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
typescript
// 错误示例
event: "dataStudioLibraryCreated"  // 驼峰命名
event: "data-studio-library-created"  // 连字符命名
event: "Data_Studio_Library_Created"  // 混合大小写

// 正确示例
event: "data_studio_library_created"  // 蛇形命名

Don't: Track PII or sensitive data

错误做法:追踪个人身份信息(PII)或敏感数据

typescript
// 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
});
typescript
// 错误 - 不要追踪用户邮箱、姓名或敏感数据
trackSimpleEvent({
  event: "user_logged_in",
  event_detail: user.email,  // ❌ 属于PII
});

// 正确 - 仅追踪非敏感标识符
trackSimpleEvent({
  event: "user_logged_in",
  target_id: user.id,  // ✓ 仅使用ID
});

Don't: Forget to track both success and failure

错误做法:忘记追踪成功和失败两种结果

typescript
// 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" });
}
typescript
// 错误 - 仅追踪成功情况
try {
  await saveData();
  trackDataSaved();
} catch (error) {
  // ❌ 未追踪失败情况
}

// 正确 - 追踪两种结果
try {
  await saveData();
  trackDataSaved({ result: "success" });
} catch (error) {
  trackDataSaved({ result: "failure" });
}

Testing Analytics Events

测试分析事件

While developing, you can verify events are firing:
  1. Check browser console - When
    SNOWPLOW_ENABLED=true
    in dev, events are logged
  2. Use shouldLogAnalytics - Set in
    metabase/env
    to see all analytics in console
  3. Check Snowplow debugger - Browser extension for Snowplow events
Example console output:
[SNOWPLOW EVENT | event sent:true], data_studio_table_picker_filters_applied
开发过程中,你可以通过以下方式验证事件是否触发:
  1. 查看浏览器控制台 - 开发环境中设置
    SNOWPLOW_ENABLED=true
    时,事件会被记录
  2. 使用shouldLogAnalytics - 在
    metabase/env
    中设置该参数,可在控制台查看所有分析事件
  3. 使用Snowplow调试工具 - 用于查看Snowplow事件的浏览器扩展
控制台输出示例:
[SNOWPLOW EVENT | event sent:true], data_studio_table_picker_filters_applied

File Organization

文件组织

Where to put tracking functions:

追踪函数的存放位置:

Feature-specific analytics functions:
frontend/src/metabase/{feature}/analytics.ts
enterprise/frontend/src/metabase-enterprise/{feature}/analytics.ts

Event type definitions (all in one place):
frontend/src/metabase-types/analytics/event.ts

Core tracking utilities:
frontend/src/metabase/lib/analytics.ts
功能专属分析函数:
frontend/src/metabase/{feature}/analytics.ts
enterprise/frontend/src/metabase-enterprise/{feature}/analytics.ts

事件类型定义(统一存放):
frontend/src/metabase-types/analytics/event.ts

核心追踪工具:
frontend/src/metabase/lib/analytics.ts

Real-World Examples

实际示例

See these files for reference:
  • Simple events:
    enterprise/frontend/src/metabase-enterprise/data-studio/analytics.ts
  • Events with variants:
    frontend/src/metabase/dashboard/analytics.ts
  • Complex events:
    frontend/src/metabase/query_builder/analytics.js
  • Event type examples:
    frontend/src/metabase-types/analytics/event.ts
可参考以下文件:
  • 简单事件
    enterprise/frontend/src/metabase-enterprise/data-studio/analytics.ts
  • 带变体的事件
    frontend/src/metabase/dashboard/analytics.ts
  • 复杂事件
    frontend/src/metabase/query_builder/analytics.js
  • 事件类型示例
    frontend/src/metabase-types/analytics/event.ts

Workflow Summary

工作流程总结

  1. Identify the user interaction to track
  2. Decide on event name (snake_case, descriptive)
  3. Define event type in
    event.ts
    using
    ValidateEvent
  4. Add to union type (create feature union if needed)
  5. Create tracking function in feature's
    analytics.ts
  6. Import and call at the interaction point
  7. Test that events fire correctly
  1. 确定需要追踪的用户交互
  2. 确定事件名称(蛇形命名法,描述性强)
  3. event.ts
    中使用
    ValidateEvent
    定义事件类型
  4. 添加到联合类型(如有需要,创建功能专属联合类型)
  5. 在功能专属的
    analytics.ts
    中创建追踪函数
  6. 在交互触发点导入并调用追踪函数
  7. 测试事件是否正确触发

Tips

小贴士

  • Be specific -
    filters_applied
    is better than
    action_performed
  • Use past tense -
    library_created
    not
    create_library
  • Group related events - Create feature-specific event union types
  • Track meaningful actions - Not every click needs tracking
  • Consider the data - What would you want to analyze later?
  • Stay consistent - Follow existing naming patterns in the codebase
  • Document context - Use
    triggered_from
    to track where the action happened
  • 保持具体 -
    filters_applied
    action_performed
    更清晰
  • 使用过去式 -
    library_created
    而非
    create_library
  • 分组相关事件 - 创建功能专属的事件联合类型
  • 追踪有意义的操作 - 并非每一次点击都需要追踪
  • 考虑数据用途 - 思考后续需要分析哪些数据
  • 保持一致性 - 遵循代码库中已有的命名模式
  • 记录上下文 - 使用
    triggered_from
    追踪操作触发位置