尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

HarmonyOS7 AOP 能干嘛?无侵入性能监控和日志埋点实战

HarmonyOS7 AOP 能干嘛?无侵入性能监控和日志埋点实战
📅 发布时间:2026/7/1 18:25:23

文章目录

    • 前言
    • ArkTS 里的 AOP:装饰器就是切面
    • 第一个切面:@LogExecution
    • @TrackTime:性能耗时追踪
    • @CatchError:统一异常捕获
    • 全链路追踪:组合装饰器
    • PerfMonitor 数据收集
    • 踩过的坑
    • 小结

前言

你有没有过这种体验:业务代码写得好好的,PM 说"加个性能监控",于是你在每个方法前后都塞上了Date.now()和console.info。代码瞬间变得又长又丑,业务逻辑和监控代码搅在一起,改起来特别痛苦。

这就是典型的横切关注点问题——日志、性能监控、错误捕获这些逻辑,跟业务本身没关系,但又散落在到处都是。AOP(面向切面编程)就是专门搞定这类事的。

ArkTS 里的 AOP:装饰器就是切面

在 Java 世界,AOP 靠的是 Spring AOP 或者 AspectJ,底层用动态代理或字节码织入。ArkTS 没这么复杂——装饰器天然就是 AOP 的实现方式。

方法装饰器可以在方法执行前后插入逻辑,完全不需要修改原始代码。这比 Java 那套优雅多了。

第一个切面:@LogExecution

先来个最简单的,自动记录方法调用和参数:

// decorators/LogExecution.etsfunctionLogExecution(target:Object,propertyKey:string,descriptor:PropertyDescriptor):PropertyDescriptor{constoriginalMethod=descriptor.value descriptor.value=function(...args:any[]){conststart=Date.now()console.info(`[Log] 调用${propertyKey},参数:${JSON.stringify(args)}`)try{constresult=originalMethod.apply(this,args)constelapsed=Date.now()-startconsole.info(`[Log]${propertyKey}完成,耗时${elapsed}ms`)returnresult}catch(e){console.error(`[Log]${propertyKey}异常:${e}`)throwe}}returndescriptor}

用法特别简单,往方法上一贴就行:

classUserService{@LogExecutionasyncfetchUser(userId:string):Promise<User>{constresponse=awaithttp.request(`/api/users/${userId}`)returnJSON.parse(response.resultasstring)}}

业务代码干干净净,日志逻辑全在装饰器里。想加就加,想摘就摘,零侵入。

@TrackTime:性能耗时追踪

做性能优化的时候,需要知道每个关键方法的实际耗时。手动埋点太累,用装饰器批量搞定:

// decorators/TrackTime.etsimport{PerfMonitor}from'../monitor/PerfMonitor'functionTrackTime(tag?:string){returnfunction(target:Object,propertyKey:string,descriptor:PropertyDescriptor):PropertyDescriptor{constoriginalMethod=descriptor.valueconstlabel=tag??propertyKey descriptor.value=function(...args:any[]){conststart=Date.now()PerfMonitor.getInstance().markStart(label)constfinalize=()=>{constelapsed=Date.now()-start PerfMonitor.getInstance().record(label,elapsed)if(elapsed>100){console.warn(`[Perf] 慢方法告警:${label}耗时${elapsed}ms`)}}try{constresult=originalMethod.apply(this,args)// 处理异步方法if(resultinstanceofPromise){returnresult.then((val)=>{finalize();returnval},(err)=>{finalize();throwerr})}finalize()returnresult}catch(e){finalize()throwe}}returndescriptor}}

这里有个细节——异步方法需要特殊处理。如果直接finalize(),拿到的是同步耗时,没有意义。判断返回值是不是 Promise,是的话就在.then()里再记录。

@CatchError:统一异常捕获

每个方法都写 try-catch 太烦了。装饰器统一管理:

// decorators/CatchError.etsfunctionCatchError(fallbackValue?:any){returnfunction(target:Object,propertyKey:string,descriptor:PropertyDescriptor):PropertyDescriptor{constoriginalMethod=descriptor.value descriptor.value=asyncfunction(...args:any[]){try{returnawaitoriginalMethod.apply(this,args)}catch(e){// 上报到错误监控平台ErrorReporter.getInstance().report({method:propertyKey,error:e,args,timestamp:Date.now()})// 返回降级值,避免页面崩溃if(fallbackValue!==undefined){console.warn(`[CatchError]${propertyKey}异常,返回降级值`)returnfallbackValue}throwe}}returndescriptor}}

有了这个装饰器,网络请求失败、数据解析出错这些场景都能优雅降级,不会直接白屏:

classProductRepository{@CatchError([])asyncgetProductList():Promise<Product[]>{constdata=awaithttp.request('/api/products')returnJSON.parse(data.resultasstring)}@CatchError(null)asyncgetProductDetail(id:string):Promise<Product|null>{constdata=awaithttp.request(`/api/products/${id}`)returnJSON.parse(data.resultasstring)}}

全链路追踪:组合装饰器

单个装饰器好用,但实际项目中经常需要多个切面叠加。来个全链路追踪的完整实现:

