前言
随着微服务数量不断增长,需要跟踪一个请求从一个微服务到下一个微服务的传播过程, Spring Cloud Sleuth 正是解决这个问题,它在日志中引入唯一ID,以保证微服务调用之间的一致性,这样你就能跟踪某个请求是如何从一个微服务传递到下一个。
如果你有使用AOP拦截Servlet的经验,做一个基于AOP的简单服务统计和跟踪很容易。但要像Zipkin那样能够跟踪服务调用链就比较困难了。所谓调用链,就是A服务调用B服务,B服务又调用了C、D服务。这样一个链要想统计跟踪,要写不少代码。而Spring Cloud Sleuth能让你不写一行代码的情况下完成这些。
应用
1 增加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> <version>2.0.1.RELEASE</version> </dependency>
2 配置logback
logback众所周知可以输出到console,file和远程服务器,我们项目中定义输入到统一的服务器:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<jmxConfigurator/>
<property name="LOG_FILE_NAME" value="@[email protected]"/>
<include resource="com/puhui/cloud/logging/logback/puhui-logback-standardization.xml"/>
<appender name="ERRORLOG" class="com.puhui.log.logback.HttpAppender">
<url>https://api.puhuifinance.com/log-server/log</url>
<localSystemName>@project.artifactId@</localSystemName>
<env>@config.branch@</env>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder charset="UTF-8">
<!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n</pattern>-->
<pattern>%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} | %clr(${LOG_LEVEL_PATTERN:-%5p}){magenta}
|[%X{X-B3-TraceId:-}]| [%X{token}] | %clr(---){faint}| %clr([%15.15t]){faint} |
%clr(%-40.40logger{39}){cyan} |
%m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
<appender-ref ref="ERRORLOG"/>
</root>
<!-- 需要配置的日志 -->
<logger name="com.puhui" level="INFO" additivity="true"/>
</configuration>
pattern的含义:
- 时间,加颜色clr
- 日志级别
- TraceId
- 自定义输出属性token,token在filter中存入MDC即可,代码:
MDC.put("token", "111888");
- 分隔符----
- thread
- logger内容 也就是包路径,类
- 信息,包括异常
3. 日志查询
- 每个请求都会生成一个traceid
- spanId在跨微服务的时候发生变化,微服务内不变化
- 查询条件区分大小写,比如AND,不能写成and
4 问题
一开始调试的时候,在logback增加了自定义属性token,但是本地调试就是不输出,后来发现我改动的logback的appender是输出到远程服务器到,本地当然不会修改,部署到云平台后,日志系统可以正常输出,没有问题。
5 自定义key
除了traceid和spanid等四个默认属性,可以自定义属性,只要存入MDC就可以,代码如下:
写一个拦截器:
package com.puhui.goosecard.bank.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.puhui.goosecard.bank.controller.BindCardController;
import com.puhui.goosecard.common.model.bank.shyk.BindCardCheckReq;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
/**
* 2 * @Author: kerry
* 3 * @Date: 2018/9/19 16:52
* 4
*/
@Component(value = "handlerInterceptor")
public class MDCInterceptor implements HandlerInterceptor {
private String getBody(HttpServletRequest request) {
String wholeStr = "";
try {
BufferedReader br = request.getReader();
String str = "";
while ((str = br.readLine()) != null) {
wholeStr += str;
}
} catch (Exception e) {
}
return wholeStr;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String body = getBody(request);
if (((HandlerMethod) handler).getBean() instanceof BindCardController) {
BindCardCheckReq bindCardCheckReq = JSONObject.parseObject(body, BindCardCheckReq.class);
MDC.put("outTradeNo", bindCardCheckReq.getOutTradeNo());
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
注册拦截器:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(handlerInterceptor);
}
}