看過我之前的文章的就可以一步一步搭建起日志傳輸到搜索引擎 不知道的 看下之前的文章
(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值不變 我們要融合成什么樣子才能達到想要的目的的,這個就不好說了 ,通過表達式篩選已經很方便了還有么有必要這樣做那,做了之后怎么避免副作用那!有待考究