本博文来源于学习阿里云知行动手实验室笔记,传送门
写在前面,博主从17年接触到Java,开启半年自学入行,博客名为逆流者blog,希望一直可以保持乐观、积极的学习心态,在编程的世界里逆流而上。
Java 诊断工具 Arthas 进阶教程
Arthas 进阶
我们接着以一个普通的Spring Boot应用为例,演示Arthas命令的详细用法。
启动demo
下载demo-arthas-spring-boot.jar,再用java -jar命令启动:
wget https://code.aliyun.com/middleware-container/handsonLabExternedFiles/raw/master/demo-arthas-spring-boot.jar;java -jar demo-arthas-spring-boot.jar
demo-arthas-spring-boot是一个很简单的spring boot应用,源代码:查看
启动之后,可以官方文档上面的点击查看链接,会跳转一个公网页面:
启动arthas-boot
在新的Terminal 2里,下载arthas-boot.jar,再用java -jar命令启动:
wget https://arthas.aliyun.com/arthas-boot.jar;java -jar arthas-boot.jar --target-ip 0.0.0.0
arthas-boot是Arthas的启动程序,它启动后,会列出所有的Java进程,用户可以选择需要诊断的目标进程。
--2022-07-28 22:36:33-- https://arthas.aliyun.com/arthas-boot.jar
Resolving arthas.aliyun.com (arthas.aliyun.com)... 203.119.214.116
Connecting to arthas.aliyun.com (arthas.aliyun.com)|203.119.214.116|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 141872 (139K) [application/java-archive]
Saving to: ‘arthas-boot.jar’
arthas-boot.jar 100%[=========================================================================================>] 138.55K 347KB/s in 0.4s
2022-07-28 22:36:34 (347 KB/s) - ‘arthas-boot.jar’ saved [141872/141872]
[INFO] arthas-boot version: 3.6.3
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 1266 demo-arthas-spring-boot.jar
选择第一个进程,输入 1 ,再Enter/回车:
1
[INFO] Start download arthas from remote server: https://arthas.aliyun.com/download/3.6.2?mirror=aliyun
[INFO] Download arthas success.
[INFO] arthas home: /home/shell/.arthas/lib/3.6.2/arthas
[INFO] Try to attach process 1266
[INFO] Attach process 1266 success.
[INFO] arthas-client connect 0.0.0.0 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
wiki https://arthas.aliyun.com/doc
tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html
version 3.6.2
main_class
pid 1266
time 2022-07-28 22:36:44
[arthas@1266]$
Attach成功之后,会打印Arthas LOGO。输入 help 可以获取到更多的帮助信息。
help
查看JVM信息
下面介绍Arthas里查看JVM信息的命令。
sysprop
sysprop 可以打印所有的System Properties信息。
sysprop
KEY VALUE
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
awt.toolkit sun.awt.X11.XToolkit
file.encoding.pkg sun.io
java.specification.version 1.8
sun.cpu.isalist
sun.jnu.encoding UTF-8
java.class.path demo-arthas-spring-boot.jar
java.vm.vendor Private Build
sun.arch.data.model 64
java.vendor.url http://java.oracle.com/
catalina.useNaming false
user.timezone Asia/Shanghai
org.jboss.logging.provider slf4j
os.name Linux
java.vm.specification.version 1.8
user.country US
sun.java.launcher SUN_STANDARD
sun.boot.library.path /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64
sun.java.command demo-arthas-spring-boot.jar
sun.cpu.endian little
user.home /home/shell
user.language en
java.specification.vendor Oracle Corporation
java.home /usr/lib/jvm/java-8-openjdk-amd64/jre
file.separator /
line.separator
java.vm.specification.vendor Oracle Corporation
java.specification.name Java Platform API Specification
java.awt.graphicsenv sun.awt.X11GraphicsEnvironment
java.awt.headless true
sun.boot.class.path /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar:/usr/lib/jvm/java-8-openjdk-amd6
4/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jv
m/java-8-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jfr.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/class
es
java.protocol.handler.pkgs org.springframework.boot.loader
sun.management.compiler HotSpot 64-Bit Tiered Compilers
java.runtime.version 1.8.0_292-8u292-b10-0ubuntu1~18.04-b10
user.name shell
path.separator :
os.version 3.10.0-957.21.3.el7.x86_64
java.endorsed.dirs /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/endorsed
java.runtime.name OpenJDK Runtime Environment
file.encoding UTF-8
spring.beaninfo.ignore true
java.vm.name OpenJDK 64-Bit Server VM
java.vendor.url.bug http://bugreport.sun.com/bugreport/
java.io.tmpdir /tmp
catalina.home /tmp/tomcat.2774955006028160993.61000
java.version 1.8.0_292
user.dir /home/shell
os.arch amd64
PID 1266
java.vm.specification.name Java Virtual Machine Specification
java.awt.printerjob sun.print.PSPrinterJob
sun.os.patch.level unknown
catalina.base /tmp/tomcat.2774955006028160993.61000
java.library.path /usr/java/packages/lib/amd64:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib
java.vm.info mixed mode
java.vendor Private Build
java.vm.version 25.292-b10
java.ext.dirs /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext:/usr/java/packages/lib/ext
sun.io.unicode.encoding UnicodeLittle
java.class.version 52.0
也可以指定单个key: sysprop java.version
[arthas@1266]$ sysprop java.version
KEY VALUE
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
java.version 1.8.0_292
也可以通过grep来过滤: sysprop | grep user
[arthas@1266]$ sysprop | grep user
user.timezone Asia/Shanghai
user.country US
user.home /home/shell
user.language en
user.name shell
user.dir /home/shell
可以设置新的value: sysprop testKey testValue
[arthas@1266]$ sysprop testKey testValue
Successfully changed the system property.
KEY VALUE
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
testKey testValue
sysenv
sysenv 命令可以获取到环境变量。和sysprop命令类似。
sysenv
KEY VALUE
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PATH /usr/local/nvm/versions/node/v12.13.1/bin:/home/shell/.local/bin:/home/shell/bin:/usr/shell/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:
/usr/bin:/sbin:/bin
USE_CLOUD_STORAGE false
ACCOUNT_ID 1458456115779729
TZ Asia/Shanghai
ALIBABA_CLOUD_VENDOR CloudShell/0.2
CLOUD_SHELL_ROOT_KEY_ID LTAI4G8C8WqQihqKWH6L54Cr
ALIYUN_ACCESS_KEY_SECRET 8EXzXhXG4h3cD7LPSo7rEnmg2iZMQyPayg5FHfi14RZw
USER_ID 1458456115779729
ALIBABA_CLOUD_ACCESS_KEY_SECRET 8EXzXhXG4h3cD7LPSo7rEnmg2iZMQyPayg5FHfi14RZw
GOCACHE /go/.cache/go-build
DEFAULT_REGION cn-hangzhou
ALICLOUD_REGION cn-hangzhou
LOGNAME shell
CLOUDSHELL_CONNECT_KEY f5xpj6rgm2wmljak
PWD /home/shell
NVM_CD_FLAGS
CHANNEL_ID OFFICIAL
ALICLOUD_ACCOUNT_ID 1458456115779729
ACCESS_KEY_ID TMP.3KeWMvmMVSnTVotxChCeKJd1AF6YEvwFFb4GCSVgyih7bCLFzMPeQWSmjwCgRfgkMmr2obV3USjaZ6MqM5gM3fzHRfpTvN
GOPATH /go
OLDPWD /
ALIYUN_ACCESS_KEY_ID TMP.3KeWMvmMVSnTVotxChCeKJd1AF6YEvwFFb4GCSVgyih7bCLFzMPeQWSmjwCgRfgkMmr2obV3USjaZ6MqM5gM3fzHRfpTvN
GEM_HOME /home/shell/.gems
REGION cn-hangzhou
CLOUDSHELL_TENANT_CERT -----BEGIN CERTIFICATE-----\nMIIDbDCCAlSgAwIBAgIRAPuaKm1M0qoeKIvbd9APAa4wDQYJKoZIhvcNAQELBQAw\nQjEWMBQGA1UECgwNY2xvdWRzaGVsbCBDQTETMBEGA1
UECwwKY2xvdWRzaGVsbDET\nMBEGA1UEAwwKY2xvdWRzaGVsbDAeFw0yMjA3MjYwMTM2MzhaFw0zMjA3MjYwMTM2\nMzhaMEIxFjAUBgNVBAoMDWNsb3Vkc2hlbGwgQ0ExEzARBgN
VBAsMCmNsb3Vkc2hl\nbGwxEzARBgNVBAMMCmNsb3Vkc2hlbGwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQChYD604D4aD4CAzWaJ1mQuDgTuZs0AmfNwf8pkCHiy
dWlJ3s+312/f\nVPCnqtgJVtDaJwOCQ9ULTeVn1VjJZ3vHdhqgTn2SLnjIIF+ix3jBt+vHvW1J9zVr\n2ZMOpCwGKVTsqqIA8upX6NjTuRAoP3/XSb6cpe+yG1eotMW/MKIdIPXV/
RAFFC+B\nKIi1ETwiBEm65QaxYjG06sxDyqxGFWO+U5OUWnMXyxzvYzJFMYEXctcG7atAMZWj\nww5bXohnEAgroAz4EuaLJPaT9JznRbjFqO5PLtNb/e/B6oZv7k8NEOFHJfTHl9
di\ncEvQaD39K8HUijLw5EWkSOokdae2FRJvAgMBAAGjXTBbMB8GA1UdIwQYMBaAFDGh\nmR6dlPoHvqelX8/p6ZG/VU0AMB0GA1UdDgQWBBQxoZkenZT6B76npV/P6emRv1VN\nA
DALBgNVHQ8EBAMCAgQwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA\neGCv/WlGhsgCLSOFgbdC8SJICC7UopGwUP7EwoLOcabDJh/LQGcfgPdXQgItAdAH\nNL47Bw
bTMofIhraJVR3MuwY1NW17LQto/p3tJHkxcaHGrFuwfJ0H2IcauZb1Nmxd\ndixxfy9HqB3WVABsD3Uf+QxbBUm0DfD1vPW4cLW92XKzySTq5w1uGvdj34Wo89RE\n2iDp6CHgmnr
tnq3hsFwy1hd8pX53j6brxiwHmQUxeaLRjtNlUYuDiuTN1MOsz7Ag\nh8t0K2oOEPVyOLc4MVY8XSOSVq/Am236uuHUKwCOlKkGcmdDWr9Jw+ExmezhDJUX\n4yddDTFqNQKPxh6X
9mYrzA==\n-----END CERTIFICATE-----\n
ALIBABA_CLOUD_ACCOUNT_ID 1458456115779729
ALIBABA_CLOUD_ACCESS_KEY_ID TMP.3KeWMvmMVSnTVotxChCeKJd1AF6YEvwFFb4GCSVgyih7bCLFzMPeQWSmjwCgRfgkMmr2obV3USjaZ6MqM5gM3fzHRfpTvN
LS_COLORS rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=
34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tl
z=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz
=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;3
1:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.sw
m=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm
=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01
;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35
:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=
01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36
:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
CLOUD_SHELL_ROOT_KEY_SECRET cf4x2LqNAyQXdSqzR5dLUkDrNQQ5VZ
SHLVL 3
CLOUDSHELL_PROXY_ACCESS_KEY 48eb4b2b-251c-4e44-ab27-49334355f190
default_serverless_devs_access {"AccountID":"1458456115779729","AccessKeyID":"TMP.3KeWMvmMVSnTVotxChCeKJd1AF6YEvwFFb4GCSVgyih7bCLFzMPeQWSmjwCgRfgkMmr2obV3USjaZ6MqM5gM3f
zHRfpTvN","AccessKeySecret":"8EXzXhXG4h3cD7LPSo7rEnmg2iZMQyPayg5FHfi14RZw"}
ALIYUN_DEFAULT_REGION cn-hangzhou
TERM xterm-256color
LANG en_US.UTF-8
GOPROXY https://mirrors.aliyun.com/goproxy/
ALICLOUD_SECRET_KEY 8EXzXhXG4h3cD7LPSo7rEnmg2iZMQyPayg5FHfi14RZw
ACCESS_KEY_SECRET 8EXzXhXG4h3cD7LPSo7rEnmg2iZMQyPayg5FHfi14RZw
CLOUD_SHELL_LANG zh
_ /usr/bin/java
ALIBABA_CLOUD_DEFAULT_REGION cn-hangzhou
CLOUDSHELL_DOWNLOAD_MAX_SIZE 5368709120
NVM_DIR /usr/local/nvm
CLOUD_SHELL true
FC_NO_ISOLATION y
USER shell
GEM_PATH /home/shell/.gems:/var/lib/gems/2.5.0
ALICLOUD_ACCESS_KEY TMP.3KeWMvmMVSnTVotxChCeKJd1AF6YEvwFFb4GCSVgyih7bCLFzMPeQWSmjwCgRfgkMmr2obV3USjaZ6MqM5gM3fzHRfpTvN
ALIYUN_USER_AGENT CloudShell/0.2
HOSTNAME 503bf2508c1e
MOUNT_FAILED false
NVM_BIN /usr/local/nvm/versions/node/v12.13.1/bin
TF_PLUGIN_CACHE_DIR /home/shell/.terraform.d/plugin-cache
CLOUDSHELL_PROXY_ACCESS_SECRET 62ed338f-2315-406e-81ed-3c0917b6b9dd
HOME /home/shell
jvm
jvm 命令会打印出JVM的各种详细信息。
jvm
RUNTIME
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MACHINE-NAME 1729@503bf2508c1e
JVM-START-TIME 2022-07-28 22:44:38
MANAGEMENT-SPEC-VERSION 1.2
SPEC-NAME Java Virtual Machine Specification
SPEC-VENDOR Oracle Corporation
SPEC-VERSION 1.8
VM-NAME OpenJDK 64-Bit Server VM
VM-VENDOR Private Build
VM-VERSION 25.292-b10
INPUT-ARGUMENTS []
CLASS-PATH demo-arthas-spring-boot.jar
BOOT-CLASS-PATH /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar:/usr/lib/jvm/java
-8-openjdk-amd64/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-8-openjdk-amd
64/jre/lib/jce.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jfr.ja
r:/usr/lib/jvm/java-8-openjdk-amd64/jre/classes
LIBRARY-PATH /usr/java/packages/lib/amd64:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/l
ib:/usr/lib
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CLASS-LOADING
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
LOADED-CLASS-COUNT 8551
TOTAL-LOADED-CLASS-COUNT 8551
UNLOADED-CLASS-COUNT 0
IS-VERBOSE false
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
COMPILATION
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
NAME HotSpot 64-Bit Tiered Compilers
TOTAL-COMPILE-TIME 19363
[time (ms)]
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GARBAGE-COLLECTORS
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Copy name : Copy
[count/time (ms)] collectionCount : 92
collectionTime : 387
MarkSweepCompact name : MarkSweepCompact
[count/time (ms)] collectionCount : 2
collectionTime : 187
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MEMORY-MANAGERS
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CodeCacheManager Code Cache
Metaspace Manager Metaspace
Compressed Class Space
Copy Eden Space
Survivor Space
MarkSweepCompact Eden Space
Survivor Space
Tenured Gen
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MEMORY
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
HEAP-MEMORY-USAGE init : 31457280(30.0 MiB)
[memory in bytes] used : 44316080(42.3 MiB)
committed : 49762304(47.5 MiB)
max : 466288640(444.7 MiB)
NO-HEAP-MEMORY-USAGE init : 2555904(2.4 MiB)
[memory in bytes] used : 65258256(62.2 MiB)
committed : 66650112(63.6 MiB)
max : -1(-1 B)
PENDING-FINALIZE-COUNT 0
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OPERATING-SYSTEM
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OS Linux
ARCH amd64
PROCESSORS-COUNT 1
LOAD-AVERAGE 0.75
VERSION 3.10.0-957.21.3.el7.x86_64
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
THREAD
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
COUNT 31
DAEMON-COUNT 29
PEAK-COUNT 31
STARTED-COUNT 38
DEADLOCK-COUNT 0
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FILE-DESCRIPTOR
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MAX-FILE-DESCRIPTOR-COUNT 1048576
OPEN-FILE-DESCRIPTOR-COUNT 68
dashboard
dashboard 命令可以查看当前系统的实时数据面板。
dashboard
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPTED DAEMON
-1 C1 CompilerThread1 - -1 - 0.67 0.033 0:2.105 false true
45 Timer-for-arthas-dashboard-fa5385e5-3ce2-48 system 5 RUNNABLE 0.38 0.018 0:0.021 false true
-1 C2 CompilerThread0 - -1 - 0.37 0.018 0:7.673 false true
43 arthas-NettyHttpTelnetBootstrap-3-2 system 5 RUNNABLE 0.22 0.010 0:0.116 false true
-1 VM Periodic Task Thread - -1 - 0.09 0.004 0:0.110 false true
-1 VM Thread - -1 - 0.01 0.000 0:0.519 false true
26 http-nio-61000-ClientPoller-0 main 5 RUNNABLE 0.01 0.000 0:0.011 false true
15 NioBlockingSelector.BlockPoller-1 main 5 RUNNABLE 0.01 0.000 0:0.009 false true
28 http-nio-61000-AsyncTimeout main 5 TIMED_WAITING 0.01 0.000 0:0.008 false true
2 Reference Handler system 10 WAITING 0.0 0.000 0:0.009 false true
3 Finalizer system 8 WAITING 0.0 0.000 0:0.015 false true
4 Signal Dispatcher system 9 RUNNABLE 0.0 0.000 0:0.000 false true
31 Attach Listener system 9 RUNNABLE 0.0 0.000 0:0.006 false true
33 arthas-timer system 9 WAITING 0.0 0.000 0:0.000 false true
36 arthas-NettyHttpTelnetBootstrap-3-1 system 5 RUNNABLE 0.0 0.000 0:0.028 false true
37 arthas-NettyWebsocketTtyBootstrap-4-1 system 5 RUNNABLE 0.0 0.000 0:0.001 false true
38 arthas-NettyWebsocketTtyBootstrap-4-2 system 5 RUNNABLE 0.0 0.000 0:0.001 false true
39 arthas-shell-server system 9 TIMED_WAITING 0.0 0.000 0:0.000 false true
40 arthas-session-manager system 9 TIMED_WAITING 0.0 0.000 0:0.000 false true
41 arthas-UserStat system 9 WAITING 0.0 0.000 0:0.000 false true
44 arthas-command-execute system 5 TIMED_WAITING 0.0 0.000 0:0.029 false true
13 ContainerBackgroundProcessor[StandardEngine main 5 TIMED_WAITING 0.0 0.000 0:0.002 false true
14 container-0 main 5 TIMED_WAITING 0.0 0.000 0:0.000 false false
16 http-nio-61000-exec-1 main 5 WAITING 0.0 0.000 0:0.000 false true
17 http-nio-61000-exec-2 main 5 WAITING 0.0 0.000 0:0.000 false true
18 http-nio-61000-exec-3 main 5 WAITING 0.0 0.000 0:0.000 false true
19 http-nio-61000-exec-4 main 5 WAITING 0.0 0.000 0:0.000 false true
20 http-nio-61000-exec-5 main 5 WAITING 0.0 0.000 0:0.000 false true
21 http-nio-61000-exec-6 main 5 WAITING 0.0 0.000 0:0.000 false true
22 http-nio-61000-exec-7 main 5 WAITING 0.0 0.000 0:0.000 false true
23 http-nio-61000-exec-8 main 5 WAITING 0.0 0.000 0:0.000 false true
24 http-nio-61000-exec-9 main 5 WAITING 0.0 0.000 0:0.000 false true
25 http-nio-61000-exec-10 main 5 WAITING 0.0 0.000 0:0.000 false true
27 http-nio-61000-Acceptor-0 main 5 RUNNABLE 0.0 0.000 0:0.000 false true
30 DestroyJavaVM main 5 RUNNABLE 0.0 0.000 0:4.001 false false
-1 Service Thread - -1 - 0.0 0.000 0:0.000 false true
Memory used total max usage GC
heap 45M 47M 444M 10.16% gc.copy.count 92
eden_space 11M 13M 122M 9.55% gc.copy.time(ms) 387
survivor_space 1M 1M 15M 10.61% gc.marksweepcompact.count 2
tenured_gen 31M 32M 306M 10.38% gc.marksweepcompact.time(ms) 187
nonheap 61M 63M -1 97.05%
code_cache 11M 12M 240M 4.69%
metaspace 45M 45M -1 98.13%
compressed_class_space 5M 5M 1024M 0.55%
direct 8K 8K - 100.01%
mapped 0K 0K - 0.00%
Runtime
os.name Linux
os.version 3.10.0-957.21.3.el7.x86_64
java.version 1.8.0_292
java.home /usr/lib/jvm/java-8-openjdk-amd64/jre
systemload.average 0.20
processors 1
timestamp/uptime Thu Jul 28 22:46:48 CST 2022/129s
输入 q 或者 Ctrl+C 可以退出dashboard命令。
sc/sm 查看已加载的类
下面介绍Arthas里查找已加载类的命令。
sc
sc 命令可以查找到所有JVM已经加载到的类。 如果搜索的是接口,还会搜索所有的实现类。比如查看所有的Filter实现类:
sc javax.servlet.Filter
com.example.demo.arthas.AdminFilterConfig$AdminFilter
javax.servlet.Filter
org.apache.tomcat.websocket.server.WsFilter
org.springframework.boot.web.filter.OrderedCharacterEncodingFilter
org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter
org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter
org.springframework.boot.web.filter.OrderedRequestContextFilter
org.springframework.web.filter.CharacterEncodingFilter
org.springframework.web.filter.GenericFilterBean
org.springframework.web.filter.HiddenHttpMethodFilter
org.springframework.web.filter.HttpPutFormContentFilter
org.springframework.web.filter.OncePerRequestFilter
org.springframework.web.filter.RequestContextFilter
org.springframework.web.servlet.resource.ResourceUrlEncodingFilter
Affect(row-cnt:14) cost in 51 ms.
通过-d参数,可以打印出类加载的具体信息,很方便查找类加载问题。
sc -d javax.servlet.Filter
class-info com.example.demo.arthas.AdminFilterConfig$AdminFilter
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/
name com.example.demo.arthas.AdminFilterConfig$AdminFilter
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass true
isPrimitive false
isSynthetic false
simple-name AdminFilter
modifier static
annotation
interfaces javax.servlet.Filter
super-class +-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
class-info javax.servlet.Filter
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/tomcat-embed-core-8.5.31.jar!/
name javax.servlet.Filter
isInterface true
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name Filter
modifier abstract,interface,public
annotation
interfaces
super-class
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
class-info org.apache.tomcat.websocket.server.WsFilter
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/tomcat-embed-websocket-8.5.31.jar!/
name org.apache.tomcat.websocket.server.WsFilter
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name WsFilter
modifier public
annotation
interfaces javax.servlet.Filter
super-class +-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
class-info org.springframework.boot.web.filter.OrderedCharacterEncodingFilter
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-1.5.13.RELEASE.jar!/
name org.springframework.boot.web.filter.OrderedCharacterEncodingFilter
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name OrderedCharacterEncodingFilter
modifier public
annotation
interfaces org.springframework.core.Ordered
super-class +-org.springframework.web.filter.CharacterEncodingFilter
+-org.springframework.web.filter.OncePerRequestFilter
+-org.springframework.web.filter.GenericFilterBean
+-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
class-info org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-1.5.13.RELEASE.jar!/
name org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name OrderedHiddenHttpMethodFilter
modifier public
annotation
interfaces org.springframework.core.Ordered
super-class +-org.springframework.web.filter.HiddenHttpMethodFilter
+-org.springframework.web.filter.OncePerRequestFilter
+-org.springframework.web.filter.GenericFilterBean
+-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
class-info org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-1.5.13.RELEASE.jar!/
name org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name OrderedHttpPutFormContentFilter
modifier public
annotation
interfaces org.springframework.core.Ordered
super-class +-org.springframework.web.filter.HttpPutFormContentFilter
+-org.springframework.web.filter.OncePerRequestFilter
+-org.springframework.web.filter.GenericFilterBean
+-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
class-info org.springframework.boot.web.filter.OrderedRequestContextFilter
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-1.5.13.RELEASE.jar!/
name org.springframework.boot.web.filter.OrderedRequestContextFilter
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name OrderedRequestContextFilter
modifier public
annotation
interfaces org.springframework.core.Ordered
super-class +-org.springframework.web.filter.RequestContextFilter
+-org.springframework.web.filter.OncePerRequestFilter
+-org.springframework.web.filter.GenericFilterBean
+-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
class-info org.springframework.web.filter.CharacterEncodingFilter
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-web-4.3.17.RELEASE.jar!/
name org.springframework.web.filter.CharacterEncodingFilter
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name CharacterEncodingFilter
modifier public
annotation
interfaces
super-class +-org.springframework.web.filter.OncePerRequestFilter
+-org.springframework.web.filter.GenericFilterBean
+-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
class-info org.springframework.web.filter.GenericFilterBean
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-web-4.3.17.RELEASE.jar!/
name org.springframework.web.filter.GenericFilterBean
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name GenericFilterBean
modifier abstract,public
annotation
interfaces javax.servlet.Filter,org.springframework.beans.factory.BeanNameAware,org.springframework.context.EnvironmentAware,org.springframework.core.env.Environment
Capable,org.springframework.web.context.ServletContextAware,org.springframework.beans.factory.InitializingBean,org.springframework.beans.factory.Disposabl
eBean
super-class +-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
class-info org.springframework.web.filter.HiddenHttpMethodFilter
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-web-4.3.17.RELEASE.jar!/
name org.springframework.web.filter.HiddenHttpMethodFilter
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name HiddenHttpMethodFilter
modifier public
annotation
interfaces
super-class +-org.springframework.web.filter.OncePerRequestFilter
+-org.springframework.web.filter.GenericFilterBean
+-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
class-info org.springframework.web.filter.HttpPutFormContentFilter
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-web-4.3.17.RELEASE.jar!/
name org.springframework.web.filter.HttpPutFormContentFilter
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name HttpPutFormContentFilter
modifier public
annotation
interfaces
super-class +-org.springframework.web.filter.OncePerRequestFilter
+-org.springframework.web.filter.GenericFilterBean
+-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
class-info org.springframework.web.filter.OncePerRequestFilter
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-web-4.3.17.RELEASE.jar!/
name org.springframework.web.filter.OncePerRequestFilter
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name OncePerRequestFilter
modifier abstract,public
annotation
interfaces
super-class +-org.springframework.web.filter.GenericFilterBean
+-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
class-info org.springframework.web.filter.RequestContextFilter
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-web-4.3.17.RELEASE.jar!/
name org.springframework.web.filter.RequestContextFilter
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name RequestContextFilter
modifier public
annotation
interfaces
super-class +-org.springframework.web.filter.OncePerRequestFilter
+-org.springframework.web.filter.GenericFilterBean
+-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
class-info org.springframework.web.servlet.resource.ResourceUrlEncodingFilter
code-source file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-webmvc-4.3.17.RELEASE.jar!/
name org.springframework.web.servlet.resource.ResourceUrlEncodingFilter
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name ResourceUrlEncodingFilter
modifier public
annotation
interfaces
super-class +-org.springframework.web.filter.GenericFilterBean
+-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
classLoaderHash 19469ea2
sc支持通配,比如搜索所有的StringUtils:
sc *StringUtils
[arthas@1729]$ sc *StringUtils
com.taobao.arthas.core.util.StringUtils
org.apache.tomcat.util.buf.StringUtils
org.springframework.util.StringUtils
Affect(row-cnt:3) cost in 75 ms.
sm
sm命令则是查找类的具体函数。比如:
sm java.math.RoundingMode
[arthas@1729]$ sm java.math.RoundingMode
java.math.RoundingMode <init>(Ljava/lang/String;II)V
java.math.RoundingMode values()[Ljava/math/RoundingMode;
java.math.RoundingMode valueOf(I)Ljava/math/RoundingMode;
java.math.RoundingMode valueOf(Ljava/lang/String;)Ljava/math/RoundingMode;
Affect(row-cnt:4) cost in 20 ms.
通过-d参数可以打印函数的具体属性:
sm -d java.math.RoundingMode
[arthas@1729]$ sm -d java.math.RoundingMode
declaring-class java.math.RoundingMode
constructor-name <init>
modifier private
annotation
parameters java.lang.String
int
int
exceptions
classLoaderHash null
declaring-class java.math.RoundingMode
method-name values
modifier public,static
annotation
parameters
return java.math.RoundingMode[]
exceptions
classLoaderHash null
declaring-class java.math.RoundingMode
method-name valueOf
modifier public,static
annotation
parameters int
return java.math.RoundingMode
exceptions
classLoaderHash null
declaring-class java.math.RoundingMode
method-name valueOf
modifier public,static
annotation
parameters java.lang.String
return java.math.RoundingMode
exceptions
classLoaderHash null
Affect(row-cnt:4) cost in 26 ms.
也可以查找特定的函数,比如查找构造函数:
sm java.math.RoundingMode <init>
[arthas@1729]$ sm java.math.RoundingMode <init>
java.math.RoundingMode <init>(Ljava/lang/String;II)V
Affect(row-cnt:1) cost in 17 ms.
Jad
可以通过 jad 命令来反编译代码:
jad com.example.demo.arthas.user.UserController
[arthas@1729]$ jad com.example.demo.arthas.user.UserController
ClassLoader:
+-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
Location:
file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* com.example.demo.arthas.user.User
* org.slf4j.Logger
* org.slf4j.LoggerFactory
* org.springframework.web.bind.annotation.GetMapping
* org.springframework.web.bind.annotation.PathVariable
* org.springframework.web.bind.annotation.RestController
*/
package com.example.demo.arthas.user;
import com.example.demo.arthas.user.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@GetMapping(value={"/user/{id}"})
public User findUserById(@PathVariable Integer id) {
/*15*/ logger.info("id: {}", (Object)id);
/*17*/ if (id != null && id < 1) {
throw new IllegalArgumentException("id < 1");
}
return new User(id.intValue(), "name" + id);
}
}
Affect(row-cnt:1) cost in 1680 ms.
通过–source-only参数可以只打印出在反编译的源代码:
jad --source-only com.example.demo.arthas.user.UserController
[arthas@1729]$ jad --source-only com.example.demo.arthas.user.UserController
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* com.example.demo.arthas.user.User
* org.slf4j.Logger
* org.slf4j.LoggerFactory
* org.springframework.web.bind.annotation.GetMapping
* org.springframework.web.bind.annotation.PathVariable
* org.springframework.web.bind.annotation.RestController
*/
package com.example.demo.arthas.user;
import com.example.demo.arthas.user.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@GetMapping(value={"/user/{id}"})
public User findUserById(@PathVariable Integer id) {
/*15*/ logger.info("id: {}", (Object)id);
/*17*/ if (id != null && id < 1) {
throw new IllegalArgumentException("id < 1");
}
return new User(id.intValue(), "name" + id);
}
}
Ognl
在Arthas里,有一个单独的ognl命令,可以动态执行代码。
调用static函数
ognl '@java.lang.System@out.println("hello ognl")'
可以检查Terminal 1里的进程输出,可以发现打印出了hello ognl。
查找UserController的ClassLoader
sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash
[arthas@1266]$ sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash
classLoaderHash 19469ea2
注意hashcode是变化的,需要先查看当前的ClassLoader信息,提取对应ClassLoader的hashcode。
如果你使用-c,你需要手动输入hashcode:-c
ognl -c 19469ea2 @com.example.demo.arthas.user.UserController@logger
[arthas@1266]$ ognl -c 19469ea2 @com.example.demo.arthas.user.UserController@logger
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]
对于只有唯一实例的ClassLoader可以通过–classLoaderClass指定class name,使用起来更加方便:
[arthas@1266]$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader @org.springframework.boot.SpringApplication@logger
@SLF4JLocationAwareLog[
serialVersionUID=@Long[-2379157579039314822],
name=@String[org.springframework.boot.SpringApplication],
logger=@Logger[Logger[org.springframework.boot.SpringApplication]],
FQCN=@String[org.apache.commons.logging.impl.SLF4JLocationAwareLog],
]
–classLoaderClass 的值是ClassLoader的类名,只有匹配到唯一的ClassLoader实例时才能工作,目的是方便输入通用命令,而-c 是动态变化的。
获取静态类的静态字段
获取UserController类里的logger字段:
ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader @com.example.demo.arthas.user.UserController@logger
[arthas@1266]$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader @com.example.demo.arthas.user.UserController@logger
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]
还可以通过-x参数控制返回值的展开层数。比如:
ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -x 2 @com.example.demo.arthas.user.UserController@logger
[arthas@1266]$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -x 2 @com.example.demo.arthas.user.UserController@logger
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[Logger[com.example.demo.arthas]],
childrenList=@CopyOnWriteArrayList[isEmpty=false;size=1],
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[
DEFAULT_PACKAGING_DATA=@Boolean[false],
root=@Logger[Logger[ROOT]],
size=@Integer[357],
noAppenderWarning=@Integer[0],
loggerContextListenerList=@ArrayList[isEmpty=false;size=1],
loggerCache=@ConcurrentHashMap[isEmpty=false;size=357],
loggerContextRemoteView=@LoggerContextVO[LoggerContextVO{name='default', propertyMap={}, birthTime=1659021898283}],
turboFilterList=@TurboFilterList[isEmpty=true;size=0],
packagingDataEnabled=@Boolean[false],
maxCallerDataDepth=@Integer[8],
resetCount=@Integer[2],
frameworkPackages=@ArrayList[isEmpty=true;size=0],
birthTime=@Long[1659021898283],
name=@String[default],
sm=@BasicStatusManager[ch.qos.logback.core.BasicStatusManager@492fc505],
propertyMap=@HashMap[isEmpty=true;size=0],
objectMap=@HashMap[isEmpty=false;size=7],
configurationLock=@LogbackLock[ch.qos.logback.core.spi.LogbackLock@5b7a4866],
scheduledExecutorService=null,
scheduledFutures=@ArrayList[isEmpty=true;size=0],
lifeCycleManager=@LifeCycleManager[ch.qos.logback.core.LifeCycleManager@4d9e66a3],
started=@Boolean[false],
],
]
执行多行表达式,赋值给临时变量,返回一个List
ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
[arthas@1266]$ ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
@ArrayList[
@String[/usr/lib/jvm/java-8-openjdk-amd64/jre],
@String[OpenJDK Runtime Environment],
]
更多
在Arthas里ognl表达式是很重要的功能,在很多命令里都可以使用ognl表达式。
一些更复杂的用法,可以参考:
OGNL特殊用法请参考:https://github.com/alibaba/arthas/issues/71
OGNL表达式官方指南:https://commons.apache.org/proper/commons-ognl/language-guide.html
Tips
为了更好使用Arthas,下面先介绍Arthas里的一些使用技巧。
帮助信息
Arthas里每一个命令都有详细的帮助信息。可以用-h来查看。帮助信息里有EXAMPLES和WIKI链接。
比如:
sysprop -h
USAGE:
sysprop [-h] [property-name] [property-value]
SUMMARY:
Display, and change the system properties.
EXAMPLES:
sysprop
sysprop file.encoding
sysprop production.mode true
WIKI:
https://arthas.aliyun.com/doc/sysprop
OPTIONS:
-h, --help this help
<property-name> property name
<property-value> property value
自动补全
Arthas支持丰富的自动补全功能,在使用有疑惑时,可以输入Tab来获取更多信息。
比如输入 sysprop java. 之后,再输入Tab,会补全出对应的key:
$ sysprop java.
java.runtime.name java.protocol.handler.pkgs java.vm.version
java.vm.vendor java.vendor.url java.vm.name
readline的快捷键支持
Arthas支持常见的命令行快捷键,比如Ctrl + A跳转行首,Ctrl + E跳转行尾。
更多的快捷键可以用 keymap 命令查看。
keymap
Shortcut Description Name
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
"\C-a" Ctrl + a beginning-of-line
"\C-e" Ctrl + e end-of-line
"\C-f" Ctrl + f forward-word
"\C-b" Ctrl + b backward-word
"\e[D" Left arrow backward-char
"\e[C" Right arrow forward-char
"\e[A" Up arrow history-search-backward
"\e[B" Down arrow history-search-forward
"\C-h" Ctrl + h backward-delete-char
"\C-?" Ctrl + ? backward-delete-char
"\C-u" Ctrl + u undo
"\C-d" Ctrl + d delete-char
"\C-k" Ctrl + k kill-line
"\C-i" Ctrl + i complete
"\C-j" Ctrl + j accept-line
"\C-m" Ctrl + m accept-line
"\C-w" Ctrl + w backward-delete-word
"\C-x\e[3~" "\C-x\e[3~" backward-kill-line
"\e\C-?" "\e\C-?" backward-kill-word
"\e[1~" "\e[1~" beginning-of-line
"\e[4~" "\e[4~" end-of-line
"\e[5~" "\e[5~" beginning-of-history
"\e[6~" "\e[6~" end-of-history
"\e[3~" "\e[3~" delete-char
"\e[2~" "\e[2~" quoted-insert
"\e[7~" "\e[7~" beginning-of-line
"\e[8~" "\e[8~" end-of-line
"\eOH" "\eOH" beginning-of-line
"\eOF" "\eOF" end-of-line
"\e[H" "\e[H" beginning-of-line
"\e[F" "\e[F" end-of-line
历史命令的补全
如果想再执行之前的命令,可以在输入一半时,按Up/↑ 或者 Ddown/↓,来匹配到之前的命令。
比如之前执行过sysprop java.version,那么在输入sysprop ja之后,可以输入Up/↑,就会自动补全为sysprop java.version。
如果想查看所有的历史命令,也可以通过 history 命令查看到。
[arthas@1729]$ history
1 sysprop
2 sysprop java.version
3 sysprop | grep user
4 sysprop testKey testValue
5 sysenv
6 jvm
7 dashboard
8 sysprop -h
9 keymap
10 history
pipeline
Arthas支持在pipeline之后,执行一些简单的命令,比如:
sysprop | grep java
[arthas@1729]$ sysprop | grep java
java.specification.version 1.8
java.class.path demo-arthas-spring-boot.jar
java.vm.vendor Private Build
java.vendor.url http://java.oracle.com/
java.vm.specification.version 1.8
sun.java.launcher SUN_STANDARD
sun.boot.library.path /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64
sun.java.command demo-arthas-spring-boot.jar
java.specification.vendor Oracle Corporation
java.home /usr/lib/jvm/java-8-openjdk-amd64/jre
java.vm.specification.vendor Oracle Corporation
java.specification.name Java Platform API Specification
java.awt.graphicsenv sun.awt.X11GraphicsEnvironment
java.awt.headless true
sun.boot.class.path /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar:/usr/lib/jvm/java-8-openjdk-amd6
4/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jv
m/java-8-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jfr.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/class
java.protocol.handler.pkgs org.springframework.boot.loader
java.runtime.version 1.8.0_292-8u292-b10-0ubuntu1~18.04-b10
java.endorsed.dirs /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/endorsed
java.runtime.name OpenJDK Runtime Environment
java.vm.name OpenJDK 64-Bit Server VM
java.vendor.url.bug http://bugreport.sun.com/bugreport/
java.io.tmpdir /tmp
java.version 1.8.0_292
java.vm.specification.name Java Virtual Machine Specification
java.awt.printerjob sun.print.PSPrinterJob
java.library.path /usr/java/packages/lib/amd64:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib
java.vm.info mixed mode
java.vendor Private Build
java.vm.version 25.292-b10
java.ext.dirs /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext:/usr/java/packages/lib/ext
java.class.version 52.0
sysprop | wc -l
[arthas@1729]$ sysprop | wc -l
64
案例: 排查函数调用异常
现象
目前,访问 http://localhost:61000/user/0 ,会返回500异常:
curl http://localhost:61000/user/0
{"timestamp":1550223186170,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"id < 1","path":"/user/0"}
但请求的具体参数,异常栈是什么呢?
查看UserController的 参数/异常
在Arthas里执行:
watch com.example.demo.arthas.user.UserController * '{params, throwExp}'
- 第一个参数是类名,支持通配
- 第二个参数是函数名,支持通配 访问 curl http://localhost:61000/user/0 ,watch命令会打印调用的参数和异常
curl http://localhost:61000/user/0
[arthas@1266]$ watch com.example.demo.arthas.user.UserController * '{params, throwExp}'
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 2) cost in 193 ms, listenerId: 1
method=com.example.demo.arthas.user.UserController.findUserById location=AtExceptionExit
ts=2022-07-28 23:37:11; [cost=1.598127ms] result=@ArrayList[
@Object[][isEmpty=false;size=1],
@IllegalArgumentException[java.lang.IllegalArgumentException: id < 1],
]
可以看到实际抛出的异常是IllegalArgumentException
。
可以输入 q 或者 Ctrl+C 退出watch命令。
如果想把获取到的结果展开,可以用-x参数:
watch com.example.demo.arthas.user.UserController * '{params, throwExp}' -x 2
[arthas@1266]$ watch com.example.demo.arthas.user.UserController * '{params, throwExp}' -x 2
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 2) cost in 56 ms, listenerId: 2
method=com.example.demo.arthas.user.UserController.findUserById location=AtExceptionExit
ts=2022-07-28 23:39:20; [cost=8.416671ms] result=@ArrayList[
@Object[][
@Integer[0],
],
java.lang.IllegalArgumentException: id < 1
at com.example.demo.arthas.user.UserController.findUserById(UserController.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
,
]
返回值表达式
在上面的例子里,第三个参数是返回值表达式,它实际上是一个ognl表达式,它支持一些内置对象:
- loader
- clazz
- method
- target
- params
- returnObj
- throwExp
- isBefore
- isThrow
- isReturn
你可以利用这些内置对象来组成不同的表达式。比如返回一个数组:
watch com.example.demo.arthas.user.UserController * '{params[0], target, returnObj}'
[arthas@1266]$ watch com.example.demo.arthas.user.UserController * '{params[0], target, returnObj}'
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 2) cost in 62 ms, listenerId: 3
method=com.example.demo.arthas.user.UserController.findUserById location=AtExceptionExit
ts=2022-07-28 23:42:39; [cost=2.860676ms] result=@ArrayList[
@Integer[0],
@UserController[com.example.demo.arthas.user.UserController@128457b8],
null,
]
更多参考: https://arthas.aliyun.com/doc/advice-class.html
条件表达式
watch命令支持在第4个参数里写条件表达式,比如:
watch com.example.demo.arthas.user.UserController * returnObj 'params[0] > 100'
当访问 user/1 (curl http://localhost:61000/user/1
)时,watch命令没有输出
当访问 user/101(curl http://localhost:61000/user/101
) 时,watch会打印出结果。
[arthas@1266]$ watch com.example.demo.arthas.user.UserController * returnObj 'params[0] > 100'
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 2) cost in 44 ms, listenerId: 4
method=com.example.demo.arthas.user.UserController.findUserById location=AtExit
ts=2022-07-28 23:45:36; [cost=0.313823ms] result=@User[
id=@Integer[101],
name=@String[name101],
]
当异常时捕获
watch命令支持-e选项,表示只捕获抛出异常时的请求:
watch com.example.demo.arthas.user.UserController * "{params[0],throwExp}" -e
执行两个请求:
shell@Alicloud:~$ curl http://localhost:61000/user/1
{"id":1,"name":"name1"}
shell@Alicloud:~$ curl http://localhost:61000/user/0
{"timestamp":1659023295622,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"id < 1","path":"/user/0"}shell@Alicloud:~$
[arthas@1266]$ watch com.example.demo.arthas.user.UserController * "{params[0],throwExp}" -e
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 2) cost in 55 ms, listenerId: 5
method=com.example.demo.arthas.user.UserController.findUserById location=AtExceptionExit
ts=2022-07-28 23:48:15; [cost=0.359706ms] result=@ArrayList[
@Integer[0],
@IllegalArgumentException[java.lang.IllegalArgumentException: id < 1],
]
可以看到id为0的报了异常
按照耗时进行过滤
watch命令支持按请求耗时进行过滤,比如:
watch com.example.demo.arthas.user.UserController * '{params, returnObj}' '#cost>200'
上面表示大于 200ms
案例: 热更新代码
下面介绍通过jad/mc/redefine
命令实现动态更新代码的功能。
目前,访问 http://localhost:61000/user/0 ,会返回500异常:
curl http://localhost:61000/user/0
{"timestamp":1550223186170,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"id < 1","path":"/user/0"}
下面通过热更新代码,修改这个逻辑。
jad反编译UserController
jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java
jad反编译的结果保存在 /tmp/UserController.java文件里了。
再打开一个Terminal 3,然后用vim来编辑/tmp/UserController.java:
vim /tmp/UserController.java
比如当 user id 小于1时,也正常返回,不抛出异常:
@GetMapping(value={"/user/{id}"})
public User findUserById(@PathVariable Integer id) {
logger.info("id: {}", (Object)id);
if (id != null && id < 1) {
return new User(id, "name" + id);
// throw new IllegalArgumentException("id < 1");
}
return new User(id.intValue(), "name" + id);
}
sc查找加载UserController的ClassLoader
sc -d *UserController | grep classLoaderHash
[arthas@1265]$ sc -d *UserController | grep classLoaderHash
classLoaderHash 19469ea2
可以发现是 spring boot LaunchedURLClassLoader@19469ea2 加载的。
请记下你的classLoaderHash,后面需要使用它。在这里,它是 19469ea2。
mc
保存好/tmp/UserController.java之后,使用mc(Memory Compiler)命令来编译,并且通过-c或者–classLoaderClass参数指定ClassLoader:
mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
[arthas@1265]$ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 3587 ms.
也可以通过mc -c <classLoaderHash> /tmp/UserController.java -d /tmp
,使用-c参数指定ClassLoaderHash:
mc -c 19469ea2 /tmp/UserController.java -d /tmp
[arthas@1265]$ mc -c 19469ea2 /tmp/UserController.java -d /tmp
Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 2023 ms.
redefine
再使用redefine命令重新加载新编译好的UserController.class:
redefine /tmp/com/example/demo/arthas/user/UserController.class
[arthas@1265]$ redefine /tmp/com/example/demo/arthas/user/UserController.class
redefine success, size: 1, classes:
com.example.demo.arthas.user.UserController
热修改代码结果
redefine成功之后,再次访问 user/0 ,结果是:
{
"id": 0,
"name": "name0"
}
案例: 动态更新应用Logger Level
在这个案例里,动态修改应用的Logger Level。
查找UserController的ClassLoader
sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash
[arthas@1265]$ sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash
classLoaderHash 19469ea2
用ognl获取logger
ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger'
[arthas@1265]$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger'
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]
可以知道UserController@logger实际使用的是logback。可以看到level=null,则说明实际最终的level是从root logger里来的。
单独设置UserController的logger level
ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger.setLevel(@ch.qos.logback.classic.Level@DEBUG)'
[arthas@1265]$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger.setLevel(@ch.qos.logback.classic.Level@DEBUG)'
null
再次获取UserController@logger,可以发现已经是DEBUG了:
[arthas@1265]$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger'
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=@Level[DEBUG],
effectiveLevelInt=@Integer[10000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]
修改logback的全局logger level
通过获取root logger,可以修改全局的logger level:
ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)'
[arthas@1265]$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)'
null
案例: 排查logger冲突问题
在这个案例里,展示排查logger冲突的方法。
确认应用使用的logger系统
以UserController为例,它使用的是slf4j api,但实际使用到的logger系统是logback。
ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger'
[arthas@1265]$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger'
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=@Level[DEBUG],
effectiveLevelInt=@Integer[10000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]
获取logback实际加载的配置文件
ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '#map1=@org.slf4j.LoggerFactory@getLogger("root").loggerContext.objectMap, #map1.get("CONFIGURATION_WATCH_LIST")'
[arthas@1265]$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '#map1=@org.slf4j.LoggerFactory@getLogger("root").loggerContext.objectMap, #map1.get("CONFIGURATION_WATCH_LIST")'
@ConfigurationWatchList[
mainURL=@URL[jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/logback-spring.xml],
fileWatchList=@ArrayList[isEmpty=true;size=0],
lastModifiedList=@ArrayList[isEmpty=true;size=0],
noContextWarning=@Integer[0],
context=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
declaredOrigin=@ConfigurationWatchList[ch.qos.logback.core.joran.spi.ConfigurationWatchList@7ec933f],
]
从这一行 mainURL=@URL[jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/logback-spring.xml],
可以看出时间加载配置文件
使用classloader命令查找可能存在的logger配置文件
classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -r logback-spring.xml
[arthas@1265]$ classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -r logback-spring.xml
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/logback-spring.xml
Affect(row-cnt:1) cost in 4 ms.
可以知道加载的配置的具体来源。
可以尝试加载容易冲突的文件:
[arthas@1265]$ classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -r logback.xml
Affect(row-cnt:0) cost in 7 ms.
[arthas@1265]$ classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -r log4j.properties
Affect(row-cnt:0) cost in 3 ms.
·cnt:0· 显示没有这些冲突的文件
案例: 获取Spring Context
在这个案例里,展示获取spring context,再获取bean,然后调用函数。
使用tt命令获取到spring context
tt即 TimeTunnel,它可以记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测。
命令详细地址:https://arthas.aliyun.com/doc/tt.html
tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod
访问:curl http://localhost:61000/user/1
可以看到tt命令捕获到了一个请求:
[arthas@1265]$ tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 246 ms, listenerId: 1
INDEX TIMESTAMP COST(ms) IS-RET IS-EX OBJECT CLASS METHOD
P
-----------------------------------------------------------------------------------------------------------------------------
1000 2022-07-31 10:58: 272.2165 true false 0x1fbfb464 RequestMappingHandlerAdapte invokeHandlerMethod
44 08 r
使用tt命令从调用记录里获取到spring context
输入 q 或者 Ctrl + C 退出上面的 tt -t命令。
tt -i 1000 -w 'target.getApplicationContext()'
[arthas@1265]$ tt -i 1000 -w 'target.getApplicationContext()'
@AnnotationConfigEmbeddedWebApplicationContext[
reader=@AnnotatedBeanDefinitionReader[org.springframework.context.annotation.AnnotatedBeanDefinitionReader@3d0af4e0],
scanner=@ClassPathBeanDefinitionScanner[org.springframework.context.annotation.ClassPathBeanDefinitionScanner@36a60ad0],
annotatedClasses=null,
basePackages=null,
logger=@SLF4JLocationAwareLog[org.apache.commons.logging.impl.SLF4JLocationAwareLog@6c5d518f],
DISPATCHER_SERVLET_NAME=@String[dispatcherServlet],
embeddedServletContainer=@TomcatEmbeddedServletContainer[org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer@6d920dec],
servletConfig=null,
namespace=null,
servletContext=@ApplicationContextFacade[org.apache.catalina.core.ApplicationContextFacade@7d5594a],
themeSource=@ResourceBundleThemeSource[org.springframework.ui.context.support.ResourceBundleThemeSource@12333ea],
beanFactory=@DefaultListableBeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@dcf3e99: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,demoArthasApplication,org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory,adminFilterConfig,userController,serviceMonitor,helloWorldService,welcomeController,tomcatConfiguration,testFilter,staticResourceCustomizer,org.springframework.boot.autoconfigure.AutoConfigurationPackages,org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,org.springframework.boot.autoconfigure.condition.BeanTypeRegistry,propertySourcesPlaceholderConfigurer,org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration,standardJacksonObjectMapperBuilderCustomizer,spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties,org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor,org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.store,org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration,jacksonObjectMapperBuilder,org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration,jacksonObjectMapper,org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,jsonComponentModule,org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration$TomcatWebSocketConfiguration,websocketContainerCustomizer,org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration$EmbeddedTomcat,tomcatEmbeddedServletContainerFactory,org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,embeddedServletContainerCustomizerBeanPostProcessor,errorPageRegistrarBeanPostProcessor,org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletConfiguration,dispatcherServlet,spring.mvc-org.springframework.boot.autoconfigure.web.WebMvcProperties,org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration,dispatcherServletRegistration,org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,defaultValidator,methodValidationPostProcessor,org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration,conventionErrorViewResolver,org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,errorAttributes,basicErrorController,errorPageCustomizer,preserveErrorControllerTargetClassPostProcessor,spring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties,org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration,requestMappingHandlerAdapter,requestMappingHandlerMapping,mvcValidator,mvcContentNegotiationManager,mvcPathMatcher,mvcUrlPathHelper,viewControllerHandlerMapping,beanNameHandlerMapping,resourceHandlerMapping,mvcResourceUrlProvider,defaultServletHandlerMapping,mvcConversionService,mvcUriComponentsContributor,httpRequestHandlerAdapter,simpleControllerHandlerAdapter,handlerExceptionResolver,mvcViewResolver,mvcHandlerMappingIntrospector,org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter$FaviconConfiguration,faviconHandlerMapping,faviconRequestHandler,org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter,defaultViewResolver,viewResolver,welcomePageHandlerMapping,requestContextFilter,org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,hiddenHttpMethodFilter,httpPutFormContentFilter,org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,mbeanExporter,objectNamingStrategy,mbeanServer,org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$JdkDynamicAutoProxyConfiguration,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration,stringHttpMessageConverter,spring.http.encoding-org.springframework.boot.autoconfigure.web.HttpEncodingProperties,org.springframework.boot.autoconfigure.web.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration,mappingJackson2HttpMessageConverter,org.springframework.boot.autoconfigure.web.JacksonHttpMessageConvertersConfiguration,org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,messageConverters,org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration$FreeMarkerWebConfiguration,freeMarkerConfigurer,freeMarkerConfiguration,freeMarkerViewResolver,org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,spring.freemarker-org.springframework.boot.autoconfigure.freemarker.FreeMarkerProperties,org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties,org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,characterEncodingFilter,localeCharsetMappingsCustomizer,org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,multipartConfigElement,multipartResolver,spring.http.multipart-org.springframework.boot.autoconfigure.web.MultipartProperties,org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,serverProperties,duplicateServerPropertiesDetector,org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration$RestTemplateConfiguration,restTemplateBuilder,org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration]; root of factory hierarchy],
resourceLoader=null,
customClassLoader=@Boolean[false],
refreshed=@AtomicBoolean[true],
MESSAGE_SOURCE_BEAN_NAME=@String[messageSource],
LIFECYCLE_PROCESSOR_BEAN_NAME=@String[lifecycleProcessor],
APPLICATION_EVENT_MULTICASTER_BEAN_NAME=@String[applicationEventMulticaster],
logger=@SLF4JLocationAwareLog[org.apache.commons.logging.impl.SLF4JLocationAwareLog@774db68],
id=@String[application:61000],
displayName=@String[org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@2d38eb89],
parent=null,
environment=@StandardServletEnvironment[StandardServletEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[MapPropertySource {name='server.ports'}, StubPropertySource {name='servletConfigInitParams'}, ServletContextPropertySource {name='servletContextInitParams'}, MapPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, PropertiesPropertySource {name='applicationConfig: [classpath:/application.properties]'}]}],
beanFactoryPostProcessors=@ArrayList[isEmpty=false;size=3],
startupDate=@Long[1659236159627],
active=@AtomicBoolean[true],
closed=@AtomicBoolean[false],
startupShutdownMonitor=@Object[java.lang.Object@7a744b07],
shutdownHook=@[Thread[Thread-3,5,main]],
resourcePatternResolver=@ServletContextResourcePatternResolver[org.springframework.web.context.support.ServletContextResourcePatternResolver@5746420a],
lifecycleProcessor=@DefaultLifecycleProcessor[org.springframework.context.support.DefaultLifecycleProcessor@4f62876b],
messageSource=@DelegatingMessageSource[org.springframework.context.support.DelegatingMessageSource@6e6cba74],
applicationEventMulticaster=@SimpleApplicationEventMulticaster[org.springframework.context.event.SimpleApplicationEventMulticaster@3618f8cd],
applicationListeners=@LinkedHashSet[isEmpty=false;size=14],
earlyApplicationEvents=null,
classLoader=@LaunchedURLClassLoader[org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2],
protocolResolvers=@LinkedHashSet[isEmpty=true;size=0],
]
Affect(row-cnt:1) cost in 157 ms.
获取spring bean,并调用函数
tt -i 1000 -w 'target.getApplicationContext().getBean("helloWorldService").getHelloMessage()'
结果是:
[arthas@1265]$ tt -i 1000 -w 'target.getApplicationContext().getBean("helloWorldService").getHelloMessage()'
@String[Hello World]
Affect(row-cnt:1) cost in 4 ms.
案例: 排查HTTP请求返回401
在这个案例里,展示排查HTTP 401问题的技巧。
访问: curl http://localhost:61000/admin
结果是:
Something went wrong: 401 Unauthorized
我们知道401通常是被权限管理的Filter拦截了,那么到底是哪个Filter处理了这个请求,返回了401?
跟踪所有的Filter函数
开始trace:
trace javax.servlet.Filter *
访问: curl http://localhost:61000/admin
可以在调用树的最深层,找到AdminFilterConfig$AdminFilter返回了401:
Press Q or Ctrl+C to abort.
Affect(class count: 14 , method count: 75) cost in 892 ms, listenerId: 2
`---ts=2022-07-31 11:08:04;thread_name=http-nio-61000-exec-8;id=17;is_daemon=true;priority=5;TCCL=org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader@34810d7e
`---[11.727949ms] org.springframework.web.filter.OncePerRequestFilter:doFilter()
+---[1.43% 0.168039ms ] org.springframework.web.filter.OncePerRequestFilter:getAlreadyFilteredAttributeName() #95
| `---[85.07% 0.142946ms ] org.springframework.web.filter.OncePerRequestFilter:getAlreadyFilteredAttributeName()
| `---[76.02% 0.108668ms ] org.springframework.web.filter.OncePerRequestFilter:getFilterName() #161
| `---[61.56% 0.066899ms ] org.springframework.web.filter.GenericFilterBean:getFilterName()
| `---[34.64% 0.023177ms ] javax.servlet.FilterConfig:getFilterName() #293
+---[0.19% 0.022219ms ] javax.servlet.ServletRequest:getAttribute() #96
+---[1.72% 0.2018ms ] org.springframework.web.filter.OncePerRequestFilter:skipDispatch() #98
| `---[90.48% 0.18258ms ] org.springframework.web.filter.OncePerRequestFilter:skipDispatch()
| +---[77.33% 0.141193ms ] org.springframework.web.filter.OncePerRequestFilter:isAsyncDispatch() #118
| | `---[87.16% 0.123059ms ] org.springframework.web.filter.OncePerRequestFilter:isAsyncDispatch()
| | +---[63.62% 0.078291ms ] org.springframework.web.context.request.async.WebAsyncUtils:getAsyncManager() #137
| | `---[7.03% 0.008655ms ] org.springframework.web.context.request.async.WebAsyncManager:hasConcurrentResult() #137
| `---[7.41% 0.013537ms ] javax.servlet.http.HttpServletRequest:getAttribute() #121
+---[0.23% 0.026399ms ] org.springframework.web.filter.OncePerRequestFilter:shouldNotFilter() #98
| `---[17.61% 0.004648ms ] org.springframework.web.filter.OncePerRequestFilter:shouldNotFilter()
+---[0.20% 0.023243ms ] javax.servlet.ServletRequest:setAttribute() #105
+---[94.52% 11.085815ms ] org.springframework.web.filter.OncePerRequestFilter:doFilterInternal() #107
| `---[99.77% 11.060142ms ] org.springframework.web.filter.CharacterEncodingFilter:doFilterInternal()
| +---[0.26% 0.028245ms ] org.springframework.web.filter.CharacterEncodingFilter:getEncoding() #188
| | `---[32.41% 0.009153ms ] org.springframework.web.filter.CharacterEncodingFilter:getEncoding()
| +---[0.21% 0.023577ms ] org.springframework.web.filter.CharacterEncodingFilter:isForceRequestEncoding() #190
| | `---[32.24% 0.007601ms ] org.springframework.web.filter.CharacterEncodingFilter:isForceRequestEncoding()
| +---[0.13% 0.01429ms ] javax.servlet.http.HttpServletRequest:setCharacterEncoding() #191
| +---[0.18% 0.020444ms ] org.springframework.web.filter.CharacterEncodingFilter:isForceResponseEncoding() #193
| | `---[24.96% 0.005102ms ] org.springframework.web.filter.CharacterEncodingFilter:isForceResponseEncoding()
| `---[98.76% 10.922827ms ] javax.servlet.FilterChain:doFilter() #197
| `---[99.74% 10.894521ms ] org.springframework.web.filter.OncePerRequestFilter:doFilter()
| +---[0.54% 0.058318ms ] org.springframework.web.filter.OncePerRequestFilter:getAlreadyFilteredAttributeName() #95
| | `---[80.10% 0.046711ms ] org.springframework.web.filter.OncePerRequestFilter:getAlreadyFilteredAttributeName()
| | `---[74.74% 0.03491ms ] org.springframework.web.filter.OncePerRequestFilter:getFilterName() #161
| | `---[47.55% 0.016599ms ] org.springframework.web.filter.GenericFilterBean:getFilterName()
| | `---[33.96% 0.005637ms ] javax.servlet.FilterConfig:getFilterName() #293
| +---[0.06% 0.006133ms ] javax.servlet.ServletRequest:getAttribute() #96
| +---[0.73% 0.079293ms ] org.springframework.web.filter.OncePerRequestFilter:skipDispatch() #98
| | `---[85.13% 0.067502ms ] org.springframework.web.filter.OncePerRequestFilter:skipDispatch()
| | +---[59.29% 0.040022ms ] org.springframework.web.filter.OncePerRequestFilter:isAsyncDispatch() #118
| | | `---[70.97% 0.028404ms ] org.springframework.web.filter.OncePerRequestFilter:isAsyncDispatch()
| | | +---[19.93% 0.005662ms ] org.springframework.web.context.request.async.WebAsyncUtils:getAsyncManager() #137
| | | `---[16.29% 0.004628ms ] org.springframework.web.context.request.async.WebAsyncManager:hasConcurrentResult() #137
| | `---[9.06% 0.006118ms ] javax.servlet.http.HttpServletRequest:getAttribute() #121
| +---[0.27% 0.029301ms ] org.springframework.web.filter.OncePerRequestFilter:shouldNotFilter() #98
| | `---[15.14% 0.004436ms ] org.springframework.web.filter.OncePerRequestFilter:shouldNotFilter()
| +---[0.07% 0.007503ms ] javax.servlet.ServletRequest:setAttribute() #105
| +---[97.79% 10.653682ms ] org.springframework.web.filter.OncePerRequestFilter:doFilterInternal() #107
| | `---[99.80% 10.632739ms ] org.springframework.web.filter.HiddenHttpMethodFilter:doFilterInternal()
| | +---[0.90% 0.096057ms ] javax.servlet.http.HttpServletRequest:getMethod() #74
| | `---[98.67% 10.490869ms ] javax.servlet.FilterChain:doFilter() #81
| | `---[99.71% 10.460729ms ] org.springframework.web.filter.OncePerRequestFilter:doFilter()
| | +---[0.88% 0.091861ms ] org.springframework.web.filter.OncePerRequestFilter:getAlreadyFilteredAttributeName() #95
| | | `---[84.71% 0.077811ms ] org.springframework.web.filter.OncePerRequestFilter:getAlreadyFilteredAttributeName()
| | | `---[84.89% 0.066053ms ] org.springframework.web.filter.OncePerRequestFilter:getFilterName() #161
| | | `---[49.47% 0.032676ms ] org.springframework.web.filter.GenericFilterBean:getFilterName()
| | | `---[27.58% 0.009013ms ] javax.servlet.FilterConfig:getFilterName() #293
| | +---[0.08% 0.008042ms ] javax.servlet.ServletRequest:getAttribute() #96
| | +---[0.79% 0.082689ms ] org.springframework.web.filter.OncePerRequestFilter:skipDispatch() #98
| | | `---[84.96% 0.070255ms ] org.springframework.web.filter.OncePerRequestFilter:skipDispatch()
| | | +---[61.97% 0.043534ms ] org.springframework.web.filter.OncePerRequestFilter:isAsyncDispatch() #118
| | | | `---[68.51% 0.029826ms ] org.springframework.web.filter.OncePerRequestFilter:isAsyncDispatch()
| | | | +---[19.84% 0.005917ms ] org.springframework.web.context.request.async.WebAsyncUtils:getAsyncManager() #137
| | | | `---[16.33% 0.00487ms ] org.springframework.web.context.request.async.WebAsyncManager:hasConcurrentResult() #137
| | | `---[9.21% 0.006467ms ] javax.servlet.http.HttpServletRequest:getAttribute() #121
| | +---[0.18% 0.018508ms ] org.springframework.web.filter.OncePerRequestFilter:shouldNotFilter() #98
| | | `---[24.56% 0.004545ms ] org.springframework.web.filter.OncePerRequestFilter:shouldNotFilter()
| | +---[0.08% 0.008386ms ] javax.servlet.ServletRequest:setAttribute() #105
| | +---[97.41% 10.189293ms ] org.springframework.web.filter.OncePerRequestFilter:doFilterInternal() #107
| | | `---[99.78% 10.167312ms ] org.springframework.web.filter.HttpPutFormContentFilter:doFilterInternal()
| | | +---[0.23% min=0.006886ms,max=0.01665ms,total=0.023536ms,count=2] javax.servlet.http.HttpServletRequest:getMethod() #94
| | | `---[99.29% 10.094763ms ] javax.servlet.FilterChain:doFilter() #109
| | | `---[99.78% 10.072333ms ] org.springframework.web.filter.OncePerRequestFilter:doFilter()
| | | +---[0.50% 0.050747ms ] org.springframework.web.filter.OncePerRequestFilter:getAlreadyFilteredAttributeName() #95
| | | | `---[77.53% 0.039345ms ] org.springframework.web.filter.OncePerRequestFilter:getAlreadyFilteredAttributeName()
| | | | `---[71.66% 0.028196ms ] org.springframework.web.filter.OncePerRequestFilter:getFilterName() #161
| | | | `---[57.42% 0.016189ms ] org.springframework.web.filter.GenericFilterBean:getFilterName()
| | | | `---[29.68% 0.004805ms ] javax.servlet.FilterConfig:getFilterName() #293
| | | +---[0.06% 0.006146ms ] javax.servlet.ServletRequest:getAttribute() #96
| | | +---[0.75% 0.075631ms ] org.springframework.web.filter.OncePerRequestFilter:skipDispatch() #98
| | | | `---[84.09% 0.063599ms ] org.springframework.web.filter.OncePerRequestFilter:skipDispatch()
| | | | +---[63.14% 0.040154ms ] org.springframework.web.filter.OncePerRequestFilter:isAsyncDispatch() #118
| | | | | `---[71.90% 0.028871ms ] org.springframework.web.filter.OncePerRequestFilter:isAsyncDispatch()
| | | | | +---[20.31% 0.005865ms ] org.springframework.web.context.request.async.WebAsyncUtils:getAsyncManager() #137
| | | | | `---[16.89% 0.004875ms ] org.springframework.web.context.request.async.WebAsyncManager:hasConcurrentResult() #137
| | | | `---[9.31% 0.005924ms ] javax.servlet.http.HttpServletRequest:getAttribute() #121
| | | +---[0.17% 0.016676ms ] org.springframework.web.filter.OncePerRequestFilter:shouldNotFilter() #98
| | | | `---[26.94% 0.004492ms ] org.springframework.web.filter.OncePerRequestFilter:shouldNotFilter()
| | | +---[0.08% 0.007673ms ] javax.servlet.ServletRequest:setAttribute() #105
| | | +---[97.61% 9.83128ms ] org.springframework.web.filter.OncePerRequestFilter:doFilterInternal() #107
| | | | `---[99.74% 9.805524ms ] org.springframework.web.filter.RequestContextFilter:doFilterInternal()
| | | | +---[0.27% 0.026736ms ] org.springframework.web.context.request.ServletRequestAttributes:<init>() #95
| | | | +---[1.83% 0.179362ms ] org.springframework.web.filter.RequestContextFilter:initContextHolders() #96
| | | | | `---[89.38% 0.160307ms ] org.springframework.web.filter.RequestContextFilter:initContextHolders()
| | | | | +---[16.07% 0.025762ms ] javax.servlet.http.HttpServletRequest:getLocale() #111
| | | | | +---[18.79% 0.030129ms ] org.springframework.context.i18n.LocaleContextHolder:setLocale() #111
| | | | | +---[17.73% 0.028421ms ] org.springframework.web.context.request.RequestContextHolder:setRequestAttributes() #112
| | | | | `---[16.03% 0.02569ms ] org.apache.commons.logging.Log:isDebugEnabled() #113
| | | | +---[96.32% 9.444643ms ] javax.servlet.FilterChain:doFilter() #99
| | | | | `---[1.12% 0.105421ms ] com.example.demo.arthas.AdminFilterConfig$AdminFilter:doFilter()
| | | | | `---[38.80% 0.040903ms ] javax.servlet.http.HttpServletResponse:sendError() #38
| | | | +---[0.68% 0.066797ms ] org.springframework.web.filter.RequestContextFilter:resetContextHolders() #102
| | | | | `---[75.50% 0.050435ms ] org.springframework.web.filter.RequestContextFilter:resetContextHolders()
| | | | | +---[41.78% 0.021073ms ] org.springframework.context.i18n.LocaleContextHolder:resetLocaleContext() #119
| | | | | `---[20.19% 0.010185ms ] org.springframework.web.context.request.RequestContextHolder:resetRequestAttributes() #120
| | | | +---[0.06% 0.005758ms ] org.apache.commons.logging.Log:isDebugEnabled() #103
| | | | `---[0.20% 0.01992ms ] org.springframework.web.context.request.ServletRequestAttributes:requestCompleted() #106
| | | `---[0.26% 0.0266ms ] javax.servlet.ServletRequest:removeAttribute() #111
| | `---[0.06% 0.006689ms ] javax.servlet.ServletRequest:removeAttribute() #111
| `---[0.06% 0.006431ms ] javax.servlet.ServletRequest:removeAttribute() #111
`---[0.05% 0.005963ms ] javax.servlet.ServletRequest:removeAttribute() #111
通过stack获取调用栈
上面是通过trace命令来获取信息,从结果里,我们可以知道通过stack跟踪HttpServletResponse:sendError(),同样可以知道是哪个Filter返回了401
执行:
stack javax.servlet.http.HttpServletResponse sendError 'params[0]==401'
访问: curl http://localhost:61000/admin
[arthas@1265]$ stack javax.servlet.http.HttpServletResponse sendError 'params[0]==401'
Press Q or Ctrl+C to abort.
Affect(class count: 3 , method count: 4) cost in 154 ms, listenerId: 3
ts=2022-07-31 11:12:25;thread_name=http-nio-61000-exec-10;id=19;is_daemon=true;priority=5;TCCL=org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader@34810d7e
@org.apache.catalina.connector.Response.sendError()
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:462)
at com.example.demo.arthas.AdminFilterConfig$AdminFilter.doFilter(AdminFilterConfig.java:38)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
ts=2022-07-31 11:12:25;thread_name=http-nio-61000-exec-10;id=19;is_daemon=true;priority=5;TCCL=org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader@34810d7e
@org.apache.catalina.connector.ResponseFacade.sendError()
at com.example.demo.arthas.AdminFilterConfig$AdminFilter.doFilter(AdminFilterConfig.java:38)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
案例: 排查HTTP请求返回404
在这个案例里,展示排查HTTP 404问题的技巧。
访问: curl http://localhost:61000/a.txt
结果是:
Something went wrong: 404 Not Found
那么到底是哪个Servlet处理了这个请求,返回了404?
跟踪所有的Servlet函数
开始trace:
trace javax.servlet.Servlet * > /tmp/servlet.txt
访问: curl http://localhost:61000/a.txt
在Terminal 3里,查看/tmp/servlet.txt的内容:
less /tmp/servlet.txt
/tmp/servlet.txt里的内容会比较多,需要耐心找到调用树里最长的地方。
可以发现请求最终是被freemarker处理的:
`---[13.974188ms] org.springframework.web.servlet.ViewResolver:resolveViewName(); +---[0.045561ms] javax.servlet.GenericServlet:<init>()
+---[min=0.045545ms,max=0.074342ms,total=0.119887ms,count=2] org.springframework.web.servlet.view.freemarker.FreeMarkerView$GenericServletAdapter:<init>()
+---[0.170895ms] javax.servlet.GenericServlet:init()
| `---[0.068578ms] javax.servlet.GenericServlet:init()
| `---[0.021793ms] javax.servlet.GenericServlet:init()
`---[0.164035ms] javax.servlet.GenericServlet:getServletContext()
案例: 理解Spring Boot应用的ClassLoader结构
下面介绍classloader命令的功能。
先访问一个jsp网页,触发jsp的加载: 访问: curl http://localhost:61000/hello
shell@Alicloud:~$ curl http://localhost:61000/hello
<html>
<body>
<h1>Hello World - jsp</h1>
</body>
列出所有ClassLoader
classloader -l
[arthas@1265]$ classloader -l
name loadedCount hash parent
BootstrapClassLoader 3610 null null
com.taobao.arthas.agent.ArthasClassloader@650ca0b5 1702 650ca0b5 sun.misc.Launcher$ExtClassLoader@7
d0587f1
g.apache.jasper.servlet.JasperLoader@105e9f49 1 105e9f49 TomcatEmbeddedWebappClassLoader
context: ROOT
delegate: true
----------> Parent Classloader:
org.springframework.boot.loader.La
unchedURLClassLoader@19469ea2
0 34810d7e org.springframework.boot.loader.La
unchedURLClassLoader@19469ea2
9ea2
org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2 5336 19469ea2 sun.misc.Launcher$AppClassLoader@1
b6d3586
sun.misc.Launcher$AppClassLoader@1b6d3586 45 1b6d3586 sun.misc.Launcher$ExtClassLoader@7
d0587f1
sun.misc.Launcher$ExtClassLoader@7d0587f1 63 7d0587f1 null
Affect(row-cnt:7) cost in 12 ms.
TomcatEmbeddedWebappClassLoade
r 加载的class数量是0,所以在spring boot embedded tomcat里,它只是一个空壳,所有的类加载都是LaunchedURLClassLoader
完成的
列出ClassLoader里加载的所有类
列出上面的org.apache.jasper.servlet.JasperLoader
加载的类:
classloader -a --classLoaderClass org.apache.jasper.servlet.JasperLoader
注:同ognl, 也可用-c 而不用–classLoaderClass指定
反编译jsp的代码
jad org.apache.jsp.jsp.hello_jsp
[arthas@1919]$ jad org.apache.jsp.jsp.hello_jsp
ClassLoader:
+-org.apache.jasper.servlet.JasperLoader@34470234
r@19469ea2
+-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
查看ClassLoader树
classloader -t
[arthas@1919]$ classloader -t
+-BootstrapClassLoader
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
+-com.taobao.arthas.agent.ArthasClassloader@650ca0b5
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
assLoader@19469ea2
+-org.apache.jasper.servlet.JasperLoader@34470234
Affect(row-cnt:7) cost in 23 ms.
[arthas@1919]$
[arthas@1919]$ classloader -t
+-BootstrapClassLoader
+-sun.misc.Launcher$ExtClassLoader@7d0587f1
+-com.taobao.arthas.agent.ArthasClassloader@650ca0b5
+-sun.misc.Launcher$AppClassLoader@1b6d3586
+-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
assLoader@19469ea2
+-org.apache.jasper.servlet.JasperLoader@34470234
Affect(row-cnt:7) cost in 4 ms.
注意:请使用你的classLoaderHash值覆盖 ,然后手动执行下面相关命令:
列出ClassLoader的urls
比如上面查看到的spring LaunchedURLClassLoader的 hashcode是19469ea2,可以通过-c或者–classLoaderClass参数来列出它的所有urls:
classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader
[arthas@1919]$ classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-starter-aop-1.5.13.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-starter-1.5.13.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-1.5.13.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-autoconfigure-1.5.13.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-starter-logging-1.5.13.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/logback-classic-1.1.11.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/logback-core-1.1.11.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/jcl-over-slf4j-1.7.25.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/jul-to-slf4j-1.7.25.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/log4j-over-slf4j-1.7.25.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/snakeyaml-1.17.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-aop-4.3.17.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-beans-4.3.17.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/aspectjweaver-1.8.13.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/tomcat-embed-jasper-8.5.31.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/tomcat-embed-core-8.5.31.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/tomcat-annotations-api-8.5.31.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/tomcat-embed-el-8.5.31.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/ecj-3.12.3.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/jstl-1.2.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-starter-web-1.5.13.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-starter-tomcat-1.5.13.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/tomcat-embed-websocket-8.5.31.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/hibernate-validator-5.3.6.Final.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/validation-api-1.1.0.Final.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/jboss-logging-3.3.2.Final.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/classmate-1.3.4.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/jackson-databind-2.8.11.1.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/jackson-annotations-2.8.0.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/jackson-core-2.8.11.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-web-4.3.17.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-context-4.3.17.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-webmvc-4.3.17.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-expression-4.3.17.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-starter-freemarker-1.5.13.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/freemarker-2.3.28.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-context-support-4.3.17.RELEASE.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/slf4j-api-1.7.25.jar!/
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-core-4.3.17.RELEASE.jar!/
Affect(row-cnt:80) cost in 9 ms.
加载指定ClassLoader里的资源文件
查找指定的资源文件: classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -r logback-spring.xml
[arthas@1919]$ classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -r logback-spring.xml
jar:file:/home/shell/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/logback-spring.xml
Affect(row-cnt:1) cost in 1 ms.
[arthas@1919]$
尝试加载指定的类
比如用上面的spring LaunchedURLClassLoader 尝试加载 java.lang.String :
classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --load java.lang.String
[arthas@1266]$ classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --load java.lang.String
load class success.
class-info java.lang.String
code-source
name java.lang.String
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name String
modifier final,public
annotation
interfaces java.io.Serializable,java.lang.Comparable,java.lang.CharSequence
super-class +-java.lang.Object
class-loader
classLoaderHash null
案例:查找Top N线程
查看所有线程信息
thread
[arthas@1266]$ thread
Threads Total: 35, NEW: 0, RUNNABLE: 11, BLOCKED: 0, WAITING: 14, TIMED_WAITING: 5, TERMINATED: 0, Internal threads: 5
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPTE DAEMON
44 arthas-command-execute system 5 RUNNABLE 0.62 0.001 0:0.018 false true
-1 VM Periodic Task Thread - -1 - 0.09 0.000 0:0.104 false true
-1 VM Thread - -1 - 0.02 0.000 0:0.568 false true
2 Reference Handler system 10 WAITING 0.0 0.000 0:0.008 false true
3 Finalizer system 8 WAITING 0.0 0.000 0:0.014 false true
4 Signal Dispatcher system 9 RUNNABLE 0.0 0.000 0:0.000 false true
31 Attach Listener system 9 RUNNABLE 0.0 0.000 0:0.006 false true
33 arthas-timer system 9 WAITING 0.0 0.000 0:0.000 false true
36 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.025 false true
37 arthas-NettyWebsocketTtyBootst system 5 RUNNABLE 0.0 0.000 0:0.001 false true
38 arthas-NettyWebsocketTtyBootst system 5 RUNNABLE 0.0 0.000 0:0.001 false true
39 arthas-shell-server system 9 TIMED_WAI 0.0 0.000 0:0.000 false true
40 arthas-session-manager system 9 TIMED_WAI 0.0 0.000 0:0.000 false true
41 arthas-UserStat system 9 WAITING 0.0 0.000 0:0.000 false true
43 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.090 false true
13 ContainerBackgroundProcessor[S main 5 TIMED_WAI 0.0 0.000 0:0.002 false true
14 container-0 main 5 TIMED_WAI 0.0 0.000 0:0.000 false false
15 NioBlockingSelector.BlockPolle main 5 RUNNABLE 0.0 0.000 0:0.008 false true
16 http-nio-61000-exec-1 main 5 WAITING 0.0 0.000 0:0.000 false true
17 http-nio-61000-exec-2 main 5 WAITING 0.0 0.000 0:0.000 false true
18 http-nio-61000-exec-3 main 5 WAITING 0.0 0.000 0:0.000 false true
19 http-nio-61000-exec-4 main 5 WAITING 0.0 0.000 0:0.000 false true
20 http-nio-61000-exec-5 main 5 WAITING 0.0 0.000 0:0.000 false true
21 http-nio-61000-exec-6 main 5 WAITING 0.0 0.000 0:0.000 false true
22 http-nio-61000-exec-7 main 5 WAITING 0.0 0.000 0:0.000 false true
23 http-nio-61000-exec-8 main 5 WAITING 0.0 0.000 0:0.000 false true
24 http-nio-61000-exec-9 main 5 WAITING 0.0 0.000 0:0.000 false true
25 http-nio-61000-exec-10 main 5 WAITING 0.0 0.000 0:0.000 false true
26 http-nio-61000-ClientPoller-0 main 5 RUNNABLE 0.0 0.000 0:0.010 false true
27 http-nio-61000-Acceptor-0 main 5 RUNNABLE 0.0 0.000 0:0.000 false true
28 http-nio-61000-AsyncTimeout main 5 TIMED_WAI 0.0 0.000 0:0.008 false true
30 DestroyJavaVM main 5 RUNNABLE 0.0 0.000 0:3.208 false false
-1 C1 CompilerThread1 - -1 - 0.0 0.000 0:1.615 false true
-1 C2 CompilerThread0 - -1 - 0.0 0.000 0:6.116 false true
-1 Service Thread - -1 - 0.0 0.000 0:0.000 false true
查看具体线程的栈
查看线程ID 16的栈:
thread 16
[arthas@1266]$ thread 16
"http-nio-61000-exec-1" Id=16 WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@7df9709a
at sun.misc.Unsafe.park(Native Method)
- waiting on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@7df9709a
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:103)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:31)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
查看CPU使用率top n线程的栈
thread -n 3
"C1 CompilerThread1" [Internal] cpuUsage=2.25% deltaTime=4ms time=1644ms
"arthas-command-execute" Id=44 cpuUsage=0.38% deltaTime=0ms time=32ms RUNNABLE
at sun.management.ThreadImpl.dumpThreads0(Native Method)
at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:461)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:206)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:122)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:385)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"VM Periodic Task Thread" [Internal] cpuUsage=0.05% deltaTime=0ms time=156ms
查看5秒内的CPU使用率top n线程栈
thread -n 3 -i 5000
[arthas@1266]$ thread -n 3 -i 5000
"VM Periodic Task Thread" [Internal] cpuUsage=0.07% deltaTime=3ms time=183ms
"C1 CompilerThread1" [Internal] cpuUsage=0.06% deltaTime=2ms time=1650ms
"arthas-command-execute" Id=44 cpuUsage=0.01% deltaTime=0ms time=36ms RUNNABLE
at sun.management.ThreadImpl.dumpThreads0(Native Method)
at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:461)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:206)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:122)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:385)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
查找线程是否有阻塞
thread -b
[arthas@1266]$ thread -b
No most blocking thread found!
Web Console
Arthas支持通过Web Socket来连接。
本地体验
当在本地启动时,可以访问 http://127.0.0.1:8563/ ,通过浏览器来使用Arthas。
推荐通过“快速入门”来体验: https://arthas.aliyun.com/doc/quick-start.html
Exit/Stop
reset
Arthas在 watch/trace 等命令时,实际上是修改了应用的字节码,插入增强的代码。显式执行 reset 命令,可以清除掉这些增强代码。
reset
[arthas@1266]$ reset
Affect(class count: 0 , method count: 0) cost in 1 ms, listenerId: 0
退出Arthas
用 exit 或者 quit 命令可以退出Arthas。
exit
Ctrl+C
退出Arthas之后,还可以再次用 java -jar arthas-boot.jar 来连接。
java -jar arthas-boot.jar
彻底退出Arthas
exit/quit命令只是退出当前session,arthas server还在目标进程中运行。
想完全退出Arthas,可以执行 stop 命令。
stop
arthas-boot支持的参数
arthas-boot.jar 支持很多参数,可以执行 java -jar arthas-boot.jar -h
来查看。
java -jar arthas-boot.jar -h
允许外部访问
默认情况下, arthas server侦听的是 127.0.0.1 这个IP,如果希望远程可以访问,可以使用–target-ip的参数。
java -jar arthas-boot.jar --target-ip
列出所有的版本
java -jar arthas-boot.jar --versions
使用指定版本:
java -jar arthas-boot.jar --use-version 3.1.0
只侦听Telnet端口,不侦听HTTP端口
java -jar arthas-boot.jar --telnet-port 9999 --http-port -1
打印运行的详情
java -jar arthas-boot.jar -v