🎂 日志管理
Forest在发送请求时和接受响应数据时都会自动打印出HTTP请求相关的日志,其中包括:请求日志、响应状态日志、响应内容日志。
# 请求日志
请求日志会打印出所有请求发送的内容,其中包括请求行、请求头、请求体三部分
[Forest] Request: 
	POST http://localhost:8080/test HTTP
	Headers: 
		accessToken: abcdefg123456
	Body: username=foo&password=bar
 2
3
4
5
这段内容就是请求日志,包含的发送HTTP请求的所有几乎所有信息,也很容易看懂。如若看不懂,还请参见《HTTP请求报文 (opens new window)》,这里不再赘述。
# 响应状态日志
响应状态日志包含了HTTP请求响应后接受到的状态码,以及响应时间
[Forest] Response: Status = 200, Time = 11ms
 Status为状态码:标准的HTTP协议定义的请求响应状态码,如若不清楚,可以参见《HTTP状态码 (opens new window)》
Time为请求响应时间:从客户端请求发送到接受到响应数据的总时间
# 响应内容日志
响应内容日志则会打印出请求发送的目标服务器响应后,返回给请求接受方的实际数据内容
[Forest] Response: Content={"flag":"success","message":"成功"}
 Content=后面的内容便是请求响应接受到的数据
友情提示
响应内容日志默认是关闭的
# 日志总开关
很多时候,我们只在调试的时候打印日志,上了生产环境后为了节省性能便不打印了(并不是所有公司都不在生产打印日志,毕竟万一出了问题也是要有据可循的,这里只是举个栗子( •⌄• )), 便需要通过配置的方式打开和关闭HTTP请求的日志。
forest:
  ## 日志总开关,打开/关闭Forest请求/响应日志(默认为 true)
  log-enabled: true
  ## 打开/关闭Forest请求日志(默认为 true)
  log-request: true
  ## 打开/关闭Forest响应状态日志(默认为 true)
  log-response-status: true
  ## 打开/关闭Forest响应内容日志(默认为 false)
  log-response-content: true
 2
3
4
5
6
7
8
9
# 单个请求的日志开关
日志的总开关控制粒度太过粗放,如果想要只关闭和打开某一个请求的日志有办法吗?有的~
Forest自1.50-BETA1版本后提供了@LogEnabled注解来专门干这个事。
@LogEnabled注解的各属性如下所示:
| 属性 | 作用 | 默认值 | 
|---|---|---|
| value | 是否打印请求/响应日志 | true | 
| logRequest | 是否打印请求日志 | true | 
| logResponseStatus | 是否打印响应状态日志 | true | 
| logResponseContent | 是否打印响应内容日志 | false | 
具体如何使用看下面例子:
/** 默认开关:允许打印请求日志、响应状态日志,但不打印响应内容日志 */
@Get("http://localhost:8080/send")
@LogEnabled
String send(@Query("msg") String message);
/** 同上 */
@Get("http://localhost:8080/send")
@LogEnabled(true)
String send(@Query("msg") String message);
/** 关闭该请求的所有日志 */
@Get("http://localhost:8080/send")
@LogEnabled(false)
String send(@Query("msg") String message);
/** 不打印请求日志 */
@Get("http://localhost:8080/send")
@LogEnabled(logRequest = false)
String send(@Query("msg") String message);
/** 不打印响应状态日志 */
@Get("http://localhost:8080/send")
@LogEnabled(logResponseStatus = false)
String send(@Query("msg") String message);
/** 打印响应内容日志 */
@Get("http://localhost:8080/send")
@LogEnabled(logResponseContent = true)
String send(@Query("msg") String message);
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 自定义日志处理器
如果有小伙伴觉得Forest默认的日志打印格式太丑,领导都看不下去了怎么办?
好消息是自1.5.0-BETA1版本后可以通过自定义扩展日志处理器来按自己喜欢的格式打印日志。
实现过程也不复杂:实现com.dtflys.forest.logging.ForestLogHandler接口,或者继承com.dtflys.forest.logging.DefaultLogHandler类。
这里的例子使用继承DefaultLogHandler类的方式,因为可以少些很多代码
/**
 * 我自定义的日志处理器
 */
public class TestLogHandler extends DefaultLogHandler {
  
    /**
     * 所有的请求最终会调用这个方法打印日志
     */
    @Override
    public void logContent(String content) {
        super.logContent("[哈哈,这是我自己的日志]: " + content);
    }
    /**
     * 该方法生成Forest请求的日志内容字符串
     * @param requestLogMessage 请求日志字符串
     * @return 日志内容字符串
     */
    @Override
    protected String requestLoggingContent(RequestLogMessage requestLogMessage) {
        StringBuilder builder = new StringBuilder();
        builder.append("请求: \n\t");
        // 插入重试信息
        builder.append(retryContent(requestLogMessage));
        // 插入代理信息
        builder.append(proxyContent(requestLogMessage));
        // 插入请求类型变更历史信息
        builder.append(requestTypeChangeHistory(requestLogMessage));
        // 插入请求行信息
        builder.append(requestLogMessage.getRequestLine());
        // 获取并插入所有请求头内容
        String headers = requestLoggingHeaders(requestLogMessage);
        if (StringUtils.isNotEmpty(headers)) {
            builder.append("\n\t请求头: \n");
            builder.append(headers);
        }
        // 获取并插入所有请求体内容
        String body = requestLoggingBody(requestLogMessage);
        if (StringUtils.isNotEmpty(body)) {
            builder.append("\n\t请求体: \n");
            builder.append(body);
        }
        return builder.toString();
    }
    /**
     * 该方法生成Forest请求响应结果的日志内容字符串
     * @param responseLogMessage 请求响应日志字符串
     * @return 日志内容字符串
     */
    @Override
    protected String responseLoggingContent(ResponseLogMessage responseLogMessage) {
        ForestResponse response = responseLogMessage.getResponse();
        if (response != null && response.getException() != null) {
            return "[网络错误]: " + response.getException().getMessage();
        }
        // 获取请求响应状态码
        int status = responseLogMessage.getStatus();
        // 获取请求响应时间
        long time = responseLogMessage.getTime();
        if (status >= 0) {
            return "请求响应: 状态码: " + status + ", 耗时: " + time + "ms";
        } else {
            return "[网络错误]: 未知的网络错误!";
        }
    }
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 配置全局日志处理器
定义完自己的日志处理器后并不会马上就生效,还需要在配置中指定您定义的日志处理器类。
日志处理器的配置也分两种:全局级别和接口/方法级别。
我们先看全局日志处理器怎么配置:
forest:
  ## 全局日志处理器类
  log-handler: com.your.site.logging.TestLogHandler
 2
3
4
5
# 配置单个请求的日志处理器
和日志的开关一样,我只想对某个或某几个请求用自定义的日志处理器,也一样有办法。
Forest提供了@LogHandler注解来解决这个问题
@Get(url = "http://localhost:8080/send")
@LogHandler(com.your.site.logging.TestLogHandler.class)
String send(@Query("msg") String message);
 2
3