Ever Gauzy 中集成 PostHog:使用 @gauzy/plugin-posthog-ui 实现产品分析与埋点追踪

原创2026-09-30 20:27:48308 阅读
文章标签:后端前端企业应用MCP 服务

Ever Gauzy 中集成 PostHog:使用 @gauzy/plugin-posthog-ui 实现产品分析与埋点追踪

导读

本文以 packages/plugins/posthog-ui/README.md 为核心骨架,系统讲解 Ever Gauzy 中面向 Angular 应用的产品分析插件 @gauzy/plugin-posthog-ui:如何完成安装与初始化配置、如何实现事件/页面浏览/表单/错误/HTTP 请求等多维度追踪、如何使用指令在模板中零成本埋点,并深入源码剖析其底层实现原理。读完本文,你将掌握在 Ever Gauzy(或任意 Angular 21+ 应用)中接入 PostHog 的完整实战方案,包括配置项的含义、服务 API 的选用策略,以及测试与发布该插件库的方法。

插件概述:把 PostHog 的开放产品分析能力装进 Angular

Ever Gauzy 是一套开源的企业经营管理平台(ERP/CRM/HRM/ATS/PM),前端基于 Angular 构建。PostHog 则是开源的产品分析平台,提供事件追踪、会话录制、功能开关(Feature Flags)、漏斗分析、错误监控等能力。@gauzy/plugin-posthog-ui 正是将两者桥接的 UI 插件——它封装了 posthog-js SDK,以 Angular 惯用的模块、服务、指令、拦截器形式,让 Gauzy 应用能够实时采集用户行为、功能使用情况与应用事件。

从仓库实际使用情况看,该插件已经在 Gauzy 主应用中被启用:apps/gauzy/src/app/app.module.ts 中通过 PostHogModule.forRoot(...) 注入,且 apps/gauzy/src/app/pages/settings/monitoring/monitoring.component.ts 提供了 posthogEnabled、posthogKey、posthogHost、posthogFlushInterval 等全局/租户级设置项,说明它在 Gauzy 中既可用于开发环境调试,也可在生产环境按租户配置启用。

插件核心能力(源自其 package.json 与 posthog.interface.ts)包括:

  • 事件追踪:采集自定义事件与用户操作;
  • 页面浏览分析:基于 Angular Router 的自动页面浏览追踪;
  • 用户识别:跨会话识别与追踪个体用户;
  • 会话录制:录制用户会话供回放与分析;
  • 功能开关:在 Angular 应用中实现 Feature Flags 与 A/B 测试;
  • 错误监控:追踪前端错误与异常;
  • HTTP 请求追踪:自动采集 API 调用及耗时指标;
  • 表单追踪:追踪表单提交与字段交互;
  • 分组分析(Group Analytics):将用户归属到公司、团队或组织;
  • 超级属性(Super Properties):设置跨事件持久化的属性;
  • SSR 兼容:可在服务端渲染场景下安全使用。

安装

使用你偏好的包管理器安装插件及其核心依赖:

npm install @gauzy/plugin-posthog-ui posthog-js
# 或
yarn add @gauzy/plugin-posthog-ui posthog-js

根据 package.json 的声明,该插件将 posthog-js(当前锁定 ^1.236.2)作为直接依赖,@angular/common 与 @angular/core(21.0.7)作为 peerDependencies,rxjs 与 tslib 为运行依赖。因此你的项目应运行在 Angular 21 与 Node.js >= 22 环境(engines 声明),并使用 Yarn >= 1.22 或 npm 进行依赖管理。

配置

基础配置:PostHogModule.forRoot

在主应用模块中导入 PostHogModule 并调用 forRoot 传入配置:

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { PostHogModule } from '@gauzy/plugin-posthog-ui';
import { environment } from '../environments/environment';

