JetBrains 在 Igor Kulakov 撰写的文章中,介绍了如何在 IntelliJ IDEA 2026.2 中使用 Logpoints,在运行期间诊断 Java 应用程序错误。其理念类似于向程序中添加 println 语句,但无需修改代码、重新构建应用程序或重新部署应用程序,并且不会暂停执行。因此,这种方法适合检查在本地、Docker 容器内或远程主机上运行的服务。
示例所解决的问题
示例基于通过 gRPC 通信的客户端和服务器。服务器会为某些租户返回错误的折扣值。当发送针对 JetBrains 租户和 EMEA 区域的请求时,控制台中会显示价格值为 100.00 美元且 discount_bps=0,而预期值应为 80.00 美元且 discount_bps=2000。
服务器通过 Docker 启动,并公开监听端口和调试端口,随后客户端会定期发送请求。服务器和客户端启动后,开发者可以将 IntelliJ IDEA 调试器附加到该进程,即使该进程并非从本地调试会话启动。根据文章说明,在本地环境、独立环境和远程主机上,调试器都通过套接字进行通信,因此 Java 进程运行位置的变化不会改变附加调试器的基本原理。
Logpoints 如何揭示错误原因?
在 IntelliJ IDEA 2026.2 中,可以通过在编辑器左侧空白区域、两个可执行代码行之间单击,以更快地创建 Logpoint,然后输入要记录的表达式。开发者可以先记录请求处理开始时的信息,再随着请求持续到达逐步添加或修改 Logpoint。
跟踪调用链会指向 discountBpsFor() 函数。输出显示,租户名称以 JetBrains 的形式传入,而比较逻辑预期的值是 jetbrains。输出还表明折扣没有被应用,这意味着返回 2,000 个基点的代码分支没有执行。示例建议使用 equalsIgnoreCase 修复比较逻辑,然后测试预期行为。
其价值并不局限于在控制台中显示文本;单击输出中的某一行时,IntelliJ IDEA 可以跳转到对应的 Logpoint 或相关代码部分。如果进程在 IntelliJ IDEA 调试器下运行,这种导航同样适用于 println 语句。
它何时优于 println 和断点?
文章指出,Logpoints 不会污染代码,也不会留下可能被误发送到生产环境的调试语句。此外,还可以更改记录的内容和记录时机,包括对重复事件进行采样、在依赖项内部添加日志,以及避免成本高昂的重新部署操作。
而传统断点在停止服务本身就是问题的一部分时,可能并不适用。在示例中,客户端为 gRPC 请求设置了超时时间,该超时时间可以传递到服务器。当调试器暂停服务器时,请求会超时并进入取消路径,因此开发者将无法再看到导致错误的状态。此时使用断点需要逐个发送请求,并在超时窗口内完成操作。
相比之下,Logpoints 可以在不暂停服务器的情况下提供信息,从而能够在请求执行期间进行监控,并避免因超时而触发取消路径。
在运行期间临时修改行为
文章还使用 Logpoints 临时测试修复或修改程序行为的影响,同时提醒读者,Logpoints 的主要设计目的仍然是记录,而不是改变应用程序。通过使用具有副作用的表达式,可以在运行期间修改 gRPC 库中的超时值。示例展示了将 timeoutNanos 修改为五分钟的超时时间。
为了减少这一修改的影响,可以将其限制为携带 Debug 标头的请求,同时为其他请求保留正常超时时间。示例包含多行逻辑,用于读取标头,并且仅在其值匹配时重置超时时间,然后显示 Timeout reset 消息,以确认该分支已被访问。
需要注意什么?
JetBrains 警告不要在热点路径中放置计算量很大的操作,因为日志表达式会在虚拟机本身内部执行,并非没有时间成本。文章提到,IntelliJ IDEA 2026.2 会通过 instrumentation 消除调试器带来的开销,但这并不会消除表达式或高密度记录所产生的成本。
文章还指出,可以使用 Mark Object 功能,从 Logpoint 表达式字段访问任意对象;同时还可以使用随附的 ij-debugger 人工智能代理技能,该技能能够确定读取超时时间的位置,并创建针对测试请求的表达式。不过,这一路径仍然是手动理解的替代方案,而不能证明临时修改在所有环境中都是安全的。
certi.news 的编辑解读
这里的实际价值并不在于增加一种新的日志消息类型,而在于将诊断转移到一个可以在运行期间修改的层面。这对于远程服务、存在竞态条件的服务或具有时间限制的服务尤其重要,因为暂停执行可能会掩盖问题本身。另一方面,这种方法要求严格控制表达式的选择并监控其成本;使用副作用修改行为也应仅限于明确且临时的调试场景,而不应演变为源代码修复的替代方案。