天天看點

Spring Cloud建構微服務架構:分布式服務跟蹤(整合logstash)【Dalston版】

通過之前的 入門示例 ,我們已經為

tr

ace-1

trace-2

引入了Spring Cloud Sleuth的基礎子產品

spring-cloud-starter-sleuth

,實作了為各微服務的日志資訊中添加跟蹤資訊的功能。但是,由于日志檔案都離散的存儲在各個服務執行個體的檔案系統之上,僅僅通過檢視日志檔案來分析我們的請求鍊路依然是一件相當麻煩的差事,是以我們還需要一些工具來幫助我們集中的收集、存儲和搜尋這些跟蹤資訊。引入基于日志的分析系統是一個不錯的選擇,比如:ELK平台,它可以輕松的幫助我們來收集和存儲這些跟蹤日志,同時在需要的時候我們也可以根據Trace ID來輕松地搜尋出對應請求鍊路相關的明細日志。

ELK平台主要有由ElasticSearch、Logstash和Kiabana三個開源免費工具組成:

  • Elasticsearch是個開源分布式搜尋引擎,它的特點有:分布式,零配置,自動發現,索引自動分片,索引副本機制,restful風格接口,多資料源,自動搜尋負載等。
  • Logstash是一個完全開源的工具,他可以對你的日志進行收集、過濾,并将其存儲供以後使用。
  • Kibana 也是一個開源和免費的工具,它Kibana可以為 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以幫助您彙總、分析和搜尋重要資料日志。

Spring Cloud Sleuth在與ELK平台整合使用時,實際上我們隻要實作與負責日志收集的Logstash完成資料對接即可,是以我們需要為Logstash準備json格式的日志輸出。由于Spring Boot應用預設使用了logback來記錄日志,而Logstash自身也有對logback日志工具的支援工具,是以我們可以直接通過在logback的配置中增加對logstash的appender,就能非常友善的将日志轉換成以json的格式存儲和輸出了。

下面我們來詳細介紹一下在快速入門示例的基礎上,如何實作面向Logstash的日志輸出配置:

  • pom.xml

    依賴中引入

    logstash-logback-encoder

    依賴,具體如下:
<dependency>
  <groupId>net.logstash.logback</groupId>
  <artifactId>logstash-logback-encoder</artifactId>
  <version>4.6</version>
</dependency>      
  • 在工程

    /resource

    目錄下建立

    bootstrap.properties

    配置檔案,将

    spring.application.name=trace-1

    配置移動到該檔案中去。由于

    logback-spring.xml

    的加載在

    application.properties

    之前,是以之前的配置

    logback-spring.xml

    無法擷取到

    spring.application.name

    屬性,是以這裡将該屬性移動到最先加載的

    bootstrap.properties

    配置檔案中。
  • /resource

    目錄下建立logback配置檔案

    logback-spring.xml

    ,具體内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
      
    <springProperty scope="context" name="springAppName" source="spring.application.name"/>
    <!-- 日志在工程中的輸出位置 -->
    <property name="LOG_FILE" value="${BUILD_FOLDER:-build}/${springAppName}"/>
    <!-- 控制台的日志輸出樣式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr([${springAppName:-},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]){yellow} %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>

    <!-- 控制台Appender -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">           
            <level>INFO</level>
        </filter>
         <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
       </encoder>
    </appender>

    <!-- 為logstash輸出的json格式的Appender -->
    <appender name="logstash" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_FILE}.json</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE}.json.%d{yyyy-MM-dd}.gz</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <timestamp>
                    <timeZone>UTC</timeZone>
                </timestamp>
                <pattern>
                    <pattern>
                        {
                          "severity": "%level",
                          "service": "${springAppName:-}",
                          "trace": "%X{X-B3-TraceId:-}",
                          "span": "%X{X-B3-SpanId:-}",
                          "exportable": "%X{X-Span-Export:-}",
                          "pid": "${PID:-}",
                          "thread": "%thread",
                          "class": "%logger{40}",
                          "rest": "%message"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>
    </appender>
      
    <root level="INFO">
        <appender-ref ref="console"/>
        <appender-ref ref="logstash"/>
    </root>
</configuration>      

對logstash支援主要通過名為

logstash

的appender實作,内容并不複雜,主要是對日志資訊的格式化處理,上面為了友善調試檢視我們先将json日志輸出到檔案中。

完成上面的改造之後,我們再将快速入門的示例運作起來,并發起對

trace-1

的接口通路。此時我們可以在

trace-1

trace-2

的工程目錄下發現有一個

build

目錄,下面分别建立了以各自應用名稱命名的json檔案,該檔案就是在

logback-spring.xml

中配置的名為

logstash

的appender輸出的日志檔案,其中記錄了類似下面格式的json日志:

{"@timestamp":"2016-12-04T06:57:58.970+00:00","severity":"INFO","service":"trace-1","trace":"589ee5f7b860132f","span":"a9e891273affb7fc","exportable":"false","pid":"19756","thread":"http-nio-9101-exec-1","class":"c.d.TraceApplication$$EnhancerBySpringCGLIB$$a9604da6","rest":"===<call trace-1>==="}
{"@timestamp":"2016-12-04T06:57:59.061+00:00","severity":"INFO","service":"trace-1","trace":"589ee5f7b860132f","span":"2df8511ddf3d79a2","exportable":"false","pid":"19756","thread":"http-nio-9101-exec-1","class":"o.s.c.a.AnnotationConfigApplicationContext","rest":"Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@64951f38: startup date [Sun Dec 04 14:57:59 CST 2016]; parent: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@4b8c8f15"}
      

我們除了可以通過上面的方式生成json檔案之外,也可以使用

LogstashTcpSocketAppender

将日志内容直接通過Tcp Socket輸出到logstash服務端,比如:

<appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
  <destination>127.0.0.1:9250</destination>
  ...
</appender>      

本文完整示例:

讀者可以根據喜好選擇下面的兩個倉庫中檢視

trace-1

trace-2

兩個項目:

如果您對這些感興趣,歡迎star、follow、收藏、轉發給予支援!

本文内容部分節選自我的《Spring Cloud微服務實戰》,但對依賴的Spring Boot和Spring Cloud版本做了更新。