@NgModule({
	declarations: [AppComponent],
	imports: [
		BrowserModule,
		PostHogModule.forRoot({
			apiKey: environment.posthogApiKey,
			apiHost: environment.posthogHost || 'https://app.posthog.com',
			// 可选配置
			captureHttpEvents: true,
			options: {
				capture_pageview: false, // 页面浏览由 Router 接管处理
				persistence: 'localStorage',
				session_recording: {
					enabled: environment.production
				}
			}
		})
	],
	bootstrap: [AppComponent]
})
export class AppModule {}

配置对象对应源码中的 PostHogModuleConfig 接口(见 posthog.interface.ts),除 apiKey 必填外,还支持以下扩展配置:

配置项 类型 说明
apiKey string 必填。PostHog 项目 API Key
options Partial<PostHogConfig> 透传给 posthog-js 的原生配置(api_host、persistence、session_recording、bootstrap 等)
debug boolean 是否开启调试模式,注入到 POSTHOG_DEBUG_MODE token,默认 false
routeTracking { excluded?, included? } 路由追踪白/黑名单:提供 excluded 时匹配路由不追踪;提供 included 时仅匹配路由被追踪;两者同时提供时 excluded 优先
httpTracking { captureResponseBodies?, excludeUrls?, additionalProperties? } HTTP 请求追踪配置,见下文

从 posthog.module.ts 的实现可以看到 forRoot 实际完成的初始化工作:

  1. 将 PostHogService、PostHogServiceManager 注册为 provider;
  2. 把配置注入 POSTHOG_CONFIG token,把 config.debug || false 注入 POSTHOG_DEBUG_MODE token;
  3. 通过 provideAppInitializer 在应用启动阶段调用 posthog-init.factory.ts 完成初始化;
  4. 当 options.capture_exceptions !== false 时,条件性地注册 PostHogInterceptor 到 HTTP_INTERCEPTORS(多 provider),实现 HTTP 错误/请求自动追踪。

需要注意的是,初始化工厂对“占位 Key”做了防护:当 apiKey 缺失或等于 'DOCKER_POSTHOG_KEY'(Docker 部署未配置环境变量时的占位值)时直接返回,初始化失败时也会捕获异常并 resolve,从而保证应用启动不会被 PostHog 阻塞——这与 apps/gauzy/src/app/app.module.ts 中对 DOCKER_POSTHOG_KEY 的告警逻辑一致。

环境配置

在环境文件中添加以下变量:

// environments/environment.ts
export const environment = {
	production: false,
	posthogApiKey: 'YOUR_POSTHOG_API_KEY',
	posthogHost: 'https://app.posthog.com' // 或你的自托管实例地址
};

Gauzy 主应用中对应的环境变量为 POSTHOG_ENABLED、POSTHOG_KEY、POSTHOG_HOST(见 app.module.ts)。posthog-js 的原生配置里 api_host 默认值为 https://app.posthog.com,若使用自托管 PostHog 实例,请将其替换为你的实例地址(源码见 posthog.service.ts)。

使用指南

服务选择:PostHogServiceManager 与 PostHogService

插件提供两个服务层:

  • PostHogServiceManager(高层 API,推荐应用代码使用):面向组件封装,自动携带初始化状态检查、路由追踪与配置驱动行为;
  • PostHogService(底层 API):直接封装 posthog-js 的全部能力(captureEvent、capturePageview、identify、alias、group、Feature Flags、会话录制、超级属性等),并负责 SSR 安全(仅在浏览器平台初始化)与全局错误监听。

两个服务都以 providedIn: 'root' 注册(见 posthog-manager.service.ts 与 posthog.service.ts),可直接构造函数注入。

基础事件追踪

在组件中注入高层服务并调用 trackEvent:

import { Component } from '@angular/core';
import { PostHogServiceManager } from '@gauzy/plugin-posthog-ui';

@Component({
	selector: 'app-example',
	template: `<button (click)="trackButtonClick()">Click Me</button>`
})
export class ExampleComponent {
	constructor(private posthogManager: PostHogServiceManager) {}

	trackButtonClick() {
		this.posthogManager.trackEvent('button_clicked', {
			buttonName: 'example_button',
			section: 'homepage'
		});
	}
}

