zoukankan      html  css  js  c++  java
  • spring cloud 微服务日志跟踪 sleuth logback elk 整合

    看过我之前的文章的就可以一步一步搭建起日志传输到搜索引擎 不知道的 看下之前的文章 

    (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="logslogback.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值不变  我们要融合成什么样子才能达到想要的目的的,这个就不好说了 ,通过表达式筛选已经很方便了还有么有必要这样做那,做了之后怎么避免副作用那!有待考究 

     

  • 相关阅读:
    java List转换为字符串并加入分隔符的一些方法总结
    jackson 实体转json 为NULL或者为空不参加序列化
    马云告别演讲
    Linux chmod命令
    Linux执行shell脚本的方法
    2019第36周日
    2019第36周六
    eclipse中的maven插件
    SpringBoot要点之使用Actuator监控
    eclipse隐藏的列编辑
  • 原文地址:https://www.cnblogs.com/zhyg/p/9167425.html
Copyright © 2011-2022 走看看