analytics-events
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFrontend 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:
- - Event type definitions
frontend/src/metabase-types/analytics/event.ts - - Schema registry
frontend/src/metabase-types/analytics/schema.ts - - Core tracking functions
frontend/src/metabase/lib/analytics.ts - Feature-specific files - Tracking function wrappers
analytics.ts
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 file
analytics.ts - Import and call tracking function at the interaction point
- Use for basic events (most common)
trackSimpleEvent()
添加新分析事件时:
- 在中定义事件类型
frontend/src/metabase-types/analytics/event.ts - 将事件添加到对应的联合类型中(如、
DataStudioEvent)SimpleEvent - 在功能专属的文件中创建追踪函数
analytics.ts - 在交互触发点导入并调用追踪函数
- 基础事件使用(最常用)
trackSimpleEvent()
Event Schema Types
事件架构类型
1. Simple Events (Most Common)
1. 简单事件(最常用)
Use for straightforward tracking. It supports these standard fields:
SimpleEventSchematypescript
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.
对于基础追踪场景,使用。它支持以下标准字段:
SimpleEventSchematypescript
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: , ,
DashboardEventSchemaCleanupEventSchemaQuestionEventSchema仅在极特殊情况下才考虑添加新的自定义事件架构。
示例: 、、
DashboardEventSchemaCleanupEventSchemaQuestionEventSchemaStep-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.tstypescript
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.tstypescript
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 file (e.g., ):
analytics.tsenterprise/frontend/src/metabase-enterprise/data-studio/analytics.tstypescript
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.tsenterprise/frontend/src/metabase-enterprise/data-studio/analytics.tstypescript
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 hyphentypescript
// 正确示例
"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" suffixtypescript
// 正确示例
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" prefixtypescript
// 正确示例
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_casetypescript
// 错误示例
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:
- Check browser console - When in dev, events are logged
SNOWPLOW_ENABLED=true - Use shouldLogAnalytics - Set in to see all analytics in console
metabase/env - Check Snowplow debugger - Browser extension for Snowplow events
Example console output:
[SNOWPLOW EVENT | event sent:true], data_studio_table_picker_filters_applied开发过程中,你可以通过以下方式验证事件是否触发:
- 查看浏览器控制台 - 开发环境中设置时,事件会被记录
SNOWPLOW_ENABLED=true - 使用shouldLogAnalytics - 在中设置该参数,可在控制台查看所有分析事件
metabase/env - 使用Snowplow调试工具 - 用于查看Snowplow事件的浏览器扩展
控制台输出示例:
[SNOWPLOW EVENT | event sent:true], data_studio_table_picker_filters_appliedFile 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.tsReal-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
工作流程总结
- Identify the user interaction to track
- Decide on event name (snake_case, descriptive)
- Define event type in using
event.tsValidateEvent - Add to union type (create feature union if needed)
- Create tracking function in feature's
analytics.ts - Import and call at the interaction point
- Test that events fire correctly
- 确定需要追踪的用户交互
- 确定事件名称(蛇形命名法,描述性强)
- 在中使用
event.ts定义事件类型ValidateEvent - 添加到联合类型(如有需要,创建功能专属联合类型)
- 在功能专属的中创建追踪函数
analytics.ts - 在交互触发点导入并调用追踪函数
- 测试事件是否正确触发
Tips
小贴士
- Be specific - is better than
filters_appliedaction_performed - Use past tense - not
library_createdcreate_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 to track where the action happened
triggered_from
- 保持具体 - 比
filters_applied更清晰action_performed - 使用过去式 - 而非
library_createdcreate_library - 分组相关事件 - 创建功能专属的事件联合类型
- 追踪有意义的操作 - 并非每一次点击都需要追踪
- 考虑数据用途 - 思考后续需要分析哪些数据
- 保持一致性 - 遵循代码库中已有的命名模式
- 记录上下文 - 使用追踪操作触发位置
triggered_from