底层实现上,trackEvent 先做初始化检查(isInitialized()),再转发给 posthogService.captureEvent,后者调用 posthog.capture(eventName, properties, options),可选 sendInstantly 参数控制是否立即发送(源码见 posthog.service.ts)。

指令式事件追踪(模板零成本埋点)

插件提供了 Angular 独立指令(standalone directive),可在模板中直接声明式埋点。[phTrack] 指令支持 click、mouseenter、focus 三种事件类型,也支持在组件初始化时捕获:

<!-- 基础事件追踪 -->
<button [phTrack]="'signup_button_clicked'" [phProperties]="{ location: 'header', variant: 'primary' }">Sign Up</button>

<!-- 高级配置 -->
<button
	[phTrack]="'button_clicked'"
	[phProperties]="{ buttonName: 'submit' }"
	[phOnInit]="true"
	[phEventType]="'click'"
	[phStopPropagation]="true"
>
	Submit
</button>

指令实现见 posthog-track.directive.ts:@HostListener 监听 click/mouseenter/focus,仅当 phEventType 与触发事件匹配时才上报;phOnInit 为 true 时在 ngOnInit 阶段立即上报一次;phStopPropagation 控制是否阻止事件冒泡。由于该指令已由 PostHogModule 统一 export,只要导入模块即可直接使用。

表单追踪

phFormTrack 指令自动采集表单提交与字段交互。以下示例追踪 signup_form 表单,启用字段级交互追踪,并排除敏感字段、开启值脱敏:

<form
	phFormTrack
	[phFormName]="'signup_form'"
	[phTrackFields]="true"
	[phExcludeFields]="['password', 'credit-card']"
	[phSanitizeValues]="true"
	(ngSubmit)="onSubmit()"
>
	<input name="email" type="email" />
	<input name="password" type="password" />
	<button type="submit">Submit</button>
</form>

表单指令的能力远不止 README 示例,源码 posthog-form-track.directive.ts 揭示了完整输入参数与安全机制:

输入 默认值 说明
phFormName 'form' 表单名称标识
phTrackFields false 是否追踪单个字段的交互
phExcludeFields ['password', 'secret', 'token', 'credit', 'card'] 排除字段(支持字符串包含匹配与 RegExp)
phSanitizeValues true 是否对字段值脱敏(截断超过 100 字符的值)
phTrackFocus / phTrackBlur / phTrackChange false / false / true 是否追踪焦点、失焦、变更事件
phIncludeMetadata true 是否附带 field_id、field_required、field_max_length 等元数据
phFieldPrefix '' 字段交互事件名的前缀,如 myform_field_change

该指令自动上报三类事件:组件初始化时的 form_viewed(携带表单名、id、action 与字段清单)、提交时的 form_submitted(携带 form_valid 校验状态与全部字段数据)、以及 field_focus/field_blur/field_change 字段交互事件。安全性方面做到了三层防护:shouldExcludeField 排除敏感字段名、shouldRedactFieldValue 对 password 类型及 password/secret/token/auth/key/credit/card/cvv/ssn/social 等敏感模式强制脱敏为 [REDACTED]、sanitizeFieldValue 截断超长值。指令通过 Renderer2.listen 动态绑定字段监听器,并在 ngOnDestroy 中统一清理,避免内存泄漏。

用户识别

用户登录成功后调用 identifyUser:

login(user) {
  // 认证成功后
  this.posthogManager.identifyUser(user.id, {
    email: user.email,
    name: user.name,
    plan: user.subscriptionPlan,
    signupDate: user.createdAt
  });
}

identifyUser 底层转发到 posthog.identify(distinctId, userProperties)(见 posthog-manager.service.ts)。若应用启动配置了 options.bootstrap.distinctID,初始化阶段会自动调用 identifyUser 完成身份绑定;注销或切换账号时可调用 resetUser() 清空当前身份。

功能开关(Feature Flags)

import { Component, OnInit } from '@angular/core';
import { PostHogServiceManager } from '@gauzy/plugin-posthog-ui';

