mirror of
https://github.com/alibaba/arthas.git
synced 2024-04-21 10:21:39 +00:00
760 lines
19 KiB
Markdown
760 lines
19 KiB
Markdown
# Http API
|
||
|
||
[`Http API` online tutorial](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=en&id=case-http-api)
|
||
|
||
## Overview
|
||
|
||
Http API provides a RESTful-like interactive interface, and both
|
||
requests and responses data in JSON format. Compared with
|
||
Telnet/WebConsole's output unstructured text data, Http API can provide
|
||
structured data and support more complex interactive functions, such as
|
||
a series of diagnostic operations in specific application scenarios.
|
||
|
||
### Access address
|
||
|
||
The Http API address is: `http://ip:port/api`, the request parameters
|
||
must be submitted using `POST`. Such as POST
|
||
`http://127.0.0.1:8563/api`.
|
||
|
||
Note: The telnet port `3658` has compatibility issues with the Chrome
|
||
browser. It is recommended to use the http port `8563` to access the
|
||
http api.
|
||
|
||
### Request data format
|
||
|
||
```json
|
||
{
|
||
"action": "exec",
|
||
"requestId": "req112",
|
||
"sessionId": "94766d3c-8b39-42d3-8596-98aee3ccbefb",
|
||
"consumerId": "955dbd1325334a84972b0f3ac19de4f7_2",
|
||
"command": "version",
|
||
"execTimeout": "10000"
|
||
}
|
||
```
|
||
|
||
Request data format description:
|
||
|
||
- `action` : The requested action/behavior, please refer to "Request
|
||
Actions" for optional values.
|
||
- `requestId` : Optional request ID, generated by the client.
|
||
- `sessionId` : Arthas session ID, one-time command does not need to
|
||
set the session ID.
|
||
- `consumerId` : Arthas consumer ID, used for multi-person sharing
|
||
sessions.
|
||
- `command` : Arthas command line
|
||
- `execTimeout` : Timeout for executing commands (ms), default value is 30000.
|
||
|
||
Note: Different actions use different parameters. Set the parameters
|
||
according to the specific action.
|
||
|
||
### Request Actions
|
||
|
||
Currently supported request actions are as follows:
|
||
|
||
- `exec` : The command is executed synchronously, and the command
|
||
results is returned after the command execution end or interrupted.
|
||
- `async_exec` : The command is executed asynchronously, and the
|
||
scheduling result of the command is returned immediately. The command
|
||
execution result is obtained through `pull_results` action.
|
||
- `interrupt_job` : To interrupt the foreground command of the session,
|
||
similar to the function of Telnet `Ctrl + c`.
|
||
- `pull_results` : Get the result of the command executed
|
||
asynchronously, and execute it repeatedly in http long-polling mode.
|
||
- `init_session` : Create new session.
|
||
- `join_session` : Join the session, used to support multiple people
|
||
sharing the same Arthas session.
|
||
- `close_session` : Close the session.
|
||
|
||
### Response status
|
||
|
||
The state attribute in the response indicates the request processing
|
||
state, and its value is as follows:
|
||
|
||
- `SCHEDULED`: When the command is executed asynchronously, it means that
|
||
the job has been created, and may not be executed yet or is being
|
||
executed;
|
||
- `SUCCEEDED`: The request is processed successfully (completed status);
|
||
- `FAILED`: Request processing failed (completed status), usually
|
||
accompanied by a message explaining the reason;
|
||
- `REFUSED`: The request is rejected (completed status), usually
|
||
accompanied by a message explaining the reason;
|
||
|
||
## One-time command
|
||
|
||
Similar to executing batch commands, the one-time commands are executed
|
||
synchronously. No need to create a session, no need to set the
|
||
`sessionId` option.
|
||
|
||
```json
|
||
{
|
||
"action": "exec",
|
||
"command": "<Arthas command line>"
|
||
}
|
||
```
|
||
|
||
For example, get the Arthas version number:
|
||
|
||
```bash
|
||
curl -Ss -XPOST http://localhost:8563/api -d '
|
||
{
|
||
"action":"exec",
|
||
"command":"version"
|
||
}
|
||
'
|
||
```
|
||
|
||
The response is as follows:
|
||
|
||
```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
|
||
}
|
||
}
|
||
```
|
||
|
||
Response data format description:
|
||
|
||
- `state`: Request processing status, refer to the description of
|
||
"Response Status".
|
||
- `sessionId `: Arthas session ID, one-time command to automatically
|
||
create and destroy temporary sessions.
|
||
- `body.jobId`: The job ID of the command, all output results of the
|
||
same job are the same jobId.
|
||
- `body.jobStatus`: The job status of the command.
|
||
- `body.timeExpired`: Whether the job execution timed out.
|
||
- `body/results`: Command execution results.
|
||
|
||
**Command result format description**
|
||
|
||
```json
|
||
[
|
||
{
|
||
"type": "version",
|
||
"version": "3.3.7",
|
||
"jobId": 5
|
||
},
|
||
{
|
||
"jobId": 5,
|
||
"statusCode": 0,
|
||
"type": "status"
|
||
}
|
||
]
|
||
```
|
||
|
||
- `type` : The command result type, except for the special ones such as
|
||
`status`, the others remain the same as the Arthas command name.
|
||
Please refer to the section
|
||
"[Special command results](#special_command_results)".
|
||
- `jobId` : The job ID of the command.
|
||
- Other fields are the data of each different command.
|
||
|
||
Note: You can also use a one-time command to execute continuous output
|
||
commands such as watch/trace, but you can't interrupt the command
|
||
execution, and there may be hang up for a long time. Please refer to the
|
||
example in the
|
||
"[Make watch command output a map object](#change_watch_value_to_map)"
|
||
section.
|
||
|
||
Please try to deal with it in the following way:
|
||
|
||
- Set a reasonable `execTimeout` to forcibly interrupt the command
|
||
execution after the timeout period is reached to avoid a long hang.
|
||
- Use the `-n` parameter to specify a smaller number of executions.
|
||
- Ensure the methods of the command matched can be successfully hit and
|
||
the `condition-express` is written correctly. If the `watch/trace` does
|
||
not hit, even if `-n 1` is specified, it will hang and wait until the
|
||
execution timeout.
|
||
|
||
## Session interaction
|
||
|
||
Users create and manage Arthas sessions, which are suitable for complex
|
||
interactive processes. The access process is as follows:
|
||
|
||
- Create a session
|
||
- Join the session (optional)
|
||
- Pull command results
|
||
- Execute a series of commands
|
||
- Interrupt command execution
|
||
- Close the session
|
||
|
||
### Create session
|
||
|
||
```bash
|
||
curl -Ss -XPOST http://localhost:8563/api -d '
|
||
{
|
||
"action":"init_session"
|
||
}
|
||
'
|
||
```
|
||
|
||
Response result:
|
||
|
||
```json
|
||
{
|
||
"sessionId": "b09f1353-202c-407b-af24-701b744f971e",
|
||
"consumerId": "5ae4e5fbab8b4e529ac404f260d4e2d1_1",
|
||
"state": "SUCCEEDED"
|
||
}
|
||
```
|
||
|
||
The new session ID is: `b09f1353-202c-407b-af24-701b744f971e`, and
|
||
consumer ID is: `5ae4e5fbab8b4e529ac404f260d4e2d1_1`.
|
||
|
||
### Join session
|
||
|
||
Specify the session ID to join, and the server will assign a new
|
||
consumer ID. Multiple consumers can receive the same command results of
|
||
target session. This interface is used to support multiple people
|
||
sharing the same session or refreshing the page to retrieve the session
|
||
history.
|
||
|
||
```bash
|
||
curl -Ss -XPOST http://localhost:8563/api -d '
|
||
{
|
||
"action":"join_session",
|
||
"sessionId" : "b09f1353-202c-407b-af24-701b744f971e"
|
||
}
|
||
'
|
||
```
|
||
|
||
Response result:
|
||
|
||
```json
|
||
{
|
||
"consumerId": "8f7f6ad7bc2d4cb5aa57a530927a95cc_2",
|
||
"sessionId": "b09f1353-202c-407b-af24-701b744f971e",
|
||
"state": "SUCCEEDED"
|
||
}
|
||
```
|
||
|
||
The new consumer ID is `8f7f6ad7bc2d4cb5aa57a530927a95cc_2 ` .
|
||
|
||
### Pull command results
|
||
|
||
The action of pulling the command result message is `pull_results`.
|
||
Please use the Http long-polling method to periodically pull the result
|
||
messages. The consumer's timeout period is 5 minutes. After the timeout,
|
||
you need to call `join_session` to allocate a new consumer.
|
||
|
||
Each consumer is allocated a cache queue separately, and the pull order
|
||
does not affect the content received by the consumer.
|
||
|
||
The request parameters require session ID and consumer ID:
|
||
|
||
```bash
|
||
curl -Ss -XPOST http://localhost:8563/api -d '
|
||
{
|
||
"action":"pull_results",
|
||
"sessionId" : "b09f1353-202c-407b-af24-701b744f971e",
|
||
"consumerId" : "8f7f6ad7bc2d4cb5aa57a530927a95cc_2"
|
||
}
|
||
'
|
||
```
|
||
|
||
Use Bash scripts to regularly pull results messages:
|
||
|
||
```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
|
||
```
|
||
|
||
Note: The `json_pp` tool formats the output content as pretty json.
|
||
|
||
The response content is as follows:
|
||
|
||
```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"
|
||
}
|
||
```
|
||
|
||
### Execute commands asynchronously
|
||
|
||
```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"
|
||
}
|
||
'''
|
||
```
|
||
|
||
Response of `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` : The status of `SCHEDULED` means that the command has been
|
||
parsed and generated the job, but the execution has not started.
|
||
- `body.jobId` : The job id of command execution, filter the command
|
||
results output in `pull_results` according to this job ID.
|
||
- `body.jobStatus` : The job status `READY` means that execution has not started.
|
||
|
||
The shell output of the script that continuously pulls the result message:
|
||
|
||
```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"
|
||
}
|
||
```
|
||
|
||
The `value` of the watch command result is the value of watch-experss,
|
||
and the above command is `{params, returnObj, throwExp}`, so the value
|
||
of the watch result is an array of length 3, and each element
|
||
corresponds to the expression in the corresponding order.
|
||
|
||
Please refer to the section "[Make watch command output a map object](#change_watch_value_to_map)".
|
||
|
||
### Interrupt command execution
|
||
|
||
Interrupt the running foreground job of the session:
|
||
|
||
```bash
|
||
curl -Ss -XPOST http://localhost:8563/api -d '''
|
||
{
|
||
"action":"interrupt_job",
|
||
"sessionId" : "2b085b5d-883b-4914-ab35-b2c5c1d5aa2a"
|
||
}
|
||
'''
|
||
```
|
||
|
||
```json
|
||
{
|
||
"state": "SUCCEEDED",
|
||
"body": {
|
||
"jobStatus": "TERMINATED",
|
||
"jobId": 3
|
||
}
|
||
}
|
||
```
|
||
|
||
### Close session
|
||
|
||
Specify the session ID to close the session.
|
||
|
||
```bash
|
||
curl -Ss -XPOST http://localhost:8563/api -d '''
|
||
{
|
||
"action":"close_session",
|
||
"sessionId" : "2b085b5d-883b-4914-ab35-b2c5c1d5aa2a"
|
||
}
|
||
'''
|
||
```
|
||
|
||
```json
|
||
{
|
||
"state": "SUCCEEDED"
|
||
}
|
||
```
|
||
|
||
## Authentication
|
||
|
||
- Reference: [auth](auth.md)
|
||
|
||
## Web UI
|
||
|
||

