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:
lanterngamescn
2026-06-26 07:09:14 +08:00
co-authored by Claude Opus 4.8
parent 9be1a23635
commit b461b291f5
4 changed files with 61 additions and 2 deletions
@@ -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 {