@Component({
	selector: 'app-feature',
	template: `
        @if (newFeatureEnabled) {
            <h2>New Feature</h2>
            <!-- 功能内容 -->
        }
	`
})
export class FeatureComponent implements OnInit {
	newFeatureEnabled = false;

	constructor(private posthogManager: PostHogServiceManager) {}

	ngOnInit() {
		this.newFeatureEnabled = this.posthogManager.isFeatureEnabled('new-feature');
	}
}

高层服务还提供 getFeatureFlagValue<T>(flagKey, defaultValue) 获取带类型的开关值、reloadFeatureFlags() 手动重载。值得注意的细节:在 initialize 的 setupConfigBasedBehaviors 中(见 posthog-manager.service.ts),当 options.advanced_disable_feature_flags !== true 且未禁用首载时,插件会自动调用 reloadFeatureFlags() 预拉取开关,因此组件中调用 isFeatureEnabled 时开关通常已就绪。底层 PostHogService 还额外暴露了 getFeatureFlags(keys: string[])(批量取值)、setFeatureFlagOverride(key, value)(本地覆盖,常用于测试,对应 posthog.featureFlags.override)。

分组分析(Group Analytics)

将用户与组织/团队关联:

// 当用户加入或切换团队时
this.posthogManager.setGroup('company', 'company-id-123', {
	name: 'Acme Corp',
	industry: 'Technology',
	tier: 'Enterprise'
});

底层对应 posthog.group(groupType, groupKey, groupProperties),适合 Gauzy 这类多租户企业管理平台按公司、团队维度做聚合分析。

错误追踪

插件在浏览器环境下自动为 window 注册 error 与 unhandledrejection 监听器(见 posthog.service.ts),自动捕获全局 JS 异常(附带 source/lineno/colno 定位信息)与未处理的 Promise rejection,统一以 $exception 事件上报。HTTP 错误则由 PostHogInterceptor 自动捕获。也可以手动上报业务错误:

try {
	// 某些有风险的操作
} catch (error) {
	this.posthogManager.trackError(error, {
		context: 'payment_processing',
		user_action: 'checkout'
	});
}

captureError 会将 error_message、error_type、error_stack 等结构化信息一并上报(见 posthog.service.ts)。若希望完全关闭自动异常捕获,可在 options 中设置 capture_exceptions: false——这同时会阻止拦截器的注册(见 posthog.module.ts)。

HTTP 请求追踪

通过 httpTracking 配置可以控制 API 调用追踪的粒度(posthog.interface.ts):

PostHogModule.forRoot({
	apiKey: environment.posthogApiKey,
	httpTracking: {
		// 是否采集响应体(谨慎!可能含敏感数据),默认 false
		captureResponseBodies: false,
		// 排除特定路由(支持字符串与 RegExp)
		excludeUrls: ['/api/analytics', /\/reports\/\d+/],
		// 随 API 事件附加的自定义属性
		additionalProperties: { app: 'gauzy', env: 'production' }
	}
})

拦截器 posthog.interceptor.ts 的实现要点:

  • 对每个被拦截的请求,用 performance.now() 记录开始时间,剥离 query 参数后构造事件属性(path、method、url、withCredentials),并合并 additionalProperties;
  • 通过 tap 在响应成功时写入 status、success(200-399 区间判定)与可选的脱敏 responseBody;出错时写入 status/success: false/errorMessage/errorType/statusText;
  • 在 finalize 中计算耗时 duration(毫秒),统一上报 api_request 事件;
  • shouldSkipTracking 依据 excludeUrls 决定是否放行(不追踪);
  • sanitizeResponseBody 以递归方式(深度上限 10 层)脱敏 password/token/secret/key/auth/credit/card 等敏感字段,防止响应体泄露隐私。

超级属性(Super Properties)

注册随每个事件一起发送的全局属性:

// 在应用初始化时设置一次
this.posthogManager.register({
	app_version: '1.2.3',
	theme: 'dark',
	locale: 'en-US'
});

