看過我之前的文章的就可以一步一步搭建起日志傳輸到搜尋引擎 不知道的 看下之前的文章
(1)
記一次logback傳輸日志到logstash根據自定義設定動态建立ElasticSearch索引 (2) 關于” 記一次logback傳輸日志到logstash根據自定義設定動态建立ElasticSearch索引” 這篇部落格相關的優化采坑記錄 (3) 日志收集(ElasticSearch)串聯查詢 MDC這裡我們結合sleuth 可以降服務之間的調用使用唯一辨別串聯起來已達到我們通過一個辨別可以檢視所有跨服務調用的串聯日志,與上一篇 的MDC不同
sleuth 簡單原理說下
就是在最初發起調用者的時候在請求頭head中添加唯一辨別傳遞到直接調用的服務上面
然後之後的服務做類似的操作
好了 不多比比了
上代碼
首先所有的服務或spring boot項目都引入以下包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>com.cwbase</groupId>
<artifactId>logback-redis-appender</artifactId>
<version>1.1.5</version>
</dependency>
一個是傳輸redis使用一個是調用鍊跟蹤使用
下面是logback配置檔案
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="1 seconds">
<include resource="org/springframework/boot/logging/logback/base.xml" />
<!-- <jmxConfigurator/> -->
<contextName>logback</contextName>
<property name="log.path" value="\logs\logback.log" />
<property name="log.pattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} -%5p ${PID} --- traceId:[%X{mdc_trace_id}] [%15.15t] %-40.40logger{39} : %m%n" />
<appender name="file"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}</file>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>info-%d{yyyy-MM-dd}-%i.log
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>10</maxHistory>
</rollingPolicy>
</appender>
<appender name="redis" class="com.cwbase.logback.RedisAppender">
<tags>test</tags>
<host>IP</host><!--redis IP-->
<port>6379</port><!--redis端口-->
<key>test</key><!--redis隊列名稱-->
<!-- <mdc>true</mdc> -->
<callerStackIndex>0</callerStackIndex>
<location>true</location>
<additionalField>
<key>X-B3-ParentSpanId</key>
<value>@{X-B3-ParentSpanId}</value>
</additionalField>
<additionalField>
<key>X-B3-SpanId</key>
<value>@{X-B3-SpanId}</value>
</additionalField>
<additionalField>
<key>X-B3-TraceId</key>
<value>@{X-B3-TraceId}</value>
</additionalField>
</appender>
<root level="info">
<!-- <appender-ref ref="CONSOLE" /> -->
<!-- <appender-ref ref="file" /> -->
<!-- <appender-ref ref="UdpSocket" /> -->
<!-- <appender-ref ref="TcpSocket" /> -->
<appender-ref ref="redis" />
</root>
<!-- <logger name="com.example.logback" level="warn" /> -->
</configuration>
與之前的logback.xml配置檔案相比主要更改一下内容
<additionalField>
<key>X-B3-ParentSpanId</key>
<value>@{X-B3-ParentSpanId}</value>
</additionalField>
<additionalField>
<key>X-B3-SpanId</key>
<value>@{X-B3-SpanId}</value>
</additionalField>
<additionalField>
<key>X-B3-TraceId</key>
<value>@{X-B3-TraceId}</value>
</additionalField>
一會在詳細解釋上述三個字段含義 下面先看項目目錄結構

