feat(docs): new docs site by vuepress2 (#2220)

pull/2224/head
Fatpandac 3 years ago committed by GitHub
parent c8645db3f0
commit 55147e7cc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,21 @@
name: auto prettier
on:
push:
paths:
- 'site-vuepress/**'
jobs:
prettier:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
ref: ${{ github.head_ref }}
- name: Prettify code
uses: creyD/prettier_action@v4.2
with:
prettier_options: --config ./site-vuepress/.prettierrc.json --ignore-path ./site-vuepress/.prettierignore --write ./site-vuepress

@ -0,0 +1,3 @@
node_modules
.temp
.cache

@ -0,0 +1,3 @@
{
"tabWidth": 2
}

@ -0,0 +1,14 @@
<h1>Arthas 文档网站<sub>(power by vuepress)</sub></h1>
## 项目运行
```shell
# 安装依赖
npm install
# 启动项目
npm run docs:dev
# 发布项目(打包到 docs/.vuepress/dist 目录)
npm run docs:build
```

@ -0,0 +1,53 @@
import { defineClientConfig } from "@vuepress/client";
import { usePageData } from "@vuepress/client";
import oldContributorsData from "./oldContributorsData.json";
const addOldDocsContributors = () => {
const page = usePageData();
if (!page.value.git) return;
const filePath = page.value.filePathRelative;
const contributors = page.value.git.contributors;
const oldContributors = oldContributorsData[filePath];
const haveSameContributor = (contributors, oldContributor) => {
return contributors.find(
(contributor) =>
contributor.name === oldContributor.name &&
contributor.email === oldContributor.email
);
};
if (oldContributors) {
oldContributors.forEach((oldContributor) => {
if (!haveSameContributor(contributors, oldContributor)) {
contributors.push(oldContributor);
} else {
haveSameContributor(contributors, oldContributor).commits +=
oldContributor.commits;
}
});
}
// sort contributors by commits
contributors?.sort((a, b) => b.commits - a.commits);
};
export default defineClientConfig({
enhance({ router }) {
router.afterEach((to, from) => {
if (to.fullPath !== from.fullPath) {
addOldDocsContributors();
}
});
router.beforeEach((to, from, next) => {
if (typeof _hmt != "undefined") {
if (to.path && to.fullPath !== from.fullPath) {
_hmt.push(["_trackPageview", to.fullPath]);
}
}
next();
});
},
});

@ -0,0 +1,109 @@
const { localTheme } = require("./theme/index");
const { copyCodePlugin } = require("vuepress-plugin-copy-code2");
const { redirectPlugin } = require("vuepress-plugin-redirect");
const { searchPlugin } = require("@vuepress/plugin-search");
module.exports = {
title: "arthas",
description: "arthas user document",
head: require("./configs/head"),
locales: {
"/": {
lang: "zh-CN",
title: "arthas",
description: "arthas 使用文档",
},
"/en/": {
lang: "en-US",
title: "arthas",
description: "arthas user document",
},
},
theme: localTheme({
logo: "/images/arthas_light.png",
logoDark: "/images/arthas_dark.png",
repo: "alibaba/arthas",
docsDir: "site-vuepress/docs",
docsBranch: "master",
locales: {
"/": {
selectLanguageName: "简体中文",
selectLanguageText: "Languages",
editLinkText: "在 GitHub 上编辑此页",
lastUpdated: "上次更新",
contributorsText: "贡献者",
backToHome: "回到首页",
warning: "注意",
tip: "提示",
danger: "警告",
// 404 page
notFound: [
"这里什么都没有",
"我们怎么到这来了?",
"这是一个 404 页面",
"看起来我们进入了错误的链接",
],
backToHome: "返回首页",
openInNewWindow: "在新窗口打开",
toggleColorMode: "切换颜色模式",
toggleSidebar: "切换侧边栏",
navbar: require("./configs/navbar/zh"),
sidebar: require("./configs/sidebar/zh"),
sidebarDepth: 0,
},
"/en/": {
selectLanguageName: "English",
selectLanguageText: "Languages",
editLinkText: "Edit this page on GitHub",
navbar: require("./configs/navbar/en"),
sidebar: require("./configs/sidebar/en"),
sidebarDepth: 0,
},
},
}),
plugins: [
copyCodePlugin({
showInMobile: false,
pure: true,
locales: {
"/": {
hint: "复制代码",
},
"/en/": {
hint: "Copy code",
},
},
}),
searchPlugin({
locales: {
"/": {
placeholder: "搜索文档",
},
"/en/": {
placeholder: "Search Docs",
},
},
}),
redirectPlugin({
config: (app) => {
const redirects = Object.fromEntries(
app.pages
.filter((page) => page.path.startsWith("/en/doc/"))
.map((page) => [
page.path.replace(/^\/en\/doc\//, "/doc/en/"),
page.path,
])
);
delete redirects["/doc/en/"];
redirects["/doc/en/index.html"] = "/en/doc/index.html";
redirects["/en-us/index.html"] = "/en/index.html";
redirects["/zh-cn/index.html"] = "/index.html";
return redirects;
},
}),
],
};

@ -0,0 +1,20 @@
module.exports = [
["link", { rel: "icon", href: "/images/favicon.ico" }],
[
"meta",
{ name: "viewport", content: "width=device-width, initial-scale=1.0" },
],
[
"script",
{},
`
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?d5c5e25b100f0eb51a4c35c8a86ea9b4";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
`,
],
];

@ -0,0 +1,52 @@
module.exports = [
{
text: "HOME",
link: "/en/",
},
{
text: "ONLINE TUTORIALS",
link: "/doc/arthas-tutorials.html?language=en&id=arthas-basics",
target: "_blank",
},
{
text: "DOCS",
link: "/en/doc",
},
{
text: "SOLUTIONS",
children: [
{
text: "Microservice solutions",
link: "https://cn.aliyun.com/product/aliware/mse?spm=arthas.topbar.0.0.0",
},
{
text: "Distributed transaction solutions",
link: "https://www.aliyun.com/aliware/txc?spm=arthas.topbar.0.0.0",
},
{
text: "High-availability solution",
link: "https://www.aliyun.com/product/ahas?spm=arthas.topbar.0.0.0",
},
{
text: "Serverless solution for miscoservices",
link: "https://cn.aliyun.com/product/aliware/sae?spm=arthas.topbar.0.0.0",
},
{
text: "PaaS solution",
link: "https://www.aliyun.com/product/edas?spm=arthas.topbar.0.0.0",
},
{
text: "Service mesh solution",
link: "https://www.aliyun.com/product/servicemesh?spm=arthas.topbar.0.0.0",
},
],
},
{
text: "DOWNLOAD",
link: "/en/doc/download.md",
},
{
text: "COMMUNITY",
link: "https://github.com/alibaba/arthas/issues",
},
];

@ -0,0 +1,52 @@
module.exports = [
{
text: "首页",
link: "/",
},
{
text: "在线教程",
link: "/doc/arthas-tutorials.html?language=cn&id=arthas-basics",
target: "_blank",
},
{
text: "文档",
link: "/doc/",
},
{
text: "解决方案",
children: [
{
text: "微服务解决方案",
link: "https://cn.aliyun.com/product/aliware/mse?spm=arthas.topbar.0.0.0",
},
{
text: "分布式事务解决方案",
link: "https://www.aliyun.com/aliware/txc?spm=arthas.topbar.0.0.0",
},
{
text: "高可用解决方案",
link: "https://www.aliyun.com/product/ahas?spm=arthas.topbar.0.0.0",
},
{
text: "微服务Serverless解决方案",
link: "https://cn.aliyun.com/product/aliware/sae?spm=arthas.topbar.0.0.0",
},
{
text: "PaaS解决方案",
link: "https://www.aliyun.com/product/edas?spm=arthas.topbar.0.0.0",
},
{
text: "服务网格解决方案",
link: "https://www.aliyun.com/product/servicemesh?spm=arthas.topbar.0.0.0",
},
],
},
{
text: "下载",
link: "/doc/download.md",
},
{
text: "社区",
link: "https://github.com/alibaba/arthas/issues",
},
];

@ -0,0 +1,148 @@
module.exports = {
"/en/doc": [
{
text: "DOCS",
children: [
"/en/doc/README.md",
"/en/doc/quick-start.md",
"/en/doc/install-detail.md",
"/en/doc/download.md",
"/en/doc/advanced-use.md",
{
text: "Other features",
collapsible: true,
children: [
"/en/doc/async.md",
"/en/doc/save-log.md",
"/en/doc/batch-support.md",
{
text: "How to use ognl",
link: "",
children: [
{
text: "Basic ognl example",
link: "https://github.com/alibaba/arthas/issues/11",
},
{
text: "Ognl special uses",
link: "https://github.com/alibaba/arthas/issues/71",
},
],
},
],
},
{
text: "Commands",
collapsible: true,
link: "/en/doc/commands.md",
children: [
{
text: "jvm",
collapsible: false,
children: [
"/en/doc/dashboard.md",
"/en/doc/thread.md",
"/en/doc/jvm.md",
"/en/doc/memory.md",
"/en/doc/sysprop.md",
"/en/doc/sysenv.md",
"/en/doc/vmoption.md",
"/en/doc/perfcounter.md",
"/en/doc/logger.md",
"/en/doc/mbean.md",
"/en/doc/getstatic.md",
"/en/doc/ognl.md",
"/en/doc/heapdump.md",
"/en/doc/vmtool.md",
],
},
{
text: "class/classloader",
collapsible: false,
children: [
"/en/doc/sc.md",
"/en/doc/sm.md",
"/en/doc/jad.md",
"/en/doc/classloader.md",
"/en/doc/mc.md",
"/en/doc/dump.md",
"/en/doc/retransform.md",
"/en/doc/redefine.md",
],
},
{
text: "monitor/watch/trace - related",
collapsible: false,
children: [
"/en/doc/monitor.md",
"/en/doc/watch.md",
"/en/doc/trace.md",
"/en/doc/stack.md",
"/en/doc/tt.md",
],
},
{
text: "other",
collapsible: false,
children: [
"/en/doc/profiler.md",
"/en/doc/cat.md",
"/en/doc/echo.md",
"/en/doc/grep.md",
"/en/doc/base64.md",
"/en/doc/tee.md",
"/en/doc/pwd.md",
"/en/doc/auth.md",
"/en/doc/options.md",
],
},
{
text: "Basic",
collapsible: false,
children: [
"/en/doc/help.md",
"/en/doc/cls.md",
"/en/doc/session.md",
"/en/doc/reset.md",
"/en/doc/history.md",
"/en/doc/quit.md",
"/en/doc/stop.md",
{
text: "keymap",
link: "/en/doc/keymap.md",
},
],
},
],
},
"/en/doc/web-console.md",
"/en/doc/tunnel.md",
"/en/doc/http-api.md",
"/en/doc/docker.md",
"/en/doc/spring-boot-starter.md",
"/en/doc/idea-plugin.md",
"/en/doc/faq.md",
{
text: "User cases",
link: "https://github.com/alibaba/arthas/issues?q=label%3Auser-case",
},
{
text: "Start me at github",
link: "https://github.com/alibaba/arthas",
},
{
text: "Compile and debug/CONTRIBUTING",
link: "https://github.com/alibaba/arthas/blob/master/CONTRIBUTING.md",
},
{
text: "Release Notes",
link: "https://github.com/alibaba/arthas/releases",
},
{
text: "Contact us",
link: "/en/doc/contact-us.md",
},
],
},
],
};

@ -0,0 +1,152 @@
module.exports = {
"/doc/": [
{
text: "文档",
children: [
"/doc/README.md",
"/doc/quick-start.md",
{
text: "在线教程(阿里云)",
link: "https://start.aliyun.com/handson-lab?category=arthas",
},
"/doc/install-detail.md",
"/doc/download.md",
"/doc/advanced-use.md",
{
text: "其他特性",
collapsible: true,
children: [
"/doc/async.md",
"/doc/save-log.md",
"/doc/batch-support.md",
{
text: "ognl 表达式用法",
link: "",
children: [
{
text: "活用ognl表达式",
link: "https://github.com/alibaba/arthas/issues/11",
},
{
text: "一些ognl特殊用法",
link: "https://github.com/alibaba/arthas/issues/71",
},
],
},
],
},
{
text: "命令列表",
collapsible: true,
link: "/doc/commands.md",
children: [
{
text: "jvm相关",
collapsible: false,
children: [
"/doc/dashboard.md",
"/doc/thread.md",
"/doc/jvm.md",
"/doc/memory.md",
"/doc/sysprop.md",
"/doc/sysenv.md",
"/doc/vmoption.md",
"/doc/perfcounter.md",
"/doc/logger.md",
"/doc/mbean.md",
"/doc/getstatic.md",
"/doc/ognl.md",
"/doc/heapdump.md",
"/doc/vmtool.md",
],
},
{
text: "class/classloader相关",
collapsible: false,
children: [
"/doc/sc.md",
"/doc/sm.md",
"/doc/jad.md",
"/doc/classloader.md",
"/doc/mc.md",
"/doc/dump.md",
"/doc/retransform.md",
"/doc/redefine.md",
],
},
{
text: "monitor/watch/trace相关",
collapsible: false,
children: [
"/doc/monitor.md",
"/doc/watch.md",
"/doc/trace.md",
"/doc/stack.md",
"/doc/tt.md",
],
},
{
text: "其他",
collapsible: false,
children: [
"/doc/profiler.md",
"/doc/cat.md",
"/doc/echo.md",
"/doc/grep.md",
"/doc/base64.md",
"/doc/tee.md",
"/doc/pwd.md",
"/doc/auth.md",
"/doc/options.md",
],
},
{
text: "基础命令",
collapsible: false,
children: [
"/doc/help.md",
"/doc/cls.md",
"/doc/session.md",
"/doc/reset.md",
"/doc/history.md",
"/doc/quit.md",
"/doc/stop.md",
{
text: "keymap",
link: "/doc/keymap.md",
},
],
},
],
},
"/doc/web-console.md",
"/doc/tunnel.md",
"/doc/http-api.md",
"/doc/docker.md",
"/doc/spring-boot-starter.md",
"/doc/idea-plugin.md",
"/doc/faq.md",
{
text: "用户案列",
link: "https://github.com/alibaba/arthas/issues?q=label%3Auser-case",
},
{
text: "Start me at github",
link: "https://github.com/alibaba/arthas",
},
{
text: "编译调试/参与贡献",
link: "https://github.com/alibaba/arthas/blob/master/CONTRIBUTING.md",
},
{
text: "Release Notes",
link: "https://github.com/alibaba/arthas/releases",
},
{
text: "QQ群/钉钉群",
link: "/doc/contact-us.md",
},
],
},
],
};

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 KiB

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

@ -0,0 +1,3 @@
.site-name.can-hide {
display: none;
}

@ -0,0 +1,115 @@
<script>
/* eslint-disable import/first, import/no-duplicates, import/order */
import { defineComponent } from "vue";
export default defineComponent({
inheritAttrs: false,
});
/* eslint-enable import/order */
</script>
<script setup>
import GitHub from "./icons/GitHub.vue";
import { useSiteData } from "@vuepress/client";
import { isLinkHttp, isLinkMailto, isLinkTel } from "@vuepress/shared";
import { computed, toRefs } from "vue";
import { useRoute } from "vue-router";
const props = defineProps({
item: {
type: Object,
required: true,
},
});
const route = useRoute();
const site = useSiteData();
const { item } = toRefs(props);
// if the link has http protocol
const hasHttpProtocol = computed(() => isLinkHttp(item.value.link));
// if the link has non-http protocol
const hasNonHttpProtocol = computed(
() => isLinkMailto(item.value.link) || isLinkTel(item.value.link)
);
// resolve the `target` attr
const linkTarget = computed(() => {
if (hasNonHttpProtocol.value) return undefined;
if (item.value.target) return item.value.target;
if (hasHttpProtocol.value) return "_blank";
return undefined;
});
// if the `target` attr is '_blank'
const isBlankTarget = computed(() => linkTarget.value === "_blank");
// is `<RouterLink>` or not
const isRouterLink = computed(
() =>
!hasHttpProtocol.value && !hasNonHttpProtocol.value && !isBlankTarget.value
);
// resolve the `rel` attr
const linkRel = computed(() => {
if (hasNonHttpProtocol.value) return undefined;
if (item.value.rel) return item.value.rel;
if (isBlankTarget.value) return "noopener noreferrer";
return undefined;
});
// resolve the `aria-label` attr
const linkAriaLabel = computed(() => item.value.ariaLabel || item.value.text);
// should be active when current route is a subpath of this link
const shouldBeActiveInSubpath = computed(() => {
const localeKeys = Object.keys(site.value.locales);
if (localeKeys.length) {
return !localeKeys.some((key) => key === item.value.link);
}
return item.value.link !== "/";
});
// if this link is active in subpath
const isActiveInSubpath = computed(() => {
if (!shouldBeActiveInSubpath.value) {
return false;
}
return route.path.startsWith(item.value.link);
});
// if this link is active
const isActive = computed(() => {
if (!isRouterLink.value) {
return false;
}
if (item.value.activeMatch) {
return new RegExp(item.value.activeMatch).test(route.path);
}
return isActiveInSubpath.value;
});
</script>
<template>
<RouterLink
v-if="isRouterLink"
:class="{ 'router-link-active': isActive }"
:to="item.link"
:aria-label="linkAriaLabel"
v-bind="$attrs"
>
<slot name="before" />
{{ item.text }}
<slot name="after" />
</RouterLink>
<a
v-else
class="external-link"
:href="item.link"
:rel="linkRel"
:target="linkTarget"
:aria-label="linkAriaLabel"
v-bind="$attrs"
>
<slot name="before" />
<GitHub v-if="item.text === 'GitHub'" />
<span v-else>{{ item.text }}</span>
<AutoLinkExternalIcon v-if="isBlankTarget && item.text !== 'GitHub'" />
<slot name="after" />
</a>
</template>

@ -0,0 +1,72 @@
<template>
<a class="my-badge" :href="URL" target="_blank">
<component :is="comp" />
&nbsp;
<span>{{ number }}</span>
</a>
</template>
<script setup>
import { useThemeLocaleData } from "@vuepress/theme-default/lib/client/composables";
import { ref } from "vue";
const props = defineProps({
comp: {
type: Object,
required: true,
},
data: {
type: Number,
required: true,
},
});
const number = ref(0);
// add number to number until number is equal to data
var t = setInterval(() => {
number.value += 234;
if (number.value >= props.data) {
number.value = props.data;
clearInterval(t);
}
}, 1);
const themeData = useThemeLocaleData();
const repoURL = `https://github.com/${themeData.value.repo}`;
const repoStarsURL = repoURL;
const repoForksURL = `${repoURL}/fork`;
let URL = repoURL;
switch (props.comp.name) {
case "Star":
URL = repoStarsURL;
break;
case "Fork":
URL = repoForksURL;
break;
}
</script>
<style lang="scss" scoped>
.my-badge {
width: fit-content;
width: -webkit-fit-content;
width: -moz-fit-content;
padding: 5px;
margin: 0 10px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
color: var(--c-text);
background-color: rgba(89, 95, 101, 0.2);
user-select: none;
}
@media (max-width: 719px) {
.my-badge {
margin: 0 5px;
}
}
</style>

@ -0,0 +1,18 @@
<script setup>
import HomeUserBoards from "./HomeUserBoards.vue";
import HomeFeatures from "./HomeFeatures.vue";
import HomeHero from "./HomeHero.vue";
import HomeContent from "@theme/HomeContent.vue";
import HomeFooter from "@theme/HomeFooter.vue";
</script>
<template>
<main class="home">
<HomeHero />
<HomeFeatures />
<HomeContent />
<HomeUserBoards />
<HomeFooter />
</main>
</template>

@ -0,0 +1,38 @@
<template>
<div class="badges">
<Badge :comp="Star" :data="star" />
<Badge :comp="Fork" :data="fork" />
</div>
</template>
<script setup>
import Star from "./icons/Star.vue";
import Fork from "./icons/Fork.vue";
import Badge from "./Badge.vue";
import { ref, onBeforeMount } from "vue";
const star = ref(29582);
const fork = ref(6494);
const getData = async () => {
const { forks, stargazers_count } = await fetch(
"https://api.github.com/repos/alibaba/arthas"
).then((res) => res.json());
star.value = stargazers_count;
fork.value = forks;
};
onBeforeMount(getData);
</script>
<style scoped>
.badges {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
</style>

@ -0,0 +1,36 @@
<script setup>
import { usePageFrontmatter } from "@vuepress/client";
import { isArray } from "@vuepress/shared";
import { computed } from "vue";
const frontmatter = usePageFrontmatter();
const features = computed(() => {
if (isArray(frontmatter.value.features)) {
return frontmatter.value.features;
}
return [];
});
</script>
<template>
<div v-if="features.length" class="features">
<div v-for="feature in features" :key="feature.title" class="feature">
<div v-if="feature.icon" class="icon">{{ feature.icon }}</div>
<h2>{{ feature.title }}</h2>
<p>{{ feature.details }}</p>
</div>
</div>
</template>
<style scoped>
.icon {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
border-radius: 6px;
width: 48px;
height: 48px;
font-size: 30px;
}
</style>

@ -0,0 +1,102 @@
<script setup>
import HomeBadges from "./HomeBadges.vue";
import AutoLink from "@theme/AutoLink.vue";
import {
ClientOnly,
usePageFrontmatter,
useSiteLocaleData,
withBase,
} from "@vuepress/client";
import { isArray } from "@vuepress/shared";
import { computed, h } from "vue";
import { useDarkMode } from "@vuepress/theme-default/lib/client/composables";
const frontmatter = usePageFrontmatter();
const siteLocale = useSiteLocaleData();
const isDarkMode = useDarkMode();
const heroImage = computed(() => {
if (isDarkMode.value && frontmatter.value.heroImageDark !== undefined) {
return frontmatter.value.heroImageDark;
}
return frontmatter.value.heroImage;
});
const heroText = computed(() => {
if (frontmatter.value.heroText === null) {
return null;
}
return frontmatter.value.heroText || siteLocale.value.title || "Hello";
});
const heroAlt = computed(
() => frontmatter.value.heroAlt || heroText.value || "hero"
);
const tagline = computed(() => {
if (frontmatter.value.tagline === null) {
return null;
}
return (
frontmatter.value.tagline ||
siteLocale.value.description ||
"Welcome to your VuePress site"
);
});
const actions = computed(() => {
if (!isArray(frontmatter.value.actions)) {
return [];
}
return frontmatter.value.actions.map(({ text, link, type = "primary" }) => ({
text,
link,
type,
}));
});
const HomeHeroImage = () => {
if (!heroImage.value) return null;
const img = h("img", {
src: withBase(heroImage.value),
style: "width: 60%;",
alt: heroAlt.value,
});
if (frontmatter.value.heroImageDark === undefined) {
return img;
}
// wrap hero image with <ClientOnly> to avoid ssr-mismatch
// when using a different hero image in dark mode
return h(ClientOnly, () => img);
};
</script>
<template>
<header class="hero">
<HomeHeroImage />
<div>
<h1 v-if="heroText" id="main-title">
{{ heroText }}
</h1>
<p v-if="tagline" class="description">
{{ tagline }}
</p>
<HomeBadges />
<p v-if="actions.length" class="actions">
<AutoLink
v-for="action in actions"
:key="action.text"
class="action-button"
:class="[action.type]"
:item="action"
/>
</p>
</div>
</header>
</template>

@ -0,0 +1,37 @@
<template>
<div class="user-boards" v-if="imgs">
<h1>{{ pageData.users_title }}</h1>
<p v-html="pageData.users_details"></p>
<div class="user-logos">
<UserBoard v-for="img in imgs" :key="img.logo" :img="img" />
</div>
</div>
</template>
<script setup>
import UserBoard from "./UserBoard.vue";
import { toRaw } from "vue";
import { usePageFrontmatter, usePageLang } from "@vuepress/client";
const pageData = usePageFrontmatter();
const imgs = toRaw(pageData.value.users) || [];
</script>
<style lang="scss" scoped>
.user-boards {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-top: 1px solid var(--c-border);
padding: 1.2rem 0;
margin-top: 20px;
.user-logos {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
}
</style>

@ -0,0 +1,127 @@
<script setup>
import Translate from "./icons/Translate.vue";
import AutoLink from "@theme/AutoLink.vue";
import DropdownTransition from "@theme/DropdownTransition.vue";
import { computed, ref, toRefs, watch } from "vue";
import { useRoute } from "vue-router";
const props = defineProps({
item: {
type: Object,
required: true,
},
});
const { item } = toRefs(props);
const dropdownAriaLabel = computed(
() => item.value.ariaLabel || item.value.text
);
const open = ref(false);
const route = useRoute();
watch(
() => route.path,
() => {
open.value = false;
}
);
/**
* Open the dropdown when user tab and click from keyboard.
*
* Use event.detail to detect tab and click from keyboard.
* The Tab + Click is UIEvent > KeyboardEvent, so the detail is 0.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
*/
const handleDropdown = (e) => {
const isTriggerByTab = e.detail === 0;
if (isTriggerByTab) {
open.value = !open.value;
} else {
open.value = false;
}
};
const isLastItemOfArray = (item, arr) => arr[arr.length - 1] === item;
</script>
<template>
<div class="navbar-dropdown-wrapper" :class="{ open }">
<button
class="navbar-dropdown-title"
type="button"
:aria-label="dropdownAriaLabel"
@click="handleDropdown"
>
<Translate v-if="item.text === 'Languages'" />
<span class="title" v-else>{{ item.text }}</span>
<span class="arrow down" />
</button>
<button
class="navbar-dropdown-title-mobile"
type="button"
:aria-label="dropdownAriaLabel"
@click="open = !open"
>
<Translate v-if="item.text === 'Languages'" />
<span class="title" v-else>{{ item.text }}</span>
<span class="arrow" :class="open ? 'down' : 'right'" />
</button>
<DropdownTransition>
<ul v-show="open" class="navbar-dropdown">
<li
v-for="child in item.children"
:key="child.text"
class="navbar-dropdown-item"
>
<template v-if="child.children">
<h4 class="navbar-dropdown-subtitle">
<AutoLink
v-if="child.link"
:item="child"
@focusout="
isLastItemOfArray(child, item.children) &&
child.children.length === 0 &&
(open = false)
"
/>
<span v-else>{{ child.text }}</span>
</h4>
<ul class="navbar-dropdown-subitem-wrapper">
<li
v-for="grandchild in child.children"
:key="grandchild.link"
class="navbar-dropdown-subitem"
>
<AutoLink
:item="grandchild"
@focusout="
isLastItemOfArray(grandchild, child.children) &&
isLastItemOfArray(child, item.children) &&
(open = false)
"
/>
</li>
</ul>
</template>
<template v-else>
<AutoLink
:item="child"
@focusout="
isLastItemOfArray(child, item.children) && (open = false)
"
/>
</template>
</li>
</ul>
</DropdownTransition>
</div>
</template>

@ -0,0 +1,21 @@
<template>
<img class="users-logo" :src="img.logo" />
</template>
<script setup>
defineProps({
img: {
type: Object,
required: true,
},
});
</script>
<style lang="scss" scoped>
.users-logo {
max-width: 140px;
object-fit: contain;
margin: 10px;
user-select: none;
}
</style>

@ -0,0 +1,42 @@
<template>
<svg
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="131 -131 512 512"
style="
enable-background: new 131 -131 512 512;
height: 1rem;
fill: var(--c-text);
"
xml:space="preserve"
>
<g id="XMLID_2_">
<path
id="XMLID_8_"
d="M312.8,317c0-8.5-3.4-16.2-9.4-23s-13.7-9.4-23-9.4c-9.3,0-16.2,3.4-23,9.4c-6,6-9.4,13.7-9.4,23
s3.4,16.2,9.4,23c6,6,13.7,9.4,23,9.4c9.3,0,16.2-3.4,23-9.4C310.2,334,312.8,325.5,312.8,317z M312.8-67c0-8.5-3.4-16.2-9.4-23
c-6-6-13.7-9.4-23-9.4c-9.3,0-16.2,3.4-23,9.4c-6,6-9.4,13.7-9.4,23c0,8.5,3.4,16.2,9.4,23c6,6,13.7,9.4,23,9.4
c9.3,0,16.2-3.4,23-9.4C310.2-49.9,312.8-58.5,312.8-67z M526.1-24.3c0-8.5-3.4-16.2-9.4-23s-13.7-9.4-23-9.4s-16.2,3.4-23,9.4
s-9.4,13.7-9.4,23s3.4,16.2,9.4,23c6,6,13.7,9.4,23,9.4s16.2-3.4,23-9.4C522.7-8.1,526.1-15.8,526.1-24.3z M557.7-24.3
c0,11.9-2.6,22.2-8.5,32.4s-13.7,17.9-23,23c-0.9,64-25.6,110.1-75.1,138.2c-15.4,8.5-37.5,17.1-67.4,27.3
c-28.2,8.5-46.9,17.1-56.3,23.9c-9.4,6.8-13.7,17.9-13.7,33.3v8.5c9.4,6,17.9,13.7,23,23c5.1,9.4,8.5,20.5,8.5,32.4
c0,17.9-6,33.3-18.8,45.2c-12.8,11.9-28.1,18.1-46.1,18.1s-33.3-6-45.2-18.8s-18.8-27.3-18.8-45.2c0-11.9,2.6-22.2,8.5-32.4
c6-10.2,13.7-17.9,23-23V-11.5c-9.4-6-17.9-13.7-23-23s-8.5-20.6-8.5-32.5c0-17.9,6-33.3,18.8-45.2c12.8-11.9,27.3-18.8,45.2-18.8
c17.9,0,33.3,6,45.2,18.8s18.8,27.3,18.8,45.2c0,11.9-2.6,22.2-8.5,32.4c-5.9,10.2-13.7,17.9-23,23V154c11.9-6,29-11.9,51.2-18.8
c11.9-3.4,22.2-6.8,29-10.2c6.8-3.4,15.4-6,23.9-10.2s15.4-8.5,19.6-12.8s9.4-10.2,13.7-17.1s7.7-14.5,9.4-23
c1.7-8.5,2.6-18.8,2.6-30.7c-9.4-6-17.9-13.7-23-23s-8.5-20.5-8.5-32.4c0-17.9,6-33.3,18.8-45.2c12.8-12.8,27.3-18.8,45.2-18.8
s33.3,6,45.2,18.8C551.7-57.6,557.7-42.3,557.7-24.3z"
/>
</g>
</svg>
</template>
<script>
export default {
name: "Fork",
};
</script>

@ -0,0 +1,19 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 24 24"
style="height: 1.25rem; width: 1.25rem; vertical-align: bottom"
>
<path
fill="currentColor"
d="M12 2A10 10 0 0 0 2 12c0 4.42 2.87 8.17 6.84 9.5c.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34c-.46-1.16-1.11-1.47-1.11-1.47c-.91-.62.07-.6.07-.6c1 .07 1.53 1.03 1.53 1.03c.87 1.52 2.34 1.07 2.91.83c.09-.65.35-1.09.63-1.34c-2.22-.25-4.55-1.11-4.55-4.92c0-1.11.38-2 1.03-2.71c-.1-.25-.45-1.29.1-2.64c0 0 .84-.27 2.75 1.02c.79-.22 1.65-.33 2.5-.33c.85 0 1.71.11 2.5.33c1.91-1.29 2.75-1.02 2.75-1.02c.55 1.35.2 2.39.1 2.64c.65.71 1.03 1.6 1.03 2.71c0 3.82-2.34 4.66-4.57 4.91c.36.31.69.92.69 1.85V21c0 .27.16.59.67.5C19.14 20.16 22 16.42 22 12A10 10 0 0 0 12 2Z"
></path>
</svg>
</template>
<script>
export default {
name: "MdiGithub",
};
</script>

@ -0,0 +1,32 @@
<template>
<svg
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 512 512"
style="
enable-background: new 0 0 512 512;
height: 1rem;
fill: var(--c-text);
"
xml:space="preserve"
>
<g>
<path
d="M175.1,168.9L13.7,186.8c-5.8,0.7-11,4.6-12.9,10.5c-1.9,5.9,0,12.1,4.3,16c48,43.8,120.1,109.4,120.1,109.4
c-0.1,0-19.8,95.4-32.9,159.1c-1.1,5.8,1,11.9,6,15.5c5,3.7,11.4,3.7,16.5,0.9C171.3,466,256,417.7,256,417.7l141.1,80.5
c5.1,2.8,11.6,2.8,16.6-0.9c5-3.7,7.1-9.7,6-15.5l-32.8-159.1L507,213.4c4.3-4,6.2-10.2,4.3-16.1c-1.9-5.9-7.1-9.8-12.9-10.4
c-64.6-7.2-161.5-18-161.5-18L269.9,20.8c-2.5-5.3-7.8-9-14-9c-6.2,0-11.5,3.7-13.9,9L175.1,168.9z"
/>
</g>
</svg>
</template>
<script>
export default {
name: "Star",
};
</script>

@ -0,0 +1,15 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="1em"
height="1em"
style="height: 1.25rem; width: 1.25rem; vertical-align: bottom"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M13.35 22q-.6 0-.862-.387q-.263-.388-.063-.963l3.65-9.675q.15-.4.563-.688Q17.05 10 17.5 10q.425 0 .85.287q.425.288.575.688l3.65 9.675q.2.575-.062.963q-.263.387-.888.387q-.275 0-.5-.175q-.225-.175-.325-.425l-.85-2.45H15.1l-.875 2.45q-.1.25-.35.425q-.25.175-.525.175Zm2.35-4.8h3.6l-1.75-4.95h-.1ZM7.15 8.55q.4.725.85 1.337q.45.613 1.05 1.263q1.1-1.2 1.825-2.462Q11.6 7.425 12.1 6H2q-.425 0-.712-.287Q1 5.425 1 5t.288-.713Q1.575 4 2 4h6V3q0-.425.288-.713Q8.575 2 9 2t.713.287Q10 2.575 10 3v1h6q.425 0 .712.287Q17 4.575 17 5t-.288.713Q16.425 6 16 6h-1.9q-.525 1.775-1.425 3.45q-.9 1.675-2.225 3.15l2.4 2.45l-.75 2.05L9 14l-4.3 4.3q-.275.275-.7.275q-.425 0-.7-.275q-.275-.275-.275-.7q0-.425.275-.7l4.35-4.35q-.675-.775-1.25-1.563q-.575-.787-1.025-1.662Q5.1 8.8 5.35 8.4t.875-.4q.25 0 .525.162q.275.163.4.388Z"
></path>
</svg>
</template>

@ -0,0 +1,18 @@
const { defaultTheme } = require("@vuepress/theme-default");
const { path } = require("@vuepress/utils");
exports.localTheme = (options) => {
return {
name: "vuepress-theme-arthas",
extends: defaultTheme(options),
alias: {
"@theme/Home.vue": path.resolve(__dirname, "components/Home.vue"),
"@theme/NavbarDropdown.vue": path.resolve(
__dirname,
"components/NavbarDropdown.vue"
),
"@theme/AutoLink.vue": path.resolve(__dirname, "components/AutoLink.vue"),
},
};
};

@ -0,0 +1,53 @@
---
home: true
heroImage: /images/arthas_light.png
heroImageDark: /images/arthas_dark.png
heroText: null
tagline: "Java 应用诊断利器"
actions:
- text: 快速入门
link: /doc/quick-start.html
type: primary
- text: 查看github
link: https://github.com/alibaba/arthas
type: secondary
features:
- icon: 🖥
title: Dashboard
details: 实时查看系统的运行状况。
- icon: 🔬
title: 查看入参/返回值/异常
details: 查看函数调用的参数,返回值和异常。
- icon: 🔩
title: 在线热更新
details: jad/sc/redefine 一条龙热更新代码。
- icon: 🩺
title: 类冲突
details: 秒解类冲突问题,定位类加载路径。
- icon: ⚡️
title: 性能热点
details: 快速定位应用的热点,生成火焰图。
- icon: 📡
title: WebConsole
details: 在线诊断,点开网页诊断线上应用。
users_title: "用户"
users_details: "请在 <a href='https://github.com/alibaba/arthas/issues/111' target='_blank'>Wanted: who's using arthas</a> 上提供信息来帮助Arthas做的更好。"
users:
- name: Alibaba Group
logo: /images/users/users_alibaba.png
- name: Didiglobal
logo: /images/users/users_didi.png
- name: Kaola
logo: /images/users/users_kaola.png
- name: Qunar
logo: /images/users/users_qunar.png
- name: Telecom
logo: /images/users/users_telecom.png
- name: Weidian
logo: /images/users/users_weidian.png
- name: ICBC
logo: /images/users/users_icbc.png
- name: Chinaums
logo: /images/users/users_yinlian.png
footer: Apache-2.0 license | Copyright 2018-present, Alibaba Middleware Group, and contributors
---

@ -0,0 +1,24 @@
# 简介
Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息并能在不修改应用代码的情况下对业务问题进行诊断包括查看方法调用的出入参、异常监测方法执行耗时类加载信息等大大提升线上问题排查效率。
## Arthas阿尔萨斯能为你做什么
![](/images/arthas.png)
`Arthas` 是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。
当你遇到以下类似问题而束手无策时,`Arthas`可以帮助你解决:
0. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception
1. 我改的代码为什么没有执行到?难道是我没 commit分支搞错了
2. 遇到问题无法在线上 debug难道只能通过加日志再重新发布吗
3. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug线下无法重现
4. 是否有一个全局视角来查看系统的运行状况?
5. 有什么办法可以监控到 JVM 的实时运行状态?
6. 怎么快速定位应用的热点,生成火焰图?
7. 怎样直接从 JVM 内查找某个类的实例?
`Arthas` 支持 JDK 6+,支持 Linux/Mac/Windows采用命令行交互模式同时提供丰富的 `Tab` 自动补全功能,进一步方便进行问题的定位和诊断。
**如果您在使用 Arthas请让我们知道您的使用对我们非常重要[查看](https://github.com/alibaba/arthas/issues/111)**

@ -0,0 +1,157 @@
# 进阶使用
## 基础命令
- [help](help.md)——查看命令帮助信息
- [cat](cat.md)——打印文件内容,和 linux 里的 cat 命令类似
- [echo](echo.md)——打印参数,和 linux 里的 echo 命令类似
- [grep](grep.md)——匹配查找,和 linux 里的 grep 命令类似
- [base64](base64.md)——base64 编码转换,和 linux 里的 base64 命令类似
- [tee](tee.md)——复制标准输入到标准输出和指定的文件,和 linux 里的 tee 命令类似
- [pwd](pwd.md)——返回当前的工作目录,和 linux 命令类似
- [cls](cls.md)——清空当前屏幕区域
- [session](session.md)——查看当前会话的信息
- [reset](reset.md)——重置增强类,将被 Arthas 增强过的类全部还原Arthas 服务端关闭时会重置所有增强过的类
- [version](version.md)——输出当前目标 Java 进程所加载的 Arthas 版本号
- [history](history.md)——打印命令历史
- [quit](quit.md)——退出当前 Arthas 客户端,其他 Arthas 客户端不受影响
- [stop](stop.md)——关闭 Arthas 服务端,所有 Arthas 客户端全部退出
- [keymap](keymap.md)——Arthas 快捷键列表及自定义快捷键
## jvm 相关
- [dashboard](dashboard.md)——当前系统的实时数据面板
- [thread](thread.md)——查看当前 JVM 的线程堆栈信息
- [jvm](jvm.md)——查看当前 JVM 的信息
- [sysprop](sysprop.md)——查看和修改 JVM 的系统属性
- [sysenv](sysenv.md)——查看 JVM 的环境变量
- [vmoption](vmoption.md)——查看和修改 JVM 里诊断相关的 option
- [perfcounter](perfcounter.md)——查看当前 JVM 的 Perf Counter 信息
- [logger](logger.md)——查看和修改 logger
- [getstatic](getstatic.md)——查看类的静态属性
- [ognl](ognl.md)——执行 ognl 表达式
- [mbean](mbean.md)——查看 Mbean 的信息
- [heapdump](heapdump.md)——dump java heap, 类似 jmap 命令的 heap dump 功能
- [vmtool](vmtool.md)——从 jvm 里查询对象,执行 forceGc
## class/classloader 相关
- [sc](sc.md)——查看 JVM 已加载的类信息
- [sm](sm.md)——查看已加载类的方法信息
- [jad](jad.md)——反编译指定已加载类的源码
- [mc](mc.md)——内存编译器,内存编译`.java`文件为`.class`文件
- [retransform](retransform.md)——加载外部的`.class`文件retransform 到 JVM 里
- [redefine](redefine.md)——加载外部的`.class`文件redefine 到 JVM 里
- [dump](dump.md)——dump 已加载类的 byte code 到特定目录
- [classloader](classloader.md)——查看 classloader 的继承树urls类加载信息使用 classloader 去 getResource
## monitor/watch/trace 相关
::: warning
请注意,这些命令,都通过字节码增强技术来实现的,会在指定类的方法中插入一些切面来实现数据统计和观测,因此在线上、预发使用时,请尽量明确需要观测的类、方法以及条件,诊断结束要执行 `stop` 或将增强过的类执行 `reset` 命令。
:::
- [monitor](monitor.md)——方法执行监控
- [watch](watch.md)——方法执行数据观测
- [trace](trace.md)——方法内部调用路径,并输出方法路径上的每个节点上耗时
- [stack](stack.md)——输出当前方法被调用的调用路径
- [tt](tt.md)——方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
## profiler/火焰图
- [profiler](profiler.md)--使用[async-profiler](https://github.com/jvm-profiling-tools/async-profiler)对应用采样,生成火焰图
## 鉴权
- [auth](auth.md)——鉴权
## options
- [options](options.md)——查看或设置 Arthas 全局开关
## 管道
Arthas 支持使用管道对上述命令的结果进行进一步的处理,如`sm java.lang.String * | grep 'index'`
- [grep](grep.md)——搜索满足条件的结果
- plaintext——将命令的结果去除 ANSI 颜色
- wc——按行统计输出结果
## 后台异步任务
当线上出现偶发的问题,比如需要 watch 某个条件,而这个条件一天可能才会出现一次时,异步后台任务就派上用场了,详情请参考[这里](async.md)
- 使用 `>` 将结果重写向到日志文件,使用 `&` 指定命令是后台运行session 断开不影响任务执行(生命周期默认为 1 天)
- jobs——列出所有 job
- kill——强制终止任务
- fg——将暂停的任务拉到前台执行
- bg——将暂停的任务放到后台执行
## Web Console
通过 websocket 连接 Arthas。
- [Web Console](web-console.md)
## Arthas Properties
- [Arthas Properties](arthas-properties.md)
## 以 java agent 方式启动
- [以 java agent 方式启动](agent.md)
## as.sh 和 arthas-boot 技巧
- 通过`select`功能选择 attach 的进程。
正常情况下,每次执行`as.sh`/`arthas-boot.jar`需要选择,或者指定 PID。这样会比较麻烦因为每次启动应用它的 PID 会变化。
比如,已经启动了`math-game.jar`,使用`jps`命令查看:
```bash
$ jps
58883 math-game.jar
58884 Jps
```
通过`select`参数可以指定进程名字,非常方便。
```bash
$ ./as.sh --select math-game
Arthas script version: 3.3.6
[INFO] JAVA_HOME: /tmp/java/8.0.222-zulu
Arthas home: /Users/admin/.arthas/lib/3.3.6/arthas
Calculating attach execution time...
Attaching to 59161 using version /Users/admin/.arthas/lib/3.3.6/arthas...
real 0m0.572s
user 0m0.281s
sys 0m0.039s
Attach success.
telnet connecting to arthas server... current timestamp is 1594280799
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
wiki https://arthas.aliyun.com/doc
tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html
version 3.3.6
pid 58883
```
## 用户数据回报
在`3.1.4`版本后,增加了用户数据回报功能,方便统一做安全或者历史数据统计。
在启动时,指定`stat-url`,就会回报执行的每一行命令,比如: `./as.sh --stat-url 'http://192.168.10.11:8080/api/stat'`
在 tunnel server 里有一个示例的回报代码,用户可以自己在服务器上实现。
[StatController.java](https://github.com/alibaba/arthas/blob/master/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/StatController.java)

@ -0,0 +1,43 @@
# 表达式核心变量
无论是匹配表达式也好、观察表达式也罢,他们核心判断变量都是围绕着一个 Arthas 中的通用通知对象 `Advice` 进行。
它的简略代码结构如下
```java
public class Advice {
private final ClassLoader loader;
private final Class<?> clazz;
private final ArthasMethod method;
private final Object target;
private final Object[] params;
private final Object returnObj;
private final Throwable throwExp;
private final boolean isBefore;
private final boolean isThrow;
private final boolean isReturn;
// getter/setter
}
```
这里列一个表格来说明不同变量的含义
| 变量名 | 变量解释 |
| --------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| loader | 本次调用类所在的 ClassLoader |
| clazz | 本次调用类的 Class 引用 |
| method | 本次调用方法反射引用 |
| target | 本次调用类的实例 |
| params | 本次调用参数列表,这是一个数组,如果方法是无参方法则为空数组 |
| returnObj | 本次调用返回的对象。当且仅当 `isReturn==true` 成立时候有效,表明方法调用是以正常返回的方式结束。如果当前方法无返回值 `void`,则值为 null |
| throwExp | 本次调用抛出的异常。当且仅当 `isThrow==true` 成立时有效,表明方法调用是以抛出异常的方式结束。 |
| isBefore | 辅助判断标记,当前的通知节点有可能是在方法一开始就通知,此时 `isBefore==true` 成立,同时 `isThrow==false``isReturn==false`,因为在方法刚开始时,还无法确定方法调用将会如何结束。 |
| isThrow | 辅助判断标记,当前的方法调用以抛异常的形式结束。 |
| isReturn | 辅助判断标记,当前的方法调用以正常返回的形式结束。 |
所有变量都可以在表达式中直接使用,如果在表达式中编写了不符合 OGNL 脚本语法或者引入了不在表格中的变量,则退出命令的执行;用户可以根据当前的异常信息修正`条件表达式`或`观察表达式`
- 特殊用法请参考:[https://github.com/alibaba/arthas/issues/71](https://github.com/alibaba/arthas/issues/71)
- OGNL 表达式官网:[https://commons.apache.org/proper/commons-ognl/language-guide.html](https://commons.apache.org/proper/commons-ognl/language-guide.html)

@ -0,0 +1,13 @@
# 以 Java Agent 的方式启动
通常 Arthas 是以动态 attach 的方式来诊断应用,但从`3.2.0`版本起Arthas 支持直接以 java agent 的方式启动。
比如下载全量的 arthas zip 包,解压之后以 `-javaagent` 的参数指定`arthas-agent.jar`来启动:
```
java -javaagent:/tmp/test/arthas-agent.jar -jar math-game.jar
```
默认的配置项在解压目录里的`arthas.properties`文件里。参考:[Arthas Properties](arthas-properties.md)
Java Agent 机制参考: https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html

@ -0,0 +1,62 @@
# Arthas Properties
`arthas.properties`文件在 arthas 的目录下。
- 如果是自动下载的 arthas则目录在`~/.arthas/lib/3.x.x/arthas/`下面
- 如果是下载的完整包,在 arthas 解压目录下
## 支持的配置项
::: warning
注意配置必须是`驼峰`的,和 spring boot 的`-`风格不一样。spring boot 应用才同时支持`驼峰` 和 `-`风格的配置。
:::
```
#arthas.config.overrideAll=true
arthas.telnetPort=3658
arthas.httpPort=8563
arthas.ip=127.0.0.1
# seconds
arthas.sessionTimeout=1800
#arthas.appName=demoapp
#arthas.tunnelServer=ws://127.0.0.1:7777/ws
#arthas.agentId=mmmmmmyiddddd
```
- 如果配置 `arthas.telnetPort`为 -1 ,则不 listen telnet 端口。`arthas.httpPort`类似。
- 如果配置 `arthas.telnetPort`为 0 ,则随机 telnet 端口,在`~/logs/arthas/arthas.log`里可以找到具体端口日志。`arthas.httpPort`类似。
:::tip
如果是防止一个机器上启动多个 arthas 端口冲突。可以配置为随机端口,或者配置为 -1并且通过 tunnel server 来使用 arthas。
:::
### 禁止指定命令
::: tip
since 3.5.2
:::
比如配置:
```
arthas.disabledCommands=stop,dump
```
也可以在命令行配置: `--disabled-commands stop,dump`
::: tip
默认情况下arthas-spring-boot-starter 会禁掉`stop`命令。
:::
## 配置的优先级
配置的优先级是:命令行参数 > System Env > System Properties > arthas.properties 。
比如:
- `./as.sh --telnet-port 9999` 传入的配置会覆盖掉`arthas.properties`里的默认值`arthas.telnetPort=3658`。
- 如果应用自身设置了 system properties `arthas.telnetPort=8888`,则会覆盖掉`arthas.properties`里的默认值`arthas.telnetPort=3658`。
如果想要 `arthas.properties`的优先级最高,则可以配置 `arthas.config.overrideAll=true`

@ -1,6 +1,7 @@
### Arthas3.0 的新特性
#### 在线诊断功能
Arthas3.0 中最重要的特性,不需要登陆机器就可以对应用进行诊断,体验和本地诊断完全一致
##### 使用步骤
@ -12,8 +13,8 @@ TODO
TODO
#### 管道支持
Arthas 3.0开始支持管道, 率先提供了`grep`,`wc`,`plaintext`的支持。
Arthas 3.0 开始支持管道, 率先提供了`grep`,`wc`,`plaintext`的支持。
### 去 groovy 依赖
@ -101,12 +102,8 @@ ID NAME GROU
22 EagleEye-AsyncAppender-Thread-BizLog main 5 TIMED_WAITING 1 0:0 false true
```
#### trace 命令自动高亮显示最耗时方法调用
trace 命令现在会自动显示
![Untitled2](TODO /Untitled2.gif)

@ -0,0 +1,80 @@
# Arthas 后台异步任务
[`后台异步任务`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials?language=cn&id=case-async-jobs)
arthas 中的后台异步任务,使用了仿 linux 系统任务相关的命令。[linux 任务相关介绍](https://ehlxr.me/2017/01/18/Linux-%E4%B8%AD-fg%E3%80%81bg%E3%80%81jobs%E3%80%81-%E6%8C%87%E4%BB%A4/)。
## 1. 使用&在后台执行任务
比如希望执行后台执行 trace 命令,那么调用下面命令
```bash
trace Test t &
```
这时命令在后台执行,可以在 console 中继续执行其他命令。
## 2. 通过 jobs 查看任务
如果希望查看当前有哪些 arthas 任务在执行,可以执行 jobs 命令,执行结果如下
```bash
$ jobs
[10]*
Stopped watch com.taobao.container.Test test "params[0].{? #this.name == null }" -x 2
execution count : 19
start time : Fri Sep 22 09:59:55 CST 2017
timeout date : Sat Sep 23 09:59:55 CST 2017
session : 3648e874-5e69-473f-9eed-7f89660b079b (current)
```
可以看到目前有一个后台任务在执行。
- job id 是 10, `*` 表示此 job 是当前 session 创建
- 状态是 Stopped
- execution count 是执行次数,从启动开始已经执行了 19 次
- timeout date 是超时的时间,到这个时间,任务将会自动超时退出
## 3. 任务暂停和取消
当任务正在前台执行,比如直接调用命令`trace Test t`或者调用后台执行命令`trace Test t &`后又通过`fg`命令将任务转到前台。这时 console 中无法继续执行命令,但是可以接收并处理以下事件:
- ctrl + z将任务暂停。通过`jbos`查看任务状态将会变为 Stopped通过`bg <job-id>`或者`fg <job-id>`可让任务重新开始执行
- ctrl + c停止任务
- ctrl + d按照 linux 语义应当是退出终端,目前 arthas 中是空实现,不处理
## 4. fg、bg 命令,将命令转到前台、后台继续执行
- 任务在后台执行或者暂停状态(`ctrl + z`暂停任务)时,执行`fg <job-id>`将可以把对应的任务转到前台继续执行。在前台执行时,无法在 console 中执行其他命令
- 当任务处于暂停状态时(`ctrl + z`暂停任务),执行`bg <job-id>`将可以把对应的任务在后台继续执行
- 非当前 session 创建的 job只能由当前 session fg 到前台执行
## 5. 任务输出重定向
可通过`>`或者`>>`将任务输出结果输出到指定的文件中,可以和`&`一起使用,实现 arthas 命令的后台异步任务。比如:
```bash
$ trace Test t >> test.out &
```
这时 trace 命令会在后台执行,并且把结果输出到~/logs/arthas-cache/test.out。可继续执行其他命令。并可查看文件中的命令执行结果。
当连接到远程的 arthas server 时可能无法查看远程机器的文件arthas 同时支持了自动重定向到本地缓存路径。使用方法如下:
```bash
$ trace Test t >> &
job id : 2
cache location : /Users/gehui/logs/arthas-cache/28198/2
```
可以看到并没有指定重定向文件位置arthas 自动重定向到缓存中了,执行命令后会输出 job id 和 cache location。cache location 就是重定向文件的路径,在系统 logs 目录下,路径包括 pid 和 job id避免和其他任务冲突。命令输出结果到`/Users/gehui/logs/arthas-cache/28198/2`中job id 为 2。
## 6. 停止命令
异步执行的命令,如果希望停止,可执行`kill <job-id>`
## 7. 其他
- 最多同时支持 8 个命令使用重定向将结果写日志
- 请勿同时开启过多的后台异步命令,以免对目标 JVM 性能造成影响
- 如果不想停止 arthas继续执行后台任务可以执行 `quit` 退出 arthas 控制台(`stop` 会停止 arthas 服务)

@ -1,7 +1,8 @@
auth
===
# auth
> 验证当前会话
::: tip
验证当前会话
:::
### 配置用户名和密码
@ -11,9 +12,9 @@ auth
java -jar arthas-boot.jar --password ppp
```
* 可以通过 `--username` 选项来指定用户,默认值是`arthas`。
* 也可以在 `arthas.properties` 里中配置 username/password。命令行的优先级大于配置文件。
* 如果只配置`username`,没有配置`password`,则会生成随机密码,打印在`~/logs/arthas/arthas.log`中
- 可以通过 `--username` 选项来指定用户,默认值是`arthas`。
- 也可以在 `arthas.properties` 里中配置 username/password。命令行的优先级大于配置文件。
- 如果只配置`username`,没有配置`password`,则会生成随机密码,打印在`~/logs/arthas/arthas.log`中
```
Using generated security password: 0vUBJpRIppkKuZ7dYzYqOKtranj4unGh
@ -45,8 +46,7 @@ Error! command not permitted, try to use 'auth' command to authenticates.
Authentication result: true
```
* 可以通过 `--username` 选项来指定用户,默认值是`arthas`。
- 可以通过 `--username` 选项来指定用户,默认值是`arthas`。
### Web console 密码验证
@ -56,12 +56,11 @@ Authentication result: true
### HTTP API 验证
#### Authorization Header 方式(推荐)
Arthas 采用的是 HTTP 标准的 Basic Authorization客户端请求时增加对应的header即可。
* 参考:[https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication)
Arthas 采用的是 HTTP 标准的 Basic Authorization客户端请求时增加对应的 header 即可。
- 参考:[https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication)
例如,用户名是:`admin`,密码是 `admin`,则组合为字符串: `admin:admin`base64 结果是: `YWRtaW46YWRtaW4=`,则 HTTP 请求增加`Authorization` header
@ -79,4 +78,3 @@ curl 'http://localhost:8563/api' \
curl 'http://localhost:8563/api?password=admin' \
--data-raw '{"action":"exec","command":"version"}'
```

@ -1,9 +1,8 @@
base64
===
> base64编码转换和linux里的 base64 命令类似。
# base64
::: tip
base64 编码转换,和 linux 里的 base64 命令类似。
:::
### 对文件进行 base64 编码

@ -1,7 +1,8 @@
批处理功能
===
# 批处理功能
> 通过批处理功能arthas支持一次性批量运行多个命令并取得命令执行的结果。
::: tip
通过批处理功能arthas 支持一次性批量运行多个命令,并取得命令执行的结果。
:::
### 使用方法
@ -9,11 +10,13 @@
这里我们新建了一个`test.as`脚本,为了规范,我们采用了.as 后缀名,但事实上任意的文本文件都 ok。
> 注意事项
> * 目前需要每个命令占一行
> * dashboard务必指定执行次数(`-n`),否则会导致批处理脚本无法终止
> * watch/tt/trace/monitor/stack等命令务必指定执行次数(`-n`),否则会导致批处理脚本无法终止
> * 可以使用异步后台任务,如 `watch c.t.X test returnObj > &`,让命令一直在后台运行,通过日志获取结果,[获取更多异步任务的信息](async.md)
::: tip
- 目前需要每个命令占一行
- dashboard 务必指定执行次数(`-n`),否则会导致批处理脚本无法终止
- watch/tt/trace/monitor/stack 等命令务必指定执行次数(`-n`),否则会导致批处理脚本无法终止
- 可以使用异步后台任务,如 `watch c.t.X test returnObj > &`,让命令一直在后台运行,通过日志获取结果,[获取更多异步任务的信息](async.md)
:::
```
➜ arthas git:(develop) cat /var/tmp/test.as

@ -1,10 +1,10 @@
cat
===
# cat
[`cat`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-cat)
> 打印文件内容和linux里的cat命令类似。
::: tip
打印文件内容,和 linux 里的 cat 命令类似。
:::
```bash
$ cat /tmp/a.txt

@ -1,19 +1,19 @@
classloader
===
# classloader
[`classloader`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials?language=cn&id=command-classloader)
> 查看classloader的继承树urls类加载信息
::: tip
查看 classloader 的继承树urls类加载信息
:::
`classloader` 命令将 JVM 中所有的 classloader 的信息统计出来并可以展示继承树urls 等。
可以让指定的 classloader 去 getResources打印出所有查找到的 resources 的 url。对于`ResourceNotFoundException`比较有用。
### 参数说明
| 参数名称 | 参数说明 |
|---:|:---|
| --------------------: | :----------------------------------------- |
| [l] | 按类加载实例进行统计 |
| [t] | 打印所有 ClassLoader 的继承树 |
| [a] | 列出所有 ClassLoader 加载的类,请谨慎使用 |
@ -70,7 +70,7 @@ file:/Users/hengyunabc/.arthas/lib/3.0.5/arthas/arthas-agent.jar
Affect(row-cnt:9) cost in 3 ms.
```
*注意* hashcode是变化的需要先查看当前的ClassLoader信息提取对应ClassLoader的hashcode。
_注意_ hashcode 是变化的,需要先查看当前的 ClassLoader 信息,提取对应 ClassLoader 的 hashcode。
对于只有唯一实例的 ClassLoader 可以通过 class name 指定,使用起来更加方便:
@ -127,7 +127,9 @@ load class success.
#### 统计 ClassLoader 实际使用 URL 和未使用的 URL
> 注意基于JVM目前已加载的所有类统计不代表`Unused URLs`可以从应用中删掉。因为可能将来需要从`Unused URLs`里加载类,或者需要加载`resources`。
::: warning
注意,基于 JVM 目前已加载的所有类统计,不代表`Unused URLs`可以从应用中删掉。因为可能将来需要从`Unused URLs`里加载类,或者需要加载`resources`。
:::
```
$ classloader --url-stat

@ -0,0 +1,7 @@
# cls
清空当前屏幕区域。
::: tip
非终端模式下使用 cls 指令,会提示"Command 'cls' is only support tty session."。
:::

@ -0,0 +1,50 @@
# 命令列表
- [dashboard](dashboard.md)
- [thread](thread.md)
- [jvm](jvm.md)
- [memory](memory.md)
- [sysprop](sysprop.md)
- [sysenv](sysenv.md)
- [vmoption](vmoption.md)
- [perfcounter](perfcounter.md)
- [logger](logger.md)
- [mbean](mbean.md)
- [getstatic](getstatic.md)
- [ognl](ognl.md)
- [sc](sc.md)
- [sm](sm.md)
- [dump](dump.md)
- [heapdump](heapdump.md)
- [vmtool](vmtool.md)
- [jad](jad.md)
- [classloader](classloader.md)
- [mc](mc.md)
- [retransform](retransform.md)
- [redefine](redefine.md)
- [monitor](monitor.md)
- [watch](watch.md)
- [trace](trace.md)
- [stack](stack.md)
- [tt](tt.md)
- [profiler](profiler.md)
- [cat](cat.md)
- [echo](echo.md)
- [grep](grep.md)
- [base64](base64.md)
- [tee](tee.md)
- [pwd](pwd.md)
- [auth](auth.md)
- [options](options.md)
### Arthas 基础命令
- [help](help.md)
- [cls](cls.md)
- [session](session.md)
- [reset](reset.md)
- [version](version.md)
- [history](history.md)
- [quit](quit.md)
- [stop](stop.md)
- [keymap](keymap.md)

@ -0,0 +1,35 @@
# 联系我们
## 招聘
- [期待你的加入](https://mp.weixin.qq.com/s/XQv8GnqGT3pzceVwzeiy-A)
### Issues
使用疑问,意见可以直接在 Issues 里提出: [https://github.com/alibaba/arthas/issues](https://github.com/alibaba/arthas/issues)
### 微信公众号
欢迎关注公众号,获取 Arthas 项目的信息、源码分析、案例实践。
![](/images/qrcode_gongzhonghao.jpg)
### 钉钉群
- Arthas 开源交流钉钉群: 21965291 ,搜索群号即可加入。
![](/images/dingding_qr.jpg)
- Arthas 开源交流钉钉群 2 30707824 ,搜索群号即可加入。
![](/images/dingding2_qr.jpg)
### QQ 群
Arthas 开源交流 QQ 群: 916328269
![](/images/qqgroup_qr.jpg)
Arthas 开源交流 QQ 群 2 854625984
![](/images/qqgroup2_qr.jpg)

@ -1,16 +1,17 @@
dashboard
===
# dashboard
[`dashboard`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-dashboard)
> 当前系统的实时数据面板,按 ctrl+c 退出。
::: tip
当前系统的实时数据面板,按 ctrl+c 退出。
:::
当运行在 Ali-tomcat 时,会显示当前 tomcat 的实时信息,如 HTTP 请求的 qps, rt, 错误数, 线程池信息等等。
### 参数说明
| 参数名称 | 参数说明 |
|---:|:---|
| -------: | :--------------------------------------- |
| [i:] | 刷新实时数据的时间间隔 (ms),默认 5000ms |
| [n:] | 刷新实时数据的次数 |
@ -53,30 +54,31 @@ uptime 272s
### 数据说明
* ID: Java级别的线程ID注意这个ID不能跟jstack中的nativeID一一对应。
* NAME: 线程名
* GROUP: 线程组名
* PRIORITY: 线程优先级, 1~10之间的数字,越大表示优先级越高
* STATE: 线程的状态
* CPU%: 线程的cpu使用率。比如采样间隔1000ms某个线程的增量cpu时间为100ms则cpu使用率=100/1000=10%
* DELTA_TIME: 上次采样之后线程运行增量CPU时间,数据格式为`秒`
* TIME: 线程运行总CPU时间,数据格式为`分:秒`
* INTERRUPTED: 线程当前的中断位状态
* DAEMON: 是否是daemon线程
- ID: Java 级别的线程 ID注意这个 ID 不能跟 jstack 中的 nativeID 一一对应。
- NAME: 线程名
- GROUP: 线程组名
- PRIORITY: 线程优先级, 1~10 之间的数字,越大表示优先级越高
- STATE: 线程的状态
- CPU%: 线程的 cpu 使用率。比如采样间隔 1000ms某个线程的增量 cpu 时间为 100ms则 cpu 使用率=100/1000=10%
- DELTA_TIME: 上次采样之后线程运行增量 CPU 时间,数据格式为`秒`
- TIME: 线程运行总 CPU 时间,数据格式为`分:秒`
- INTERRUPTED: 线程当前的中断位状态
- DAEMON: 是否是 daemon 线程
#### JVM 内部线程
Java 8 之后支持获取 JVM 内部线程 CPU 时间,这些线程只有名称和 CPU 时间,没有 ID 及状态等信息(显示 ID 为-1
通过内部线程可以观测到 JVM 活动,如 GC、JIT 编译等占用 CPU 情况,方便了解 JVM 整体运行状况。
* 当JVM 堆(heap)/元数据(metaspace)空间不足或OOM时可以看到GC线程的CPU占用率明显高于其他的线程。
* 当执行`trace/watch/tt/redefine`等命令后可以看到JIT线程活动变得更频繁。因为JVM热更新class字节码时清除了此class相关的JIT编译结果需要重新编译。
- 当 JVM 堆(heap)/元数据(metaspace)空间不足或 OOM 时,可以看到 GC 线程的 CPU 占用率明显高于其他的线程。
- 当执行`trace/watch/tt/redefine`等命令后,可以看到 JIT 线程活动变得更频繁。因为 JVM 热更新 class 字节码时清除了此 class 相关的 JIT 编译结果,需要重新编译。
JVM 内部线程包括下面几种:
* JIT编译线程: 如 `C1 CompilerThread0`, `C2 CompilerThread0`
* GC线程: 如`GC Thread0`, `G1 Young RemSet Sampling`
* 其它内部线程: 如`VM Periodic Task Thread`, `VM Thread`, `Service Thread`
- JIT 编译线程: 如 `C1 CompilerThread0`, `C2 CompilerThread0`
- GC 线程: 如`GC Thread0`, `G1 Young RemSet Sampling`
- 其它内部线程: 如`VM Periodic Task Thread`, `VM Thread`, `Service Thread`
### 截图展示
![](_static/dashboard.png "dashboard")
![](/images/dashboard.png "dashboard")

@ -0,0 +1,118 @@
# Docker
## 在 Docker 里使用 JDK
很多时候,应用在 docker 里出现 arthas 无法工作的问题,是因为应用没有安装 JDK ,而是安装了 JRE 。如果只安装了 JRE则会缺少很多 JAVA 的命令行工具和类库Arthas 也没办法正常工作。下面介绍两种常见的在 Docker 里使用 JDK 的方式。
### 使用公开的 JDK 镜像
- https://hub.docker.com/_/openjdk/
比如:
```
FROM openjdk:8-jdk
```
或者:
```
FROM openjdk:8-jdk-alpine
```
### 通过包管理软件来安装
比如:
```bash
# Install OpenJDK-8
RUN apt-get update && \
apt-get install -y openjdk-8-jdk && \
apt-get install -y ant && \
apt-get clean;
# Fix certificate issues
RUN apt-get update && \
apt-get install ca-certificates-java && \
apt-get clean && \
update-ca-certificates -f;
# Setup JAVA_HOME -- useful for docker commandline
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/
RUN export JAVA_HOME
```
或者:
```bash
RUN yum install -y \
java-1.8.0-openjdk \
java-1.8.0-openjdk-devel
ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk/
RUN export JAVA_HOME
```
## 通过 Docker 快速入门
1. 删除本地已有的`math-game` docker container非必要
```sh
$ docker stop math-game || true && docker rm math-game || true
```
1. 启动`math-game`
```sh
$ docker run --name math-game -it hengyunabc/arthas:latest /bin/sh -c "java -jar /opt/arthas/math-game.jar"
```
1. 启动`arthas-boot`来进行诊断
```sh
$ docker exec -it math-game /bin/sh -c "java -jar /opt/arthas/arthas-boot.jar"
* [1]: 9 jar
[INFO] arthas home: /opt/arthas
[INFO] Try to attach process 9
[INFO] Attach process 9 success.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
wiki: https://arthas.aliyun.com/doc
version: 3.0.5
pid: 9
time: 2018-12-18 11:30:36
```
## 诊断 Docker 里的 Java 进程
```sh
docker exec -it ${containerId} /bin/bash -c "wget https://arthas.aliyun.com/arthas-boot.jar && java -jar arthas-boot.jar"
```
## 诊断 k8s 里容器里的 Java 进程
```sh
kubectl exec -it ${pod} --container ${containerId} -- /bin/bash -c "wget https://arthas.aliyun.com/arthas-boot.jar && java -jar arthas-boot.jar"
```
## 把 Arthas 安装到基础镜像里
可以很简单把 Arthas 安装到你的 Docker 镜像里。
```
FROM openjdk:8-jdk-alpine
# copy arthas
COPY --from=hengyunabc/arthas:latest /opt/arthas /opt/arthas
```
如果想指定版本,可以查看具体的 tags
[https://hub.docker.com/r/hengyunabc/arthas/tags](https://hub.docker.com/r/hengyunabc/arthas/tags)

@ -1,5 +1,4 @@
下载
===
# 下载
## 下载全量包
@ -7,7 +6,6 @@
最新版本,点击下载:[![](https://img.shields.io/maven-central/v/com.taobao.arthas/arthas-packaging.svg?style=flat-square "Arthas")](https://arthas.aliyun.com/download/latest_version?mirror=aliyun)
### 从 Github Releases 页下载
[https://github.com/alibaba/arthas/releases](https://github.com/alibaba/arthas/releases)
@ -40,9 +38,6 @@ java -jar arthas-boot.jar
java -jar arthas-boot.jar -h
```
## 下载离线文档
下载文档:[![](https://img.shields.io/maven-central/v/com.taobao.arthas/arthas-packaging.svg?style=flat-square "Arthas")](https://arthas.aliyun.com/download/doc/latest_version?mirror=aliyun)

@ -0,0 +1,65 @@
# dump
[`dump`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials?language=cn&id=command-dump)
::: tip
dump 已加载类的 bytecode 到特定目录
:::
### 参数说明
| 参数名称 | 参数说明 |
| --------------------: | :----------------------------------------- |
| _class-pattern_ | 类名表达式匹配 |
| `[c:]` | 类所属 ClassLoader 的 hashcode |
| `[classLoaderClass:]` | 指定执行表达式的 ClassLoader 的 class name |
| `[d:]` | 设置类文件的目标目录 |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
### 使用参考
```bash
$ dump java.lang.String
HASHCODE CLASSLOADER LOCATION
null /Users/admin/logs/arthas/classdump/java/lang/String.class
Affect(row-cnt:1) cost in 119 ms.
```
```bash
$ dump demo.*
HASHCODE CLASSLOADER LOCATION
3d4eac69 +-sun.misc.Launcher$AppClassLoader@3d4eac69 /Users/admin/logs/arthas/classdump/sun.misc.Launcher$AppClassLoader-3d4eac69/demo/MathGame.class
+-sun.misc.Launcher$ExtClassLoader@66350f69
Affect(row-cnt:1) cost in 39 ms.
```
```bash
$ dump -d /tmp/output java.lang.String
HASHCODE CLASSLOADER LOCATION
null /tmp/output/java/lang/String.class
Affect(row-cnt:1) cost in 138 ms.
```
- 指定 classLoader
注意 hashcode 是变化的,需要先查看当前的 ClassLoader 信息,提取对应 ClassLoader 的 hashcode。
如果你使用`-c`,你需要手动输入 hashcode`-c <hashcode>`
```bash
$ dump -c 3d4eac69 demo.*
```
对于只有唯一实例的 ClassLoader 可以通过`--classLoaderClass`指定 class name使用起来更加方便
```bash
$ dump --classLoaderClass sun.misc.Launcher$AppClassLoader demo.*
HASHCODE CLASSLOADER LOCATION
3d4eac69 +-sun.misc.Launcher$AppClassLoader@3d4eac69 /Users/admin/logs/arthas/classdump/sun.misc.Launcher$AppClassLoader-3d4eac69/demo/MathGame.class
+-sun.misc.Launcher$ExtClassLoader@66350f69
Affect(row-cnt:1) cost in 39 ms.
```
- 注: 这里 classLoaderClass 在 java 8 是 sun.misc.Launcher$AppClassLoader而java 11的classloader是jdk.internal.loader.ClassLoaders$AppClassLoaderkatacoda 目前环境是 java8。
`--classLoaderClass` 的值是 ClassLoader 的类名,只有匹配到唯一的 ClassLoader 实例时才能工作,目的是方便输入通用命令,而`-c <hashcode>`是动态变化的。

@ -1,10 +1,10 @@
echo
===
# echo
[`echo`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-echo)
> 打印参数和linux里的echo命令类似。
::: tip
打印参数,和 linux 里的 echo 命令类似。
:::
```bash
$ echo 'hello'

@ -1,18 +1,17 @@
# FAQ
## FAQ
> 不在本列表里的问题请到issue里搜索。 [https://github.com/alibaba/arthas/issues](https://github.com/alibaba/arthas/issues)
::: tip
不在本列表里的问题,请到 issue 里搜索。 [https://github.com/alibaba/arthas/issues](https://github.com/alibaba/arthas/issues)
:::
##### 日志文件在哪里?
日志文件路径: `~/logs/arthas/arthas.log`
##### Arthas attach 之后对原进程性能有多大的影响
[https://github.com/alibaba/arthas/issues/44](https://github.com/alibaba/arthas/issues/44)
##### target process not responding or HotSpot VM not loaded
com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: target process not responding or HotSpot VM not loaded
@ -21,6 +20,7 @@ com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: ta
2. 尝试使用 `jstack -l $pid`,如果进程没有反应,则说明进程可能假死,无法响应 JVM attach 信号。所以同样基于 attach 机制的 Arthas 无法工作。尝试使用`jmap` heapdump 后分析。
3. 尝试按[quick-start](quick-start.md)里的方式 attach math-game。
4. 更多情况参考: [https://github.com/alibaba/arthas/issues/347](https://github.com/alibaba/arthas/issues/347)
##### trace/watch 等命令能否增强 jdk 里的类?
默认情况下会过滤掉`java.`开头的类和被`BootStrap ClassLoader`加载的类。可以通过参数开启。
@ -31,7 +31,9 @@ options unsafe true
更多参考 [options](options.md)
> 通过 java.lang.instrument.Instrumentation#appendToBootstrapClassLoaderSearch append到`Bootstrap ClassLoader`的jar包需要开启unsafe。
::: tip
通过 java.lang.instrument.Instrumentation#appendToBootstrapClassLoaderSearch append 到`Bootstrap ClassLoader`的 jar 包需要开启 unsafe。
:::
##### 怎么以`json`格式查看结果
@ -41,18 +43,15 @@ options json-format true
更多参考 [options](options.md)
##### Arthas 能否跟踪 native 函数
不能。
##### 能不能查看内存里某个变量的值
1. 可以使用[`vmtool`](vmtool.md)命令。
2. 可以用一些技巧,用[`tt`](tt.md)命令拦截到对象,或者从静态函数里取到对象。
##### 方法同名过滤
同名方法过滤可以通过匹配表达式,可以使用[表达式核心变量](advice-class.md)中所有变量作为已知条件,可以通过判断参数个数`params.length ==1`, 参数类型`params[0] instanceof java.lang.Integer`、返回值类型 `returnObj instanceof java.util.List` 等等一种或者多种组合进行过滤。
@ -93,7 +92,6 @@ ognl '@java.lang.System@out.println("Hello \u4e2d\u6587")'
比如: 使用 skywalking V8.1.0 以下版本 [无法 trace、watch 被 skywalking agent 增强过的类](https://github.com/alibaba/arthas/issues/1141), V8.1.0 以上版本可以兼容使用,更多参考 skywalking 配置 [skywalking compatible with other javaagent bytecode processing](https://github.com/apache/skywalking/blob/master/docs/en/FAQ/Compatible-with-other-javaagent-bytecode-processing.md#)。
#### class redefinition failed: attempted to change the schema (add/remove fields)
参考: [https://github.com/alibaba/arthas/issues/2165](https://github.com/alibaba/arthas/issues/2165)
@ -108,6 +106,6 @@ ognl '@java.lang.System@out.println("Hello \u4e2d\u6587")'
##### 为什么下载了新版本的 Arthas连接的却是旧版本
比如启动的 `as.sh/arthas-boot.jar` 版本是3.5.* 的但是连接上之后打印的arthas版本是 3.4.* 的。
比如启动的 `as.sh/arthas-boot.jar` 版本是 3.5._ 的,但是连接上之后,打印的 arthas 版本是 3.4._ 的。
可能是之前使用旧版本的 arthas 诊断过目标进程。可以先执行`stop`停止掉旧版本的 arthas再重新使用新版本 attach。

@ -0,0 +1,70 @@
# getstatic
[`getstatic`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-getstatic)
- 推荐直接使用[ognl](ognl.md)命令,更加灵活。
通过 getstatic 命令可以方便的查看类的静态属性。使用方法为`getstatic class_name field_name`
```bash
$ getstatic demo.MathGame random
field: random
@Random[
serialVersionUID=@Long[3905348978240129619],
seed=@AtomicLong[120955813885284],
multiplier=@Long[25214903917],
addend=@Long[11],
mask=@Long[281474976710655],
DOUBLE_UNIT=@Double[1.1102230246251565E-16],
BadBound=@String[bound must be positive],
BadRange=@String[bound must be greater than origin],
BadSize=@String[size must be non-negative],
seedUniquifier=@AtomicLong[-3282039941672302964],
nextNextGaussian=@Double[0.0],
haveNextNextGaussian=@Boolean[false],
serialPersistentFields=@ObjectStreamField[][isEmpty=false;size=3],
unsafe=@Unsafe[sun.misc.Unsafe@2eaa1027],
seedOffset=@Long[24],
]
```
- 指定 classLoader
注意 hashcode 是变化的,需要先查看当前的 ClassLoader 信息,使用`sc -d <ClassName>`提取对应 ClassLoader 的 hashcode。
如果你使用`-c`,你需要手动输入 hashcode`-c <hashcode>`
```bash
$ getstatic -c 3d4eac69 demo.MathGame random
```
对于只有唯一实例的 ClassLoader 可以通过`--classLoaderClass`指定 class name使用起来更加方便
`getstatic --classLoaderClass sun.misc.Launcher$AppClassLoader demo.MathGame random`
- 注: 这里 classLoaderClass 在 java 8 是 sun.misc.Launcher$AppClassLoader而java 11的classloader是jdk.internal.loader.ClassLoaders$AppClassLoaderkatacoda 目前环境是 java8。
`--classLoaderClass` 的值是 ClassLoader 的类名,只有匹配到唯一的 ClassLoader 实例时才能工作,目的是方便输入通用命令,而`-c <hashcode>`是动态变化的。
如果该静态属性是一个复杂对象,还可以支持在该属性上通过 ognl 表示进行遍历,过滤,访问对象的内部属性等操作。
- OGNL 特殊用法请参考:[https://github.com/alibaba/arthas/issues/71](https://github.com/alibaba/arthas/issues/71)
- OGNL 表达式官方指南:[https://commons.apache.org/proper/commons-ognl/language-guide.html](https://commons.apache.org/proper/commons-ognl/language-guide.html)
例如,假设 n 是一个 MapMap 的 Key 是一个 Enum我们想过滤出 Map 中 Key 为某个 Enum 的值,可以写如下命令
```
$ getstatic com.alibaba.arthas.Test n 'entrySet().iterator.{? #this.key.name()=="STOP"}'
field: n
@ArrayList[
@Node[STOP=bbb],
]
Affect(row-cnt:1) cost in 68 ms.
$ getstatic com.alibaba.arthas.Test m 'entrySet().iterator.{? #this.key=="a"}'
field: m
@ArrayList[
@Node[a=aaa],
]
```

@ -1,10 +1,10 @@
grep
===
# grep
[`grep`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-grep)
> 类似传统的`grep`命令。
::: tip
类似传统的`grep`命令。
:::
```
USAGE:

@ -1,7 +1,8 @@
groovy
===
# groovy
> Arthas 支持 groovy 脚本增强,允许像 BTrace 一样编写脚本来解决问题,可以在 groovy 脚本中进行if/for/switch/while 等控制语句,不受限制,但相比 BTrace 而言拥有更多的限制范围。
::: tip
Arthas 支持 groovy 脚本增强,允许像 BTrace 一样编写脚本来解决问题,可以在 groovy 脚本中进行 if/for/switch/while 等控制语句,不受限制,但相比 BTrace 而言拥有更多的限制范围。
:::
### 限制内容
@ -11,10 +12,10 @@ groovy
### 参数说明
| 参数名称 | 参数说明 |
|---:|:---|
|*class-pattern*|类名表达式匹配|
|*method-pattern*|方法名表达式匹配|
|*script-filepath*|groovy 脚本的绝对路径|
| ----------------: | :----------------------------------- |
| _class-pattern_ | 类名表达式匹配 |
| _method-pattern_ | 方法名表达式匹配 |
| _script-filepath_ | groovy 脚本的绝对路径 |
| [S] | 匹配所有的子类 |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |

@ -1,10 +1,10 @@
heapdump
===
# heapdump
[`heapdump`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-heapdump)
> dump java heap, 类似jmap命令的heap dump功能。
::: tip
dump java heap, 类似 jmap 命令的 heap dump 功能。
:::
### 使用参考
@ -31,6 +31,3 @@ Heap dump file created
Dumping heap to /var/folders/my/wy7c9w9j5732xbkcyt1mb4g40000gp/T/heapdump2019-09-03-16-385121018449645518991.hprof...
Heap dump file created
```

@ -1,14 +1,15 @@
help
===
# help
查看命令帮助信息,可以查看当前 arthas 版本支持的指令,或者查看具体指令的使用说明。
> [help 指令]的等同于[指令 -help],都是查看具体指令的使用说明。
::: tip
[help 指令]的等同于[指令 -help],都是查看具体指令的使用说明。
:::
### 参数说明
| 参数名称 | 参数说明 |
| -------: | :--------------------------------------- |
| -------: | :----------------------------------------- |
| 不接参数 | 查询当前 arthas 版本支持的指令以及指令描述 |
| [name:] | 查询具体指令的使用说明 |
@ -84,4 +85,3 @@ $ help
-i, --interval <value> The interval (in ms) between two executions, default is 5000 ms.
-n, --number-of-execution <value> The number of times this command will be executed.
```

@ -0,0 +1,31 @@
# history
打印命令历史。
::: tip
历史指令会通过一个名叫 history 的文件持久化,所以 history 指令可以查看当前 arthas 服务器的所有历史命令,而不仅只是当前次会话使用过的命令。
:::
### 参数说明
| 参数名称 | 参数说明 |
| -------: | :---------------------- |
| [c:] | 清空历史指令 |
| [n:] | 显示最近执行的 n 条指令 |
### 使用参考
```
#查看最近执行的3条指令
$ history 3
269 thread
270 cls
271 history 3
```
```
#清空指令
$ history -c
$ history 3
1 history 3
```

@ -0,0 +1,684 @@
# Http API
[`Http API`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=case-http-api)
### 概览
Http API
提供类似 RESTful 的交互接口,请求和响应均为 JSON 格式的数据。相对于 Telnet/WebConsole 的输出非结构化文本数据Http
API 可以提供结构化的数据,支持更复杂的交互功能,比如特定应用场景的一系列诊断操作。
#### 访问地址
Http API 接口地址为:`http://ip:port/api`,必须使用 POST 方式提交请求参数。如 POST
`http://127.0.0.1:8563/api`
注意telnet 服务的 3658 端口与 Chrome 浏览器有兼容性问题,建议使用 http 端口 8563 来访问 http 接口。
#### 请求数据格式
```json
{
"action": "exec",
"requestId": "req112",
"sessionId": "94766d3c-8b39-42d3-8596-98aee3ccbefb",
"consumerId": "955dbd1325334a84972b0f3ac19de4f7_2",
"command": "version",
"execTimeout": "10000"
}
```
请求数据格式说明:
- `action` : 请求的动作/行为,可选值请参考"请求 Action"小节。
- `requestId` : 可选请求 ID由客户端生成。
- `sessionId` : Arthas 会话 ID一次性命令不需要设置会话 ID。
- `consumerId` : Arthas 消费者 ID用于多人共享会话。
- `command` : Arthas command line 。
- `execTimeout` : 命令同步执行的超时时间(ms),默认为 30000。
注意: 不同的 action 使用到参数不同,根据具体的 action 来设置参数。
#### 请求 Action
目前支持的请求 Action 如下:
- `exec` : 同步执行命令,命令正常结束或者超时后中断命令执行后返回命令的执行结果。
- `async_exec` : 异步执行命令,立即返回命令的调度结果,命令执行结果通过`pull_results`获取。
- `interrupt_job` : 中断会话当前的命令,类似 Telnet `Ctrl + c`的功能。
- `pull_results` : 获取异步执行的命令的结果,以 http 长轮询long-polling方式重复执行
- `init_session` : 创建会话
- `join_session` : 加入会话,用于支持多人共享同一个 Arthas 会话
- `close_session` : 关闭会话
#### 响应状态
响应中的 state 属性表示请求处理状态,取值如下:
- `SCHEDULED`:异步执行命令时表示已经创建 job 并已提交到命令执行队列,命令可能还没开始执行或者执行中;
- `SUCCEEDED`:请求处理成功(完成状态);
- `FAILED`:请求处理失败(完成状态),通常附带 message 说明原因;
- `REFUSED`:请求被拒绝(完成状态),通常附带 message 说明原因;
### 一次性命令
与执行批处理命令类似,一次性命令以同步方式执行。不需要创建会话,不需要设置`sessionId`选项。
```json
{
"action": "exec",
"command": "<Arthas command line>"
}
```
比如获取 Arthas 版本号:
```bash
curl -Ss -XPOST http://localhost:8563/api -d '
{
"action":"exec",
"command":"version"
}
'
```
响应内容如下:
```json
{
"state": "SUCCEEDED",
"sessionId": "ee3bc004-4586-43de-bac0-b69d6db7a869",
"body": {
"results": [
{
"type": "version",
"version": "3.3.7",
"jobId": 5
},
{
"jobId": 5,
"statusCode": 0,
"type": "status"
}
],
"timeExpired": false,
"command": "version",
"jobStatus": "TERMINATED",
"jobId": 5
}
}
```
响应数据解析:
- `state`: 请求处理状态,参考“接口响应状态”说明
- `sessionId `: Arthas 会话 ID一次性命令自动创建及销毁临时会话
- `body.jobId`: 命令的任务 ID同一任务输出的所有 Result 都是相同的 jobId
- `body.jobStatus`: 任务状态,同步执行正常结束为`TERMINATED `
- `body.timeExpired`: 任务执行是否超时
- `body/results`: 命令执行的结果列表
**命令结果格式说明**
```json
[
{
"type": "version",
"version": "3.3.7",
"jobId": 5
},
{
"jobId": 5,
"statusCode": 0,
"type": "status"
}
]
```
- `type` : 命令结果类型,除了`status`等特殊的几个外,其它的保持与 Arthas 命令名称一致。请参考"[特殊命令结果](#special_command_results)"小节。
- `jobId` : 处理命令的任务 ID。
- 其它字段为每个不同命令的数据。
注意:也可以使用一次性命令的方式执行 watch/trace 等连续输出的命令,但不能中断命令执行,可能出现长时间没有结束的问题。请参考"[watch 命令输出 map 对象](#change_watch_value_to_map)"小节的示例。
请尽量按照以下方式处理:
- 设置合理的`execTimeout`,到达超时时间后强制中断命令执行,避免长时间挂起。
- 通过`-n`参数指定较少的执行次数。
- 保证命令匹配的方法可以成功命中和 condition-express 编写正确,如果 watch/trace 没有命中就算指定`-n 1`也会挂起等待到执行超时。
### 会话交互
由用户创建及管理 Arthas 会话,适用于复杂的交互过程。访问流程如下:
- 创建会话
- 加入会话(可选)
- 拉取命令结果
- 执行一系列命令
- 中断命令执行
- 关闭会话
#### 创建会话
```bash
curl -Ss -XPOST http://localhost:8563/api -d '
{
"action":"init_session"
}
'
```
响应结果:
```json
{
"sessionId": "b09f1353-202c-407b-af24-701b744f971e",
"consumerId": "5ae4e5fbab8b4e529ac404f260d4e2d1_1",
"state": "SUCCEEDED"
}
```
当前会话 ID 为: `b09f1353-202c-407b-af24-701b744f971e` 当前消费者 ID 为:`5ae4e5fbab8b4e529ac404f260d4e2d1_1 `。
#### 加入会话
指定要加入的会话 ID服务端将分配一个新的消费者 ID。多个消费者可以接收到同一个会话的命令结果。本接口用于支持多人共享同一个会话或刷新页面后重新拉取会话历史记录。
```bash
curl -Ss -XPOST http://localhost:8563/api -d '
{
"action":"join_session",
"sessionId" : "b09f1353-202c-407b-af24-701b744f971e"
}
'
```
响应结果:
```json
{
"consumerId": "8f7f6ad7bc2d4cb5aa57a530927a95cc_2",
"sessionId": "b09f1353-202c-407b-af24-701b744f971e",
"state": "SUCCEEDED"
}
```
新的消费者 ID 为`8f7f6ad7bc2d4cb5aa57a530927a95cc_2 ` 。
#### 拉取命令结果
拉取命令结果消息的 action 为`pull_results`。请使用 Http long-polling 方式,定时循环拉取结果消息。
消费者的超时时间为 5 分钟,超时后需要调用`join_session`分配新的消费者。每个消费者单独分配一个缓存队列,按顺序拉取命令结果,不会影响到其它消费者。
请求参数需要指定会话 ID 及消费者 ID:
```bash
curl -Ss -XPOST http://localhost:8563/api -d '
{
"action":"pull_results",
"sessionId" : "b09f1353-202c-407b-af24-701b744f971e",
"consumerId" : "8f7f6ad7bc2d4cb5aa57a530927a95cc_2"
}
'
```
用 Bash 脚本定时拉取结果消息:
```bash
while true; do curl -Ss -XPOST http://localhost:8563/api -d '
{
"action":"pull_results",
"sessionId" : "2b085b5d-883b-4914-ab35-b2c5c1d5aa2a",
"consumerId" : "8ecb9cb7c7804d5d92e258b23d5245cc_1"
}
' | json_pp; sleep 2; done
```
注: `json_pp` 工具将输出内容格式化为 pretty json。
响应内容如下:
```json
{
"body": {
"results": [
{
"inputStatus": "DISABLED",
"jobId": 0,
"type": "input_status"
},
{
"type": "message",
"jobId": 0,
"message": "Welcome to arthas!"
},
{
"tutorials": "https://arthas.aliyun.com/doc/arthas-tutorials.html",
"time": "2020-08-06 15:56:43",
"type": "welcome",
"jobId": 0,
"pid": "7909",
"wiki": "https://arthas.aliyun.com/doc",
"version": "3.3.7"
},
{
"inputStatus": "ALLOW_INPUT",
"type": "input_status",
"jobId": 0
}
]
},
"sessionId": "b09f1353-202c-407b-af24-701b744f971e",
"consumerId": "8f7f6ad7bc2d4cb5aa57a530927a95cc_2",
"state": "SUCCEEDED"
}
```
#### 异步执行命令
```bash
curl -Ss -XPOST http://localhost:8563/api -d '''
{
"action":"async_exec",
"command":"watch demo.MathGame primeFactors \"{params, returnObj, throwExp}\" ",
"sessionId" : "2b085b5d-883b-4914-ab35-b2c5c1d5aa2a"
}
'''
```
`async_exec` 的结果:
```json
{
"sessionId": "2b085b5d-883b-4914-ab35-b2c5c1d5aa2a",
"state": "SCHEDULED",
"body": {
"jobStatus": "READY",
"jobId": 3,
"command": "watch demo.MathGame primeFactors \"{params, returnObj, throwExp}\" "
}
}
```
- `state` : `SCHEDULED` 状态表示已经解析命令生成任务,但未开始执行。
- `body.jobId` :
异步执行命令的任务 ID可以根据此任务 ID 来过滤在`pull_results`输出的命令结果。
- `body.jobStatus` : 任务状态`READY`表示未开始执行。
查看上面自动拉取结果消息脚本的 shell 输出:
```json
{
"body" : {
"results" : [
{
"type" : "command",
"jobId" : 3,
"state" : "SCHEDULED",
"command" : "watch demo.MathGame primeFactors \"{params, returnObj, throwExp}\" "
},
{
"inputStatus" : "ALLOW_INTERRUPT",
"jobId" : 0,
"type" : "input_status"
},
{
"success" : true,
"jobId" : 3,
"effect" : {
"listenerId" : 3,
"cost" : 24,
"classCount" : 1,
"methodCount" : 1
},
"type" : "enhancer"
},
{
"sizeLimit" : 10485760,
"expand" : 1,
"jobId" : 3,
"type" : "watch",
"cost" : 0.071499,
"ts" : 1596703453237,
"value" : [
[
-170365
],
null,
{
"stackTrace" : [
{
"className" : "demo.MathGame",
"classLoaderName" : "app",
"methodName" : "primeFactors",
"nativeMethod" : false,
"lineNumber" : 46,
"fileName" : "MathGame.java"
},
...
],
"localizedMessage" : "number is: -170365, need >= 2",
"@type" : "java.lang.IllegalArgumentException",
"message" : "number is: -170365, need >= 2"
}
]
},
{
"type" : "watch",
"cost" : 0.033375,
"jobId" : 3,
"ts" : 1596703454241,
"value" : [
[
1
],
[
2,
2,
2,
2,
13,
491
],
null
],
"sizeLimit" : 10485760,
"expand" : 1
}
]
},
"consumerId" : "8ecb9cb7c7804d5d92e258b23d5245cc_1",
"sessionId" : "2b085b5d-883b-4914-ab35-b2c5c1d5aa2a",
"state" : "SUCCEEDED"
}
```
watch 命令结果的`value`为 watch-experss 的值,上面命令中为`{params, returnObj, throwExp}`,所以 watch 结果的 value 为一个长度为 3 的数组,每个元素分别对应相应顺序的表达式。
请参考"[watch 命令输出 map 对象](#change_watch_value_to_map)"小节。
#### 中断命令执行
中断会话正在运行的前台 Job前台任务
```bash
curl -Ss -XPOST http://localhost:8563/api -d '''
{
"action":"interrupt_job",
"sessionId" : "2b085b5d-883b-4914-ab35-b2c5c1d5aa2a"
}
'''
```
```
{
"state" : "SUCCEEDED",
"body" : {
"jobStatus" : "TERMINATED",
"jobId" : 3
}
}
```
#### 关闭会话
指定会话 ID关闭会话。
```bash
curl -Ss -XPOST http://localhost:8563/api -d '''
{
"action":"close_session",
"sessionId" : "2b085b5d-883b-4914-ab35-b2c5c1d5aa2a"
}
'''
```
```json
{
"state": "SUCCEEDED"
}
```
### 鉴权
参考: [auth](auth.md)
### Web UI
![](/images/arthas-web-ui.png "arthas web ui")
一个基于 Http API 接口实现的 Web UI访问地址为 [http://127.0.0.1:8563/ui](http://127.0.0.1:8563/ui) 。
已实现功能:
- 创建会话
- 复制并打开 url 加入会话,多人共享会话
- 周期性拉取会话命令结果消息
- 刷新页面或者加入会话拉取会话历史命令消息
- 输入命令/中断命令状态控制
待开发功能:
- 改进将命令结果消息可读性
- 命令输入支持自动完成及命令模板
- 提供命令帮助
- 支持个人选项设置
<a id="special_command_results"></a>
### 特殊命令结果
#### status
```json
{
"jobId": 5,
"statusCode": 0,
"type": "status"
}
```
`type`为`status`表示命令执行状态:
每个命令执行结束后都有唯一一个 status 结果。`statusCode`
为 0 表示执行成功,`statusCode` 为非 0 值表示执行失败,类似进程退出码(exit code)。
命令执行失败时一般会提供错误消息,如:
```json
{
"jobId": 3,
"message": "The argument 'class-pattern' is required",
"statusCode": -10,
"type": "status"
}
```
#### input_status
```json
{
"inputStatus": "ALLOW_INPUT",
"type": "input_status",
"jobId": 0
}
```
`type`为`input_status`表示输入状态:
用于 UI 交互时控制用户输入,每次执行命令前后会发送改变的消息。
`inputStatus` 的值说明:
- `ALLOW_INPUT` :
允许用户输入命令,表示会话没有在执行的前台命令,可以接受新的命令。
- `ALLOW_INTERRUPT` :
允许用户中断命令执行,表示当前正在执行命令,用户可以发送`interrupt_job`中断执行。
- `DISABLED` : 禁用状态,不能输入命令也不能中断命令。
#### command
```json
{
"type": "command",
"jobId": 3,
"state": "SCHEDULED",
"command": "watch demo.MathGame primeFactors \"{params, returnObj, throwExp}\" "
}
```
`type` 为`command`表示输入的命令数据:
用于交互 UI 回显用户输入的命令,拉取的会话命令消息历史会包含`command`类型的消息,按顺序处理即可。
#### enhancer
```json
{
"success": true,
"jobId": 3,
"effect": {
"listenerId": 3,
"cost": 24,
"classCount": 1,
"methodCount": 1
},
"type": "enhancer"
}
```
`type`为`enhancer`表示类增强结果:
`trace/watch/jad/tt`等命令需要对类进行增强,会接收到这个`enhancer`结果。可能出现`enhancer`结果成功,但没有命中方法的情况,客户端可以根据`enhancer`结果提示用户。
### 案例
#### 获取 Java 应用的 Classpath
通过 Http api 查询 Java 应用的 System properties提取`java.class.path`的值。
```bash
json_data=$(curl -Ss -XPOST http://localhost:8563/api -d '
{
"action":"exec",
"command":"sysprop"
}')
```
- 使用`sed`提取值:
```bash
class_path=$(echo $json_data | tr -d '\n' | sed 's/.*"java.class.path":"\([^"]*\).*/\1/')
echo "classpath: $class_path"
```
- 使用`json_pp/awk`提取值
```bash
class_path=$(echo $json_data | tr -d '\n' | json_pp | grep java.class.path | awk -F'"' '{ print $4 }')
echo "classpath: $class_path"
```
输出内容:
```
classpath: demo-arthas-spring-boot.jar
```
注意:
- `echo $json_data | tr -d '\n'` : 删除换行符(`line.separator`的值),避免影响`sed`/`json_pp`命令处理。
- `awk -F'"' '{ print $4 }'` : 使用双引号作为分隔符号
<a id="change_watch_value_to_map"></a>
#### watch 命令输出 map 对象
watch 的结果值由计算`watch-express` ognl 表达式产生,可以通过改变 ognl 表达式来生成想要的值,请参考[OGNL 文档](https://commons.apache.org/proper/commons-ognl/language-guide.html)。
::: tip
Maps can also be created using a special syntax.
#{ "foo" : "foo value", "bar" : "bar value" }
This creates a Map initialized with mappings for "foo" and "bar".
:::
下面的命令生成 map 格式的值:
```bash
watch *MathGame prime* '#{ "params" : params, "returnObj" : returnObj, "throwExp": throwExp}' -x 2 -n 5
```
在 Telnet shell/WebConsole 中执行上面的命令,输出的结果:
```bash
ts=2020-08-06 16:57:20; [cost=0.241735ms] result=@LinkedHashMap[
@String[params]:@Object[][
@Integer[1],
],
@String[returnObj]:@ArrayList[
@Integer[2],
@Integer[241],
@Integer[379],
],
@String[throwExp]:null,
]
```
用 Http api 执行上面的命令,注意对 JSON 双引号转义:
```bash
curl -Ss -XPOST http://localhost:8563/api -d @- << EOF
{
"action":"exec",
"execTimeout": 30000,
"command":"watch *MathGame prime* '#{ \"params\" : params, \"returnObj\" : returnObj, \"throwExp\": throwExp}' -n 3 "
}
EOF
```
Http api 执行结果:
```json
{
"body": {
...
"results": [
...
{
...
"type": "watch",
"value": {
"params": [
1
],
"returnObj": [
2,
5,
17,
23,
23
]
}
},
{
...
"type": "watch",
"value": {
"params": [
-98278
],
"throwExp": {
"@type": "java.lang.IllegalArgumentException",
"localizedMessage": "number is: -98278, need >= 2",
"message": "number is: -98278, need >= 2",
"stackTrace": [
...
]
}
}
},
...
}
```
可以看到 watch 结果的 value 变成 map 对象,程序可以通过 key 读取结果。

@ -0,0 +1,23 @@
# IDEA Plugin
#### Arthas-idea部分命令可视化
::: tip
插件由社区开发者提供。
:::
- Jetbrains 插件获取地址: [https://plugins.jetbrains.com/plugin/13581-arthas-idea](https://plugins.jetbrains.com/plugin/13581-arthas-idea)
- 使用文档:[https://www.yuque.com/arthas-idea-plugin](https://www.yuque.com/arthas-idea-plugin)
- 源码地址: [https://github.com/WangJi92/arthas-idea-plugin](https://github.com/WangJi92/arthas-idea-plugin)
#### Alibaba Cloud Toolkit 热部署组件(一键 retransform
::: tip
热部署组件支持一键将编辑器中修改的 Java 源码快速编译,并更新到远端应用服务中,免去手动 dump、mc 的过程。此外,也可以一键还原 retransform 的类文件。
:::
![](/images/alibabacloud_hotreload.png)
- Jetbrains 插件获取地址: [https://plugins.jetbrains.com/plugin/11386-alibaba-cloud-toolkit](https://plugins.jetbrains.com/plugin/11386-alibaba-cloud-toolkit)
- 使用文档:[https://help.aliyun.com/document_detail/381077.html](https://help.aliyun.com/document_detail/381077.html)
- 联系我们:请加 Alibaba Cloud Toolkit (应用观测器) 钉钉用户交流群(群号:**34965379**

@ -1,5 +1,4 @@
Arthas Install
=============
# Arthas Install
## 快速安装
@ -18,7 +17,7 @@ java -jar arthas-boot.jar
java -jar arthas-boot.jar -h
```
* 如果下载速度比较慢可以使用aliyun的镜像:
- 如果下载速度比较慢,可以使用 aliyun 的镜像:
```bash
java -jar arthas-boot.jar --repo-mirror aliyun --use-http
@ -54,13 +53,10 @@ java -jar arthas-boot.jar
java -jar arthas-boot.jar -h
```
## 手动安装
[手动安装](manual-install.md)
## 通过 rpm/deb 来安装
在 releases 页面下载 rpm/deb 包: https://github.com/alibaba/arthas/releases
@ -70,6 +66,7 @@ java -jar arthas-boot.jar -h
```bash
sudo dpkg -i arthas*.deb
```
### 安装 rpm
```bash
@ -86,7 +83,7 @@ as.sh
## 通过 Cloud Toolkit 插件使用 Arthas
* [通过Cloud Toolkit插件使用Arthas一键诊断远程服务器](https://github.com/alibaba/arthas/issues/570)
- [通过 Cloud Toolkit 插件使用 Arthas 一键诊断远程服务器](https://github.com/alibaba/arthas/issues/570)
## 离线帮助文档
@ -94,12 +91,13 @@ as.sh
## 卸载
* 在 Linux/Unix/Mac 平台
- 在 Linux/Unix/Mac 平台
删除下面文件:
```bash
rm -rf ~/.arthas/
rm -rf ~/logs/arthas
```
* Windows平台直接删除user home下面的`.arthas`和`logs/arthas`目录
- Windows 平台直接删除 user home 下面的`.arthas`和`logs/arthas`目录

@ -1,20 +1,21 @@
jad
===
# jad
[`jad`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials?language=cn&id=command-jad)
> 反编译指定已加载类的源码
::: tip
反编译指定已加载类的源码
:::
`jad` 命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码,便于你理解业务逻辑;
* 在 Arthas Console 上,反编译出来的源码是带语法高亮的,阅读更方便
* 当然,反编译出来的 java 代码可能会存在语法错误,但不影响你进行阅读理解
- 在 Arthas Console 上,反编译出来的源码是带语法高亮的,阅读更方便
- 当然,反编译出来的 java 代码可能会存在语法错误,但不影响你进行阅读理解
### 参数说明
| 参数名称 | 参数说明 |
|---:|:---|
|*class-pattern*|类名表达式匹配|
| --------------------: | :----------------------------------------- |
| _class-pattern_ | 类名表达式匹配 |
| `[c:]` | 类所属 ClassLoader 的 hashcode |
| `[classLoaderClass:]` | 指定执行表达式的 ClassLoader 的 class name |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
@ -129,7 +130,9 @@ public static void main(String[] args) throws InterruptedException {
#### 反编译时指定 ClassLoader
> 当有多个 `ClassLoader` 都加载了这个类时,`jad` 命令会输出对应 `ClassLoader` 实例的 `hashcode`,然后你只需要重新执行 `jad` 命令,并使用参数 `-c <hashcode>` 就可以反编译指定 ClassLoader 加载的那个类了;
::: tip
当有多个 `ClassLoader` 都加载了这个类时,`jad` 命令会输出对应 `ClassLoader` 实例的 `hashcode`,然后你只需要重新执行 `jad` 命令,并使用参数 `-c <hashcode>` 就可以反编译指定 ClassLoader 加载的那个类了;
:::
```java
$ jad org.apache.log4j.Logger

@ -1,9 +1,10 @@
jvm
===
# jvm
[`jvm`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-jvm)
> 查看当前JVM信息
::: tip
查看当前 JVM 信息
:::
### 使用参考
@ -111,13 +112,13 @@ Affect(row-cnt:0) cost in 88 ms.
### THREAD 相关
* COUNT: JVM当前活跃的线程数
* DAEMON-COUNT: JVM当前活跃的守护线程数
* PEAK-COUNT: 从JVM启动开始曾经活着的最大线程数
* STARTED-COUNT: 从JVM启动开始总共启动过的线程次数
* DEADLOCK-COUNT: JVM当前死锁的线程数
- COUNT: JVM 当前活跃的线程数
- DAEMON-COUNT: JVM 当前活跃的守护线程数
- PEAK-COUNT: 从 JVM 启动开始曾经活着的最大线程数
- STARTED-COUNT: 从 JVM 启动开始总共启动过的线程次数
- DEADLOCK-COUNT: JVM 当前死锁的线程数
### 文件描述符相关
* MAX-FILE-DESCRIPTOR-COUNTJVM进程最大可以打开的文件描述符数
* OPEN-FILE-DESCRIPTOR-COUNTJVM当前打开的文件描述符数
- MAX-FILE-DESCRIPTOR-COUNTJVM 进程最大可以打开的文件描述符数
- OPEN-FILE-DESCRIPTOR-COUNTJVM 当前打开的文件描述符数

@ -1,5 +1,4 @@
Arthas 命令行快捷键
===
# Arthas 命令行快捷键
[`keymap`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-keymap)
@ -7,12 +6,8 @@ Arthas 命令行快捷键
默认的快捷键如下:
| 快捷键 | 快捷键说明 | 命令名称 | 命令说明 |
| ------------- | ---------------- | -------------------- | ---------------- |
| ------------- | ---------------- | -------------------- | -------------------------------- |
| `"\C-a"` | ctrl + a | beginning-of-line | 跳到行首 |
| ` "\C-e"` | ctrl + e | end-of-line | 跳到行尾 |
| `"\C-f"` | ctrl + f | forward-word | 向前移动一个单词 |
@ -33,10 +28,8 @@ Arthas 命令行快捷键
| `"\C-x\e[3~"` | | backward-kill-line | |
| `"\e\C-?"` | | backward-kill-word | |
* 任何时候 `tab` 键,会根据当前的输入给出提示
* 命令后敲 `-``--` ,然后按 `tab` 键,可以展示出此命令具体的选项
- 任何时候 `tab` 键,会根据当前的输入给出提示
- 命令后敲 `-``--` ,然后按 `tab` 键,可以展示出此命令具体的选项
#### 自定义快捷键
@ -70,11 +63,7 @@ Arthas 命令行快捷键
#### 后台异步命令相关快捷键
* ctrl + c: 终止当前命令
* ctrl + z: 挂起当前命令,后续可以 bg/fg 重新支持此命令,或 kill 掉
* ctrl + a: 回到行首
* ctrl + e: 回到行尾
- ctrl + c: 终止当前命令
- ctrl + z: 挂起当前命令,后续可以 bg/fg 重新支持此命令,或 kill 掉
- ctrl + a: 回到行首
- ctrl + e: 回到行尾

@ -1,9 +1,10 @@
logger
===
# logger
[`logger`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials?language=cn&id=command-logger)
> 查看logger信息更新logger level
::: tip
查看 logger 信息,更新 logger level
:::
### 使用参考
@ -46,7 +47,6 @@ logger
</configuration>
```
使用`logger`命令打印的结果是:
```bash
@ -78,10 +78,9 @@ logger
从`appenders`的信息里,可以看到
* `CONSOLE` logger的target是`System.out`
* `APPLICATION` logger是`RollingFileAppender`它的file是`app.log`
* `ASYNC`它的`appenderRef`是`APPLICATION`,即异步输出到文件里
- `CONSOLE` logger 的 target 是`System.out`
- `APPLICATION` logger 是`RollingFileAppender`,它的 file 是`app.log`
- `ASYNC`它的`appenderRef`是`APPLICATION`,即异步输出到文件里
#### 查看指定名字的 logger 信息
@ -134,7 +133,7 @@ logger
`logger --classLoaderClass sun.misc.Launcher$AppClassLoader`
* 注: 这里classLoaderClass 在 java 8 是 sun.misc.Launcher$AppClassLoader而java 11的classloader是jdk.internal.loader.ClassLoaders$AppClassLoader。
- 注: 这里 classLoaderClass 在 java 8 是 sun.misc.Launcher$AppClassLoader而java 11的classloader是jdk.internal.loader.ClassLoaders$AppClassLoader。
`--classLoaderClass` 的值是 ClassLoader 的类名,只有匹配到唯一的 ClassLoader 实例时才能工作,目的是方便输入通用命令,而`-c <hashcode>`是动态变化的。
@ -157,7 +156,6 @@ update logger level success.
#### 查看没有 appender 的 logger 的信息
默认情况下,`logger`命令只打印有 appender 的 logger 的信息。如果想查看没有`appender`的 logger 的信息,可以加上参数`--include-no-appender`。
注意,通常输出结果会很长。

@ -0,0 +1,112 @@
# 手动安装 Arthas
1. 下载最新版本
**最新版本,点击下载**[![](https://img.shields.io/maven-central/v/com.taobao.arthas/arthas-packaging.svg?style=flat-square "Arthas")](https://arthas.aliyun.com/download/latest_version?mirror=aliyun)
2. 解压缩 arthas 的压缩包
```
unzip arthas-packaging-bin.zip
```
3. 安装 Arthas
安装之前最好把所有老版本的 Arthas 全都删掉
```
sudo su admin
rm -rf /home/admin/.arthas/lib/*
cd arthas
./install-local.sh
```
::: warning
注意,这里根据你需要诊断的 Java 进程的所属用户进行切换
:::
4. 启动 Arthas
启动之前,请确保老版本的 Arthas 已经`stop`.
```
./as.sh
```
## 以脚本的方式启动 as.sh/as.bat
### Linux/Unix/Mac
Arthas 支持在 Linux/Unix/Mac 等平台上一键安装,请复制以下内容,并粘贴到命令行中,敲 `回车` 执行即可:
```bash
curl -L https://arthas.aliyun.com/install.sh | sh
```
上述命令会下载启动脚本文件 `as.sh` 到当前目录,你可以放在任何地方或将其加入到 `$PATH` 中。
直接在 shell 下面执行`./as.sh`,就会进入交互界面。
也可以执行`./as.sh -h`来获取更多参数信息。
### Windows
最新版本,点击下载:[![](https://img.shields.io/maven-central/v/com.taobao.arthas/arthas-packaging.svg?style=flat-square "Arthas")](https://arthas.aliyun.com/download/latest_version?mirror=aliyun)
下载解压后在 bin 目录有 `as.bat`。此脚本暂时只接受一个参数 pid即只能诊断本机上的 Java 进程。(欢迎精通 bat 脚本的开发者改进)
```
as.bat <pid>
```
使用以下命令诊断 windows 服务模式运行的 Java 进程 (--interact 打开服务 UI 交互模式,方便诊断问题)
```
as-service.bat -port <port>
as-service.bat -pid <pid>
as-service.bat -pid <pid> --interact
```
清理 arthas windows 服务执行以下命令:
```
as-service.bat -remove
```
## 手动拼接命令行启动
如果启动遇到问题,可以尝试手动拼接出命令行参数来启动。
1. 查找目录 jvm 的 java 文件路径。
在 linux/mac 上执行`ps aux | grep java`,在 windows 上可以通过进程管理器来查看。假设是`/opt/jdk1.8/bin/java`。
2. 拼接出命令行
```bash
/opt/jdk1.8/bin/java -Xbootclasspath/a:/opt/jdk1.8/lib/tools.jar \
-jar /tmp/arthas-packaging/arthas-core.jar \
-pid 15146 \
-target-ip 127.0.0.1 -telnet-port 3658 -http-port 8563 \
-core /tmp/arthas-packaging/arthas-core.jar \
-agent /tmp/arthas-packaging/arthas/arthas-agent.jar
```
命令行分几部分组成:
- `-Xbootclasspath` 增加 tools.jar
- `-jar /tmp/arthas-packaging/arthas-core.jar` 指定 main 函数入口
- `-pid 15146` 指定目标 java 进程 ID
- `-target-ip 127.0.0.1` 指定 IP
- `-telnet-port 3658 -http-port 8563` 指定 telnet 和 http 端口
- `-core /tmp/arthas-packaging/arthas-core.jar -agent /tmp/arthas-packaging/arthas/arthas-agent.jar` 指定 core/agent jar 包
如果是`jdk > 9`,即 9/10/11 以上的版本,不需要指定`tools.jar`,直接去掉`-Xbootclasspath` 的配置即可。
启动目志输出在`~/logs/arthas/arthas.log`里。
3. attach 成功之后,使用 telnet 连接
```bash
telnet 127.0.0.1 3658
```

@ -0,0 +1,68 @@
# mbean
[`mbean`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-mbean)
::: tip
查看 Mbean 的信息
:::
这个命令可以便捷的查看或监控 Mbean 的属性信息。
### 参数说明
| 参数名称 | 参数说明 |
| ------------------: | :--------------------------------------------------- |
| _name-pattern_ | 名称表达式匹配 |
| _attribute-pattern_ | 属性名表达式匹配 |
| [m] | 查看元信息 |
| [i:] | 刷新属性值的时间间隔 (ms) |
| [n:] | 刷新属性值的次数 |
| [E] | 开启正则表达式匹配,默认为通配符匹配。仅对属性名有效 |
### 使用参考
列出所有 Mbean 的名称:
```bash
mbean
```
查看 Mbean 的元信息:
```bash
mbean -m java.lang:type=Threading
```
查看 mbean 属性信息:
```bash
mbean java.lang:type=Threading
```
mbean 的 name 支持通配符匹配:
```bash
mbean java.lang:type=Th*
```
::: warning
注意ObjectName 的匹配规则与正常的通配符存在差异,详细参见:[javax.management.ObjectName](https://docs.oracle.com/javase/8/docs/api/javax/management/ObjectName.html?is-external=true)
:::
通配符匹配特定的属性字段:
```bash
mbean java.lang:type=Threading *Count
```
使用`-E`命令切换为正则匹配:
```bash
mbean -E java.lang:type=Threading PeakThreadCount|ThreadCount|DaemonThreadCount
```
使用`-i`命令实时监控:
```bash
mbean -i 1000 java.lang:type=Threading *Count
```

@ -1,9 +1,10 @@
mc
===
# mc
[`mc-retransform`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials?language=cn&id=command-mc-retransform)
> Memory Compiler/内存编译器,编译`.java`文件生成`.class`。
::: tip
Memory Compiler/内存编译器,编译`.java`文件生成`.class`。
:::
```bash
mc /tmp/Test.java
@ -32,4 +33,6 @@ mc -d /tmp/output /tmp/ClassA.java /tmp/ClassB.java
编译生成`.class`文件之后,可以结合[retransform](retransform.md)命令实现热更新代码。
> 注意mc命令有可能失败。如果编译失败可以在本地编译好`.class`文件,再上传到服务器。具体参考[retransform](retransform.md)命令说明。
::: warning
注意mc 命令有可能失败。如果编译失败可以在本地编译好`.class`文件,再上传到服务器。具体参考[retransform](retransform.md)命令说明。
:::

@ -1,9 +1,7 @@
memory
===
# memory
查看 JVM 内存信息。
### 使用参考
```
@ -22,4 +20,3 @@ codeheap_'non-profiled_nmethods' 685K 2496K 120032K 0.57%
mapped 0K 0K - 0.00%
direct 48M 48M - 100.00%
```

@ -1,9 +1,10 @@
monitor
=======
# monitor
[`monitor`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-monitor)
> 方法执行监控
::: tip
方法执行监控
:::
对匹配 `class-pattern``method-pattern``condition-express`的类、方法的调用进行监控。
@ -16,7 +17,7 @@ monitor
### 监控的维度说明
| 监控项 | 说明 |
|---:|:---|
| --------: | :------------------------- |
| timestamp | 时间戳 |
| class | Java 类 |
| method | 方法(构造方法、普通方法) |
@ -31,10 +32,10 @@ monitor
方法拥有一个命名参数 `[c:]`意思是统计周期cycle of output拥有一个整型的参数值
| 参数名称 | 参数说明 |
|---:|:---|
|*class-pattern*|类名表达式匹配|
|*method-pattern*|方法名表达式匹配|
|*condition-express*|条件表达式|
| ------------------: | :--------------------------------------- |
| _class-pattern_ | 类名表达式匹配 |
| _method-pattern_ | 方法名表达式匹配 |
| _condition-express_ | 条件表达式 |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
| `[c:]` | 统计周期,默认值为 120 秒 |
| [b] | 在**方法调用之前**计算 condition-express |
@ -99,7 +100,6 @@ Affect(class count: 1 , method count: 1) cost in 19 ms, listenerId: 5
#### 计算条件表达式过滤统计结果(方法执行完毕之前)
```bash
monitor -b -c 5 com.test.testes.MathGame primeFactors "params[0] <= 2"
Press Q or Ctrl+C to abort.
@ -124,4 +124,3 @@ Affect(class count: 1 , method count: 1) cost in 21 ms, listenerId: 4
----------------------------------------------------------------------------------------------
2020-09-02 09:42:17 demo.MathGame primeFactors 2 0 2 0.10 100.00%
```

@ -1,27 +1,26 @@
ognl
===
# ognl
[`ognl`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials?language=cn&id=command-ognl)
> 执行ognl表达式
::: tip
执行 ognl 表达式
:::
从 3.0.5 版本增加
### 参数说明
| 参数名称 | 参数说明 |
|---:|:---|
|*express*|执行的表达式|
| --------------------: | :--------------------------------------------------------------- |
| _express_ | 执行的表达式 |
| `[c:]` | 执行表达式的 ClassLoader 的 hashcode默认值是 SystemClassLoader |
| `[classLoaderClass:]` | 指定执行表达式的 ClassLoader 的 class name |
| [x] | 结果对象的展开层次,默认值 1 |
### 使用参考
* OGNL特殊用法请参考[https://github.com/alibaba/arthas/issues/71](https://github.com/alibaba/arthas/issues/71)
* OGNL表达式官方指南[https://commons.apache.org/proper/commons-ognl/language-guide.html](https://commons.apache.org/proper/commons-ognl/language-guide.html)
- OGNL 特殊用法请参考:[https://github.com/alibaba/arthas/issues/71](https://github.com/alibaba/arthas/issues/71)
- OGNL 表达式官方指南:[https://commons.apache.org/proper/commons-ognl/language-guide.html](https://commons.apache.org/proper/commons-ognl/language-guide.html)
调用静态函数:
@ -71,6 +70,7 @@ $ ognl -c 7f9a81e8 @org.springframework.boot.SpringApplication@logger
]
$
```
注意 hashcode 是变化的,需要先查看当前的 ClassLoader 信息,提取对应 ClassLoader 的 hashcode。
对于只有唯一实例的 ClassLoader 可以通过 class name 指定,使用起来更加方便:
@ -84,7 +84,6 @@ $ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader
]
```
执行多行表达式,赋值给临时变量,返回一个 List
```bash

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save