网络请求API监控与网络切换埋点

1.背景与目标

本文介绍 Android 工程中网络请求性能监控和网络切换监控的实现方式。

目标是:

  • 对一次 HTTP/HTTPS 请求从发起到结束的完整链路进行一次汇总上报;
  • 分阶段统计 DNS、网络连接、TLS、请求和响应的耗时;
  • 在默认网络可用或网络类型发生变化时上报一次网络切换事件;
  • 不让日志采集或埋点上报影响原始网络请求;
  • 避免记录请求体、响应体、Header 值和 URL query 参数等敏感或高基数内容。

2. 整体架构

text 复制代码
appInit
  └─ android_network_log_open
       ├─ GlobalApp.contextVar.networkLogOpen
       └─ NetworkConnectivityMonitor.update(enabled)

接口请求
  └─ ApiManager / DynamicUrlManager
       └─ OkHttp EventListener.Factory
            └─ NetworkEventListener
                 ├─ LogUtils 输出本地汇总日志
                 └─ NetworkLogReporter
                      └─ StatService
                           └─ SensorsStat.onEvent("networkLog", data)

系统默认网络变化
  └─ ConnectivityManager.NetworkCallback
       └─ NetworkConnectivityMonitor
            └─ NetworkLogReporter
                 └─ StatService
                      └─ SensorsStat.onEvent("networkChange", data)

3. 开关和初始化时机

初始化流程:

  1. Application.setUpService()StatService.instance 注入到 GlobalApp.contextVar.networkLogReporter
  2. 首次启动请求 discover/appInit
  3. DynamicWritingManger 缓存 switchConfigListList
  4. 读取 android_network_log_open,默认值为 false
  5. 将结果写入 GlobalApp.contextVar.networkLogOpen
  6. 调用 NetworkConnectivityMonitor.update(networkLogOpen)

开关行为

  • false:不采集接口性能回调,不注册网络切换回调;
  • true:注册系统默认网络回调,并对后续 OkHttp 请求开启采集;
  • NetworkEventListener 在每次 callStart() 时读取一次开关;已创建的 OkHttpClient 不需要重建;
  • 关闭开关时会注销 ConnectivityManager.NetworkCallback 并清空监控状态;
  • appInit 请求发起时开关通常尚未下发,因此该请求通常不会被这套接口性能日志记录;
  • MainViewModel 的缓存回退结果也会进入成功处理流程,因此缓存中的开关配置仍可能生效。

4. 接口监控埋点

4.1 接入位置

OkHttpClient中接入

java 复制代码
.eventListenerFactory(NetworkEventListener.factory())

4.2 一次请求只上报一次

