沒(méi)有使用框架,自己寫(xiě)的簡(jiǎn)單鏈路跟蹤甸祭,主要是下面兩個(gè)過(guò)濾器TraceRpcFilter褥影、TraceHttpFilter 。
@Activate(group = {Constants.CONSUMER,Constants.PROVIDER})
public class TraceRpcFilter implements Filter {
? ? /**
? ? * 1、保證RpcContext中存在mdcData赊抖,為了下一次獲取mdcData后寫(xiě)入到MDC
? ? * 2寨典、保證MDC中存在mdcData,為了最終的日志打印輸入
? ? * @param invoker
? ? * @param invocation
? ? * @return
? ? * @throws RpcException
? ? */
? ? public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
? ? ? ? //創(chuàng)建traceId
? ? ? ? TraceUtils.createTrace();
? ? ? ? //創(chuàng)建spanId
? ? ? ? TraceUtils.createSpan();
? ? ? ? //設(shè)置parentId
? ? ? ? TraceUtils.setParent();
? ? ? ? //設(shè)置其它信息
? ? ? ? TraceUtils.setRpcOthersInfo();
? ? ? ? return invoker.invoke(invocation);
? ? }
}
public class TraceHttpFilter extends OncePerRequestFilter implements Ordered {
? ? private static final Logger logger? = LoggerFactory.getLogger(TraceHttpFilter.class);
? ? @Autowired(required = false)
? ? private TraceLogInformation traceLogInformation;
? ? @Override
? ? public int getOrder() {
? ? ? ? return 0;
? ? }
? ? /***
? ? * 生成traceId和spanId注暗,并寫(xiě)入slf4j的本地線程變量(也就意味著墓猎,請(qǐng)求中開(kāi)啟的線程和定時(shí)任務(wù)是得不到traceId和spanId的)
? ? * @Param [request, response, filterChain]
? ? * @return void
? ? **/
? ? @Override
? ? protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
? ? ? ? try {
? ? ? ? ? ? String traceId = request.getHeader("x-http-trace");//nginx可能附加的請(qǐng)求頭
? ? ? ? ? ? if (StringUtils.isEmpty(traceId)) {
? ? ? ? ? ? ? ? MDC.put(TraceConstants.TRACE_ID, TraceUtils.getLogId());
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? MDC.put(TraceConstants.TRACE_ID, traceId);
? ? ? ? ? ? }
? ? ? ? ? ? //初次調(diào)用毙沾,生成spanId
? ? ? ? ? ? MDC.put(TraceConstants.SPAN_ID, TraceUtils.getLogId());
? ? ? ? ? ? if (traceLogInformation != null) {
? ? ? ? ? ? ? ? traceLogInformation.setLocalIp(AccessIpUtils.getLocalIp());//本地IP
? ? ? ? ? ? ? ? traceLogInformation.setRemoteIp(AccessIpUtils.getRemoteIp(request));//訪問(wèn)IP
? ? ? ? ? ? ? ? TraceUtils.setHttpOthersInfo(traceLogInformation);
? ? ? ? ? ? }
? ? ? ? }catch (Exception ex){
? ? ? ? ? ? logger.error("http請(qǐng)求過(guò)濾器處理異常:{}",ex.getMessage());
? ? ? ? }
? ? ? ? filterChain.doFilter(request ,response);
? ? }
}
public class AccessIpUtils {
? ? /***
? ? * X-Forwarded-For:? ? Squid 服務(wù)代理
? ? * Proxy-Client-IP:? ? apache 服務(wù)代理
? ? * WL-Proxy-Client-IP:? weblogic 服務(wù)代理
? ? * X-Real-IP:? ? ? ? ? nginx服務(wù)代理
? ? * HTTP_CLIENT_IP:? ? ? 有些代理服務(wù)器
? ? *對(duì)http請(qǐng)求獲取請(qǐng)求IP
? ? * @Param [request]
? ? * @return java.lang.String
? ? **/
? ? public static String getRemoteIp(HttpServletRequest request) {
? ? ? ? String ip = null;
? ? ? ? String ipAddresses = request.getHeader("X-Forwarded-For");
? ? ? ? if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
? ? ? ? ? ? ipAddresses = request.getHeader("Proxy-Client-IP");
? ? ? ? }
? ? ? ? if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
? ? ? ? ? ? ipAddresses = request.getHeader("WL-Proxy-Client-IP");
? ? ? ? }
? ? ? ? if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
? ? ? ? ? ? ipAddresses = request.getHeader("HTTP_CLIENT_IP");
? ? ? ? }
? ? ? ? if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
? ? ? ? ? ? ipAddresses = request.getHeader("X-Real-IP");
? ? ? ? }
? ? ? ? //有些網(wǎng)絡(luò)通過(guò)多層代理左胞,那么獲取到的ip就會(huì)有多個(gè),一般都是通過(guò)逗號(hào)(,)分割開(kāi)來(lái)遍烦,并且第一個(gè)ip為客戶端的真實(shí)IP
? ? ? ? if (ipAddresses != null && ipAddresses.length() != 0) {
? ? ? ? ? ? ip = ipAddresses.split(",")[0];
? ? ? ? }
? ? ? ? if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
? ? ? ? ? ? ip = request.getRemoteAddr();
? ? ? ? }
? ? ? ? return ip;
? ? }
? ? public static String getLocalIp()throws UnknownHostException{
? ? ? ? return InetAddress.getLocalHost().getHostAddress();
? ? }
}
public class TraceConstants {
? ? public static final String TRACE_TEXT_FORMAT = "[TraceId:%s]";
? ? public static final String TRACE_ID =? "traceId2";
? ? public static final String SPAN_ID? =? "spanId2";
? ? public static final String PARENT_ID =? "parentId2";
? ? public static final String APP_FLAG = UUID.randomUUID().toString();
}
public class TraceUtils {
? ? /***
? ? * 通過(guò)UUID躺枕,生成traceId拐云、spanId
? ? * @Param []
? ? * @return java.lang.String
? ? **/
? ? public static String getLogId(){
? ? ? ? return UUID.randomUUID().toString().replaceAll("-","");
? ? }
? ? /***
? ? * 生成TraceId,
? ? * 關(guān)鍵點(diǎn):traceId不應(yīng)該為空叉瘩,僅從該點(diǎn)出發(fā)判斷邏輯即可,不做任何其它邏輯上的判斷
? ? * @Param []
? ? * @return void
? ? **/
? ? public static? void? createTrace(){
? ? ? ? String? rpcTraceId? =? RpcContext.getContext().getAttachment(TraceConstants.TRACE_ID);
? ? ? ? String? mdcTraceId? =? MDC.get(TraceConstants.TRACE_ID);
? ? ? ? if(StringUtils.isEmpty(mdcTraceId)&&StringUtils.isEmpty(rpcTraceId)){
? ? ? ? ? ? rpcTraceId? =? TraceUtils.getLogId();
? ? ? ? ? ? MDC.put(TraceConstants.TRACE_ID ,rpcTraceId);
? ? ? ? ? ? RpcContext.getContext().setAttachment(TraceConstants.TRACE_ID ,rpcTraceId);
? ? ? ? }else if(StringUtils.isEmpty(mdcTraceId)&&StringUtils.isNotEmpty(rpcTraceId)){
? ? ? ? ? ? MDC.put(TraceConstants.TRACE_ID ,rpcTraceId);
? ? ? ? }else if(StringUtils.isEmpty(rpcTraceId)&&StringUtils.isNotEmpty(mdcTraceId)){
? ? ? ? ? ? RpcContext.getContext().setAttachment(TraceConstants.TRACE_ID ,mdcTraceId);
? ? ? ? }
? ? }
? ? /***
? ? * 生成SpanId,
? ? * 關(guān)鍵點(diǎn):一次調(diào)用的同一個(gè)服務(wù)中捅暴,spanId是不變的咧纠;不同的服務(wù)中,spanId是不一樣的梧奢;另外,spanId不可能為空
? ? * @Param []
? ? * @return void
? ? **/
? ? public static? void? createSpan(){
? ? ? ? String? rpcSpanId? =? RpcContext.getContext().getAttachment(TraceConstants.SPAN_ID);
? ? ? ? String? mdcSpanId? =? MDC.get(TraceConstants.SPAN_ID);
? ? ? ? if(StringUtils.isEmpty(mdcSpanId)&&StringUtils.isEmpty(rpcSpanId)){
? ? ? ? ? ? rpcSpanId? =? TraceUtils.getLogId();
? ? ? ? ? ? MDC.put(TraceConstants.SPAN_ID ,rpcSpanId);
? ? ? ? ? ? RpcContext.getContext().setAttachment(TraceConstants.SPAN_ID ,rpcSpanId);
? ? ? ? } else if(StringUtils.isEmpty(mdcSpanId)&&StringUtils.isNotEmpty(rpcSpanId)){
? ? ? ? ? ? MDC.put(TraceConstants.SPAN_ID ,TraceUtils.getLogId());
? ? ? ? }
? ? }
? ? /***
? ? * 設(shè)置parentId
? ? * 關(guān)鍵點(diǎn):同一個(gè)服務(wù)中的一次調(diào)用趋惨,parentId是不變的惦蚊;其他情況下,parentId為上一次的spanId
? ? * @Param []
? ? * @return void
? ? **/
? ? public static? void setParent(){
? ? ? ? String mdcSpanId? ? =? MDC.get(TraceConstants.SPAN_ID);
? ? ? ? String? rpcSpanId? =? RpcContext.getContext().getAttachment(TraceConstants.PARENT_ID);
? ? ? ? if(StringUtils.isNotEmpty(mdcSpanId)){
? ? ? ? ? ? RpcContext.getContext().setAttachment(TraceConstants.PARENT_ID ,mdcSpanId);
? ? ? ? }
? ? ? ? if(StringUtils.isNotEmpty(rpcSpanId)){
? ? ? ? ? ? MDC.put(TraceConstants.PARENT_ID, rpcSpanId);
? ? ? ? }
? ? }
? ? /**
? ? * 應(yīng)用名稱兆沙、Ip等信息
? ? * @Param []
? ? * @return void
? ? **/
? ? public static? void setRpcOthersInfo(){
? ? ? ? String appName = RpcContext.getContext().getUrl().getParameter("application");
? ? ? ? String localIp = RpcContext.getContext().getLocalHost();
? ? ? ? String remoteIp = RpcContext.getContext().getRemoteHost();
? ? ? ? MDC.put("appName" ,appName);
? ? ? ? MDC.put("localIp2" ,localIp);
? ? ? ? MDC.put("remoteIp2",remoteIp);
? ? }
? ? /**
? ? * 應(yīng)用名稱莉掂、Ip等信息
? ? * @Param []
? ? * @return void
? ? **/
? ? public static? void setHttpOthersInfo(TraceLogInformation traceLogInformation){
? ? ? ? MDC.put("appName" ,traceLogInformation.getApplicationName());
? ? ? ? MDC.put("localIp" ,traceLogInformation.getLocalIp());
? ? ? ? MDC.put("remoteIp" ,traceLogInformation.getRemoteIp());
? ? }
}