OpenHarmony 异常捕获、日志上报与性能监控

欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
作者 maaath

前言

在移动应用开发中,崩溃监控是保障应用质量的重要环节。对于 OpenHarmony 应用而言,由于其生态仍在快速发展中,开发者需要一套完整的崩溃监控方案来及时发现和定位问题。本文将详细介绍如何在 OpenHarmony 应用中实现一套功能完善的崩溃监控系统,包括异常捕获、日志上报、卡顿检测和性能监控等功能,并在开源鸿蒙模拟器上完成运行验证。

本文对应的完整代码已托管至 AtomGit 仓库:https://atomgit.com/example/crash-monitor-demo

一、系统架构设计

1.1 整体架构

我们的崩溃监控系统采用模块化设计,主要包含以下几个核心模块:

模块名称 功能描述
CrashMonitor 核心监控类,负责异常捕获和性能数据采集
CrashReporter 日志上报服务,支持单条上报和批量上报
CrashStorage 本地存储服务,使用 Preferences 持久化数据
CrashModels 数据模型定义,包含崩溃日志、性能指标等数据结构

1.2 数据流设计

应用异常/性能事件
       ↓
   CrashMonitor
       ↓
    ┌──┴──┐
    ↓     ↓
存储   上报
    ↓     ↓
CrashStorage  CrashReporter → 服务器

二、核心数据模型实现

2.1 崩溃日志模型

崩溃日志是监控系统的核心数据结构,需要包含丰富的上下文信息:

// CrashModels.ets
export enum CrashType {
  JS_ERROR = 'JS_ERROR',           // JavaScript 异常
  PROMISE_REJECTION = 'PROMISE_REJECTION',  // Promise 拒绝
  UNCAUGHT_EXCEPTION = 'UNCAUGHT_EXCEPTION',  // 未捕获异常
  NATIVE_CRASH = 'NATIVE_CRASH',   // 原生崩溃
  ANR = 'ANR',                     // 应用无响应
  MEMORY_WARNING = 'MEMORY_WARNING', // 内存警告
  LOW_MEMORY_KILL = 'LOW_MEMORY_KILL',  // 低内存被杀
  BUSINESS_ERROR = 'BUSINESS_ERROR'  // 业务异常
}

export enum CrashLevel {
  DEBUG = 'DEBUG',
  INFO = 'INFO',
  WARNING = 'WARNING',
  ERROR = 'ERROR',
  FATAL = 'FATAL'
}

export class CrashLog {
  id: string = '';
  type: CrashType = CrashType.JS_ERROR;
  level: CrashLevel = CrashLevel.INFO;
  message: string = '';
  stack: string = '';
  timestamp: string = '';
  deviceInfo: DeviceInfo = new DeviceInfo();
  appInfo: AppInfo = new AppInfo();
  sessionId: string = '';
  pageRoute: string = '';
  customData: Record<string, string | number | boolean> = {};
  reported: boolean = false;
}

2.2 性能指标模型

性能监控需要采集多种指标数据:

export class PerformanceMetrics {
  id: string = '';
  timestamp: string = '';
  memoryUsage: number = 0;      // 内存使用量(MB)
  memoryPercent: number = 0;    // 内存使用百分比
  cpuUsage: number = 0;         // CPU 使用率
  fps: number = 60;             // 帧率
  fpsStable: boolean = true;     // 帧率是否稳定
  mainThreadQueueLength: number = 0;  // 主线程队列长度
  freezeCount: number = 0;      // 卡顿次数
  appState: 'active' | 'inactive' | 'background' | 'suspended' = 'active';
}

export class FreezeInfo {
  id: string = '';
  startTime: string = '';
  endTime: string = '';
  duration: number = 0;          // 卡顿时长(毫秒)
  severity: 'mild' | 'moderate' | 'severe' = 'mild';
  pageRoute: string = '';
  stack: string = '';
  reported: boolean = false;
}

三、本地存储服务实现

3.1 Preferences 存储封装

使用 OpenHarmony 提供的 Preferences API 进行数据持久化,支持崩溃日志、卡顿信息和性能指标的历史记录存储:

// CrashStorage.ets
import dataPreferences from '@ohos.data.preferences';

const CRASH_LOGS_KEY = 'crash_logs';
const FREEZE_LOGS_KEY = 'freeze_logs';
const METRICS_KEY = 'performance_metrics';

export class CrashStorage {
  private preferences: dataPreferences.Preferences | null = null;
  private static instance: CrashStorage | null = null;