注意:README 中的 register 在高层服务 API 表中未单列,但底层 PostHogService.register(properties, days?) 对应 posthog.register(可指定保留天数),另有 registerOnce(仅当属性未设置时写入)、unregister(propertyName)(注销单个超级属性)。此外,在 initialize 阶段,若 options.persistence !== 'memory'(即使用 localStorage 等持久化存储),插件会自动注册 app_version 超级属性(见 posthog-manager.service.ts)。

API 参考

PostHogServiceManager(高层服务)

方法 描述
initialize(config: PostHogModuleConfig) 以配置初始化 PostHog,返回是否成功
trackEvent(eventName: string, properties?: object) 追踪自定义事件
trackPageView(url?: string, properties?: object) 捕获页面浏览事件
identifyUser(userId: string, properties?: object) 识别用户
resetUser() 重置当前用户身份
setGroup(groupType: string, groupKey: string, groupProperties?: object) 将用户与分组关联
isFeatureEnabled(flagKey: string) 检查功能开关是否开启
getFeatureFlagValue<T>(flagKey: string, defaultValue: T) 获取功能开关值
reloadFeatureFlags() 从 PostHog 重载功能开关
setUserProperties(properties: object) 设置当前用户属性
optInTracking() / optOutTracking() 选择加入/退出追踪
trackError(error: Error, properties?: object) 带上下文追踪错误
getPostHogInstance() 获取原始 PostHog 实例用于高级操作
startSessionRecording() 手动启动会话录制

高层服务几乎每个方法都先做 isInitialized() 守卫,未初始化时安全返回,便于在服务尚未就绪或 SSR 环境下调用而不抛错。

PostHogService(底层服务)

方法 描述
initialize(apiKey: string, config?: object) 初始化 PostHog(仅浏览器平台执行)
captureEvent(eventName: string, properties?: object, sendInstantly?: boolean) 追踪自定义事件
capturePageview(url?: string, properties?: object) 捕获页面浏览
captureError(error: Error, properties?: object) 追踪错误或异常
identify(distinctId: string, userProperties?: object, callback?: () => void) 识别用户
alias(alias: string, distinctId?: string, callback?: () => void) 为用户创建别名
group(groupType: string, groupKey: string, groupProperties?: object) 将用户与分组关联
reset() 重置当前用户
isFeatureEnabled(key: string, options: { send_event: boolean }) 检查功能开关
getFeatureFlag(key: string, defaultValue?: any) 获取功能开关值
setFeatureFlagOverride(key: string, value: any) 覆盖功能开关(用于测试)
reloadFeatureFlags() 重载功能开关
getFeatureFlags(keys: string[]) 批量获取多个功能开关值
startSessionRecording() / stopSessionRecording() 启动/停止会话录制
register(properties: object, days?: number) 注册超级属性
registerOnce(properties: object, defaultValue?: any, days?: number) 仅当未设置时注册属性
unregister(propertyName: string) 注销超级属性
getDistinctId() 获取当前用户 distinct ID
optOut() / optIn() / isOptedOut() 退出/加入追踪及状态查询
setPersonProperties(properties: object) 设置用户(person)属性
setPersonPropertiesOnce(properties: object) 仅当未设置时设置用户属性
setPersonPropertiesForFlags(properties: object) 为功能开关计算设置用户属性
setGroupPropertiesForFlags(map: object, reload?: boolean) 为功能开关计算设置分组属性
captureDeadClick(element: Element, properties?: object) 捕获无效点击($dead_click)事件
getInstance() 获取原始 PostHog 实例

指令

指令/输入 描述
[phTrack] 在用户交互时追踪事件
[phProperties] 随事件发送的属性
[phOnInit] 是否在组件初始化时立即捕获
[phEventType] 监听的事件类型(click、mouseenter、focus),默认 click
[phStopPropagation] 是否阻止事件冒泡
phFormTrack 追踪表单提交与字段交互
[phFormName] 被追踪表单的名称标识
[phTrackFields] 是否追踪单个字段交互
[phExcludeFields] 排除的字段
[phSanitizeValues] 是否对字段值脱敏
[phTrackFocus] 是否追踪字段焦点事件
[phTrackBlur] 是否追踪字段失焦事件
[phTrackChange] 是否追踪字段变更事件

