要知道,在实际的项目中,与各个系统之间的接口调用是我们经常会用到的,为了方便以后查看接口之间传输的数据是否正确(说白了就是要把不是自己系统的问题甩出去----甩锅!),我们要搭建自己系统内部的日志,在查找自己问题的同时也方便对外甩锅!
好像有点跑题了~~
言归正传,一般情况下我们在系统输出日志的时候都是打印在文件中,这里我们先介绍一下logback.xml日志的配置:
<configuration scan="true" scanPeriod="3 seconds">
<!-- 请求输出日志的策略 -->
<appender name="REST-OUTBOUND" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<!-- 匹配信息 -->
<pattern>%msg%n</pattern>
</encoder>
<!-- 将文件输出到指定的路径 -->
<file>/data/api_logs/map/rest.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/data/api_logs/map/%d{yyyy-MM}/rest-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<!-- 最大保存20天 -->
<MaxHistory>20</MaxHistory>
</rollingPolicy>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>5MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- 需要拦截的类路径“com.xxx.xxx.xxx.log.LogInterceptor”,并指定输出的样式:REST-OUTBOUND -->
<logger name="com.xxx.xxx.xxx.log.LogInterceptor" level="INFO" additivity="false">
<appender-ref ref= "REST-OUTBOUND"/>
</logger>
<configuration/>
上图很简单,意思就是要将LogInterceptor类中打印的日志输出到指定的目录文件
试想,如果我们将每次请求的日志都放在文件中,会造成查找时很不便捷,并且文件会越来越大,时间久了会直逼服务器的硬盘容量,等到那个时候再不得已的情况下将文件清空。如果我们线上有一个问题是几个月之前的数据造成的,那么在找那个时候的日志去锁定问题基本很将bug复现。
如果我们要将每一个请求信息打印在日志的文件的同时还需要持久化到数据库中,该如何做呢?
简单来说我们需要将每次发送请求的地方加入日志实体类的各种set方法,然后保存到数据库。这样是可以实现功能,但是会造成项目中大量的冗余代码!如果哪天日志实体类中要新加一个字段,那么灾难就来了,我们需要修改所相关的java文件,都要加入这个字段,从而失去了项目的高可维护性!!!
所以下面我们要讲的重头戏来了,利用拦截器的方式:
首先,我们需要实现一个okhttp3包下的拦截器,在进行每一次http请求时被拦截,指定拦截器要做什么
这里我们需要拦截器拦截请求http请求和响应信息保存到我们需要持久化的实体类中。
public class LogInterceptor implements Interceptor {
//保存日志对象的的service
private IHttpRequestLogService logService;
@Override
public Response intercept(Chain chain) throws IOException {
//获得请求的request信息
Request req = chain.request();
//处理请求信息
//将请求信息保存到我们将来要持久化的日志对象中,例如:
HttpRequestLog vo = new HttpRequestLog()
vo.setMethod(req.method());
vo.setRequestHeader(headersToString(req.headers()));
//获得响应的的response信息并处理
//执行这次请求,并经过拦截器返回结果响应结果
Response resp = chain.proceed(req);
vo.setStatusCode(resp.code());
vo.setResponseHeader(headersToString(resp.headers()));
//在日志总打印请求和响应结构体
logger.info(JSON.toJsonString(vo));
//在打印日志对象的同时,将对象持久化到数据库
logService.log(vo);
//将最终结果返回到客户端调用方
return resp;
}
}
我们需要实例化一个OkHttpClient实例,在httpclient实例添加拦截器
public void init() {
client = new OkHttpClient.Builder()
.addInterceptor(new LogInterceptor(logService))//添加拦截器
.readTimeout(300, TimeUnit.SECONDS)//设置读取超时时间
.writeTimeout(300, TimeUnit.SECONDS)//设置写的超时时间
.connectTimeout(5, TimeUnit.SECONDS)//设置连接超时时间
.build();
}
配置完上述操作,在通过client.post()(或者其他请求方式)都会经过拦截器LogInterceptor进行拦截,最后再返回结果到客户端。
这样我们不但可以将打印信息打印到指定的日志文件,还可以在拦截器中处理需要保存的日志信息。
网友评论