日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
生產(chǎn)環(huán)境定位日志太麻煩怎么辦?建議了解一下日志框架的MDC功能

對(duì)于每一個(gè)開(kāi)發(fā)者來(lái)說(shuō),查詢接口的執(zhí)行日志都是一個(gè)高頻率的操作,每當(dāng)測(cè)試說(shuō)接口有問(wèn)題時(shí),我們都需要去服務(wù)器或者日志系統(tǒng)上查報(bào)錯(cuò)的原因。
一般情況下,我們會(huì)通過(guò)對(duì)應(yīng)的關(guān)鍵字或者接口地址去查詢這個(gè)接口到底報(bào)了什么錯(cuò),但是這帶來(lái)一個(gè)問(wèn)題,就是我們可能少打日志或者忘打某些關(guān)鍵字的日志,導(dǎo)致查詢記錄比較麻煩。

網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、小程序設(shè)計(jì)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了陽(yáng)明免費(fèi)建站歡迎大家使用!

那么有沒(méi)有一種簡(jiǎn)單高效的方法,即使我們?cè)谌罩局胁淮蛴∪魏侮P(guān)鍵字,系統(tǒng)會(huì)自動(dòng)生成一個(gè)關(guān)鍵字,讓我們一次性查詢出這個(gè)接口的所有l(wèi)og記錄呢?

MDC

MDC是日志門(mén)面框架SLF4J提供的一個(gè)類(lèi),可以提供在多線程情況下記錄日志的功能,log4j、logback、log4j2都有對(duì)這個(gè)類(lèi)的實(shí)現(xiàn)。

從本質(zhì)上來(lái)說(shuō),MDC可以看做一個(gè)ThreadLocal,由于其線程安全的特性,可以讓我們輕松安全的保存數(shù)據(jù)。

MDC主要的API有clear()、get()、put()、remove()方法等,簡(jiǎn)潔的api讓我們使用上手基本沒(méi)有難度。

如何使用

1、修改日志打印格式

以日志框架logback為例,在logback.xml中,找到日志打印規(guī)則的配置,添加-%X{reqId}屬性,其中reqId可以任意指定,你寫(xiě)其他的屬性也可以,博主這里演示指定為reqId。


-%X{reqId} %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %file:%line %msg%n
UTF-8

2、添加過(guò)濾器MDCFilter

ps.使用攔截器也可以,效果是一樣的。對(duì)每個(gè)接口做攔截。

@Component
@AllArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class MDCFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){
try {
//給每個(gè)請(qǐng)求接口生成一個(gè)requestId
String requestId = RandomUtil.randomNumbers(10);
//這里的reqId就是上面配置的,要保持一致
MDC.put("reqId", "reqId:" + requestId);
chain.doFilter(request, response);
} finally {
MDC.clear();
}
}

@Override
public void destroy(){
}
}

經(jīng)過(guò)簡(jiǎn)單的兩步就配置好了,下面我們看一下效果。

@PostMapping(value = "/mdcTest")
public ResponseEntity mdcTest(String id, String name) {
log.info("測(cè)試日志打印,id={},name={}", id, name);
log.info("測(cè)試日志打印1");
log.info("測(cè)試日志打印2");
log.info("測(cè)試日志打印3");
log.info("測(cè)試日志打印4");
return ResponseEntity.ok().build();
}

每一行日志都有一個(gè)關(guān)鍵字reqId:9723829830,這樣我們查詢?nèi)罩緯r(shí)只需要查詢關(guān)鍵字9723829830就可以直接查出來(lái)這個(gè)接口所有的執(zhí)行記錄了。

如果想更方便的話,也可以把這個(gè)關(guān)鍵字直接輸出到每一個(gè)接口的響應(yīng)頭或者響應(yīng)體中。

進(jìn)階使用

對(duì)于普通的web應(yīng)用我們可以直接攔截每個(gè)接口,自動(dòng)生成一個(gè)請(qǐng)求id,那么對(duì)于微服務(wù)項(xiàng)目,一個(gè)接口可能會(huì)產(chǎn)生很多服務(wù)的調(diào)用,那如何一次性查出來(lái)所有系統(tǒng)內(nèi)的日志呢?

對(duì)于日志的搜集本文暫不考慮,咱們先說(shuō)如何做請(qǐng)求id的傳遞。

其實(shí)也很簡(jiǎn)單,當(dāng)我們有多個(gè)系統(tǒng)間的調(diào)用時(shí),把reqId放到request的header中進(jìn)行傳遞,然后下游系統(tǒng)獲取這個(gè)id就可以了。

比如下方的攔截器:

public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//如果有上層調(diào)用就從header中取出上層的ID
String traceId = request.getHeader("reqId");
if (traceId == null) {
//如果沒(méi)有,就生成一個(gè)默認(rèn)的
traceId = RandomUtil.randomNumbers(10);
}
MDC.put("reqId", traceId);
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 {
//調(diào)用結(jié)束后刪除
MDC.remove("reqId");
}
}

MDC存在的一些問(wèn)題

我們?cè)谏衔恼f(shuō)過(guò),MDC的本質(zhì)是ThreadLocal,它會(huì)把數(shù)據(jù)都綁定到當(dāng)前線程上。但是當(dāng)我們使用多線程的時(shí)候,就會(huì)帶來(lái)一個(gè)數(shù)據(jù)丟失的問(wèn)題。

所以,我們需要進(jìn)行線程間的數(shù)據(jù)傳遞,保證MDC數(shù)據(jù)不丟失。

以線程池傳遞數(shù)據(jù)為例,ThreadPoolTaskExecutor提供了一個(gè)taskDecorator裝飾器,通過(guò)這個(gè)屬性,我們就可以實(shí)現(xiàn)屬性的傳遞。

首先,定義一個(gè)MDCContextDecorator。

public class MDCContextDecorator implements TaskDecorator {

@Override
public Runnable decorate(Runnable runnable){
Map previous = MDC.getCopyOfContextMap();
return () -> {
try {
if (previous != null) {
MDC.setContextMap(previous);
}
runnable.run();
} finally {
MDC.clear();
}
};
}
}

然后設(shè)置線程池的taskDecorator屬性。

public ThreadPoolTaskExecutor executor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
//...其他屬性
//設(shè)置線程屬性的自動(dòng)傳遞
executor.setTaskDecorator(new MDCContextDecorator());
return executor;
}

最后

MDC的使用其實(shí)很簡(jiǎn)單,對(duì)于我們查詢?nèi)罩疽埠苡袔椭瑧?yīng)用也算是非常廣泛了。有興趣的同學(xué)也可以去看一下它的內(nèi)部實(shí)現(xiàn),代碼也并不復(fù)雜。


分享文章:生產(chǎn)環(huán)境定位日志太麻煩怎么辦?建議了解一下日志框架的MDC功能
文章位置:http://m.5511xx.com/article/dpjjddh.html