NetworkEventListener是按 OkHttpCall` 创建的。每个请求实例维护自己的时间戳和采集字段。

请求开始时:

text 复制代码
callStart()
  ├─ 读取 networkLogOpen
  ├─ 记录 callStartNanos
  └─ 保存 method 和 URL

请求结束时:

  • callEnd():以 outcome=success 汇总;
  • callFailed():以 outcome=failed 汇总;
  • logged 标记保证同一个请求不会重复汇总上报。

4.3 请求阶段和回调含义

典型 HTTPS 请求顺序如下:

java 复制代码
callStart
  ↓
dnsStart / dnsEnd
  ↓
connectStart / connectEnd
  ↓
secureConnectStart / secureConnectEnd
  ↓
connectionAcquired
  ↓
requestHeadersStart / requestHeadersEnd
  ↓
requestBodyStart / requestBodyEnd
  ↓
responseHeadersStart / responseHeadersEnd
  ↓
responseBodyStart / responseBodyEnd
  ↓
callEnd 或 callFailed
阶段 OkHttp 回调 统计内容
请求开始 callStart 总耗时起点、请求方法、URL、开关状态
DNS 开始 dnsStart DNS 开始时间、解析域名
DNS 结束 dnsEnd DNS 耗时、DNS 成功状态;当前实现不保存 DNS 返回的全部 IP
TCP 连接开始 connectStart TCP 建连开始时间、目标 IP
TCP 连接结束 connectEnd 建立连接耗时、协商后的 HTTP 协议
TLS 开始 secureConnectStart HTTPS TLS 握手开始时间
TLS 结束 secureConnectEnd TLS 握手耗时
获取连接 connectionAcquired 是否复用连接池连接、补充远端 IP
请求头 requestHeadersStart / requestHeadersEnd 发送请求头耗时
请求体 requestBodyStart / requestBodyEnd 发送请求体耗时、请求体字节数
响应头 responseHeadersStart / responseHeadersEnd 接收响应头耗时、HTTP 状态码
响应体 responseBodyStart / responseBodyEnd 接收响应体耗时、响应体字节数
请求结束 callEnd 总耗时、传输层成功
请求失败 callFailed 总耗时、最终 IO 失败原因

4.4 接口埋点字段

事件名:networkLog

字段 含义
event_type 固定为 request
outcome successfailed,表示传输层结果
method HTTP 方法,如 GETPOST
url 仅保留 scheme、host、port 和 path,不包含 query
code HTTP 状态码;未收到响应时为 -1
protocol 实际协议,如 http/1.1h2
reused 是否复用已有连接
dns_domain 执行 DNS 解析的域名;连接复用时为空
dns_status successfailednot_startedunknown
dns_error DNS 失败异常;正常或未执行时为空
connected_ip Socket 实际连接 IP,不是 DNS 返回的全部 IP;无法获取时为空
network_type 请求结束时默认网络类型,如 wificellularvpn
dns_ms DNS 解析耗时,单位毫秒
connect_ms 建立网络连接耗时,不包含 DNS;TLS 单独统计
tls_ms HTTPS TLS 握手耗时;非 HTTPS 或未发生时为 -1
request_headers_ms 发送请求头耗时
request_body_ms 发送请求体耗时
response_headers_ms 接收响应头耗时
response_body_ms 接收响应体耗时
total_ms callStart 到结束或失败的总耗时
request_bytes 已发送请求体字节数,不含请求头
response_bytes 已接收响应体字节数,不含响应头
failure 最终失败异常;成功时为空

没有发生或无法采集的阶段统一使用 -1,便于区分"耗时为 0"和"该阶段未执行"。

4.5 示例:书城接口请求

以下示例表示一次使用 Wi-Fi 的 HTTPS 请求:

java 复制代码
{
  "event_type": "request",
  "outcome": "success",
  "method": "POST",
  "url": "https://api.example.com/book/list",
  "code": 200,
  "protocol": "h2",
  "reused": false,
  "dns_domain": "api.example.com",
  "dns_status": "success",
  "dns_error": "",
  "connected_ip": "203.0.113.10",
  "network_type": "wifi",
  "dns_ms": 12,
  "connect_ms": 34,
  "tls_ms": 48,
  "request_headers_ms": 1,
  "request_body_ms": 5,
  "response_headers_ms": 86,
  "response_body_ms": 23,
  "total_ms": 214,
  "request_bytes": 128,
  "response_bytes": 4096,
  "failure": ""
}

说明:connect_ms 按 OkHttp 的 TCP 建连阶段统计,不包含 DNS;HTTPS 的 TLS 握手单独进入 tls_msconnected_ip 表示最终 Socket 实际连接的 IP,当前实现没有上报 DNS 返回的全部候选 IP。

5. 网络切换埋点

5.1 实现方式

NetworkConnectivityMonitor 使用 Android 系统的:

java 复制代码
ConnectivityManager.registerDefaultNetworkCallback()

监听当前系统默认网络,而不是监听某个具体接口。

5.2 回调处理规则

系统回调 当前处理
onAvailable 获取网络能力,识别网络类型并判断是否需要上报
onCapabilitiesChanged 更新网络能力;默认网络对象或网络类型变化时上报
onLost 如果丢失的是当前默认网络,则上报网络不可用;如果已有新默认网络,则按网络可用切换处理

当前网络类型包括:

java 复制代码
vpn、wifi、cellular、ethernet、bluetooth、wifi_aware、lowpan、other、none

5.3 网络切换字段

事件名:networkChange

字段 含义
event_type 固定为 network_change
network_status availablelost
network_type 当前目标网络类型
from_network_type 切换前网络类型
to_network_type 切换后网络类型
from_network_validated 切换前是否通过系统网络验证
network_validated 切换后是否通过系统网络验证
event_time_ms Unix 时间毫秒值
event_elapsed_realtime_ms Android 单调时钟毫秒值

5.4 示例:Wi-Fi 切换到移动网络

java 复制代码
{
  "event_type": "network_change",
  "network_status": "available",
  "network_type": "cellular",
  "from_network_type": "wifi",
  "to_network_type": "cellular",
  "from_network_validated": true,
  "network_validated": true,
  "event_time_ms": 1778822400000,
  "event_elapsed_realtime_ms": 23891234
}

6. 关键边界行为

6.1 连接复用

如果 OkHttp 复用连接池中的连接,通常不会重新执行 DNS、TCP 建连和 TLS 握手:

  • reused=true
  • dns_status=not_started
  • dns_msconnect_mstls_ms 可能为 -1
  • connected_ip 会尝试从已有连接补充。

6.2 DNS 解析 IP 的口径

dnsEnd() 回调参数中包含 DNS 返回的 inetAddressList,但当前实现没有保存该列表。埋点中的 connected_ip 来自 connectStart() 的实际连接地址,连接复用时则从已有 Socket 地址补充。因此当前埋点可以回答"实际连到了哪个 IP",不能回答"DNS 一共解析出了哪些 IP"。

6.3 HTTP 错误码

HTTP 404、500 等属于"拿到了 HTTP 响应",因此传输层通常进入 callEnd(),表现为:

text 复制代码
outcome=success
code=404 或 500

outcome 不是业务接口成功标记,而是网络传输是否完整结束的标记。

6.4 DNS 失败

OkHttp 没有单独的 DNS 失败回调。当前实现通过以下条件判断 DNS 失败:

text 复制代码
dnsStart 已触发 && dnsEnd 未触发 && callFailed 触发

此时:

text 复制代码
dns_status=failed
dns_error=异常类型:异常消息

6.5 网络验证状态变化

当前 NetworkConnectivityMonitor 的上报判断主要基于默认网络对象和网络类型:

java 复制代码
shouldReport = network changed || networkType changed

因此,如果只有 network_validatedfalse 变为 true,网络对象和网络类型都不变,当前实现不会单独上报一次切换事件。

6.6 采集失败不影响业务

接口日志输出、传感器上报和网络切换上报均有异常保护。即使日志字段构造或统计 SDK 调用失败,也不会阻断原始网络请求或改变业务流程。

相关推荐
美狐美颜sdk4 小时前
直播APP开发技术栈详解:视频美颜SDK、人脸识别与实时渲染
android·人工智能·音视频·美颜sdk·直播美颜sdk
当下新鲜事7 小时前
车间实测记录:三菱电机MR-J5伺服与压力控制方案的场景适配
网络·物联网·业界资讯
门思科技8 小时前
开源网关有哪些:主流类型梳理
网络·python·物联网
llilian_169 小时前
北斗授时卡同步解决方案 gnss授时卡 计算机时间同步板卡
大数据·网络·人工智能·功能测试·51单片机
Vicky_time9 小时前
跨境物流系统架构:美国海外仓与FBA中转海外仓选型技术方案评测
网络·人工智能
奈斯先生Vector10 小时前
当模型版本不断变化,RelayRouter 能否帮助 AI 应用摆脱深度绑定
android·java·人工智能·开源·aigc
TimeFine10 小时前
让强模型做“总工”,让高性价比模型写代码
android
多多鼠11 小时前
System Prompt 的“版本漂移”问题:从变更管理到 A/B 测试体系
开发语言·网络·人工智能·python·langchain
又见情义11 小时前
RK3568 Android 13 屏蔽 healthd 电池日志经验分享
android