HarmonyOS开发:APM性能监控SDK集成与实战
HarmonyOS开发:APM性能监控SDK集成与实战
📌 核心要点:从零设计APM架构,实现启动监控、卡顿监控、网络监控、内存监控四大核心模块,完成数据采集、上报、可视化与告警的完整闭环,并对比华为AG Connect APM与自研方案的优劣。
一、背景与动机
你有没有这样的困惑——开发阶段一切正常,上线后用户却反馈"卡"“慢”“闪退”,但你完全复现不了?这种"开发环境正常、线上环境异常"的困境,是每个应用开发者都会遇到的。
原因很简单——开发环境≠线上环境。你的测试机是旗舰款,用户的可能是千元机;你的网络是WiFi,用户的可能是弱网;你的数据是测试数据,用户的可能是海量数据。这些差异,光靠开发阶段的测试是覆盖不了的。
APM(Application Performance Monitoring,应用性能监控)就是来解决这个问题的。它像是一个"7×24小时值班的医生",时刻监控着应用的各项"生命体征"——启动速度、帧率、网络延迟、内存占用……一旦发现异常,立刻告警,让你能在用户投诉之前就发现和修复问题。
HarmonyOS生态中有两种APM方案:一是华为官方的AG Connect APM,开箱即用但定制性有限;二是自研APM SDK,灵活可控但开发成本高。今天我们就把这两种方案都讲清楚,帮你做出最适合自己项目的选择。
二、核心原理
2.1 APM架构设计
一个完整的APM系统由三大部分组成:采集端(SDK)、服务端、展示端(Dashboard)。打个比方,采集端是"传感器",负责收集数据;服务端是"大脑",负责存储和分析;展示端是"仪表盘",负责可视化呈现。
graph TD
A[APM SDK 采集端]:::primary --> B[启动监控]:::info
A --> C[卡顿监控]:::info
A --> D[网络监控]:::info
A --> E[内存监控]:::info
B --> F[数据聚合与压缩]:::warning
C --> F
D --> F
E --> F
F --> G[上报策略引擎]:::warning
G --> H{网络状态判断}:::error
H -->|WiFi| I[即时上报]:::primary
H -->|移动网络| J[批量上报]:::primary
H -->|无网络| K[本地缓存]:::error
I --> L[APM 服务端]:::primary
J --> L
K -->|网络恢复| L
L --> M[数据清洗与聚合]:::warning
M --> N[异常检测引擎]:::warning
N --> O[告警通知]:::error
M --> P[性能看板 Dashboard]:::primary
classDef primary fill:#4CAF50,stroke:#388E3C,color:#fff
classDef warning fill:#FF9800,stroke:#F57C00,color:#fff
classDef error fill:#F44336,stroke:#D32F2F,color:#fff
classDef info fill:#2196F3,stroke:#1976D2,color:#fff
2.2 四大监控模块
| 模块 | 监控内容 | 关键指标 | 采集方式 |
|---|---|---|---|
| 启动监控 | 冷/热启动耗时 | 启动时长、各阶段耗时 | hiTraceMeter + 生命周期回调 |
| 卡顿监控 | 帧率、掉帧、主线程阻塞 | FPS、掉帧率、大卡次数 | VSync回调 + 堆栈采样 |
| 网络监控 | 请求耗时、成功率、流量 | 响应时间、错误率、流量 | HTTP拦截 + 自定义埋点 |
| 内存监控 | 内存占用、GC频率、泄漏 | 堆内存、PSS、OOM次数 | 定时采样 + 引用追踪 |
2.3 数据上报策略
数据上报是APM的关键环节,策略不当会导致两个问题:数据丢失(该报的没报)和流量浪费(不该报的乱报)。
常见上报策略:
- 即时上报:关键事件(如崩溃、ANR)立即上报
- 批量上报:普通事件攒够一定数量后批量上报
- 定时上报:每隔固定时间上报一次
- WiFi优先:WiFi环境下即时上报,移动网络下延迟上报
- 降级上报:低电量或低内存时减少上报频率
三、代码实战
3.1 基础用法:APM SDK核心架构
先来实现APM SDK的核心架构,包括配置管理、数据采集、上报引擎。
// ApmSdk.ets
// APM性能监控SDK - 核心架构
import { http } from '@kit.NetworkKit';
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';
// APM配置
export interface ApmConfig {
appId: string; // 应用ID
appVersion: string; // 应用版本
reportUrl: string; // 上报地址
enableStartupMonitor: boolean; // 启用启动监控
enableJankMonitor: boolean; // 启用卡顿监控
enableNetworkMonitor: boolean; // 启用网络监控
enableMemoryMonitor: boolean; // 启用内存监控
reportInterval: number; // 上报间隔(ms)
maxCacheSize: number; // 最大缓存条数
wifiOnly: boolean; // 仅WiFi上报
debugMode: boolean; // 调试模式
}
// APM事件
export interface ApmEvent {
type: string; // 事件类型
name: string; // 事件名称
timestamp: number; // 时间戳
duration?: number; // 持续时长(ms)
attributes: Record<string, string>; // 自定义属性
metrics: Record<string, number>; // 性能指标
}
// 设备信息
interface DeviceInfo {
deviceModel: string;
osVersion: string;
screenWidth: number;
screenHeight: number;
cpuCores: number;
totalMemory: number;
}
export class ApmSdk {
private static instance: ApmSdk | null = null;
private config: ApmConfig | null = null;
private eventCache: ApmEvent[] = [];
private reportTimerId: number = -1;
private isInitialized: boolean = false;
private deviceInfo: DeviceInfo | null = null;
// 各监控模块
private startupMonitor: StartupMonitor | null = null;
private jankMonitor: JankMonitor | null = null;
private networkMonitor: NetworkMonitor | null = null;
private memoryMonitor: MemoryMonitor | null = null;
// 单例
static getInstance(): ApmSdk {
if (!ApmSdk.instance) {
ApmSdk.instance = new ApmSdk();
}
return ApmSdk.instance;
}
// 初始化SDK
init(config: ApmConfig): void {
if (this.isInitialized) {
console.warn('[APM] SDK已初始化,请勿重复调用');
return;
}
this.config = config;
this.isInitialized = true;
// 采集设备信息
this.deviceInfo = this.collectDeviceInfo();
// 初始化各监控模块
if (config.enableStartupMonitor) {
this.startupMonitor = new StartupMonitor(this);
}
if (config.enableJankMonitor) {
this.jankMonitor = new JankMonitor(this);
}
if (config.enableNetworkMonitor) {
this.networkMonitor = new NetworkMonitor(this);
}
if (config.enableMemoryMonitor) {
this.memoryMonitor = new MemoryMonitor(this);
}
// 启动定时上报
this.startReportTimer();
console.info('[APM] SDK初始化完成');
console.info(` 启动监控: ${config.enableStartupMonitor}`);
console.info(` 卡顿监控: ${config.enableJankMonitor}`);
console.info(` 网络监控: ${config.enableNetworkMonitor}`);
console.info(` 内存监控: ${config.enableMemoryMonitor}`);
}
// 记录事件
recordEvent(event: ApmEvent): void {
if (!this.isInitialized) return;
// 添加设备信息
event.attributes['device_model'] = this.deviceInfo?.deviceModel || 'unknown';
event.attributes['os_version'] = this.deviceInfo?.osVersion || 'unknown';
event.attributes['app_version'] = this.config?.appVersion || 'unknown';
this.eventCache.push(event);
if (this.config?.debugMode) {
console.info(`[APM] 事件: ${event.type}/${event.name}, 耗时: ${event.duration || 0}ms`);
}
// 缓存满时触发上报
if (this.eventCache.length >= (this.config?.maxCacheSize || 50)) {
this.flushEvents();
}
}
// 启动定时上报
private startReportTimer(): void {
if (this.reportTimerId !== -1) return;
this.reportTimerId = setInterval(() => {
this.flushEvents();
}, this.config?.reportInterval || 30000) as number;
}
// 上报事件
async flushEvents(): Promise<void> {
if (this.eventCache.length === 0) return;
// 取出待上报的事件
const eventsToReport = [...this.eventCache];
this.eventCache = [];
try {
const payload = {
appId: this.config?.appId,
appVersion: this.config?.appVersion,
deviceInfo: this.deviceInfo,
events: eventsToReport,
reportTime: Date.now()
};
if (this.config?.debugMode) {
console.info(`[APM] 上报 ${eventsToReport.length} 条事件`);
}
// 发送到服务端
const response = await http.createHttp().request(this.config?.reportUrl || '', {
method: http.RequestMethod.POST,
header: { 'Content-Type': 'application/json' },
extraData: JSON.stringify(payload)
});
if (response.responseCode === 200) {
console.info('[APM] 上报成功');
} else {
console.warn(`[APM] 上报失败: ${response.responseCode}`);
// 上报失败,将事件放回缓存
this.eventCache.unshift(...eventsToReport);
}
} catch (err) {
console.error(`[APM] 上报异常: ${JSON.stringify(err)}`);
// 上报异常,将事件放回缓存
this.eventCache.unshift(...eventsToReport);
}
}
// 采集设备信息
private collectDeviceInfo(): DeviceInfo {
// 实际项目中从deviceInfo模块获取
return {
deviceModel: 'Unknown',
osVersion: 'HarmonyOS 6.0',
screenWidth: 1080,
screenHeight: 2340,
cpuCores: 8,
totalMemory: 8192
};
}
// 销毁SDK
destroy(): void {
// 上报剩余事件
this.flushEvents();
// 停止定时器
if (this.reportTimerId !== -1) {
clearInterval(this.reportTimerId);
this.reportTimerId = -1;
}
// 停止各监控模块
this.startupMonitor?.stop();
this.jankMonitor?.stop();
this.networkMonitor?.stop();
this.memoryMonitor?.stop();
this.isInitialized = false;
console.info('[APM] SDK已销毁');
}
}
3.2 进阶用法:四大监控模块实现
接下来实现四大核心监控模块。
// ApmMonitors.ets
// APM四大监控模块实现
import { ApmSdk, ApmEvent } from './ApmSdk';
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';
// ========== 启动监控模块 ==========
export class StartupMonitor {
private apm: ApmSdk;
private appStartTime: number = 0;
private isColdStart: boolean = true;
constructor(apm: ApmSdk) {
this.apm = apm;
this.appStartTime = Date.now();
console.info('[APM-启动] 启动监控已初始化');
}
// 记录Ability.onCreate开始
recordOnCreateStart(): void {
hiTraceMeter.startTrace('apm_startup_onCreate', 1);
}
// 记录Ability.onCreate结束
recordOnCreateEnd(): void {
hiTraceMeter.finishTrace('apm_startup_onCreate', 1);
}
// 记录onWindowStageCreate开始
recordWindowStageCreateStart(): void {
hiTraceMeter.startTrace('apm_startup_windowStage', 2);
}
// 记录首帧渲染完成
recordFirstFrameRendered(): void {
const totalDuration = Date.now() - this.appStartTime;
hiTraceMeter.finishTrace('apm_startup_windowStage', 2);
const event: ApmEvent = {
type: 'startup',
name: this.isColdStart ? 'cold_start' : 'warm_start',
timestamp: this.appStartTime,
duration: totalDuration,
attributes: {
'start_type': this.isColdStart ? 'cold' : 'warm',
'page': 'index'
},
metrics: {
'total_duration_ms': totalDuration
}
};
this.apm.recordEvent(event);
// 启动耗时超过阈值,标记为异常
if (this.isColdStart && totalDuration > 3000) {
this.apm.recordEvent({
type: 'anomaly',
name: 'slow_startup',
timestamp: Date.now(),
duration: totalDuration,
attributes: { 'threshold': '3000ms' },
metrics: { 'exceeded_ms': totalDuration - 3000 }
});
}
console.info(`[APM-启动] ${this.isColdStart ? '冷' : '温'}启动耗时: ${totalDuration}ms`);
// 后续启动不再是冷启动
this.isColdStart = false;
}
stop(): void {
console.info('[APM-启动] 启动监控已停止');
}
}
// ========== 卡顿监控模块 ==========
export class JankMonitor {
private apm: ApmSdk;
private lastFrameTime: number = 0;
private frameCount: number = 0;
private jankCount: number = 0;
private bigJankCount: number = 0;
private consecutiveJanks: number = 0;
private vSyncInterval: number = 16.67; // 60FPS
private isRunning: boolean = false;
private sampleIntervalId: number = -1;
private jankThreshold: number = 200; // 卡顿阈值ms
private jankStartTime: number = 0;
private isJanking: boolean = false;
constructor(apm: ApmSdk) {
this.apm = apm;
this.start();
console.info('[APM-卡顿] 卡顿监控已初始化');
}
start(): void {
if (this.isRunning) return;
this.isRunning = true;
this.lastFrameTime = Date.now();
// 定期检查卡顿
this.sampleIntervalId = setInterval(() => {
this.checkJank();
}, 50) as number;
}
// 通知帧渲染完成
onFrameRendered(): void {
if (!this.isRunning) return;
const now = Date.now();
const frameTime = now - this.lastFrameTime;
this.lastFrameTime = now;
this.frameCount++;
const missedFrames = Math.floor(frameTime / this.vSyncInterval) - 1;
if (missedFrames > 0) {
this.jankCount++;
this.consecutiveJanks++;
if (this.consecutiveJanks >= 3) {
this.bigJankCount++;
this.reportBigJank(frameTime);
}
} else {
if (this.consecutiveJanks > 0) {
this.consecutiveJanks = 0;
}
}
// 卡顿结束
if (this.isJanking) {
this.isJanking = false;
const jankDuration = now - this.jankStartTime;
this.reportJank(jankDuration);
}
}
// 检查卡顿
private checkJank(): void {
if (!this.isRunning) return;
const now = Date.now();
const timeSinceLastFrame = now - this.lastFrameTime;
if (timeSinceLastFrame > this.jankThreshold && !this.isJanking) {
this.isJanking = true;
this.jankStartTime = this.lastFrameTime;
}
}
// 上报卡顿事件
private reportJank(duration: number): void {
const event: ApmEvent = {
type: 'jank',
name: 'frame_jank',
timestamp: this.jankStartTime,
duration: duration,
attributes: {
'jank_type': 'normal',
'stack_trace': this.captureStack()
},
metrics: {
'jank_duration_ms': duration,
'missed_frames': Math.floor(duration / this.vSyncInterval)
}
};
this.apm.recordEvent(event);
}
// 上报大卡事件
private reportBigJank(frameTime: number): void {
const event: ApmEvent = {
type: 'jank',
name: 'big_jank',
timestamp: Date.now(),
duration: frameTime,
attributes: {
'jank_type': 'big',
'consecutive_janks': String(this.consecutiveJanks),
'stack_trace': this.captureStack()
},
metrics: {
'frame_time_ms': frameTime
}
};
this.apm.recordEvent(event);
console.error(`[APM-卡顿] 大卡!帧耗时: ${frameTime}ms`);
}
// 捕获调用栈
private captureStack(): string {
try {
return new Error().stack?.split('\n').slice(1, 5).join(' | ') || '';
} catch (e) {
return '';
}
}
stop(): void {
this.isRunning = false;
if (this.sampleIntervalId !== -1) {
clearInterval(this.sampleIntervalId);
this.sampleIntervalId = -1;
}
console.info('[APM-卡顿] 卡顿监控已停止');
}
}
// ========== 网络监控模块 ==========
export class NetworkMonitor {
private apm: ApmSdk;
private pendingRequests: Map<string, { url: string; method: string; startTime: number }> = new Map();
constructor(apm: ApmSdk) {
this.apm = apm;
console.info('[APM-网络] 网络监控已初始化');
}
// 记录请求开始
recordRequestStart(requestId: string, url: string, method: string): void {
this.pendingRequests.set(requestId, {
url,
method,
startTime: Date.now()
});
}
// 记录请求结束
recordRequestEnd(requestId: string, statusCode: number, responseSize: number): void {
const requestInfo = this.pendingRequests.get(requestId);
if (!requestInfo) return;
this.pendingRequests.delete(requestId);
const duration = Date.now() - requestInfo.startTime;
const isSuccess = statusCode >= 200 && statusCode < 400;
const event: ApmEvent = {
type: 'network',
name: 'http_request',
timestamp: requestInfo.startTime,
duration: duration,
attributes: {
'url': requestInfo.url,
'method': requestInfo.method,
'status_code': String(statusCode),
'success': String(isSuccess)
},
metrics: {
'duration_ms': duration,
'response_size_bytes': responseSize
}
};
this.apm.recordEvent(event);
// 慢请求告警
if (duration > 5000) {
this.apm.recordEvent({
type: 'anomaly',
name: 'slow_request',
timestamp: requestInfo.startTime,
duration: duration,
attributes: {
'url': requestInfo.url,
'threshold': '5000ms'
},
metrics: { 'exceeded_ms': duration - 5000 }
});
}
if (!isSuccess) {
console.warn(`[APM-网络] 请求失败: ${requestInfo.method} ${requestInfo.url} → ${statusCode}`);
}
}
// 记录请求异常
recordRequestError(requestId: string, error: string): void {
const requestInfo = this.pendingRequests.get(requestId);
if (!requestInfo) return;
this.pendingRequests.delete(requestId);
const duration = Date.now() - requestInfo.startTime;
const event: ApmEvent = {
type: 'network',
name: 'http_error',
timestamp: requestInfo.startTime,
duration: duration,
attributes: {
'url': requestInfo.url,
'method': requestInfo.method,
'error': error
},
metrics: {
'duration_ms': duration
}
};
this.apm.recordEvent(event);
console.error(`[APM-网络] 请求异常: ${requestInfo.method} ${requestInfo.url} → ${error}`);
}
stop(): void {
console.info('[APM-网络] 网络监控已停止');
}
}
// ========== 内存监控模块 ==========
export class MemoryMonitor {
private apm: ApmSdk;
private isRunning: boolean = false;
private sampleIntervalId: number = -1;
private sampleInterval: number = 5000; // 5秒采样一次
private peakMemory: number = 0;
private oomRiskThreshold: number = 0.8; // 80%内存使用率告警
constructor(apm: ApmSdk) {
this.apm = apm;
this.start();
console.info('[APM-内存] 内存监控已初始化');
}
start(): void {
if (this.isRunning) return;
this.isRunning = true;
this.sampleIntervalId = setInterval(() => {
this.sampleMemory();
}, this.sampleInterval) as number;
}
// 采样内存使用情况
private sampleMemory(): void {
if (!this.isRunning) return;
// 实际项目中使用performance.memory或系统API获取内存信息
// 这里使用模拟数据
const usedMemory = this.getUsedMemory();
const totalMemory = this.getTotalMemory();
const usageRatio = usedMemory / totalMemory;
// 更新峰值
if (usedMemory > this.peakMemory) {
this.peakMemory = usedMemory;
}
// 内存使用率过高告警
if (usageRatio > this.oomRiskThreshold) {
const event: ApmEvent = {
type: 'memory',
name: 'high_memory_usage',
timestamp: Date.now(),
attributes: {
'usage_ratio': (usageRatio * 100).toFixed(1) + '%',
'threshold': (this.oomRiskThreshold * 100).toFixed(0) + '%'
},
metrics: {
'used_memory_mb': usedMemory,
'total_memory_mb': totalMemory,
'peak_memory_mb': this.peakMemory
}
};
this.apm.recordEvent(event);
console.warn(`[APM-内存] 内存使用率过高: ${(usageRatio * 100).toFixed(1)}%`);
}
// 定期上报内存快照
if (Date.now() % 60000 < this.sampleInterval) {
this.apm.recordEvent({
type: 'memory',
name: 'memory_snapshot',
timestamp: Date.now(),
attributes: {},
metrics: {
'used_memory_mb': usedMemory,
'total_memory_mb': totalMemory,
'peak_memory_mb': this.peakMemory,
'usage_ratio': usageRatio
}
});
}
}
// 获取已用内存(模拟)
private getUsedMemory(): number {
return Math.random() * 4096 + 1024; // 模拟1-5GB
}
// 获取总内存(模拟)
private getTotalMemory(): number {
return 8192; // 8GB
}
stop(): void {
this.isRunning = false;
if (this.sampleIntervalId !== -1) {
clearInterval(this.sampleIntervalId);
this.sampleIntervalId = -1;
}
console.info('[APM-内存] 内存监控已停止');
}
}
3.3 完整示例:APM SDK集成与华为AG Connect APM
下面展示如何在实际项目中集成自研APM SDK,以及如何集成华为AG Connect APM。
// ApmIntegrationDemo.ets
// APM集成实战 - 自研SDK + AG Connect APM
import { ApmSdk, ApmConfig } from './ApmSdk';
import { StartupMonitor, JankMonitor, NetworkMonitor } from './ApmMonitors';
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { http } from '@kit.NetworkKit';
// ========== 方案一:自研APM SDK集成 ==========
export class ApmIntegrationAbility extends UIAbility {
private apmSdk: ApmSdk = ApmSdk.getInstance();
private startupMonitor: StartupMonitor | null = null;
private networkMonitor: NetworkMonitor | null = null;
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 初始化APM SDK
const config: ApmConfig = {
appId: 'com.example.myapp',
appVersion: '1.0.0',
reportUrl: 'https://apm.example.com/report',
enableStartupMonitor: true,
enableJankMonitor: true,
enableNetworkMonitor: true,
enableMemoryMonitor: true,
reportInterval: 30000, // 30秒上报一次
maxCacheSize: 50,
wifiOnly: false,
debugMode: true
};
this.apmSdk.init(config);
// 获取启动监控模块
this.startupMonitor = new StartupMonitor(this.apmSdk);
this.startupMonitor.recordOnCreateStart();
// 获取网络监控模块
this.networkMonitor = new NetworkMonitor(this.apmSdk);
console.info('[APM集成] 自研SDK初始化完成');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
this.startupMonitor?.recordWindowStageCreateStart();
windowStage.loadContent('pages/Index', (err, data) => {
if (err.code) return;
// 记录首帧渲染完成
this.startupMonitor?.recordFirstFrameRendered();
});
}
onDestroy(): void {
this.apmSdk.destroy();
}
}
// ========== 方案二:华为AG Connect APM集成 ==========
// AG Connect APM的集成方式(需要先在AGC控制台开通APM服务)
export class AgConnectApmIntegration {
// AGC APM初始化
static init(context: Context): void {
// 注意:AG Connect APM需要在module.json5中配置
// "agConnectApm": {
// "enable": true,
// "samplingRate": 100
// }
console.info('[AGC-APM] AG Connect APM已配置');
// AGC APM是自动采集的,无需手动初始化
// 但可以设置自定义属性
AgConnectApmIntegration.setCustomAttributes();
}
// 设置自定义属性
static setCustomAttributes(): void {
// 实际项目中使用AGC APM SDK的API
// AGConnectAPM.setCustomAttribute('user_tier', 'premium');
// AGConnectAPM.setCustomAttribute('experiment_group', 'A');
console.info('[AGC-APM] 自定义属性已设置');
}
// 记录自定义轨迹
static startTrace(traceName: string): string {
const traceId = `trace_${Date.now()}`;
// 实际项目中使用AGC APM SDK的API
// const trace = AGConnectAPM.startTrace(traceName);
// trace.putAttribute('trace_id', traceId);
console.info(`[AGC-APM] 开始轨迹: ${traceName}`);
return traceId;
}
// 结束轨迹
static stopTrace(traceName: string, traceId: string): void {
// 实际项目中使用AGC APM SDK的API
// AGConnectAPM.stopTrace(traceName);
console.info(`[AGC-APM] 结束轨迹: ${traceName}, ID: ${traceId}`);
}
// 记录自定义指标
static recordMetric(name: string, value: number): void {
// 实际项目中使用AGC APM SDK的API
// AGConnectAPM.recordMetric(name, value);
console.info(`[AGC-APM] 记录指标: ${name} = ${value}`);
}
}
// ========== 网络请求APM拦截器 ==========
// 封装带APM监控的HTTP请求
export class ApmHttpClient {
private networkMonitor: NetworkMonitor | null = null;
private requestCounter: number = 0;
constructor(apmSdk: ApmSdk) {
this.networkMonitor = new NetworkMonitor(apmSdk);
}
// 带监控的GET请求
async get(url: string): Promise<http.HttpResponse> {
const requestId = `req_${++this.requestCounter}_${Date.now()}`;
this.networkMonitor?.recordRequestStart(requestId, url, 'GET');
try {
const response = await http.createHttp().request(url, {
method: http.RequestMethod.GET
});
this.networkMonitor?.recordRequestEnd(
requestId,
response.responseCode,
String(response.result).length
);
return response;
} catch (err) {
this.networkMonitor?.recordRequestError(requestId, String(err));
throw err;
}
}
// 带监控的POST请求
async post(url: string, data: Object): Promise<http.HttpResponse> {
const requestId = `req_${++this.requestCounter}_${Date.now()}`;
this.networkMonitor?.recordRequestStart(requestId, url, 'POST');
try {
const response = await http.createHttp().request(url, {
method: http.RequestMethod.POST,
header: { 'Content-Type': 'application/json' },
extraData: JSON.stringify(data)
});
this.networkMonitor?.recordRequestEnd(
requestId,
response.responseCode,
String(response.result).length
);
return response;
} catch (err) {
this.networkMonitor?.recordRequestError(requestId, String(err));
throw err;
}
}
}
// ========== APM数据可视化与告警 ==========
export class ApmDashboard {
// 生成性能摘要报告
static generateSummary(events: ApmEvent[]): string {
const startupEvents = events.filter(e => e.type === 'startup');
const jankEvents = events.filter(e => e.type === 'jank');
const networkEvents = events.filter(e => e.type === 'network');
const memoryEvents = events.filter(e => e.type === 'memory');
let report = '\n===== APM性能摘要报告 =====\n';
// 启动性能
if (startupEvents.length > 0) {
const avgStartup = startupEvents.reduce((sum, e) => sum + (e.duration || 0), 0) / startupEvents.length;
report += `\n📊 启动性能:\n`;
report += ` 冷启动平均耗时: ${avgStartup.toFixed(0)}ms\n`;
report += ` 采样次数: ${startupEvents.length}\n`;
}
// 卡顿情况
if (jankEvents.length > 0) {
const bigJanks = jankEvents.filter(e => e.name === 'big_jank').length;
report += `\n📊 卡顿情况:\n`;
report += ` 总卡顿次数: ${jankEvents.length}\n`;
report += ` 大卡次数: ${bigJanks}\n`;
}
// 网络性能
if (networkEvents.length > 0) {
const successRequests = networkEvents.filter(e => e.attributes['success'] === 'true');
const avgDuration = networkEvents.reduce((sum, e) => sum + (e.duration || 0), 0) / networkEvents.length;
report += `\n📊 网络性能:\n`;
report += ` 请求总数: ${networkEvents.length}\n`;
report += ` 成功率: ${(successRequests.length / networkEvents.length * 100).toFixed(1)}%\n`;
report += ` 平均耗时: ${avgDuration.toFixed(0)}ms\n`;
}
// 内存情况
if (memoryEvents.length > 0) {
const latestSnapshot = memoryEvents[memoryEvents.length - 1];
report += `\n📊 内存情况:\n`;
report += ` 当前内存: ${latestSnapshot.metrics['used_memory_mb']?.toFixed(0) || 'N/A'}MB\n`;
report += ` 内存峰值: ${latestSnapshot.metrics['peak_memory_mb']?.toFixed(0) || 'N/A'}MB\n`;
}
report += '\n========================\n';
return report;
}
// 告警判断
static checkAlerts(events: ApmEvent[]): string[] {
const alerts: string[] = [];
// 启动耗时告警
const slowStartups = events.filter(e => e.type === 'startup' && (e.duration || 0) > 3000);
if (slowStartups.length > 0) {
alerts.push(`⚠️ 慢启动告警: ${slowStartups.length}次启动超过3秒`);
}
// 大卡告警
const bigJanks = events.filter(e => e.name === 'big_jank');
if (bigJanks.length > 0) {
alerts.push(`⚠️ 大卡告警: 检测到${bigJanks.length}次大卡`);
}
// 网络错误告警
const networkErrors = events.filter(e => e.name === 'http_error');
if (networkErrors.length > 3) {
alerts.push(`⚠️ 网络异常告警: ${networkErrors.length}次请求异常`);
}
// 内存告警
const highMemory = events.filter(e => e.name === 'high_memory_usage');
if (highMemory.length > 0) {
alerts.push(`⚠️ 内存告警: ${highMemory.length}次内存使用率超过80%`);
}
return alerts;
}
}
3.4 自研APM vs AG Connect APM对比
| 维度 | 自研APM SDK | AG Connect APM |
|---|---|---|
| 接入成本 | 高(需开发SDK+服务端+看板) | 低(开箱即用) |
| 定制性 | 完全可控 | 有限定制 |
| 数据安全 | 数据在自己服务器 | 数据在华为云 |
| 监控覆盖 | 按需实现 | 全自动采集 |
| 告警能力 | 自定义 | 内置告警 |
| 费用 | 开发人力成本 | 按量计费 |
| 适用场景 | 大型项目、有隐私要求 | 中小项目、快速上线 |
选择建议:
- 中小型项目、快速迭代阶段 → 优先选择AG Connect APM,省时省力
- 大型项目、有数据安全要求 → 自研APM SDK,完全可控
- 最佳实践:AG Connect APM + 自研APM互补,AGC负责基础监控,自研负责业务定制
四、踩坑与注意事项
坑点1:APM SDK自身性能开销过大
APM SDK本身也是代码,也会消耗CPU和内存。如果SDK的采样频率过高、上报数据过大,反而会拖慢应用性能。建议APM SDK的CPU占用不超过1%,内存占用不超过10MB。生产环境可以降低采样率。
坑点2:上报数据量过大导致流量浪费
每个事件都包含设备信息、调用栈等数据,如果事件量很大,上报的流量消耗会很可观。建议对上报数据做压缩(如gzip),并控制事件量(如每分钟最多上报100条)。
坑点3:APM数据上报失败导致数据丢失
网络不稳定时,上报可能失败。如果直接丢弃失败的数据,会造成监控盲区。建议实现本地缓存+重试机制,上报失败的数据保存到本地,等网络恢复后重新上报。
坑点4:卡顿监控的堆栈采样不准确
ArkTS环境下,Error.stack获取的调用栈可能不够完整,特别是异步代码的调用栈。建议配合hiTraceMeter的trace数据来补充调用栈信息。
坑点5:启动监控的计时起点不一致
不同设备、不同系统版本,进程创建到Ability.onCreate的时间差可能很大。如果只从onCreate开始计时,会漏掉进程创建阶段的耗时。建议使用系统级的启动时间(如process.argv中的启动时间戳)作为计时起点。
坑点6:内存监控的采样频率过高
内存采样需要遍历堆内存,频率过高会影响GC和性能。建议内存采样间隔不低于5秒,生产环境可以设置为30秒甚至更长。
坑点7:AG Connect APM和自研APM数据冲突
同时使用两套APM时,可能出现数据不一致的情况(如启动耗时统计口径不同)。建议统一数据口径,以一套APM为准,另一套作为补充。
五、HarmonyOS 6适配说明
API差异
| API | HarmonyOS 5.0 | HarmonyOS 6.0 | 迁移建议 |
|---|---|---|---|
| performance.memory | 无 | 新增performance.memoryAPI |
使用标准API获取内存信息 |
| AG Connect APM | 基础APM | 新增ANR检测、网络质量评分 | 启用新功能增强监控能力 |
| hiTraceMeter | startTrace/finishTrace |
新增traceByValue数值型打点 |
使用数值打点记录性能指标 |
| Worker | 基础Worker | 新增TaskPool线程池 |
使用TaskPool替代Worker |
| BackgroundTaskManager | startBackgroundRunning |
新增ContinuousTaskNotificationParam必填 |
APM上报任务需适配通知参数 |
行为变更
-
performance.memory标准化:HarmonyOS 6.0新增了
performance.memoryAPI,开发者可以获取usedJSHeapSize、totalJSHeapSize、jsHeapSizeLimit等信息,无需依赖私有API。 -
AGC APM增强:6.0的AG Connect APM新增了ANR检测、网络质量评分、自定义告警规则等功能,监控能力大幅提升。
-
数据上报安全增强:6.0对后台数据传输增加了安全校验,APM上报数据需要使用HTTPS,且需要验证证书。
-
后台任务限制:6.0对后台任务的管理更加严格,APM的数据上报如果需要在后台执行,必须申请BackgroundMode并展示通知。
适配代码
// HarmonyOS 6.0 APM适配代码
// 6.0适配:使用performance.memory获取内存信息
function getMemoryInfo(): { used: number; total: number; limit: number } {
try {
const memory = (performance as any).memory;
if (memory) {
return {
used: memory.usedJSHeapSize / 1024 / 1024, // MB
total: memory.totalJSHeapSize / 1024 / 1024, // MB
limit: memory.jsHeapSizeLimit / 1024 / 1024 // MB
};
}
} catch (err) {
console.warn(`[适配] performance.memory不可用: ${err}`);
}
// 降级方案
return { used: 0, total: 0, limit: 0 };
}
// 6.0适配:APM后台上报任务通知
import { BackgroundTaskManager } from '@kit.BackgroundTasksKit';
async function startApmReportTask(context: Context): Promise<void> {
// 6.0必须提供通知参数
const notificationParam: BackgroundTaskManager.ContinuousTaskNotificationParam = {
label: '性能数据上报',
contentTitle: '正在上报性能数据',
contentText: '应用正在上报性能监控数据'
};
try {
await BackgroundTaskManager.startBackgroundRunning(
context,
BackgroundTaskManager.BackgroundMode.DATA_TRANSFER,
notificationParam
);
console.info('[适配] APM后台任务启动成功');
} catch (err) {
console.error(`[适配] APM后台任务启动失败: ${JSON.stringify(err)}`);
}
}
// 6.0适配:使用hiTraceMeter数值型打点
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';
function recordApmMetric(name: string, value: number): void {
// 6.0新增:使用traceByValue记录数值型指标
hiTraceMeter.traceByValue(`apm_${name}`, value);
}
六、总结
APM是应用性能的"全天候守护者",它让性能问题从"用户反馈才发现"变成"系统自动检测"。从自研APM SDK的四大监控模块到华为AG Connect APM的开箱即用,从数据采集到可视化告警,APM构建了一套完整的性能监控闭环。
| 维度 | 评价 |
|---|---|
| 学习难度 | ⭐⭐⭐⭐ |
| 使用频率 | ⭐⭐⭐⭐⭐ |
| 重要程度 | ⭐⭐⭐⭐⭐ |
APM实践的核心原则:
- 监控先行——不要等问题出现才加监控,上线前就接入APM
- 数据驱动——用数据说话,不要凭感觉优化
- 告警及时——性能异常要第一时间通知,不能等用户投诉
- 闭环管理——从发现→定位→修复→验证,形成完整闭环
- 持续迭代——APM不是一次性工程,需要持续优化和扩展
最后,送你一句话:没有APM的应用就像没有仪表盘的汽车——你不知道速度、不知道油量、不知道温度,直到它抛锚的那一刻你才知道出了问题。所以,给你的应用装上APM"仪表盘"吧,让它时刻保持健康运行。
- 点赞
- 收藏
- 关注作者
评论(0)