  private constructor() {}

  static getInstance(): CrashStorage {
    if (CrashStorage.instance === null) {
      CrashStorage.instance = new CrashStorage();
    }
    return CrashStorage.instance;
  }

  async init(context: Context): Promise<void> {
    this.preferences = await dataPreferences.getPreferences(context, 'crash_monitor_storage');
  }

  async saveCrashLog(log: CrashLog): Promise<string> {
    if (this.preferences === null) {
      return '';
    }
    const logs = await this.getCrashLogs();
    const newLog = new CrashLog();
    newLog.id = `crash_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
    newLog.type = log.type;
    newLog.level = log.level;
    newLog.message = log.message;
    newLog.timestamp = new Date().toISOString();
    // ... 复制其他字段
    
    logs.unshift(newLog);
    const logsToSave = logs.slice(0, 100);  // 最多保留100条
    await this.preferences.put(CRASH_LOGS_KEY, JSON.stringify(logsToSave));
    await this.preferences.flush();
    return newLog.id;
  }

  async getCrashLogs(): Promise<CrashLog[]> {
    if (this.preferences === null) {
      return [];
    }
    const data = await this.preferences.get(CRASH_LOGS_KEY, '[]');
    return JSON.parse(data as string) as CrashLog[];
  }

  async markAsReported(logId: string): Promise<void> {
    const logs = await this.getCrashLogs();
    for (let i = 0; i < logs.length; i++) {
      if (logs[i].id === logId) {
        logs[i].reported = true;
        logs[i].reportedAt = new Date().toISOString();
        break;
      }
    }
    await this.preferences.put(CRASH_LOGS_KEY, JSON.stringify(logs));
    await this.preferences.flush();
  }
}

export default CrashStorage.getInstance();

四、核心监控服务实现

4.1 CrashMonitor 核心类

CrashMonitor 是整个监控系统的核心,负责协调各个模块并提供统一的监控接口:

// CrashMonitor.ets
import deviceInfo from '@ohos.deviceInfo';
import display from '@ohos.display';
import { CrashLog, CrashType, CrashLevel, DeviceInfo, AppInfo, PerformanceMetrics, FreezeInfo } from '../model/CrashModels';
import CrashStorage from './CrashStorage';
import CrashReporterInstance from './CrashReporter';

export interface MonitorConfig {
  enableJSCatch: boolean;
  enablePromiseCatch: boolean;
  enableFreezeDetection: boolean;
  enableMemoryMonitor: boolean;
  enableCpuMonitor: boolean;
  enableFpsMonitor: boolean;
  autoReport: boolean;
  monitorInterval: number;
  freezeThreshold: number;
  memoryWarningPercent: number;
  memoryCriticalPercent: number;
  freezeWarningMs: number;
  freezeCriticalMs: number;
}

export class CrashMonitor {
  private static instance: CrashMonitor | null = null;
  private config: MonitorConfig = {
    enableJSCatch: true,
    enablePromiseCatch: true,
    enableFreezeDetection: true,
    enableMemoryMonitor: true,
    enableCpuMonitor: true,
    enableFpsMonitor: true,
    autoReport: false,
    monitorInterval: 1000,
    freezeThreshold: 200,
    memoryWarningPercent: 70,
    memoryCriticalPercent: 85,
    freezeWarningMs: 200,
    freezeCriticalMs: 500
  };
  
  private isMonitoring: boolean = false;
  private monitorTimer: number = -1;
  private currentFps: number = 60;
  private freezeCount: number = 0;
  private sessionId: string = '';
  private pageRoute: string = '';
  private listeners: Map<string, Function> = new Map();

  private constructor() {}

  static getInstance(): CrashMonitor {
    if (CrashMonitor.instance === null) {
      CrashMonitor.instance = new CrashMonitor();
    }
    return CrashMonitor.instance;
  }

  async init(context: Context): Promise<void> {
    await CrashStorage.init(context);
    this.sessionId = CrashStorage.getSessionId();
    if (this.config.enableJSCatch) {
      this.setupJSCatch();
    }
  }

  start(): void {
    if (this.isMonitoring) {
      return;
    }
    this.isMonitoring = true;
    this.startPerformanceMonitor();
    if (this.config.enableFpsMonitor) {
      this.startFpsMonitor();
    }
  }

  stop(): void {
    this.isMonitoring = false;
    if (this.monitorTimer !== -1) {
      clearInterval(this.monitorTimer);
      this.monitorTimer = -1;
    }
  }
  
  // 手动上报业务异常
  handleBusinessError(message: string, customData?: Record<string, string | number | boolean>): void {
    const log = this.createCrashLog(
      CrashType.BUSINESS_ERROR,
      CrashLevel.ERROR,
      message
    );
    if (customData) {
      log.customData = customData;
    }
    this.saveAndReport(log);
    this.notify('businessError', log);
  }

  // 记录卡顿事件
  recordFreeze(pageRoute: string, duration: number, stack: string = ''): void {
    let severity: 'mild' | 'moderate' | 'severe' = 'mild';
    if (duration >= this.config.freezeCriticalMs) {
      severity = 'severe';
    } else if (duration >= this.config.freezeWarningMs) {
      severity = 'moderate';
    }

    const freezeInfo = new FreezeInfo();
    freezeInfo.id = `freeze_${Date.now()}`;
    freezeInfo.duration = duration;
    freezeInfo.severity = severity;
    freezeInfo.pageRoute = pageRoute;
    freezeInfo.startTime = new Date(Date.now() - duration).toISOString();
    freezeInfo.endTime = new Date().toISOString();
    
    CrashStorage.saveFreezeInfo(freezeInfo);
    this.freezeCount++;
  }

  private startPerformanceMonitor(): void {
    const callback = (): void => {
      this.collectPerformanceMetrics();
    };
    this.monitorTimer = setInterval(callback, this.config.monitorInterval) as number;
  }

  private async collectPerformanceMetrics(): Promise<void> {
    const memoryUsage = this.estimateMemoryUsage();
    const metrics = new PerformanceMetrics();
    metrics.id = `perf_${Date.now()}`;
    metrics.timestamp = new Date().toISOString();
    metrics.memoryUsage = memoryUsage;
    metrics.memoryPercent = memoryUsage / 512 * 100;
    metrics.cpuUsage = this.estimateCpuUsage();
    metrics.fps = this.currentFps;
    metrics.fpsStable = this.currentFps >= 50;
    metrics.freezeCount = this.freezeCount;
    
    await CrashStorage.savePerformanceMetrics(metrics);
    this.checkThresholds(metrics);
  }

  private checkThresholds(metrics: PerformanceMetrics): void {
    if (metrics.memoryPercent >= this.config.memoryCriticalPercent) {
      this.notify('memoryCritical', metrics.memoryPercent);
    } else if (metrics.memoryPercent >= this.config.memoryWarningPercent) {
      this.notify('memoryWarning', metrics.memoryPercent);
    }
    if (metrics.fps < 30) {
      this.notify('fpsCritical', metrics.fps);
    }
  }

  private getDeviceInfoSync(): DeviceInfo {
    const device = new DeviceInfo();
    try {
      const displayInfo = display.getDefaultDisplaySync();
      device.brand = deviceInfo.brand || 'Unknown';
      device.model = deviceInfo.productModel || 'Unknown';
      device.osVersion = deviceInfo.osFullName || 'Unknown';
      device.screenResolution = `${displayInfo.width}x${displayInfo.height}`;
      device.screenDensity = displayInfo.densityDPI;
    } catch {
      console.info('[CrashMonitor] Failed to get device info');
    }
    return device;
  }

  private getAppInfoSync(): AppInfo {
    const app = new AppInfo();
    app.appName = 'oh_demo11';
    app.bundleName = 'com.example.oh_demo11';
    app.versionName = '1.0.0';
    return app;
  }

  private createCrashLog(type: CrashType, level: CrashLevel, message: string, stack: string = ''): CrashLog {
    const log = new CrashLog();
    log.type = type;
    log.level = level;
    log.message = message;
    log.stack = stack;
    log.timestamp = new Date().toISOString();
    log.deviceInfo = this.getDeviceInfoSync();
    log.appInfo = this.getAppInfoSync();
    log.sessionId = this.sessionId;
    log.pageRoute = this.pageRoute;
    return log;
  }

  private async saveAndReport(log: CrashLog): Promise<void> {
    await CrashStorage.saveCrashLog(log);
    if (this.config.autoReport) {
      CrashReporterInstance.report(log);
    }
  }

  getCurrentMetrics(): PerformanceMetrics {
    const metrics = new PerformanceMetrics();
    metrics.memoryUsage = this.estimateMemoryUsage();
    metrics.cpuUsage = this.estimateCpuUsage();
    metrics.fps = this.currentFps;
    metrics.fpsStable = this.currentFps >= 50;
    metrics.freezeCount = this.freezeCount;
    return metrics;
  }

  async getCrashLogs(): Promise<CrashLog[]> {
    return await CrashStorage.getCrashLogs();
  }

  async getFreezeInfos(): Promise<FreezeInfo[]> {
    return await CrashStorage.getFreezeInfos();
  }

  on(event: string, callback: Function): void {
    this.listeners.set(event, callback);
  }

  private notify(event: string, data: CrashLog | FreezeInfo | number): void {
    const callback = this.listeners.get(event);
    if (callback) {
      callback(data);
    }
  }
}

export default CrashMonitor.getInstance();

4.2 FPS 帧率监控实现

帧率监控是检测卡顿的重要手段,通过定时采样计算实时帧率:

private startFpsMonitor(): void {
  this.lastFrameTime = Date.now();
  this.frameCount = 0;

  const measureFrame = (): void => {
    if (!this.isMonitoring) {
      return;
    }
    this.frameCount++;
    const now = Date.now();
    const elapsed = now - this.lastFrameTime;

    if (elapsed >= 1000) {
      this.currentFps = Math.round(this.frameCount * 1000 / elapsed);
      this.frameCount = 0;
      this.lastFrameTime = now;
    }
    setTimeout(measureFrame, 16);  // 约60fps采样
  };
  setTimeout(measureFrame, 16);
}

五、日志上报服务实现

5.1 CrashReporter 服务

负责将崩溃日志上报到远程服务器,支持单条上报和批量上报:

// CrashReporter.ets
import http from '@ohos.net.http';
import { CrashLog, FreezeInfo, ReportConfig } from '../model/CrashModels';
import CrashStorage from './CrashStorage';

const DEFAULT_SERVER_URL = 'https://crash-report.example.com/api/v1/crash';

class RequestPayload {
  type: string = '';
  appKey: string = '';
  timestamp: string = '';
  data: CrashLog | FreezeInfo | CrashLog[] | null = null;

  constructor(type: string, appKey: string, data: CrashLog | FreezeInfo | CrashLog[] | null = null) {
    this.type = type;
    this.appKey = appKey;
    this.timestamp = new Date().toISOString();
    this.data = data;
  }
}

export class CrashReporter {
  private serverUrl: string = DEFAULT_SERVER_URL;
  private appKey: string = 'ohos_demo11';
  private enabled: boolean = true;
  private batchSize: number = 10;
  private reportInterval: number = 30000;
  private reportTimer: number = -1;

  constructor() {}

  async report(crashLog: CrashLog): Promise<boolean> {
    if (!this.enabled) {
      return false;
    }
    try {
      const success = await this.sendToServer(crashLog);
      if (success) {
        await CrashStorage.markAsReported(crashLog.id);
      }
      return success;
    } catch {
      return false;
    }
  }

  async reportBatch(logs: CrashLog[]): Promise<number> {
    if (!this.enabled || logs.length === 0) {
      return 0;
    }
    try {
      const success = await this.sendBatchToServer(logs);
      if (success) {
        for (let i = 0; i < logs.length; i++) {
          await CrashStorage.markAsReported(logs[i].id);
        }
        return logs.length;
      }
      return 0;
    } catch {
      return 0;
    }
  }

  startAutoReport(): void {
    if (this.reportTimer !== -1) {
      return;
    }
    const callback = (): void => {
      this.autoReport();
    };
    this.reportTimer = setInterval(callback, this.reportInterval) as number;
  }

  stopAutoReport(): void {
    if (this.reportTimer !== -1) {
      clearInterval(this.reportTimer);
      this.reportTimer = -1;
    }
  }

  private async autoReport(): Promise<void> {
    const unreported = await CrashStorage.getUnreportedLogs();
    if (unreported.length >= this.batchSize) {
      const batch = unreported.slice(0, this.batchSize);
      await this.reportBatch(batch);
    }
  }

  private async sendToServer(log: CrashLog): Promise<boolean> {
    const payload = new RequestPayload('crash', this.appKey, log);
    return await this.createRequest(this.serverUrl, 'POST', payload);
  }

  private async sendBatchToServer(logs: CrashLog[]): Promise<boolean> {
    const payload = new RequestPayload('crash_batch', this.appKey, logs);
    return await this.createRequest(this.serverUrl, 'POST', payload);
  }

  private createRequest(url: string, method: string, payload: RequestPayload): Promise<boolean> {
    return new Promise((resolve) => {
      const httpRequest = http.createHttp();
      httpRequest.request(
        url,
        {
          method: method as http.RequestMethod,
          header: { 'Content-Type': 'application/json' },
          extraData: JSON.stringify(payload)
        },
        (err, res) => {
          if (err) {
            resolve(false);
          } else {
            resolve(res.responseCode >= 200 && res.responseCode < 300);
          }
        }
      );
      setTimeout(() => resolve(false), 10000);  // 超时处理
    });
  }
}

export default new CrashReporter();

六、性能监控页面实现

6.1 实时监控界面

使用 ArkUI 声明式开发范式构建性能监控界面:

// PerformanceMonitorPage.ets
import { PerformanceMetrics, FreezeInfo } from '../model/CrashModels';
import CrashMonitor from '../service/CrashMonitor';

@Entry
@Component
struct PerformanceMonitorPage {
  @State metrics: PerformanceMetrics | null = null;
  @State freezeInfos: FreezeInfo[] = [];
  @State isMonitoring: boolean = false;
  private timer: number = -1;

  aboutToAppear() {
    this.startMonitoring();
  }

  aboutToDisappear() {
    this.stopMonitoring();
  }

  startMonitoring(): void {
    this.isMonitoring = true;
    this.timer = setInterval(() => {
      this.updateMetrics();
    }, 1000) as number;
    this.updateMetrics();
  }

  stopMonitoring(): void {
    this.isMonitoring = false;
    if (this.timer !== -1) {
      clearInterval(this.timer);
      this.timer = -1;
    }
  }

  async updateMetrics(): Promise<void> {
    try {
      this.metrics = CrashMonitor.getCurrentMetrics();
      this.freezeInfos = await CrashMonitor.getFreezeInfos();
    } catch (error) {
      console.error('Failed to update metrics');
    }
  }

  getStatusColor(value: number, warning: number, critical: number): string {
    if (value >= critical) return '#F44336';
    if (value >= warning) return '#FF9800';
    return '#4CAF50';
  }

  build() {
    Column() {
      this.buildHeader()
      Scroll() {
        Column() {
          this.buildOverviewCards()
          this.buildDetailedMetrics()
          this.buildFreezeSection()
          this.buildControlPanel()
        }
        .padding(16)
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F7FA')
  }

  @Builder
  buildHeader() {
    Row() {
      Text('<')
        .onClick(() => router.back())
      Blank()
      Text('性能监控')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
      Blank()
      Row() {
        Circle().size({ width: 8, height: 8 })
          .fill(this.isMonitoring ? '#4CAF50' : '#F44336')
        Text(this.isMonitoring ? '监控中' : '已停止')
          .fontSize(12)
          .margin({ left: 4 })
      }
    }
    .height(56)
    .padding({ left: 16, right: 16 })
    .backgroundColor('#FFFFFF')
  }

  @Builder
  buildOverviewCards() {
    Row() {
      this.buildMetricCard('内存', this.metrics?.memoryPercent || 0, '%', 70, 85)
      this.buildMetricCard('CPU', this.metrics?.cpuUsage || 0, '%', 80, 95)
      this.buildMetricCard('帧率', this.metrics?.fps || 60, 'FPS', 45, 30)
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
  }

  @Builder
  buildMetricCard(title: string, value: number, unit: string, warning: number, critical: number) {
    Column() {
      Text(title).fontSize(12).fontColor('#666666')
      Text(value.toFixed(1))
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor(this.getStatusColor(value, warning, critical))
      Text(unit).fontSize(12).fontColor('#999999')
    }
    .width('30%')
    .height(100)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .justifyContent(FlexAlign.Center)
  }

  @Builder
  buildControlPanel() {
    Row() {
      Button(this.isMonitoring ? '暂停监控' : '开始监控')
        .onClick(() => {
          if (this.isMonitoring) {
            this.stopMonitoring();
          } else {
            this.startMonitoring();
          }
        })

      Button('触发测试')
        .onClick(() => {
          CrashMonitor.handleBusinessError('测试业务异常', { 'test': true });
          this.updateMetrics();
        })
        .margin({ left: 12 })
    }
    .width('100%')
    .padding(16)
  }
}

七、崩溃日志查看页面

7.1 日志列表与筛选

// CrashLogPage.ets
import { CrashLog, CrashType, CrashLevel } from '../model/CrashModels';
import CrashMonitor from '../service/CrashMonitor';

export class SelectOptionData {
  value: string = '';
  label: string = '';

  constructor(value: string, label: string) {
    this.value = value;
    this.label = label;
  }
}

@Entry
@Component
struct CrashLogPage {
  @State crashLogs: CrashLog[] = [];
  @State isLoading: boolean = true;
  @State filterType: string = 'ALL';
  @State filterLevel: string = 'ALL';

  aboutToAppear() {
    this.loadCrashLogs();
  }

  async loadCrashLogs() {
    this.isLoading = true;
    try {
      this.crashLogs = await CrashMonitor.getCrashLogs();
    } catch (error) {
      console.error('Failed to load crash logs');
    } finally {
      this.isLoading = false;
    }
  }

  getFilteredLogs(): CrashLog[] {
    return this.crashLogs.filter(log => {
      if (this.filterType !== 'ALL' && log.type !== this.filterType) {
        return false;
      }
      if (this.filterLevel !== 'ALL' && log.level !== this.filterLevel) {
        return false;
      }
      return true;
    });
  }

  getCrashTypeName(type: CrashType): string {
    const names: Record<string, string> = {
      'JS_ERROR': 'JS异常',
      'PROMISE_REJECTION': 'Promise拒绝',
      'ANR': '应用无响应',
      'MEMORY_WARNING': '内存警告',
      'BUSINESS_ERROR': '业务异常'
    };
    return names[type] || type;
  }

  build() {
    Column() {
      this.buildFilterBar()
      this.buildLogList()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F7FA')
  }

  @Builder
  buildFilterBar() {
    Row() {
      Select(this.getTypeOptions())
        .selected(0)
        .value('全部')
        .onSelect((index: number) => {
          const options = ['ALL', 'JS_ERROR', 'ANR', 'MEMORY_WARNING'];
          this.filterType = options[index];
        })
      // 级别筛选同理...
      Blank()
      Text(`${this.getFilteredLogs().length}`).fontSize(12)
    }
    .padding(16)
    .backgroundColor('#FFFFFF')
  }

  getTypeOptions(): SelectOptionData[] {
    return [
      new SelectOptionData('ALL', '全部'),
      new SelectOptionData('JS_ERROR', 'JS异常'),
      new SelectOptionData('ANR', '应用无响应'),
      new SelectOptionData('MEMORY_WARNING', '内存警告')
    ];
  }
}

八、集成使用指南

8.1 在 EntryAbility 中初始化

// EntryAbility.ets
import CrashMonitor from '../service/CrashMonitor';

onWindowStageCreate(windowStage: window.WindowStage): void {
  // 加载主页面
  windowStage.loadContent('pages/Index', (err) => {
    // 初始化崩溃监控
    CrashMonitor.init(context);
    CrashMonitor.start();
    
    // 监听崩溃事件
    CrashMonitor.on('memoryWarning', (percent: number) => {
      console.info(`Memory warning: ${percent}%`);
    });
  });
}

8.2 手动上报业务异常

// 在业务代码中上报异常
CrashMonitor.handleBusinessError('用户下单失败', {
  userId: '12345',
  orderId: 'ORD001',
  errorCode: 5001
});

// 上报 JS 错误
CrashMonitor.handleJSError('TypeError: Cannot read property', stackTrace);

// 记录卡顿事件
CrashMonitor.recordFreeze('pages/Index', 600);  // 600ms 卡顿

九、运行验证

9.1 编译构建

在项目根目录下执行以下命令进行编译构建:

./gradlew build

9.2 模拟器运行截图

性能监控页面运行效果:

在这里插入图片描述

截图说明:图中展示了实时内存使用率、CPU占用率和帧率监控数据,状态指示灯显示监控服务处于运行状态。

功能验证要点:
在这里插入图片描述

  1. 点击"触发测试"按钮可生成测试业务异常
  2. 性能指标每秒钟自动刷新
  3. 日志页面支持实时筛选和刷新
  4. 卡顿事件自动记录并分级显示

十、总结与展望

本文详细介绍了在 OpenHarmony 应用中实现崩溃监控系统的完整方案,包括:

  1. 模块化架构:通过 CrashMonitor、CrashReporter、CrashStorage 三个核心模块实现关注点分离
  2. 多维度监控:覆盖 JS 异常、Promise 拒绝、内存警告、卡顿检测和帧率监控
  3. 灵活上报机制:支持手动上报、自动上报和批量上报多种模式
  4. 友好的用户界面:提供实时性能监控面板和日志查看页面

该方案可根据实际需求进行扩展,例如增加网络状态监控、电池电量监控等功能。完整代码已托管至 AtomGit 仓库,欢迎开发者交流讨论。

感谢各位阅读!

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