|
||
|
||
A Web UI based on Http API, visit url :
|
||
[http://127.0.0.1:8563/ui](http://127.0.0.1:8563/ui) .
|
||
|
||
Completed functions:
|
||
|
||
- Create a session
|
||
- Copy and open the url to join the session, share the session with
|
||
multiple people
|
||
- Continuously pull session command result messages
|
||
- Refresh the web page or join the session to pull command messages
|
||
history
|
||
- Control input or interrupt command status
|
||
|
||
Pending function:
|
||
|
||
- Improve the readability of command result messages
|
||
- Support automatic completion of input commands and command templates
|
||
- Provide command help
|
||
- Support personal profile settings
|
||
|
||
<a id="special_command_results"></a>
|
||
|
||
## Special command results
|
||
|
||
### status
|
||
|
||
```json
|
||
{
|
||
"jobId": 5,
|
||
"statusCode": 0,
|
||
"type": "status"
|
||
}
|
||
```
|
||
|
||
`type` is `status` to indicate the command execution status:
|
||
|
||
After each command is executed, there is a unique status result. If the
|
||
`statusCode` is 0, it means the execution is successful, and the
|
||
`statusCode` is a non-zero value that means the execution failed,
|
||
similar to the process exit code.
|
||
|
||
When the command execution fails, an error message is generally provided, such as:
|
||
|
||
```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` is `input_status` to indicate input status:
|
||
|
||
It is used to control user input during UI interaction, and a change
|
||
message will be sent before and after each command is executed.
|
||
|
||
Possible values of `inputStatus`:
|
||
|
||
- `ALLOW_INPUT` : Allow users to enter commands, which means that the
|
||
session has no foreground command being executed and can accept new
|
||
command.
|
||
- `ALLOW_INTERRUPT` : Allow the user to interrupt the command execution,
|
||
indicating that a command is currently being executed, and the user
|
||
can send `interrupt_job` to interrupt the execution.
|
||
- `DISABLED` : In the disabled state, commands cannot be entered or
|
||
interrupted.
|
||
|
||
### command
|
||
|
||
```json
|
||
{
|
||
"type": "command",
|
||
"jobId": 3,
|
||
"state": "SCHEDULED",
|
||
"command": "watch demo.MathGame primeFactors \"{params, returnObj, throwExp}\" "
|
||
}
|
||
```
|
||
|
||
`type` is `command` to indicate the input command data:
|
||
|
||
It is used for the interactive UI to echo the commands entered by the
|
||
user. The pulled session command message history will contain messages
|
||
of type `command`, which can be processed in order.
|
||
|
||
### enhancer
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"jobId": 3,
|
||
"effect": {
|
||
"listenerId": 3,
|
||
"cost": 24,
|
||
"classCount": 1,
|
||
"methodCount": 1
|
||
},
|
||
"type": "enhancer"
|
||
}
|
||
```
|
||
|
||
`type` is `enhancer` to indicate the result of class enhancement:
|
||
|
||
Commands such as `trace/watch/jad/tt` need to enhance the class and will
|
||
receive this `enhancer` result. It may happen that the result of
|
||
`enhancer` is successful, but there is no hit method. The client can
|
||
prompt the user according to the result of `enhancer`.
|
||
|
||
## Cases
|
||
|
||
### Get classpath of Java application
|
||
|
||
Get system properties of the Java application through Http api and
|
||
extract the value of `java.class.path`.
|
||
|
||
```bash
|
||
json_data=$(curl -Ss -XPOST http://localhost:8563/api -d '
|
||
{
|
||
"action":"exec",
|
||
"command":"sysprop"
|
||
}')
|
||
```
|
||
|
||
- Extract value with `sed`:
|
||
|
||
```bash
|
||
class_path=$(echo $json_data | tr -d '\n' | sed 's/.*"java.class.path":"\([^"]*\).*/\1/')
|
||
echo "classpath: $class_path"
|
||
```
|
||
|
||
- Extract value with `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"
|
||
```
|
||
|
||
Output:
|
||
|
||
```
|
||
classpath: demo-arthas-spring-boot.jar
|
||
```
|
||
|
||
NOTE:
|
||
|
||
- `echo $json_data | tr -d '\n'` : Delete line breaks (the value of
|
||
`line.separator`) to avoid affecting the processing of `sed`/`json_pp`
|
||
commands.
|
||
- `awk -F'"' '{ print $4 }'` : Use double quote as delimiter
|
||
|
||
<a id="change_watch_value_to_map"></a>
|
||
|
||
### Make watch command output a map object
|
||
|
||
The result value of `watch` is generated by calculating the
|
||
`watch-express` ognl expression. You can change the ognl expression to
|
||
generate the desired value, please refer to
|
||
[OGNL document](https://commons.apache.org/dormant/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".
|
||
:::
|
||
|
||
The following command generates values in map format:
|
||
|
||
```bash
|
||
watch *MathGame prime* '#{ "params" : params, "returnObj" : returnObj, "throwExp": throwExp}' -x 2 -n 5
|
||
```
|
||
|
||
Execute the above command in Telnet shell/WebConsole, the output result:
|
||
|
||
```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,
|
||
]
|
||
```
|
||
|
||
Execute the above command with Http api, pay attention to escaping the JSON double quotes:
|
||
|
||
```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 execution result:
|
||
|
||
```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": [
|
||
...
|
||
]
|
||
}
|
||
}
|
||
},
|
||
...
|
||
}
|
||
```
|
||
|
||
You can see that the value of the watch result becomes a map object, and
|
||
the program can read value through a key .
|