页面浏览与路由追踪原理

插件默认将 capture_pageview 置为 false(见 posthog.service.ts),页面浏览改由 Router 事件驱动,以获得对 URL 与计时指标的更精细控制。setupRouteTracking 订阅 router.events:在 NavigationStart 记录起始时间,在 NavigationEnd 计算加载耗时并上报 $pageview 事件,附带 load_time_ms(导航耗时)、referrer、route(重定向后的 URL)与 route_id(匹配到的路由 path)等属性(posthog.service.ts)。该逻辑在 initialize 成功后由 PostHogServiceManager 自动调用一次,且通过内部标记 _routeTrackingAttached 保证只绑定一次。若需要通过 routeTracking 配置做路由级过滤(excluded/included),可在模块配置中提供,实现上以 excluded 优先。

测试:forTesting 与单元测试

插件内置测试支持,在测试模块中导入即可获得不执行真实初始化的 mock 环境:

// test.module.ts
import { PostHogModule } from '@gauzy/plugin-posthog-ui';

@NgModule({
	imports: [PostHogModule.forTesting()]
})
export class TestModule {}

从 posthog.module.ts 的实现可见,forTesting() 以 apiKey: 'test-key' 与空 loaded 回调提供 POSTHOG_CONFIG,并将 POSTHOG_DEBUG_MODE 置为 true,且不注册 HTTP 拦截器——这样组件测试中注入 PostHogServiceManager 不会触发网络请求。运行插件单元测试(基于 Jest + jest-preset-angular):

yarn nx test plugin-posthog-ui

构建与发布

构建库:

yarn nx build plugin-posthog-ui

构建产物输出到 dist/packages/plugins/posthog-ui。发布到 npm registry:

cd dist/packages/plugins/posthog-ui && npm publish

若需要在开发模式下持续构建,可使用 yarn nx build plugin-posthog-ui --configuration=development --watch(对应 package.json 中的 lib:build、lib:build:prod、lib:watch 脚本)。

在 Ever Gauzy 中的真实落地方式

回到 Gauzy 主应用,PostHog 的启用采用“环境变量 + 运行时设置”双通道(依据 apps/gauzy/src/app/app.module.ts):

  • 环境变量 POSTHOG_ENABLED、POSTHOG_KEY、POSTHOG_HOST 控制构建期的启用开关与默认连接参数;POSTHOG_KEY 为 Docker 占位值 DOCKER_POSTHOG_KEY 时给出告警且不初始化;
  • 运行时,在设置页的监控(Monitoring)模块中(见 monitoring.component.ts),管理员可按 Global 与 Tenant 两个层级保存 posthogEnabled、posthogKey、posthogHost、posthogFlushInterval(默认刷新间隔 10000ms),从而实现多租户环境下的按需启停与独立配置。

这一落地模式说明:@gauzy/plugin-posthog-ui 不只是实验性的埋点封装,而是 Gauzy 生产级产品分析链路中的正式组件;你可以依据同样的模式在自己的 Angular 业务应用中接入,把用户行为、漏斗转化、功能开关与前端错误统一收拢到 PostHog 一处分析。

小结

@gauzy/plugin-posthog-ui 将 PostHog 的开放分析能力封装成了 Angular 生态的标准形态:模块级 forRoot 配置、双层级服务 API、声明式追踪指令、表单安全埋点、HTTP 拦截器、SSR 防护与测试支持一应俱全。无论是 Ever Gauzy 平台内按租户启用产品分析,还是在其他 Angular 项目中快速落地事件追踪与 Feature Flags,本文所覆盖的配置项、方法签名与源码实现细节都能直接指导你的实战落地。

登录后查看全文
ever-gauzy