tts-server-android 插件脚本 Console 日志输出指南:新版 console 与旧版 logger API 全解析
tts-server-android 插件脚本 Console 日志输出指南:新版 console 与旧版 logger API 全解析
导读
tts-server-android 为插件脚本、语音规则脚本等 JS 运行时内置了一套完整的日志输出体系,即本文要讲解的 Console(控制台)运行时 API。它同时提供新版 console.log/debug/info/warn/error/trace 与旧版 logger.t/d/i/w/e 两套调用方式,并支持 %s 占位符格式化、日志级别着色、调用栈输出以及 UI 悬浮日志窗口实时展示。读完本文,你将掌握在插件与规则脚本中正确输出分级日志、格式化调试信息、查看调用栈以及理解日志从脚本到界面的完整链路。
本文基于官方帮助文档 console.md,并结合 lib-script 模块源码、应用层 UI 与测试用例展开深度解读。
一、Console API 概览:两套调用方式并存
官方帮助文档 console.md 给出的核心示例完整覆盖了两代 API:
// ---- 新版 console API ----
console.log("format: %s", "str")
console.debug("format: %s", 123)
console.info("123", "223", 323)
console.warn("warn", 123)
console.error("error", 200)
console.trace("trace")
// ---- 旧版 logger API ----
logger.t("t")
logger.d("d")
logger.i("i")
logger.w("w")
logger.e("e")
新版 console.*
| 方法 | 语义 | 对应日志级别 |
|---|---|---|
console.log(...) |
常规日志 | INFO |
console.debug(...) |
调试日志 | DEBUG |
console.info(...) |
信息日志 | INFO |
console.warn(...) |
警告日志 | WARN |
console.error(...) |
错误日志 | ERROR |
console.trace(...) |
跟踪日志(附带调用栈) | TRACE |
旧版 logger.*
旧版 API 以 logger 全局对象形式暴露,方法命名取自日志级别首字母:
| 方法 | 对应日志级别 | 说明 |
|---|---|---|
logger.t(msg) |
TRACE | 与 console.trace 同级 |
logger.d(msg) |
DEBUG | |
logger.i(msg) |
INFO | |
logger.w(msg) |
WARN | |
logger.e(msg) |
ERROR | |
logger.log(msg) |
INFO | 在源码中同样被注册为 INFO 级别 |
提示:新版
console是当前推荐写法,旧版logger为兼容历史脚本保留,两者底层共用同一条日志通道,不会造成重复或冲突。
二、参数格式化:%s 占位符与多参数输出
从示例可见,Console API 支持 printf 风格格式化与多参数顺序输出两种模式:
console.log("format: %s", "str") // 输出:format: str(%s 占位符被替换)
console.debug("format: %s", 123) // 数字 123 同样可被 %s 格式化
console.info("123", "223", 323) // 多个参数按顺序拼接到一条日志
console.warn("warn", 123) // 字符串与数字混合
console.error("error", 200)
其格式化能力来自 Rhino 引擎的 NativeConsole.format(cx, scope, args):源码 ConsoleUtils.kt 中 writeFormat 先将全部实参交给 NativeConsole.format 完成占位符替换与类型转换,再写入日志通道。因此:
- 第一个参数中的
%s等占位符会被后续实参依次替换; - 没有占位符时,多个实参会被自然拼接输出;
- 格式化结果统一为字符串后进入日志系统,保证中文与混合类型内容的输出稳定。
实际项目中的典型用法,例如在插件请求 TTS 前输出请求 URL:
console.debug("请求参数:url=%s, text=%s", url, text)
console.info("开始合成:%s 字", text.length)
console.warn("备用配置触发,重试第 %d 次", retryCount)
console.error("请求失败:%s", errorMessage)
三、日志级别体系:与 Android Log 级别一一映射
日志级别并非孤立定义,而是直接对齐 Android 系统日志级别。源码 LogLevel.kt 中:
| 脚本级别 | 底层常量 | Android Log 对应值 |
|---|---|---|
| TRACE | LogLevel.TRACE |
Log.VERBOSE |
| DEBUG | LogLevel.DEBUG |
Log.DEBUG |
| INFO | LogLevel.INFO |
Log.INFO |
| WARN | LogLevel.WARN |
Log.WARN |
| ERROR | LogLevel.ERROR |
Log.ERROR |
该映射关系由 ConsoleUtils.kt 的 toLogLevel 完成:Rhino 的 NativeConsole.Level.TRACE/DEBUG/INFO/WARN/ERROR 被逐一转换为上述内部常量,再进入 Console.write(level, str)。
在 UI 展示层面,同一文件 LogLevel.kt 的 toArgb 为不同级别分配了辨识色(如 DEBUG 蓝色、INFO 绿色、WARN 橙色、ERROR 红色,并区分深色/浅色主题),悬浮日志窗口据此对每条日志着色显示。
四、源码级链路:从 console.log 到日志界面
4.1 运行时初始化:console 与 logger 的注册
脚本运行时 RhinoScriptRuntime.kt 在 init() 中完成两件事:
- 调用
NativeConsole.init(globalScope, false, printer)初始化标准console对象,并将打印回调转发给console.writeFormat(...),其中stack(调用栈信息)会随 TRACE 级别一并传入; - 调用
initLogger()(L53-L63),通过putLogger在logger对象上注册log/t/d/i/w/e六个方法,并在全局作用域额外注册了println(INFO 级别)。
也就是说,脚本里出现的 console 与 logger 均由运行时通过 defineProperty 以 READONLY 属性注入全局作用域,脚本侧无法覆盖或删除它们。
4.2 日志写入与广播:Console.write
所有级别最终汇入 Console.kt 的 write(level, str):
override fun write(@LogLevel level: Int, str: String) {
logger.info { str } // 同步写入 KotlinLogging 日志(logback)
listeners.forEach { // 广播给所有已注册监听器
it.onNewLog(LogEntry(level, str))
}
}
- 每一条日志会被双写:既进入应用日志系统(logback,配置见 app/src/main/assets/logback.xml),又通过
LogListenerManager广播给 UI 监听器; - 日志数据统一封装为 LogEntry(含
level、message、time、wrapLine字段),并实现Parcelable,可在进程/组件间传递。
4.3 TRACE 级别的调用栈
console.trace("trace") 的特殊之处在于:当级别为 TRACE 时,writeFormat 会将 Rhino 捕获的调用栈 stack 附加到消息末尾(str + "\n" + stack.contentToString())。因此 console.trace 不仅能打印文本,还能在定位脚本调用链问题时直接输出 JS 栈帧信息。
4.4 从脚本到悬浮窗:应用层的消费方
- 应用全局维护了一个 UI 控制台实例:
JsConsoleManager.ui(见 JsConsoleManager.kt); - 调试时可调用 LoggerFloatingManager.show(context, logListenerManager) 以悬浮窗形式展示日志,悬浮界面 LoggerFloatingScreen.kt 监听
LogListenerManager的onNewLog回调,逐条渲染带级别颜色的日志。
由此构成完整闭环:脚本 console.xxx() → NativeConsole.format 格式化 → Console.write → logback 落盘 + 监听器广播 → 悬浮日志窗口/调试界面实时展示。
五、测试验证:官方用例与自动化测试一致
lib-script 模块的仪器化测试资产中提供了与帮助文档逐行对应的脚本 console.js:
console.log("format: %s", "str")
console.debug("format: %s", 123)
console.info("123", "223", 323)
console.warn("warn", 123)
console.error("error", 200)
console.trace("trace")
logger.t("t")
logger.d("d")
logger.i("i")
logger.w("w")
logger.e("e")
该文件由 ScriptTest.kt 等测试驱动执行,用于验证运行时环境下日志 API 的正常调用与输出,可作为脚本编写者验证环境的参考。也就是说,文档中的每一行示例都在仓库测试资产中有对应实现,属于可运行、可验证的真实 API。
六、实际应用场景与最佳实践
在 tts-server-android 的插件脚本(如 plugin-azure.js)或语音规则脚本中,推荐这样组织日志:
// 1) 关键路径用 info,便于观察流程
console.info("开始执行插件:%s", pluginName)
// 2) 变量与中间结果用 debug,仅在需要排查时关注
console.debug("HTTP 响应状态码:%d, 耗时:%dms", status, cost)
// 3) 异常与失败用 error/warn,附加可读信息
console.warn("音频为空,启用备用发音人")
console.error("请求失败:%s", e.message)
// 4) 深挖调用链时用 trace
console.trace("进入回调函数")
实践建议:
- 按需分级:常规流程用
info,调试细节用debug,可恢复的异常用warn,致命错误用error,需要调用栈时用trace,避免全部日志都用log/info造成噪音; - 善用格式化:用
%s/%d占位符拼接变量,避免字符串拼接导致的类型问题与可读性下降; - 新旧 API 共存:维护旧脚本可直接沿用
logger.*,新脚本优先console.*,二者输出通道一致; - 结合悬浮窗调试:调试插件时通过悬浮日志窗口实时观察分级着色输出,可快速定位脚本执行到哪一步、在哪一步出错。
七、小结
tts-server-android 的 Console 运行时是一套与引擎深度集成的日志基础设施:console.* 与 logger.* 双 API 覆盖全部日志级别,%s 占位符格式化开箱即用,TRACE 级调用栈、Android Log 级别映射、logback 双写与 UI 悬浮窗广播构成了从脚本到界面的完整日志链路。无论是开发插件还是调试语音规则,掌握这套日志 API 都能显著提升脚本的调试效率与可维护性。更多运行时能力可继续阅读官方帮助文档 运行时环境 README 中的 文件 File、HTTP 客户端 与 Websocket 客户端 等章节。