一個父工程(pom工程)三個spring boot子項目 子項目調用關系如下
三個子項目代碼如下
spring-cloud-client-test工程結構及代碼
1 package application;
2
3 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory;
5 import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.boot.SpringApplication;
7 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
8 import org.springframework.boot.autoconfigure.SpringBootApplication;
9 import org.springframework.cloud.netflix.feign.EnableFeignClients;
10 import org.springframework.web.bind.annotation.GetMapping;
11 import org.springframework.web.bind.annotation.RestController;
12
13 @EnableAutoConfiguration
14 @EnableFeignClients
15 @RestController
16 @SpringBootApplication
17 public class ClientTestApplication {
18 protected final static Logger logger = LoggerFactory.getLogger(ClientTestApplication.class);
19
20 public static void main(String[] args) {
21 SpringApplication.run(ClientTestApplication.class, args);
22 }
23
24 @Autowired
25 servertest server;
26
27 @GetMapping("/client")
28 public String getString(){
29 logger.info("開始調用服務端");
30 return server.getString();
31 }
32 @GetMapping("/client1")
33 public String getString1(){
34 logger.info("開始調用服務端1");
35 return server.getString1();
36 }
37 }
1 package application;
2
3 import org.springframework.cloud.netflix.feign.FeignClient;
4 import org.springframework.http.MediaType;
5 import org.springframework.web.bind.annotation.RequestMapping;
6 import org.springframework.web.bind.annotation.RequestMethod;
7
8 @FeignClient("SPRING-CLOUD-SERVER-TEST")
9 public interface servertest {
10 @RequestMapping(value = "/server", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
11 public String getString();
12 @RequestMapping(value = "/server1", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
13 public String getString1();
14 }
spring-cloud-server-test工程及代碼結構
1 package application;
2
3 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory;
5 import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.boot.SpringApplication;
7 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
8 import org.springframework.boot.autoconfigure.SpringBootApplication;
9 import org.springframework.cloud.netflix.feign.EnableFeignClients;
10 import org.springframework.web.bind.annotation.GetMapping;
11 import org.springframework.web.bind.annotation.RestController;
12
13 @EnableAutoConfiguration
14 @EnableFeignClients
15 @RestController
16 @SpringBootApplication
17 public class ServerTestApplication {
18 protected final static Logger logger = LoggerFactory.getLogger(ServerTestApplication.class);
19
20 public static void main(String[] args) {
21 SpringApplication.run(ServerTestApplication.class, args);
22 }
23
24 @Autowired
25 servertest server;
26
27 @GetMapping("/server")
28 public String getString(){
29 logger.info("接收用戶端的調用");
30 return server.getString();
31 }
32 @GetMapping("/server1")
33 public String getString1(){
34 logger.info("接收用戶端的調用1");
35 return server.getString1();
36 }
37 }
1 package application;
2
3 import org.springframework.cloud.netflix.feign.FeignClient;
4 import org.springframework.http.MediaType;
5 import org.springframework.web.bind.annotation.RequestMapping;
6 import org.springframework.web.bind.annotation.RequestMethod;
7
8 @FeignClient("SPRING-CLOUD-SERVER1-TEST")
9 public interface servertest {
10 @RequestMapping(value = "/server", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
11 public String getString();
12 @RequestMapping(value = "/server1", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
13 public String getString1();
14 }
spring-cloud-server1-test工程及代碼結構
1 package application;
2
3 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory;
5 import org.springframework.boot.SpringApplication;
6 import org.springframework.boot.autoconfigure.SpringBootApplication;
7 import org.springframework.web.bind.annotation.GetMapping;
8 import org.springframework.web.bind.annotation.RestController;
9
10
11 @RestController
12 @SpringBootApplication
13 public class Server1TestApplication {
14 protected final static Logger logger = LoggerFactory.getLogger(Server1TestApplication.class);
15
16 public static void main(String[] args) {
17 SpringApplication.run(Server1TestApplication.class, args);
18 }
19
20
21 @GetMapping("/server")
22 public String getString(){
23 logger.info("接收用戶端的調用");
24 return "My is server";
25 }
26 @GetMapping("/server1")
27 public String getString1(){
28 logger.info("接收用戶端的調用1");
29 return "My is server1";
30 }
31 }
好了 全部代碼就是以上這些 下面看日志傳輸之後的效果
上圖就是最後的結果
我們可以通過 X-B3-TraceId 串聯所有的服務 這個值每次請求都不一樣但是會随着調用鍊一直傳遞下去
X-B3-SpanId 這個值屬于方法級别的值 也就是說 方法調用方法是父子級别的傳遞(友善調用跟蹤)
X-B3-ParentSpanId 這個值就是上一個方法的X-B3-SpanId 我說的不是很明白大家可以查閱相關資料了解
好了到這裡就基本完成了
總結思考
使用sleuth我們可以很好的串聯快服務的日志,結合MDC就可以出現很完美的調用流水查詢但是我們要做到一次查詢 要麼做表達式篩選要麼查詢兩次 。我們有沒有辦法将二者結合那,我想并不困難自己重寫sleuth相關方法可以做到,但是我們要考慮這是有問題的,什麼問題那 就是 同樣的MDC key-value 調用 sleuth會變 但是MDC值不變 我們要融合成什麼樣子才能達到想要的目的的,這個就不好說了 ,通過表達式篩選已經很友善了還有麼有必要這樣做那,做了之後怎麼避免副作用那!有待考究