Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
* The directory contains arthas-core.jar/arthas-client.jar/arthas-spy.jar.
* 1. When use-version is not empty, try to find arthas home under ~/.arthas/lib
* 2. Try set the directory where arthas-boot.jar is located to arthas home
* 3. Try to download from remote repo
*
*/
private String arthasHome;
/**
* under ~/.arthas/lib
*/
private String useVersion;
/**
* list local and remote versions
*/
private boolean versions;
/**
* download from remo repository. if timezone is +0800, default value is 'aliyun', else is 'center'.
*/
private String repoMirror;
/**
* enforce use http to download arthas. default use https
*/
private boolean useHttp = false;
private boolean attachOnly = false;
private String command;
private String batchFile;
private String tunnelServer;
private String agentId;
private String appName;
private String username;
private String password;
private String statUrl;
private String select;
static {
ARTHAS_LIB_DIR = new File(
System.getProperty("user.home") + File.separator + ".arthas" + File.separator + "lib");
try {
ARTHAS_LIB_DIR.mkdirs();
} catch (Throwable t) {
//ignore
}
if (!ARTHAS_LIB_DIR.exists()) {
// try to set a temp directory
ARTHAS_LIB_DIR = new File(System.getProperty("java.io.tmpdir") + File.separator + ".arthas" + File.separator + "lib");
try {
ARTHAS_LIB_DIR.mkdirs();
} catch (Throwable e) {
// ignore
}
}
if (!ARTHAS_LIB_DIR.exists()) {
System.err.println("Can not find directory to save arthas lib. please try to set user home by -Duser.home=");
}
}
@Argument(argName = "pid", index = 0, required = false)
@Description("Target pid")
public void setPid(long pid) {
this.pid = pid;
}
@Option(shortName = "h", longName = "help", flag = true)
@Description("Print usage")
public void setHelp(boolean help) {
this.help = help;
}
@Option(longName = "target-ip")
@Description("The target jvm listen ip, default 127.0.0.1")
public void setTargetIp(String targetIp) {
this.targetIp = targetIp;
}
@Option(longName = "telnet-port")
@Description("The target jvm listen telnet port, default 3658")
public void setTelnetPort(int telnetPort) {
this.telnetPort = telnetPort;
}
@Option(longName = "http-port")
@Description("The target jvm listen http port, default 8563")
public void setHttpPort(int httpPort) {
this.httpPort = httpPort;
}
@Option(longName = "session-timeout")
@Description("The session timeout seconds, default 1800 (30min)")
public void setSessionTimeout(Long sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
@Option(longName = "arthas-home")
@Description("The arthas home")
public void setArthasHome(String arthasHome) {
this.arthasHome = arthasHome;
}
@Option(longName = "use-version")
@Description("Use special version arthas")
public void setUseVersion(String useVersion) {
this.useVersion = useVersion;
}
@Option(longName = "repo-mirror")
@Description("Use special remote repository mirror, value is center/aliyun or http repo url.")
public void setRepoMirror(String repoMirror) {
this.repoMirror = repoMirror;
}
@Option(longName = "versions", flag = true)
@Description("List local and remote arthas versions")
public void setVersions(boolean versions) {
this.versions = versions;
}
@Option(longName = "use-http", flag = true)
@Description("Enforce use http to download, default use https")
public void setuseHttp(boolean useHttp) {
this.useHttp = useHttp;
}
@Option(longName = "attach-only", flag = true)
@Description("Attach target process only, do not connect")
public void setAttachOnly(boolean attachOnly) {
this.attachOnly = attachOnly;
}
@Option(shortName = "c", longName = "command")
@Description("Command to execute, multiple commands separated by ;")
public void setCommand(String command) {
this.command = command;
}
@Option(shortName = "f", longName = "batch-file")
@Description("The batch file to execute")
public void setBatchFile(String batchFile) {
this.batchFile = batchFile;
}
@Option(longName = "height")
@Description("arthas-client terminal height")
public void setHeight(int height) {
this.height = height;
}
@Option(longName = "width")
@Description("arthas-client terminal width")
public void setWidth(int width) {
this.width = width;
}
@Option(shortName = "v", longName = "verbose", flag = true)
@Description("Verbose, print debug info.")
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
@Option(longName = "tunnel-server")
@Description("The tunnel server url")
public void setTunnelServer(String tunnelServer) {
this.tunnelServer = tunnelServer;
}
@Option(longName = "agent-id")
@Description("The agent id register to tunnel server")
public void setAgentId(String agentId) {
this.agentId = agentId;
}
@Option(longName = "app-name")
@Description("The app name")
public void setAppName(String appName) {
this.appName = appName;
}
@Option(longName = "username")
@Description("The username")
public void setUsername(String username) {
this.username = username;
}
@Option(longName = "password")
@Description("The password")
public void setPassword(String password) {
this.password = password;
}
@Option(longName = "stat-url")
@Description("The report stat url")
public void setStatUrl(String statUrl) {
this.statUrl = statUrl;
}
@Option(longName = "select")
@Description("select target process by classname or JARfilename")
public void setSelect(String select) {
this.select = select;
}
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException,
ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
Package bootstrapPackage = Bootstrap.class.getPackage();
if (bootstrapPackage != null) {
String arthasBootVersion = bootstrapPackage.getImplementationVersion();
if (arthasBootVersion != null) {
AnsiLog.info("arthas-boot version: " + arthasBootVersion);
}
}
Bootstrap bootstrap = new Bootstrap();
CLI cli = CLIConfigurator.define(Bootstrap.class);
CommandLine commandLine = cli.parse(Arrays.asList(args));
try {
CLIConfigurator.inject(commandLine, bootstrap);
} catch (Throwable e) {
e.printStackTrace();
System.out.println(usage(cli));
System.exit(1);
}
if (bootstrap.isVerbose()) {
AnsiLog.level(Level.ALL);
}
if (bootstrap.isHelp()) {
System.out.println(usage(cli));
System.exit(0);
}
if (bootstrap.getRepoMirror() == null || bootstrap.getRepoMirror().trim().isEmpty()) {
bootstrap.setRepoMirror("center");
// if timezone is +0800, default repo mirror is aliyun
if (TimeUnit.MILLISECONDS.toHours(TimeZone.getDefault().getOffset(System.currentTimeMillis())) == 8) {
bootstrap.setRepoMirror("aliyun");
}
}
AnsiLog.debug("Repo mirror:" + bootstrap.getRepoMirror());
if (bootstrap.isVersions()) {
System.out.println(UsageRender.render(listVersions()));
System.exit(0);
}
if (JavaVersionUtils.isJava6() || JavaVersionUtils.isJava7()) {
bootstrap.setuseHttp(true);
AnsiLog.debug("Java version is {}, only support http, set useHttp to true.",
JavaVersionUtils.javaVersionStr());
}
// check telnet/http port
long telnetPortPid = -1;
long httpPortPid = -1;
if (bootstrap.getTelnetPortOrDefault() > 0) {
telnetPortPid = SocketUtils.findTcpListenProcess(bootstrap.getTelnetPortOrDefault());
if (telnetPortPid > 0) {
AnsiLog.info("Process {} already using port {}", telnetPortPid, bootstrap.getTelnetPortOrDefault());
}
}
if (bootstrap.getHttpPortOrDefault() > 0) {
httpPortPid = SocketUtils.findTcpListenProcess(bootstrap.getHttpPortOrDefault());
if (httpPortPid > 0) {
AnsiLog.info("Process {} already using port {}", httpPortPid, bootstrap.getHttpPortOrDefault());
}
}
long pid = bootstrap.getPid();
// select pid
if (pid < 0) {
try {
pid = ProcessUtils.select(bootstrap.isVerbose(), telnetPortPid, bootstrap.getSelect());
} catch (InputMismatchException e) {
System.out.println("Please input an integer to select pid.");
System.exit(1);
}
if (pid < 0) {
System.out.println("Please select an available pid.");
System.exit(1);
}
}
checkTelnetPortPid(bootstrap, telnetPortPid, pid);
if (httpPortPid > 0 && pid != httpPortPid) {
AnsiLog.error("Target process {} is not the process using port {}, you will connect to an unexpected process.",
pid, bootstrap.getHttpPortOrDefault());
AnsiLog.error("1. Try to restart arthas-boot, select process {}, shutdown it first with running the 'stop' command.",
httpPortPid);
AnsiLog.error("2. Or try to use different http port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port 9999", httpPortPid);
System.exit(1);
}
// find arthas home
File arthasHomeDir = null;
if (bootstrap.getArthasHome() != null) {
verifyArthasHome(bootstrap.getArthasHome());
arthasHomeDir = new File(bootstrap.getArthasHome());
}
if (arthasHomeDir == null && bootstrap.getUseVersion() != null) {
// try to find from ~/.arthas/lib
File specialVersionDir = new File(System.getProperty("user.home"), ".arthas" + File.separator + "lib"
+ File.separator + bootstrap.getUseVersion() + File.separator + "arthas");
if (!specialVersionDir.exists()) {
// try to download arthas from remote server.
DownloadUtils.downArthasPackaging(bootstrap.getRepoMirror(), bootstrap.isuseHttp(),
bootstrap.getUseVersion(), ARTHAS_LIB_DIR.getAbsolutePath());
}
verifyArthasHome(specialVersionDir.getAbsolutePath());
arthasHomeDir = specialVersionDir;
}
// Try set the directory where arthas-boot.jar is located to arhtas home
if (arthasHomeDir == null) {
CodeSource codeSource = Bootstrap.class.getProtectionDomain().getCodeSource();
if (codeSource != null) {
try {
// https://stackoverflow.com/a/17870390
File bootJarPath = new File(codeSource.getLocation().toURI().getSchemeSpecificPart());
verifyArthasHome(bootJarPath.getParent());
arthasHomeDir = bootJarPath.getParentFile();
} catch (Throwable e) {
// ignore
}
}
}
// try to download from remote server
if (arthasHomeDir == null) {
boolean checkFile = ARTHAS_LIB_DIR.exists() || ARTHAS_LIB_DIR.mkdirs();
if(!checkFile){
AnsiLog.error("cannot create directory {}: maybe permission denied", ARTHAS_LIB_DIR.getAbsolutePath());
System.exit(1);
}
/**
*
* 1. get local latest version
* 2. get remote latest version
* 3. compare two version
*
*/
List versionList = listNames(ARTHAS_LIB_DIR);
Collections.sort(versionList);
String localLastestVersion = null;
if (!versionList.isEmpty()) {
localLastestVersion = versionList.get(versionList.size() - 1);
}
String remoteLastestVersion = DownloadUtils.readLatestReleaseVersion();
boolean needDownload = false;
if (localLastestVersion == null) {
if (remoteLastestVersion == null) {
// exit
AnsiLog.error("Can not find Arthas under local: {} and remote repo mirror: {}", ARTHAS_LIB_DIR,
bootstrap.getRepoMirror());
AnsiLog.error(
"Unable to download arthas from remote server, please download the full package according to wiki: https://github.com/alibaba/arthas");
System.exit(1);
} else {
needDownload = true;
}
} else {
if (remoteLastestVersion != null) {
if (localLastestVersion.compareTo(remoteLastestVersion) < 0) {
AnsiLog.info("local lastest version: {}, remote lastest version: {}, try to download from remote.",
localLastestVersion, remoteLastestVersion);
needDownload = true;
}
}
}
if (needDownload) {
// try to download arthas from remote server.
DownloadUtils.downArthasPackaging(bootstrap.getRepoMirror(), bootstrap.isuseHttp(),
remoteLastestVersion, ARTHAS_LIB_DIR.getAbsolutePath());
localLastestVersion = remoteLastestVersion;
}
// get the latest version
arthasHomeDir = new File(ARTHAS_LIB_DIR, localLastestVersion + File.separator + "arthas");
}
verifyArthasHome(arthasHomeDir.getAbsolutePath());
AnsiLog.info("arthas home: " + arthasHomeDir);
if (telnetPortPid > 0 && pid == telnetPortPid) {
AnsiLog.info("The target process already listen port {}, skip attach.", bootstrap.getTelnetPortOrDefault());
} else {
//double check telnet port and pid before attach
telnetPortPid = findProcessByTelnetClient(arthasHomeDir.getAbsolutePath(), bootstrap.getTelnetPortOrDefault());
checkTelnetPortPid(bootstrap, telnetPortPid, pid);
// start arthas-core.jar
List attachArgs = new ArrayList();
attachArgs.add("-jar");
attachArgs.add(new File(arthasHomeDir, "arthas-core.jar").getAbsolutePath());
attachArgs.add("-pid");
attachArgs.add("" + pid);
if (bootstrap.getTargetIp() != null) {
attachArgs.add("-target-ip");
attachArgs.add(bootstrap.getTargetIp());
}
if (bootstrap.getTelnetPort() != null) {
attachArgs.add("-telnet-port");
attachArgs.add("" + bootstrap.getTelnetPort());
}
if (bootstrap.getHttpPort() != null) {
attachArgs.add("-http-port");
attachArgs.add("" + bootstrap.getHttpPort());
}
attachArgs.add("-core");
attachArgs.add(new File(arthasHomeDir, "arthas-core.jar").getAbsolutePath());
attachArgs.add("-agent");
attachArgs.add(new File(arthasHomeDir, "arthas-agent.jar").getAbsolutePath());
if (bootstrap.getSessionTimeout() != null) {
attachArgs.add("-session-timeout");
attachArgs.add("" + bootstrap.getSessionTimeout());
}
if (bootstrap.getAppName() != null) {
attachArgs.add("-app-name");
attachArgs.add(bootstrap.getAppName());
}
if (bootstrap.getUsername() != null) {
attachArgs.add("-username");
attachArgs.add(bootstrap.getUsername());
}
if (bootstrap.getPassword() != null) {
attachArgs.add("-password");
attachArgs.add(bootstrap.getPassword());
}
if (bootstrap.getTunnelServer() != null) {
attachArgs.add("-tunnel-server");
attachArgs.add(bootstrap.getTunnelServer());
}
if (bootstrap.getAgentId() != null) {
attachArgs.add("-agent-id");
attachArgs.add(bootstrap.getAgentId());
}
if (bootstrap.getStatUrl() != null) {
attachArgs.add("-stat-url");
attachArgs.add(bootstrap.getStatUrl());
}
AnsiLog.info("Try to attach process " + pid);
AnsiLog.debug("Start arthas-core.jar args: " + attachArgs);
ProcessUtils.startArthasCore(pid, attachArgs);
AnsiLog.info("Attach process {} success.", pid);
}
if (bootstrap.isAttachOnly()) {
System.exit(0);
}
// start java telnet client
// find arthas-client.jar
URLClassLoader classLoader = new URLClassLoader(
new URL[] { new File(arthasHomeDir, "arthas-client.jar").toURI().toURL() });
Class> telnetConsoleClas = classLoader.loadClass("com.taobao.arthas.client.TelnetConsole");
Method mainMethod = telnetConsoleClas.getMethod("main", String[].class);
List telnetArgs = new ArrayList();
if (bootstrap.getCommand() != null) {
telnetArgs.add("-c");
telnetArgs.add(bootstrap.getCommand());
}
if (bootstrap.getBatchFile() != null) {
telnetArgs.add("-f");
telnetArgs.add(bootstrap.getBatchFile());
}
if (bootstrap.getHeight() != null) {
telnetArgs.add("--height");
telnetArgs.add("" + bootstrap.getHeight());
}
if (bootstrap.getWidth() != null) {
telnetArgs.add("--width");
telnetArgs.add("" + bootstrap.getWidth());
}
// telnet port ,ip
telnetArgs.add(bootstrap.getTargetIpOrDefault());
telnetArgs.add("" + bootstrap.getTelnetPortOrDefault());
AnsiLog.info("arthas-client connect {} {}", bootstrap.getTargetIpOrDefault(), bootstrap.getTelnetPortOrDefault());
AnsiLog.debug("Start arthas-client.jar args: " + telnetArgs);
// fix https://github.com/alibaba/arthas/issues/833
Thread.currentThread().setContextClassLoader(classLoader);
mainMethod.invoke(null, new Object[] { telnetArgs.toArray(new String[0]) });
}
private static void checkTelnetPortPid(Bootstrap bootstrap, long telnetPortPid, long targetPid) {
if (telnetPortPid > 0 && targetPid != telnetPortPid) {
AnsiLog.error("The telnet port {} is used by process {} instead of target process {}, you will connect to an unexpected process.",
bootstrap.getTelnetPortOrDefault(), telnetPortPid, targetPid);
AnsiLog.error("1. Try to restart arthas-boot, select process {}, shutdown it first with running the 'stop' command.",
telnetPortPid);
AnsiLog.error("2. Or try to stop the existing arthas instance: java -jar arthas-client.jar 127.0.0.1 {} -c \"stop\"", bootstrap.getTelnetPortOrDefault());
AnsiLog.error("3. Or try to use different telnet port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port -1");
System.exit(1);
}
}
private static long findProcessByTelnetClient(String arthasHomeDir, int telnetPort) {
// start java telnet client
List telnetArgs = new ArrayList();
telnetArgs.add("-c");
telnetArgs.add("session");
telnetArgs.add("--execution-timeout");
telnetArgs.add("2000");
// telnet port ,ip
telnetArgs.add("127.0.0.1");
telnetArgs.add("" + telnetPort);
try {
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
String error = null;
int status = ProcessUtils.startArthasClient(arthasHomeDir, telnetArgs, out);
if (status == STATUS_EXEC_TIMEOUT) {
error = "detection timeout";
} else if (status == STATUS_EXEC_ERROR) {
error = "detection error";
AnsiLog.error("process status: {}", status);
AnsiLog.error("process output: {}", out.toString());
} else {
// ignore connect error
}
if (error != null) {
AnsiLog.error("The telnet port {} is used, but process {}, you will connect to an unexpected process.", telnetPort, error);
AnsiLog.error("Try to use a different telnet port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port -1");
System.exit(1);
}
//parse output, find java pid
String output = out.toString("UTF-8");
String javaPidLine = null;
Scanner scanner = new Scanner(output);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line.contains("JAVA_PID")) {
javaPidLine = line;
break;
}
}
if (javaPidLine != null) {
// JAVA_PID 10473
try {
String[] strs = javaPidLine.split("JAVA_PID");
if (strs.length > 1) {
return Long.parseLong(strs[strs.length - 1].trim());
}
} catch (NumberFormatException e) {
// ignore
}
}
} catch (Throwable ex) {
AnsiLog.error("Detection telnet port error");
AnsiLog.error(ex);
}
return -1;
}
private static String listVersions() {
StringBuilder result = new StringBuilder(1024);
List versionList = listNames(ARTHAS_LIB_DIR);
Collections.sort(versionList);
result.append("Local versions:\n");
for (String version : versionList) {
result.append(" " + version).append('\n');
}
result.append("Remote versions:\n");
List remoteVersions = DownloadUtils.readRemoteVersions();
Collections.reverse(remoteVersions);
for (String version : remoteVersions) {
result.append(" " + version).append('\n');
}
return result.toString();
}
private static List listNames(File dir) {
List names = new ArrayList();
if (!dir.exists()) {
return names;
}
File[] files = dir.listFiles();
if (files == null) {
return names;
}
for (File file : files) {
String name = file.getName();
if (name.startsWith(".") || file.isFile()) {
continue;
}
names.add(name);
}
return names;
}
private static void verifyArthasHome(String arthasHome) {
File home = new File(arthasHome);
if (home.isDirectory()) {
String[] fileList = { "arthas-core.jar", "arthas-agent.jar", "arthas-spy.jar" };
for (String fileName : fileList) {
if (!new File(home, fileName).exists()) {
throw new IllegalArgumentException(
fileName + " do not exist, arthas home: " + home.getAbsolutePath());
}
}
return;
}
throw new IllegalArgumentException("illegal arthas home: " + home.getAbsolutePath());
}
private static String usage(CLI cli) {
StringBuilder usageStringBuilder = new StringBuilder();
UsageMessageFormatter usageMessageFormatter = new UsageMessageFormatter();
usageMessageFormatter.setOptionComparator(null);
cli.usage(usageStringBuilder, usageMessageFormatter);
return UsageRender.render(usageStringBuilder.toString());
}
public String getArthasHome() {
return arthasHome;
}
public String getUseVersion() {
return useVersion;
}
public String getRepoMirror() {
return repoMirror;
}
public boolean isuseHttp() {
return useHttp;
}
public String getTargetIp() {
return targetIp;
}
public String getTargetIpOrDefault() {
if (this.targetIp == null) {
return DEFAULT_TARGET_IP;
} else {
return this.targetIp;
}
}
public Integer getTelnetPort() {
return telnetPort;
}
public int getTelnetPortOrDefault() {
if (this.telnetPort == null) {
return DEFAULT_TELNET_PORT;
} else {
return this.telnetPort;
}
}
public Integer getHttpPort() {
return httpPort;
}
public int getHttpPortOrDefault() {
if (this.httpPort == null) {
return DEFAULT_HTTP_PORT;
} else {
return this.httpPort;
}
}
public String getCommand() {
return command;
}
public String getBatchFile() {
return batchFile;
}
public boolean isAttachOnly() {
return attachOnly;
}
public long getPid() {
return pid;
}
public boolean isHelp() {
return help;
}
public Long getSessionTimeout() {
return sessionTimeout;
}
public boolean isVerbose() {
return verbose;
}
public boolean isVersions() {
return versions;
}
public Integer getHeight() {
return height;
}
public Integer getWidth() {
return width;
}
public String getTunnelServer() {
return tunnelServer;
}
public String getAgentId() {
return agentId;
}
public String getAppName() {
return appName;
}
public String getStatUrl() {
return statUrl;
}
public String getSelect() {
return select;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}