日志: 远程只收「启动检测 + 报错」,info/notice/warn 一律只写本地
- MiliLog 新增 remote()(启动检测专用,仍上远程);info/notice/warn 改为只写本地 - 启动检测 4 处改用 remote(): APP-INIT / BOOT / STORAGE / JS-BRIDGE ready - error 语义不变(报错仍上远程);local* 不变(只写本地) - 调用点名称不变,其余 40+ 处只是不再上传 - AGENTS 第 5 条日志约定重写;版本号 1.6.3(46) → 1.6.4(47) - 验证: assembleRelease + 单测 127 例全过
This commit is contained in:
@@ -195,12 +195,13 @@ $BT/apksigner verify 目标名.apk
|
|||||||
|
|
||||||
### 版本号
|
### 版本号
|
||||||
|
|
||||||
- `app/build.gradle`:`versionName "1.6.3"` / `versionCode 46`
|
- `app/build.gradle`:`versionName "1.6.4"` / `versionCode 47`
|
||||||
(`1.5.5`/`37` 已发布;`1.5.6` = 更新源加 Gitee、`1.5.7` = 删除「歌曲源临时受限」弹窗、`1.5.8` = 精简远程日志、`1.5.9` = 按 HAR 对齐原版协议、`1.5.10` = 源列表只留 zk.ktv1.top:6677、`1.5.11`(命名越界,已改) / `1.6.0` = whoami 心跳/设备身份/点歌推送/带 query 的地址闸门 + 并入 selfhost-source 支线、
|
(`1.5.5`/`37` 已发布;`1.5.6` = 更新源加 Gitee、`1.5.7` = 删除「歌曲源临时受限」弹窗、`1.5.8` = 精简远程日志、`1.5.9` = 按 HAR 对齐原版协议、`1.5.10` = 源列表只留 zk.ktv1.top:6677、`1.5.11`(命名越界,已改) / `1.6.0` = whoami 心跳/设备身份/点歌推送/带 query 的地址闸门 + 并入 selfhost-source 支线、
|
||||||
`1.6.1`/`44` = App 接入自建源(自建源优先 / App 令牌 / 媒体允许清单 / 推送默认目标)、
|
`1.6.1`/`44` = App 接入自建源(自建源优先 / App 令牌 / 媒体允许清单 / 推送默认目标)、
|
||||||
`1.6.2`/`45` = **取址反转成"官方源优先、自建源兜底" + 只有官方源成功才上报**(含 6 条隐患修复与死代码清理)、
|
`1.6.2`/`45` = **取址反转成"官方源优先、自建源兜底" + 只有官方源成功才上报**(含 6 条隐患修复与死代码清理)、
|
||||||
`1.6.3`/`46` = **点歌延迟优化**(终态失败不退避 / 上游 i.php token 缓存 / 后台预热 / 分阶段耗时日志),
|
`1.6.3`/`46` = **点歌延迟优化**(终态失败不退避 / 上游 i.php token 缓存 / 后台预热 / 分阶段耗时日志),
|
||||||
以上 1.5.6~1.6.3 均尚未发布。18~21 曾被本机临时诊断包占用)
|
`1.6.4`/`47` = **远程日志只收「启动检测 + 报错」**(info/notice/warn 一律只写本地),
|
||||||
|
以上 1.5.6~1.6.4 均尚未发布。18~21 曾被本机临时诊断包占用)
|
||||||
- App 内显示统一用纯版本号(不加 `(versionCode)` 括号)
|
- App 内显示统一用纯版本号(不加 `(versionCode)` 括号)
|
||||||
- ⚠️ **本机 `/tmp` 是 tmpfs,单测必须绕开它**:`/tmp`(5.6G tmpfs)长期被"已删除但仍被打开"的
|
- ⚠️ **本机 `/tmp` 是 tmpfs,单测必须绕开它**:`/tmp`(5.6G tmpfs)长期被"已删除但仍被打开"的
|
||||||
文件占满,单测写临时文件会抛 `IOException: 超出磁盘配额`(EDQUOT),**看起来像测试失败**。
|
文件占满,单测写临时文件会抛 `IOException: 超出磁盘配额`(EDQUOT),**看起来像测试失败**。
|
||||||
@@ -516,8 +517,10 @@ $BT/apksigner verify 目标名.apk
|
|||||||
由 `MiliLog.PUBLIC_IP_URL` 查询,结果缓存 5 分钟、失败 1 分钟后重试,查询在独立后台线程,
|
由 `MiliLog.PUBLIC_IP_URL` 查询,结果缓存 5 分钟、失败 1 分钟后重试,查询在独立后台线程,
|
||||||
绝不在主线程发起),`clientip` = **客户端 IP**(设备当前网卡获取到的局域网 IP,由
|
绝不在主线程发起),`clientip` = **客户端 IP**(设备当前网卡获取到的局域网 IP,由
|
||||||
`MiliLog.updateDeviceContext(ip = ...)` 注入,网络变化时自动刷新并顺带复核公网 IP)。
|
`MiliLog.updateDeviceContext(ip = ...)` 注入,网络变化时自动刷新并顺带复核公网 IP)。
|
||||||
客户端为 `MiliLog`(有界队列 + 后台线程 + 失败重试),调用点 `MiliLog.info/notice/warn/error`
|
客户端为 `MiliLog`(有界队列 + 后台线程 + 失败重试)。⚠️ **级别语义见下面第 5 条**:
|
||||||
保持不变。启动上报(`reportDeviceBootInfo()`)只采集这 10 项:包名、版本号、安装时间、品牌、型号、
|
现在只有 `MiliLog.remote(...)`(启动检测)与 `MiliLog.error(...)`(报错)会上传,
|
||||||
|
`info/notice/warn` 已改为**只写本地**(调用点名可以不变,但不要再指望它们出现在远程服务上)。
|
||||||
|
启动上报(`reportDeviceBootInfo()`)只采集这 10 项:包名、版本号、安装时间、品牌、型号、
|
||||||
设备名、Android 版本、CPU 架构、内核版本、屏幕分辨率
|
设备名、Android 版本、CPU 架构、内核版本、屏幕分辨率
|
||||||
- **写日志的五条约定**(2026-09-26 依据现场日志复盘补充):
|
- **写日志的五条约定**(2026-09-26 依据现场日志复盘补充):
|
||||||
1. **持久状态只记一次**,不要每次事件都记。反例:桥不可用曾按「每次点歌一条 WARN」输出,
|
1. **持久状态只记一次**,不要每次事件都记。反例:桥不可用曾按「每次点歌一条 WARN」输出,
|
||||||
@@ -531,17 +534,15 @@ $BT/apksigner verify 目标名.apk
|
|||||||
4. **日志要能自我定位**:失败行写清"哪一步/哪个源/耗时/原因",例如
|
4. **日志要能自我定位**:失败行写清"哪一步/哪个源/耗时/原因",例如
|
||||||
`NATIVE-SONG 全部源失败 编号=... 详情=token-fail|no-url|demo-or-mismatch 用时=...ms`;
|
`NATIVE-SONG 全部源失败 编号=... 详情=token-fail|no-url|demo-or-mismatch 用时=...ms`;
|
||||||
桥侧由 `JS-BRIDGE ...` 说明 WebView 到底卡在哪一步(初始化失败/KtvApi 未导出/20s 超时)。
|
桥侧由 `JS-BRIDGE ...` 说明 WebView 到底卡在哪一步(初始化失败/KtvApi 未导出/20s 超时)。
|
||||||
5. **⚠️ 远程日志只留"每次事件一条"的汇总,细节走本地(2026-10 精简)**:
|
5. **⚠️⚠️ 远程日志只收「启动检测 + 报错」两类(2026-10-15 二次收紧,用户要求)**:
|
||||||
`MiliLog.local(msg)` / `localWarn` / `localError` **只写本地文件、不上报远程**。
|
`MiliLog` 的级别语义已定死为 ——
|
||||||
判定标准——**逐源 / 逐次重试 / 单次 HTTP 错误 / JS 控制台输出**一律用 `local*`;
|
- **上远程**:`MiliLog.remote(...)`(**仅启动检测**:`APP-INIT` / `BOOT` / `STORAGE` /
|
||||||
远程只保留"这件事的结论":
|
`JS-BRIDGE ready`)+ `MiliLog.error(...)`(**报错**);
|
||||||
- 取址:成功一条 `NATIVE-SONG 取址成功 编号=… 源=… urlHost=… 用时=…ms`,
|
- **只写本地**:`info` / `notice` / `warn` / `local` / `localWarn` / `localError`
|
||||||
失败一条 `NATIVE-SONG 全部源失败 编号=… 详情=… 用时=…ms`,
|
→ `<存储根>/logs/ktv.log`(纯文本,什么都不缺)。
|
||||||
全部只发广告一条 `SOURCE-BLOCKED 全部 N 个源只返回广告/测试资源…`
|
⚠️ **不要**把 `info`/`notice`/`warn` 改回上传;想让某条"警告类失败"也进远程,
|
||||||
→ **每次取址远程恰好一条**(改前是每个源一条,9 个源就是 9+ 条);
|
就把它**显式改成 `MiliLog.error(...)`**(一次改动、意图可见)。
|
||||||
- `JS …`(ktv_api.js 的 console 转发)**只进本地**,不再进远程;
|
现场排查一律先取本地日志文件;远程只回答"机器起来了没"与"有没有报错"。
|
||||||
JS 路径失败仍由远程的 `点歌失败 编号=… 请求源=… 详情=…` 一行说明原因。
|
|
||||||
⚠️ 新加日志时先问自己:"这条是'结论'还是'过程'?" —— 过程写本地。
|
|
||||||
6. **状态样本 + 事件**缺一不可:事件(点歌/失败)说明"发生了什么",但排查"随时间变化"的
|
6. **状态样本 + 事件**缺一不可:事件(点歌/失败)说明"发生了什么",但排查"随时间变化"的
|
||||||
问题(空间变少、曲库丢失、长期离线)需要状态样本 —— 现由 6 小时一条 `HEALTH ...`
|
问题(空间变少、曲库丢失、长期离线)需要状态样本 —— 现由 6 小时一条 `HEALTH ...`
|
||||||
(版本/在线/可用空间/曲库大小/根目录)提供。
|
(版本/在线/可用空间/曲库大小/根目录)提供。
|
||||||
|
|||||||
+2
-2
@@ -20,8 +20,8 @@ android {
|
|||||||
applicationId "com.local.ktv"
|
applicationId "com.local.ktv"
|
||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 25
|
targetSdk 25
|
||||||
versionCode 46
|
versionCode 47
|
||||||
versionName "1.6.3"
|
versionName "1.6.4"
|
||||||
ndk {
|
ndk {
|
||||||
// 双 ABI 打包:兼容 64 位(Exynos 1580 等 arm64-v8a)与 32 位设备(极米 Play 超越版
|
// 双 ABI 打包:兼容 64 位(Exynos 1580 等 arm64-v8a)与 32 位设备(极米 Play 超越版
|
||||||
// 的 Amlogic 950X2/S905X2 采用 32 位 Android 用户态,必须包含 armeabi-v7a,否则
|
// 的 Amlogic 950X2/S905X2 采用 32 位 Android 用户态,必须包含 armeabi-v7a,否则
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class KtvApplication : Application() {
|
|||||||
// init 现在可重复调用且会自动重启发送线程,这里是"最靠前"的启动点。
|
// init 现在可重复调用且会自动重启发送线程,这里是"最靠前"的启动点。
|
||||||
runCatching {
|
runCatching {
|
||||||
MiliLog.init(this)
|
MiliLog.init(this)
|
||||||
MiliLog.info("APP-INIT 进程启动 ver=${BuildConfig.VERSION_NAME} logFile=${MiliLog.localLogPath()}")
|
MiliLog.remote("APP-INIT 进程启动 ver=${BuildConfig.VERSION_NAME} logFile=${MiliLog.localLogPath()}")
|
||||||
}.onFailure { Log.e(TAG, "MiliLog.init failed", it) }
|
}.onFailure { Log.e(TAG, "MiliLog.init failed", it) }
|
||||||
OkDownload.setSingletonInstance(OkDownload.Builder(this).build())
|
OkDownload.setSingletonInstance(OkDownload.Builder(this).build())
|
||||||
Log.i(TAG, "KTV application initialized without native services")
|
Log.i(TAG, "KTV application initialized without native services")
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ class KtvJsBridge(context: Context) {
|
|||||||
unavailableReason = ""
|
unavailableReason = ""
|
||||||
}
|
}
|
||||||
Log.i(TAG, "JS runtime ready (${if (loadedFromCache) "cache" else "asset"})")
|
Log.i(TAG, "JS runtime ready (${if (loadedFromCache) "cache" else "asset"})")
|
||||||
MiliLog.info("JS-BRIDGE ready (${if (loadedFromCache) "cache" else "asset"})")
|
MiliLog.remote("JS-BRIDGE ready (${if (loadedFromCache) "cache" else "asset"})")
|
||||||
callback(true)
|
callback(true)
|
||||||
} else if (loadedFromCache && !assetRetryAttempted) {
|
} else if (loadedFromCache && !assetRetryAttempted) {
|
||||||
Log.w(TAG, "Cached JS failed to load; falling back to packaged asset")
|
Log.w(TAG, "Cached JS failed to load; falling back to packaged asset")
|
||||||
|
|||||||
@@ -3878,10 +3878,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
sb.append(" screen=").append(screenResolutionText())
|
sb.append(" screen=").append(screenResolutionText())
|
||||||
// 等主机 IP(公网出口 IP)就绪,避免启动这条日志的 serverip 为空。
|
// 等主机 IP(公网出口 IP)就绪,避免启动这条日志的 serverip 为空。
|
||||||
MiliLog.awaitServerIp()
|
MiliLog.awaitServerIp()
|
||||||
MiliLog.notice(sb.toString())
|
MiliLog.remote(sb.toString()) // BOOT 启动检测行
|
||||||
// 存储根目录探测结果(排查不同盒子存储差异用;单行便于按 STORAGE 检索)。
|
// 存储根目录探测结果(排查不同盒子存储差异用;单行便于按 STORAGE 检索)。
|
||||||
// 放在这里而不是启动主线程:既有完整上下文,又避免了主线程磁盘探测。
|
// 放在这里而不是启动主线程:既有完整上下文,又避免了主线程磁盘探测。
|
||||||
runCatching { MiliLog.notice("STORAGE ${AppPaths.storageFacts(this@MainActivity)}") }
|
runCatching { MiliLog.remote("STORAGE ${AppPaths.storageFacts(this@MainActivity)}") }
|
||||||
} catch (ignored: Exception) {
|
} catch (ignored: Exception) {
|
||||||
}
|
}
|
||||||
}, "ktv-boot-log").start()
|
}, "ktv-boot-log").start()
|
||||||
|
|||||||
@@ -153,11 +153,25 @@ object MiliLog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun info(msg: String) = enqueue("INFO", msg)
|
/**
|
||||||
fun notice(msg: String) = enqueue("INFO", msg)
|
* ⚠️ **远程日志策略(2026-10-15 收紧,用户要求)**:远程服务**只收两类** ——
|
||||||
fun warn(msg: String) = enqueue("WARN", msg)
|
* 1. **启动检测**:显式用 [remote](`APP-INIT` / `BOOT` / `STORAGE` / `JS-BRIDGE ready` 等一次性行);
|
||||||
|
* 2. **报错**:[error]。
|
||||||
|
*
|
||||||
|
* 其余一切(info / notice / warn、逐源逐次细节、JS 控制台输出、6 小时健康样本……)
|
||||||
|
* **只写本地** `<存储根>/logs/ktv.log`,不再上报远程。
|
||||||
|
*
|
||||||
|
* ⚠️ 不要顺手把 [info]/[notice]/[warn] 改回上传:远程只做"这台机器是否起来 + 有没有报错",
|
||||||
|
* 需要细节时让现场把本地日志文件发出来(那里面什么都不缺)。
|
||||||
|
*/
|
||||||
|
fun info(msg: String) = enqueueLocal("INFO", msg)
|
||||||
|
fun notice(msg: String) = enqueueLocal("INFO", msg)
|
||||||
|
fun warn(msg: String) = enqueueLocal("WARN", msg)
|
||||||
fun error(msg: String) = enqueue("ERROR", msg)
|
fun error(msg: String) = enqueue("ERROR", msg)
|
||||||
|
|
||||||
|
/** **启动检测**专用:这类一次性行仍上报远程(其余细节只写本地)。 */
|
||||||
|
fun remote(msg: String) = enqueue("INFO", msg)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* **只写本地**的详细日志(2026-10 新增)。
|
* **只写本地**的详细日志(2026-10 新增)。
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user