Arthas logger 命令完全指南:查看 logger 信息与动态更新日志级别
【免费下载链接】arthasAlibaba Java Diagnostic Tool Arthas/Alibaba Java诊断利器Arthas项目地址: https://gitcode.com/gh_mirrors/ar/arthas
logger是 Arthas(Alibaba Java Diagnostic Tool)内置的核心诊断命令之一,用于实时查看目标 JVM 中已加载的日志框架(Log4j、Logback、Log4j2)的 logger 配置信息,并支持在不重启应用的前提下动态调整日志级别。本指南以官方文档 logger.md 为骨架,结合 core 模块中 LoggerCommand.java 及其配套 Helper 类的源码实现,完整讲解该命令的每个参数、典型输出与底层原理,帮助你在生产环境排障时快速定位"日志为什么没打出来""如何临时打开 DEBUG"等实际问题。
命令能力概览
logger命令一句话总结:查看 logger 信息,更新 logger level。它由 LoggerCommand.java 通过注解声明,注册在 BuiltinCommandPack.java 的内置命令列表中,支持以下四类操作:
- 查看所有 logger 信息(默认只打印带有 appender 的 logger)
- 查看指定名字的 logger 信息(
-n) - 查看指定 ClassLoader 下的 logger 信息(
-c/--classLoaderClass) - 更新指定 logger 的 level(
--name+--level)
从源码可以看到,命令支持的框架自动探测基于三个已知类名:org.apache.log4j.Logger、ch.qos.logback.classic.Logger、org.apache.logging.log4j.Logger,因此它对 Log4j 1.x、Logback、Log4j 2.x 三大家族均可生效(见 LoggerCommand.java)。
参数速查表
根据 LoggerCommand.java 中的注解定义,logger命令的全部参数如下:
| 参数 | 长参数 | 说明 | 默认值 |
|---|---|---|---|
-n | --name | 指定 logger 名称,如org.springframework.web | 空(查看全部) |
-c | --classloader | 指定 ClassLoader 的 hashcode | SystemClassLoader |
| 无 | --classLoaderClass | 用 ClassLoader 的类名定位(需唯一实例) | 无 |
-l | --level | 设置 logger 级别,如debug、info | 无 |
| 无 | --include-no-appender | 同时打印没有 appender 的 logger | false |
其中--level选项单独使用时无意义,必须与--name组合才会触发"更新级别"分支:process方法中,只有name != null && level != null时才走level(process)更新逻辑,否则一律走loggers(process)查看逻辑(见 LoggerCommand.java)。
查看所有 logger 信息
准备一份 logback.xml
以下面的logback.xml为例(包含滚动文件 appender、异步 appender 和控制台 appender 三种典型形态):
<?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="APPLICATION" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>app.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern> <maxFileSize>100MB</maxFileSize> <maxHistory>60</maxHistory> <totalSizeCap>2GB</totalSizeCap> </rollingPolicy> <encoder> <pattern>%logger{35} - %msg%n</pattern> </encoder> </appender> <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"> <appender-ref ref="APPLICATION" /> </appender> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n </pattern> <charset>utf8</charset> </encoder> </appender> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="ASYNC" /> </root> </configuration>执行命令与输出解读
在 Arthas 控制台直接输入logger:
[arthas@2062]$ logger name ROOT class ch.qos.logback.classic.Logger classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 level INFO effectiveLevel INFO additivity true codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar appenders name CONSOLE class ch.qos.logback.core.ConsoleAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 target System.out name APPLICATION class ch.qos.logback.core.rolling.RollingFileAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 file app.log name ASYNC class ch.qos.logback.classic.AsyncAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 appenderRef [APPLICATION]从appenders信息中可以确认三件事:
CONSOLEappender 的target是System.out(控制台输出)APPLICATION是RollingFileAppender,其file为app.log(滚动文件输出)ASYNC的appenderRef是APPLICATION,即异步包装后输出到文件
输出字段与 LoggerHelper.java 中定义的常量一一对应:name、class、classLoader、classLoaderHash、codeSource、level、effectiveLevel、additivity、appenders(appender 级还有file、blocking、appenderRef、target)。字段填充逻辑见 LogbackHelper.java:level取 logger 自身配置的级别,effectiveLevel取向上继承后的有效级别,两者不一致说明该 logger 自身未显式配置 level;codeSource来自类所在的 jar 包路径,可用来确认应用实际加载的日志框架版本。
无 appender 过滤规则
getLoggers的实现中,遍历loggerContext.getLoggerList()时,默认会过滤掉没有 appender 的 logger(见 LogbackHelper.java)。这正是"直接执行logger输出不会很长"的原因。
查看指定名字的 logger
当需要聚焦某个具体 logger(例如排查 Spring Web 层的日志级别)时,使用-n指定名称:
[arthas@2062]$ logger -n org.springframework.web name org.springframework.web class ch.qos.logback.classic.Logger classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 level null effectiveLevel INFO additivity true codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar注意此处level为null、effectiveLevel为INFO:说明org.springframework.web这个 logger 自身没有显式配置级别,实际生效的INFO是从 ROOT 继承来的。这一组字段能直接解释"为什么我改了配置但日志级别没变"——很可能改动落在了一个未显式配置的 logger 上。
从源码看,-n传入的name会透传给各 Helper 的getLoggers(name, includeNoAppender):Logback 通过loggerContext.exists(name)精确查找(见 LogbackHelper.java),Log4j2 通过configuration.getLoggerConfig(name)查找,且对非 ROOT 名称会排除"查不到时回落到 ROOT"的误匹配(见 Log4j2Helper.java)。
指定 ClassLoader 查看 logger
使用 -c 指定 hashcode
注意 hashcode 是动态变化的,需要先通过classloader命令或sc -d查看当前 ClassLoader 信息,提取对应 ClassLoader 的 hashcode 后手动输入:
[arthas@2062]$ logger -c 2a139a55 name ROOT class ch.qos.logback.classic.Logger classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 level DEBUG effectiveLevel DEBUG additivity true codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar appenders name CONSOLE class ch.qos.logback.core.ConsoleAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 target System.out name APPLICATION class ch.qos.logback.core.rolling.RollingFileAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 file app.log name ASYNC class ch.qos.logback.classic.AsyncAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 appenderRef [APPLICATION]在loggers(process)中,-c的 hashcode 用于过滤getAllLoadedClasses()返回的每个类:只有当类的StringUtils.classLoaderHash(clazz)与指定 hashcode 一致时才纳入统计(见 LoggerCommand.java),从而只展示该 ClassLoader 体系内的 logger。
使用 --classLoaderClass 指定类名
对于只有唯一实例的 ClassLoader,可以更直观地通过类名指定,无需查询动态 hashcode:
logger --classLoaderClass sun.misc.Launcher$AppClassLoader两点注意事项:
- 这里的
classLoaderClass是 ClassLoader 的类名,Java 8 下是sun.misc.Launcher$AppClassLoader,Java 11 下则是jdk.internal.loader.ClassLoaders$AppClassLoader; --classLoaderClass只有在匹配到唯一ClassLoader 实例时才能工作;匹配到多个实例时,命令会返回失败并提示改用-c <classloader hash>精确定位。
该逻辑在 LoggerCommand.java 中实现:通过ClassLoaderUtils.getClassLoaderByClassName查找,恰好 1 个时自动换算成 hashcode 继续执行;多于 1 个时输出匹配列表并报错Found more than one classloader by class name...。
更新 logger level
基本更新
将 ROOT logger 的级别临时调整为debug:
[arthas@2062]$ logger --name ROOT --level debug update logger level success.更新成功后日志立即生效,无需重启应用。该操作走level(process)分支:先确定目标 ClassLoader,再调用对应框架 Helper 的updateLevel(name, level)。以 Logback 为例,内部通过loggerContext.exists(name)找到 Logger 后执行logger.setLevel(l),level 字符串经Level.toLevel(level, Level.ERROR)解析,未识别时兜底为ERROR(见 LogbackHelper.java)。
指定 classloader 更新 level
默认情况下,logger命令会在SystemClassLoader下执行(见 LoggerCommand.java)。如果应用是传统war应用,或由 spring boot fat jar 启动,日志框架可能加载在自定义 ClassLoader 中,此时需要先定位其 hashcode:
- 先执行
sc -d yourClassName查看具体类的 ClassLoader hashcode; - 更新 level 时通过
-c指定该 hashcode:
[arthas@2062]$ logger -c 2a139a55 --name ROOT --level debug如果未指定正确的 ClassLoader,更新可能失败,命令会提示:
Update logger level fail. Try to specify the classloader with the -c option. Use `sc -d CLASSNAME` to find out the classloader hashcode.该提示正是 LoggerCommand.java 中更新失败的输出文案。
查看没有 appender 的 logger
默认情况下,logger命令只打印有 appender 的 logger。如果想了解全部 logger 的继承关系与有效级别(例如确认某个包是否因为继承了过高级别而静默丢日志),可以加上--include-no-appender参数:
[arthas@2062]$ logger --include-no-appender name ROOT class ch.qos.logback.classic.Logger classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 level DEBUG effectiveLevel DEBUG additivity true codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar appenders name CONSOLE class ch.qos.logback.core.ConsoleAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 target System.out name APPLICATION class ch.qos.logback.core.rolling.RollingFileAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 file app.log name ASYNC class ch.qos.logback.classic.AsyncAppender classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 appenderRef [APPLICATION] name com class ch.qos.logback.classic.Logger classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 level null effectiveLevel DEBUG additivity true codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar name com.alibaba class ch.qos.logback.classic.Logger classLoader sun.misc.Launcher$AppClassLoader@2a139a55 classLoaderHash 2a139a55 level null effectiveLevel DEBUG additivity true codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar ...注意:通常输出结果会很长,因为像com、com.alibaba这类仅作为命名空间存在、没有配置任何 appender 的 logger 也会被全部列出。建议与-n组合使用以缩小范围。
源码级原理:命令如何跨 ClassLoader 工作
logger命令最精巧的地方在于:Arthas 自身通过独立 ClassLoader 加载,而目标应用中的日志框架位于业务 ClassLoader 内,两者互不可见。源码用三步解决了"注入 + 反射调用"的问题:
- 类型探测:遍历
Instrumentation.getAllLoadedClasses(),分别以ch.qos.logback.core.Appender.class、org/apache/logging/log4j/core/LoggerContext.class等资源是否可加载来判断该 ClassLoader 中实际存在哪种日志框架(见 LoggerCommand.java); - Helper 类重命名注入:
LogbackHelper、Log4jHelper、Log4j2Helper的字节码被读取后,经 AsmRenameUtil.java 重命名(追加 Arthas ClassLoader 与目标 ClassLoader 的 hashcode 后缀),再通过ReflectUtils.defineClass定义到目标业务 ClassLoader中,避免类冲突(见 LoggerCommand.java); - 反射调用:通过
getLoggers(String, boolean)或updateLevel(String, String)方法反射执行,结果以 LoggerModel 形式返回并渲染(模型类型为logger)。
各框架 Helper 的实现差异也值得注意:
- Logback(LogbackHelper.java):通过 SLF4J 的
ILoggerFactory获取LoggerContext,还会反射读取PatternLayoutBase.head与ThrowableProxyConverter.lengthOption字段; - Log4j 1.x(Log4jHelper.java):通过
LogManager.getLoggerRepository()操作 logger 仓库,更新时优先exists(name),名称匹配 ROOT 时直接设置根 logger 级别; - Log4j 2.x(Log4j2Helper.java):基于
LogManager.getContext(false)拿到LoggerContext与Configuration,更新时若目标LoggerConfig不存在则动态创建,最后调用updateLoggers()刷新;同时通过反射读取LoggerConfig.config字段输出配置对象。
这一机制保证了logger命令面对复杂部署形态(fat jar、war、OSGi、多个 ClassLoader 并存)时依然能够准确读写目标框架的状态。
实战建议
- 排查"日志突然不打了":先
logger -n 你的Logger名查看level与effectiveLevel,再确认 appender 是否还在,快速区分"级别过滤"与"appender 丢失"两类问题; - 临时放开 DEBUG:
logger --name 包名 --level debug,排障结束后用同样命令改回原级别,避免线上日志量暴涨; - 多 ClassLoader 应用:统一先
sc -d 目标类拿到 hashcode,再配合-c操作,避免误改到 SystemClassLoader 下并不生效的配置; - 批量巡检:
logger --include-no-appender输出较长,配合-n或终端管道过滤查看,聚焦关键包路径。
【免费下载链接】arthasAlibaba Java Diagnostic Tool Arthas/Alibaba Java诊断利器Arthas项目地址: https://gitcode.com/gh_mirrors/ar/arthas
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考