微服务分布式架构实现⽇志链路跟踪的⽅法Logback 背景
logback-core:其它两个模块的基础模块
logback-classic:它是log4j的⼀个改良版本,同时它完整实现了slf4j API使你可以很⽅便地更换成其它⽇志系统如log4j或JDK14 Logging logback-access:访问模块与Servlet容器集成提供通过Http来访问⽇志的功能
普通debug⽇志
SQL执⾏⽇志
Logback 配置案例
⽇志级别排序为:TRACE < DEBUG < INFO < WARN < ERROR
%d:表⽰⽇期
%n:换⾏
%thread:表⽰线程名
%level:⽇志级别
%msg:⽇志消息
%file:表⽰⽂件名
%class:表⽰⽂件名
%logger:Java类名(含包名,这⾥设定了36位,若超过36位,包名会精简为类似JavaBean)
%line:Java类的⾏号
注意:
%-4relative %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread][%X{TRACE_ID}] %-5level %logger{100}.%M\(%line\) - %msg%n
在logback中,%relative表⽰⾃应⽤程序启动以来打印相对时间戳(以毫秒为单位). %-4只是元素的对齐⽅式.
案例
3452487 2021-08-03 15:19:36.940 [thread-monitor-daemon][] WARN  n.util.MonitorLogger.warn(27) - 发现超时线程
由于案例中是守护线程thread-monitor-daemon,所以不记录链路ID。
对在系统设计的时候对于线程的命名规范也是有约束的
这⾥就不做详细展开后续有机会会分享。
回归正题⽐如下⾯的例⼦中记录了请求的链路ID
19006989 2021-08-04 22:35:25.776 [http-nio-0.0.0.0-8010-exec-10][1fc8pebmgwukw863w2p342rp2936a3r157w0:0:] INFO
对于上图中显⽰的系统启动时间、当前时间、当前线程、对应路径按照logback官⽅配置就可以逐步完善对于的⽇志信息,但是对于链路ID的⽣成写⼊就需要特殊处理。链路ID设计
对于链路追踪设计我个⼈⽐较喜欢两种⽅案
第⼀种
在每⼀次请求中链路编号(traceId)、单元编号(spanId)都是通过HttpHeader的⽅式进⾏传递,⽇
志的起始位置会主动⽣成traceId、spanId,⽽起始位置的Parent SpanId则是不存在的,值为null。这样每次通过restTemplate、Openfeign的形式访问其他服务的接⼝时,就会携带起始位置⽣成的traceId、spanId到下⼀个服务单元。
第⼆种
在每⼀次请求中链路编号(traceId),没经过⼀次微服务对于深度(Deep)加1
public static class ThreadTraceListener implements ThreadListener {
@Override
public void onThreadBegin(HttpServletRequest request) {
String traceToken = TranVar(TRACE_ID);
String fromServer = TranVar(FROM_SERVER);
int deep;
String traceId;
if (StringUtils.isBlank(traceToken)) {
traceId = ateID();
deep = 0;
traceToken = StringHelper.join(traceId, ":0");
} else {
int index = traceToken.lastIndexOf(':');
traceId = traceToken.substring(0, index);
deep = Integer.valueOf(traceToken.substring(index + 1));
}
ThreadLocalUtil.setLocalVar(TRACE_ID, traceId);
ThreadLocalUtil.setLocalVar(TRACE_DEEP, deep);
ThreadLocalUtil.setTranVar(TRACE_ID, StringHelper.join(traceId, ":", deep + 1));
ThreadLocalUtil.setLocalVar(FROM_SERVER, fromServer);
ThreadLocalUtil.setTranVar(FROM_SERVER, getCurrentServer());
MDC.put(TRACE_ID, StringHelper.join(traceToken, ":", fromServer));
}
@Override
public void onThreadEnd(HttpServletRequest request) {
}
}
针对请求拦截
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
// 从Header中装载传递过来的变量
Map<String, Object> tranVar = new HashMap<String, Object>();
Enumeration<String> headers = HeaderNames();
while (headers.hasMoreElements()) {
String key = Element();
if (!StringUtils.isEmpty(key)
&& key.startsWith(ThreadLocalUtil.TRAN_PREFIX)) {
tranVar.put(key.substring(ThreadLocalUtil.TRAN_PREFIX.length()),
}
}
ThreadLocalHolder.begin(tranVar, request);
try {
if (isGateway) {
response.addHeader("X-TRACE-ID", TraceId());
}
// 检查RPC调⽤深度
checkRpcDeep(request, response);
// 业务处理
chain.doFilter(request, response);
// 记录RPC调⽤次数
logRpcCount(request, response);
} catch (Throwable ex) {
// 错误处理
Response<?> result = Response(ex);
Determine determine = ExceptionUtil.determineType(ex);
ExceptionUtil.doLog(result, Status(), ex);
response.Status().value());
response.setCharacterEncoding("UTF-8");
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
} finally {
try {
doMonitor(request, response, startTime);
if (TraceUtil.isTraceLoggerOn()) {
log.warn(StringHelper.join(
"TRACE-HTTP-", Method(),
" URI:", RequestURI(),
", dt:", System.currentTimeMillis() - startTime,
", rpc:", RpcCount(),
", status:", Status()));
} else if (log.isTraceEnabled()) {
分布式和微服务的关系" URI:", RequestURI(),
", dt:", System.currentTimeMillis() - startTime,
", rpc:", RpcCount(),
", status:", Status()));
}
} finally {
}
}
}
到此这篇关于微服务分布式架构实现⽇志链路跟踪的⽅法的⽂章就介绍到这了,更多相关微服务分布式架构⽇志链路跟踪内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。