M5(T-M5-06): 可观测——BridgeTracer 全链路 traceId + ErrorCenter→APM sink
- Message 加非协议 traceId 字段(不进 toJson/toArray,对 H5 透明) - BridgeController 在 OUTBOUND/INBOUND/HANDLER/RESPONSE/DISPATCH/RETURN/DROP 各阶段打点,一条桥消息往返可经 traceId 还原 - entry 新增 ErrorReporter.installErrorSink(),打通 ErrorCenter Report→APM/Crash 上报通道(结构化字段就位,接入真实 SDK 仅改 reportToApm) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9be1a23635
commit
b461b291f5
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 错误上报接入点(T-M5-06 可观测)。entry 层向 common 的 ErrorCenter 注入 sink,
|
||||
* 把 Report 策略的错误转发到 APM/Crash 平台。
|
||||
*
|
||||
* common 层不依赖 UI / 第三方上报 SDK,故由本文件在启动时 setSink;接入真实平台
|
||||
* (如华为 APM / Bugly)只改这里的 reportToApm(),对各能力 Provider 透明。
|
||||
*/
|
||||
import { ErrorCenter, ErrorStrategy } from 'common';
|
||||
import { AppError } from 'contracts';
|
||||
import { hilog } from '@kit.PerformanceAnalysisKit';
|
||||
|
||||
const DOMAIN = 0x9527;
|
||||
const TAG = 'ErrorReporter';
|
||||
|
||||
/** 转发到 APM/Crash 平台。当前为预留实现(结构化记录),接入 SDK 时在此替换。 */
|
||||
function reportToApm(error: AppError): void {
|
||||
// TODO(M6+): 接入华为 APM / Crash SDK,上报 { kind, code, message, cause }。
|
||||
// 现阶段:结构化打到 hilog,保证上报链路已贯通、字段就位。
|
||||
const code: string = error.code !== undefined ? String(error.code) : '-';
|
||||
hilog.error(DOMAIN, TAG, 'APM report | kind=%{public}s code=%{public}s msg=%{public}s',
|
||||
error.kind, code, error.message);
|
||||
}
|
||||
|
||||
/** 在应用启动时调用,安装错误 sink。 */
|
||||
export function installErrorSink(): void {
|
||||
ErrorCenter.setSink((error: AppError, strategy: ErrorStrategy): void => {
|
||||
if (strategy === ErrorStrategy.Report) {
|
||||
reportToApm(error);
|
||||
}
|
||||
// Notify/Block 的用户可见处置由 UI 层(容器/路由)按场景实现;
|
||||
// Silent 已由 ErrorCenter 记日志,此处不重复。
|
||||
});
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { window } from '@kit.ArkUI';
|
||||
import { EventBus } from 'common';
|
||||
import { WeChatApi } from 'feature_capabilities';
|
||||
import { AppEvents } from '../routes/AppRoutes';
|
||||
import { installErrorSink } from '../di/ErrorReporter';
|
||||
|
||||
const DOMAIN = 0x0000;
|
||||
|
||||
@@ -15,6 +16,8 @@ export default class EntryAbility extends UIAbility {
|
||||
hilog.error(DOMAIN, 'testTag', 'Failed to set colorMode. Cause: %{public}s', JSON.stringify(err));
|
||||
}
|
||||
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
|
||||
// 可观测(T-M5-06):安装错误上报 sink,打通 ErrorCenter → APM/Crash 通道
|
||||
installErrorSink();
|
||||
// 微信回调(冷启动经 want 拉回):交 SDK 解析 → 路由到 Share/Login 回调
|
||||
WeChatApi.getInstance().handleWant(want);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { IWebController } from '../web/IWebController';
|
||||
import { HandlerRegistry, BridgeHandler } from './HandlerRegistry';
|
||||
import { Message } from './Message';
|
||||
import { MessageCodec } from './MessageCodec';
|
||||
import { BridgeTracer } from 'common';
|
||||
|
||||
const YY_SCHEMA: string = 'yy://';
|
||||
const YY_RETURN: string = 'yy://return/';
|
||||
@@ -98,6 +99,8 @@ export class BridgeController {
|
||||
|
||||
private doSend(handlerName: string | undefined, data: string, cb?: ResponseCallback): void {
|
||||
const m = new Message();
|
||||
m.traceId = BridgeTracer.next();
|
||||
BridgeTracer.step(m.traceId, 'OUTBOUND', handlerName ?? '(default)');
|
||||
if (data.length > 0) {
|
||||
m.data = data; // 对齐 Android !TextUtils.isEmpty(data)
|
||||
}
|
||||
@@ -144,6 +147,7 @@ export class BridgeController {
|
||||
const responseId = m.responseId;
|
||||
if (responseId !== undefined && responseId.length > 0) {
|
||||
// H5 对"原生 callHandler"的回执
|
||||
BridgeTracer.step('return', 'RETURN', responseId);
|
||||
const fn = this.responseCallbacks.get(responseId);
|
||||
if (fn !== undefined) {
|
||||
fn(m.responseData ?? '');
|
||||
@@ -152,15 +156,21 @@ export class BridgeController {
|
||||
} else {
|
||||
// H5 主动 callHandler → 分发到能力层 handler
|
||||
const callbackId = m.callbackId;
|
||||
const traceId: string = BridgeTracer.next();
|
||||
const name = m.handlerName;
|
||||
BridgeTracer.step(traceId, 'INBOUND', name ?? '(default)');
|
||||
const respFn: ResponseCallback = (callbackId !== undefined && callbackId.length > 0)
|
||||
? (d: string) => {
|
||||
this.queue(Message.response(callbackId, d));
|
||||
BridgeTracer.step(traceId, 'RESPONSE', `cb=${callbackId}`);
|
||||
const rm = Message.response(callbackId, d);
|
||||
rm.traceId = traceId; // 回执下发沿用入站 traceId,串起一条往返
|
||||
this.queue(rm);
|
||||
}
|
||||
: (_d: string) => { };
|
||||
const name = m.handlerName;
|
||||
const handler: BridgeHandler = (name !== undefined && this.registry.has(name))
|
||||
? this.registry.get(name)!
|
||||
: this.registry.default();
|
||||
BridgeTracer.step(traceId, 'HANDLER', name ?? '(default)');
|
||||
handler(m.data ?? '', respFn);
|
||||
}
|
||||
}
|
||||
@@ -173,6 +183,7 @@ export class BridgeController {
|
||||
const fn = this.getFunctionFromReturnUrl(url);
|
||||
const data = this.getDataFromReturnUrl(url);
|
||||
if (fn !== undefined) {
|
||||
BridgeTracer.step('return', 'RETURN', fn);
|
||||
const cb = this.responseCallbacks.get(fn);
|
||||
if (cb !== undefined) {
|
||||
cb(data ?? '');
|
||||
@@ -192,8 +203,15 @@ export class BridgeController {
|
||||
private dispatch(m: Message): void {
|
||||
// 非激活:抑制出站(native→H5 不下发到非激活 Web)。激活时机由 WebSlotManager 控制顺序。
|
||||
if (!this.active) {
|
||||
if (m.traceId !== undefined) {
|
||||
BridgeTracer.step(m.traceId, 'DROP', 'inactive');
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (m.traceId !== undefined) {
|
||||
const label: string = m.handlerName ?? (m.responseId !== undefined ? 'response' : '(default)');
|
||||
BridgeTracer.step(m.traceId, 'DISPATCH', label);
|
||||
}
|
||||
// lzyzsd 二次转义(复刻 \ 与 ");再补转义单引号——lzyzsd 原版遗漏,而出站脚本用
|
||||
// 单引号包裹 _handleMessageFromNative('...'),data 含 ' 会提前闭合字符串。补转义后
|
||||
// H5 JSON.parse 得到的仍是原始 data,对 H5 完全透明(边界行为不变)。
|
||||
|
||||
@@ -9,6 +9,11 @@ export class Message {
|
||||
callbackId?: string;
|
||||
responseId?: string;
|
||||
responseData?: string;
|
||||
/**
|
||||
* 全链路追踪 ID(T-M5-06)。**非桥协议字段**:MessageCodec.toJson/toArray 只挑上面 5 个
|
||||
* 协议字段,traceId 既不下发给 H5、也不参与回传解析,对 H5 完全透明(不破坏零改动铁律)。
|
||||
*/
|
||||
traceId?: string;
|
||||
|
||||
/** 构造一条回执消息(对端 callbackId → responseId)。 */
|
||||
static response(responseId: string, responseData: string): Message {
|
||||
|
||||
Reference in New Issue
Block a user