HarmonyOS开发:APM性能监控SDK集成与实战

举报
Jack20 发表于 2026/06/23 20:02:11 2026/06/23
【摘要】 HarmonyOS开发:APM性能监控SDK集成与实战📌 核心要点:从零设计APM架构,实现启动监控、卡顿监控、网络监控、内存监控四大核心模块,完成数据采集、上报、可视化与告警的完整闭环,并对比华为AG Connect APM与自研方案的优劣。 一、背景与动机你有没有这样的困惑——开发阶段一切正常,上线后用户却反馈"卡"“慢”“闪退”,但你完全复现不了?这种"开发环境正常、线上环境异常"...

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的关键环节,策略不当会导致两个问题:数据丢失(该报的没报)和流量浪费(不该报的乱报)。

常见上报策略:

  1. 即时上报:关键事件(如崩溃、ANR)立即上报
  2. 批量上报:普通事件攒够一定数量后批量上报
  3. 定时上报:每隔固定时间上报一次
  4. WiFi优先:WiFi环境下即时上报,移动网络下延迟上报
  5. 降级上报:低电量或低内存时减少上报频率

三、代码实战

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上报任务需适配通知参数

行为变更

  1. performance.memory标准化:HarmonyOS 6.0新增了performance.memoryAPI,开发者可以获取usedJSHeapSizetotalJSHeapSizejsHeapSizeLimit等信息,无需依赖私有API。

  2. AGC APM增强:6.0的AG Connect APM新增了ANR检测、网络质量评分、自定义告警规则等功能,监控能力大幅提升。

  3. 数据上报安全增强:6.0对后台数据传输增加了安全校验,APM上报数据需要使用HTTPS,且需要验证证书。

  4. 后台任务限制: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实践的核心原则

  1. 监控先行——不要等问题出现才加监控,上线前就接入APM
  2. 数据驱动——用数据说话,不要凭感觉优化
  3. 告警及时——性能异常要第一时间通知,不能等用户投诉
  4. 闭环管理——从发现→定位→修复→验证,形成完整闭环
  5. 持续迭代——APM不是一次性工程,需要持续优化和扩展

最后,送你一句话:没有APM的应用就像没有仪表盘的汽车——你不知道速度、不知道油量、不知道温度,直到它抛锚的那一刻你才知道出了问题。所以,给你的应用装上APM"仪表盘"吧,让它时刻保持健康运行。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。