// monitor/TraceContext.etsexportclassTraceContext{privatestaticinstance:TraceContextprivatetraces:Map<string,TraceSpan[]>=newMap()privatecurrentTraceId:string=''staticgetInstance():TraceContext{if(!TraceContext.instance){TraceContext.instance=newTraceContext()}returnTraceContext.instance}beginTrace(traceId:string):void{this.currentTraceId=traceIdthis.traces.set(traceId,[])}addSpan(span:TraceSpan):void{constspans=this.traces.get(this.currentTraceId)if(spans){spans.push(span)}}endTrace():TraceReport{constspans=this.traces.get(this.currentTraceId)??[]consttotal=spans.reduce((sum,s)=>sum+s.duration,0)return{traceId:this.currentTraceId,spans,totalDuration:total,spanCount:spans.length}}}exportinterfaceTraceSpan{name:stringstartTime:numberduration:numberstatus:'success'|'error'}exportinterfaceTraceReport{traceId:stringspans:TraceSpan[]totalDuration:numberspanCount:number}

然后是串联多个装饰器的组合装饰器:

// decorators/Traced.etsfunctionTraced(spanName?:string){returnfunction(target:Object,propertyKey:string,descriptor:PropertyDescriptor):PropertyDescriptor{constoriginalMethod=descriptor.valueconstname=spanName??propertyKey descriptor.value=asyncfunction(...args:any[]){conststart=Date.now()letstatus:'success'|'error'='success'try{constresult=awaitoriginalMethod.apply(this,args)returnresult}catch(e){status='error'ErrorReporter.getInstance().report({method:name,error:e,args})throwe}finally{TraceContext.getInstance().addSpan({name,startTime:start,duration:Date.now()-start,status})}}returndescriptor}}

现在给一个完整的用户下单流程打上追踪:

classOrderService{@Traced('创建订单')asynccreateOrder(items:CartItem[]):Promise<Order>{constorder=awaitthis.buildOrder(items)awaitthis.validateStock(order)awaitthis.submitOrder(order)returnorder}@Traced('构建订单')privateasyncbuildOrder(items:CartItem[]):Promise<Order>{// ...}@Traced('校验库存')@CatchError(null)privateasyncvalidateStock(order:Order):Promise<boolean>{// ...}@Traced('提交订单')privateasyncsubmitOrder(order:Order):Promise<void>{// ...}}

调用createOrder后,就能拿到整条链路上每个步骤的耗时和状态。哪个环节慢了、哪个环节报错了,一目了然。

PerfMonitor 数据收集

前面用到的PerfMonitor简单实现一下:

// monitor/PerfMonitor.etsexportclassPerfMonitor{privatestaticinstance:PerfMonitorprivaterecords:Map<string,number[]>=newMap()staticgetInstance():PerfMonitor{if(!PerfMonitor.instance){PerfMonitor.instance=newPerfMonitor()}returnPerfMonitor.instance}markStart(label:string):void{// 可扩展:记录开始时间戳}record(label:string,duration:number):void{if(!this.records.has(label)){this.records.set(label,[])}this.records.get(label)!.push(duration)}getStats(label:string):{avg:number;max:number;count:number}{constdurations=this.records.get(label)??[]if(durations.length===0)return{avg:0,max:0,count:0}return{avg:durations.reduce((a,b)=>a+b,0)/durations.length,max:Math.max(...durations),count:durations.length}}dumpReport():string{constlines:string[]=['=== 性能报告 ===']this.records.forEach((durations,label)=>{constavg=(durations.reduce((a,b)=>a+b,0)/durations.length).toFixed(1)constmax=Math.max(...durations)lines.push(`${label}: 平均${avg}ms, 最大${max}ms, 调用${durations.length}次`)})returnlines.join('\n')}}

踩过的坑

装饰器顺序很重要。多个装饰器叠加时,执行顺序是从下往上(靠近方法的先执行)。@Traced要在@CatchError上面,这样错误先被捕获,追踪记录的 status 才准确。

异步方法的耗时统计。很多人忘了async方法的返回值是 Promise,直接同步记录拿到的不是真实耗时。一定要判断返回值类型,异步的在.then()/.finally()里记录。

生产环境记得关掉详细日志。开发阶段全量记录没问题,上线后只保留慢方法告警和错误上报,不然日志量会很大。

小结

AOP 在 ArkTS 里用装饰器实现,比传统 Java 方案轻量得多。核心思路就是:把横切逻辑(日志、监控、错误处理)从业务代码里抽出来,通过装饰器织入。

我自己在项目里的做法是,给所有网络请求方法和关键业务方法统一加上@Traced+@CatchError,一行代码搞定监控和容错。再也不用在业务代码里到处写 try-catch 和Date.now()了。代码干净了,监控也没落下。

相关新闻

  • 高效Zotero笔记管理:用Mdnotes插件将学术文献秒变Markdown
  • 2026衡水黄金回收白银回收铂金回收旧料回收怎么选?五家高实价铂金白银线下门店测评清单 + 联系方式
  • 重点!2026 Agent范式选型指南。

最新新闻

  • 基于LTC6903与STM32的数字控制振荡器设计与实现
  • 终极免费AI背景移除插件:OBS背景移除插件完整使用指南
  • openeuler/skills系统技能详解:RAG技术在智能代理中的应用实践
  • openeuler/skills场景技能实战:ag_skill与log-gpt插件开发教程
  • XSS纵深防御实战:从输入净化到CSP的五层安全架构
  • Storprototrace开发者手册:API接口设计与二次开发指南

日新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

周新闻

  • Windows字体自定义终极方案:No!! MeiryoUI完全指南
  • Deepin Boot Maker:告别命令行,3分钟制作Linux启动盘的智能解决方案
  • Plain Craft Launcher 2:重新定义你的Minecraft游戏体验

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号