diff --git a/bin/as.sh b/bin/as.sh index 0bb2dd672..c9d17c429 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -133,6 +133,9 @@ TUNNEL_SERVER= # agent id AGENT_ID= +# stat report url +STAT_URL= + ############ Command Arguments ############ # if arguments contains -c/--command or -f/--batch-file, BATCH_MODE will be true @@ -400,7 +403,7 @@ usage() Usage: $0 [-h] [--target-ip ] [--telnet-port ] [--http-port ] [--session-timeout ] [--arthas-home ] - [--tunnel-server ] [--agent-id ] + [--tunnel-server ] [--agent-id ] [--stat-url ] [--use-version ] [--repo-mirror ] [--versions] [--use-http] [--attach-only] [-c ] [-f ] [-v] [pid] @@ -434,6 +437,7 @@ EXAMPLES: ./as.sh --telnet-port 9999 --http-port -1 ./as.sh --tunnel-server 'ws://192.168.10.11:7777/ws' ./as.sh --tunnel-server 'ws://192.168.10.11:7777/ws' --agent-id bvDOe8XbTM2pQWjF4cfw + ./as.sh --stat-url 'http://192.168.10.11:8080/api/stat' ./as.sh -c 'sysprop; thread' ./as.sh -f batch.as ./as.sh --use-version 3.1.3 @@ -539,6 +543,11 @@ parse_arguments() shift # past argument shift # past value ;; + --stat-url) + STAT_URL="$2" + shift # past argument + shift # past value + ;; --use-http) USE_HTTP=true shift # past argument @@ -714,6 +723,10 @@ attach_jvm() tempArgs+=("-agent-id") tempArgs+=("${AGENT_ID}") fi + if [ "${STAT_URL}" ]; then + tempArgs+=("-stat-url") + tempArgs+=("${STAT_URL}") + fi "${java_command[@]}" \ ${ARTHAS_OPTS} ${JVM_OPTS} \ diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index be334448e..43af1b9c4 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -42,6 +42,9 @@ import com.taobao.middleware.cli.annotations.Summary; @Summary("Bootstrap Arthas") @Description("EXAMPLES:\n" + " java -jar arthas-boot.jar \n" + " java -jar arthas-boot.jar --target-ip 0.0.0.0\n" + " java -jar arthas-boot.jar --telnet-port 9999 --http-port -1\n" + + " java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws'\n" + + " java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws' --agent-id bvDOe8XbTM2pQWjF4cfw\n" + + " java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'\n" + " java -jar arthas-boot.jar -c 'sysprop; thread' \n" + " java -jar arthas-boot.jar -f batch.as \n" + " java -jar arthas-boot.jar --use-version 3.1.3\n" @@ -109,6 +112,8 @@ public class Bootstrap { private String tunnelServer; private String agentId; + private String statUrl; + static { ARTHAS_LIB_DIR = new File( System.getProperty("user.home") + File.separator + ".arthas" + File.separator + "lib"); @@ -245,6 +250,12 @@ public class Bootstrap { this.agentId = agentId; } + @Option(longName = "stat-url") + @Description("The report stat url") + public void setStatUrl(String statUrl) { + this.statUrl = statUrl; + } + public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { @@ -481,6 +492,10 @@ public class Bootstrap { 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); @@ -666,4 +681,8 @@ public class Bootstrap { public String getAgentId() { return agentId; } + + public String getStatUrl() { + return statUrl; + } } diff --git a/core/src/main/java/com/taobao/arthas/core/Arthas.java b/core/src/main/java/com/taobao/arthas/core/Arthas.java index a739a20e2..116555b7f 100644 --- a/core/src/main/java/com/taobao/arthas/core/Arthas.java +++ b/core/src/main/java/com/taobao/arthas/core/Arthas.java @@ -43,8 +43,10 @@ public class Arthas { Option tunnelServer = new TypedOption().setType(String.class).setShortName("tunnel-server"); Option agentId = new TypedOption().setType(String.class).setShortName("agent-id"); + Option statUrl = new TypedOption().setType(String.class).setShortName("stat-url"); + CLI cli = CLIs.create("arthas").addOption(pid).addOption(core).addOption(agent).addOption(target) - .addOption(telnetPort).addOption(httpPort).addOption(sessionTimeout).addOption(tunnelServer).addOption(agentId); + .addOption(telnetPort).addOption(httpPort).addOption(sessionTimeout).addOption(tunnelServer).addOption(agentId).addOption(statUrl); CommandLine commandLine = cli.parse(Arrays.asList(args)); Configure configure = new Configure(); @@ -63,6 +65,7 @@ public class Arthas { configure.setTunnelServer((String) commandLine.getOptionValue("tunnel-server")); configure.setAgentId((String) commandLine.getOptionValue("agent-id")); + configure.setStatUrl((String) commandLine.getOptionValue("stat-url")); return configure; } diff --git a/core/src/main/java/com/taobao/arthas/core/command/basic1000/SessionCommand.java b/core/src/main/java/com/taobao/arthas/core/command/basic1000/SessionCommand.java index 9e0c2f23f..8b74b4d4c 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/basic1000/SessionCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/basic1000/SessionCommand.java @@ -4,6 +4,7 @@ import com.taobao.arthas.core.server.ArthasBootstrap; import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; import com.taobao.arthas.core.shell.session.Session; +import com.taobao.arthas.core.util.UserStatUtil; import com.taobao.middleware.cli.annotations.Name; import com.taobao.middleware.cli.annotations.Summary; import com.taobao.text.Decoration; @@ -43,6 +44,10 @@ public class SessionCommand extends AnnotatedCommand { } table.row("TUNNEL_SERVER", "" + tunnelClient.getTunnelServerUrl()); } + String statUrl = UserStatUtil.getStatUrl(); + if (statUrl != null) { + table.row("STAT_URL", statUrl); + } return table; } } diff --git a/core/src/main/java/com/taobao/arthas/core/config/Configure.java b/core/src/main/java/com/taobao/arthas/core/config/Configure.java index 8db9c9b7c..e2c401dba 100644 --- a/core/src/main/java/com/taobao/arthas/core/config/Configure.java +++ b/core/src/main/java/com/taobao/arthas/core/config/Configure.java @@ -27,6 +27,11 @@ public class Configure { private String tunnelServer; private String agentId; + /** + * report executed command + */ + private String statUrl; + /** * session timeout seconds */ @@ -104,6 +109,14 @@ public class Configure { this.agentId = agentId; } + public String getStatUrl() { + return statUrl; + } + + public void setStatUrl(String statUrl) { + this.statUrl = statUrl; + } + // 对象的编码解码器 private final static FeatureCodec codec = new FeatureCodec(';', '='); diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index 8bb40f55c..77d26452a 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -148,6 +148,10 @@ public class ArthasBootstrap { logger.info("as-server listening on network={};telnet={};http={};timeout={};", configure.getIp(), configure.getTelnetPort(), configure.getHttpPort(), options.getConnectionTimeout()); // 异步回报启动次数 + if (configure.getStatUrl() != null) { + logger.info("arthas stat url: {}", configure.getStatUrl()); + } + UserStatUtil.setStatUrl(configure.getStatUrl()); UserStatUtil.arthasStart(); logger.info("as-server started in {} ms", System.currentTimeMillis() - start ); diff --git a/core/src/main/java/com/taobao/arthas/core/util/UserStatUtil.java b/core/src/main/java/com/taobao/arthas/core/util/UserStatUtil.java index e4e209088..3850eaefc 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/UserStatUtil.java +++ b/core/src/main/java/com/taobao/arthas/core/util/UserStatUtil.java @@ -1,6 +1,7 @@ package com.taobao.arthas.core.util; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; @@ -20,43 +21,51 @@ public class UserStatUtil { private static final String version = URLEncoder.encode(ArthasBanner.version().replace("\n", "")); + private static volatile String statUrl = null; + + public static String getStatUrl() { + return statUrl; + } + + public static void setStatUrl(String url) { + statUrl = url; + } + public static void arthasStart() { RemoteJob job = new RemoteJob(); - job.setResource("anonymousStatStart.do"); - job.appendQueryData("productName", "Arthas"); - job.appendQueryData("productVersion", URLEncoder.encode(ArthasBanner.version())); + job.appendQueryData("ip", ip); + job.appendQueryData("version", version); + job.appendQueryData("command", "start"); try { executorService.execute(job); - } catch(Throwable t) { + } catch (Throwable t) { // } } public static void arthasUsage(String cmd, String detail) { RemoteJob job = new RemoteJob(); - job.setResource("nonAnonymousStat.do"); job.appendQueryData("ip", ip); - job.appendQueryData("productName", "Arthas"); - job.appendQueryData("productVersion", version); - job.appendQueryData("opName", URLEncoder.encode(cmd)); + job.appendQueryData("version", version); + job.appendQueryData("command", URLEncoder.encode(cmd)); if (detail != null) { - job.appendQueryData("opDetail", URLEncoder.encode(detail)); + job.appendQueryData("arguments", URLEncoder.encode(detail)); } try { executorService.execute(job); - } catch(Throwable t) { + } catch (Throwable t) { // } } public static void arthasUsageSuccess(String cmd, List args) { StringBuilder commandString = new StringBuilder(cmd); - for (String arg: args) { + for (String arg : args) { commandString.append(" ").append(arg); } - UserStatUtil.arthasUsage(cmd, commandString.toString() + " --> success"); + UserStatUtil.arthasUsage(cmd, commandString.toString()); } public static void destroy() { @@ -65,19 +74,8 @@ public class UserStatUtil { } static class RemoteJob implements Runnable { - -// private StringBuilder link = new StringBuilder("http://arthas.io/api/"); - // disable stat - private StringBuilder link = null; - - private String resource; - private StringBuilder queryData = new StringBuilder(); - public void setResource(String resource) { - this.resource = resource; - } - public void appendQueryData(String key, String value) { if (key != null && value != null) { if (queryData.length() == 0) { @@ -90,26 +88,36 @@ public class UserStatUtil { @Override public void run() { + String link = statUrl; if (link == null) { return; } + BufferedReader br = null; try { - link.append(resource); if (queryData.length() != 0) { - link.append("?").append(queryData); + link = link + "?" + queryData; } URL url = new URL(link.toString()); URLConnection connection = url.openConnection(); connection.setConnectTimeout(1000); connection.setReadTimeout(1000); connection.connect(); - BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream())); + br = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line = null; StringBuilder result = new StringBuilder(); while ((line = br.readLine()) != null) { result.append(line); } - } catch (Exception ex) { + } catch (Throwable t) { + // ignore + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException e) { + // ignore + } + } } } } diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/StatController.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/StatController.java new file mode 100644 index 000000000..11cb411a8 --- /dev/null +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/StatController.java @@ -0,0 +1,37 @@ +package com.alibaba.arthas.tunnel.server.app.web; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * + * @author hengyunabc 2019-09-24 + * + */ +@Controller +public class StatController { + private final static Logger logger = LoggerFactory.getLogger(StatController.class); + + @RequestMapping(value = "/api/stat") + @ResponseBody + public Map execute(@RequestParam(value = "ip", required = true) String ip, + @RequestParam(value = "version", required = true) String version, + @RequestParam(value = "command", required = true) String command, + @RequestParam(value = "arguments", required = false, defaultValue = "") String arguments) { + + logger.info("arthas stat, ip: {}, version: {}, command: {}, arguments: {}", ip, version, command, arguments); + + Map result = new HashMap<>(); + + result.put("success", true); + + return result; + } +}