[toc]
一、基礎(chǔ)知識
1.1 為什么要使用分布式追蹤
微服務(wù)在架構(gòu)、敏捷開發(fā)、快速部署、去中心化、組件化等方面帶來便捷和優(yōu)勢。但是隨著微服務(wù)框架落地,分布式架構(gòu)和微服務(wù)架構(gòu)給系統(tǒng)性能分析和問題定位帶來非常大挑戰(zhàn)。因此能夠匯聚業(yè)務(wù)系統(tǒng)各處理環(huán)節(jié)的實時數(shù)據(jù),實現(xiàn)對應(yīng)用的全鏈路性能監(jiān)測的微服務(wù)監(jiān)控工具必不可少。
基于微服務(wù)體系之下構(gòu)建的業(yè)務(wù)系統(tǒng)存在的問題基本上分為三類:
- 故障定位難,一個簡單操作,其背后可能是由多個微服務(wù)共同完成的,這些微服務(wù)也由不同的團隊去負(fù)責(zé)。一旦出現(xiàn)問題,最壞情況下我們也許需要這十幾個團隊一起來解決問題。
- 鏈路梳理難,應(yīng)用沒有形成應(yīng)用拓?fù)?,不知道自己的服?wù)下游會影響其他哪些人。
- 資源浪費多,容量預(yù)估難。對于一些服務(wù),其消耗的cpm和memory可能連10%不到,遠(yuǎn)遠(yuǎn)沒有充分利用物理機。這其實和容量預(yù)估關(guān)聯(lián),過大或者過小估算峰值的機器容量,都是浪費。
APM主要的目的就是解決上面所說的問題,主要的手段是通過收集、存儲、分析、分布式系統(tǒng)中的調(diào)用事件數(shù)據(jù),協(xié)助開發(fā)運營人員進行故障診斷、容量預(yù)估、性能瓶頸定位以及調(diào)用鏈路梳理。
市面上APM產(chǎn)品眾多,選擇合適的APM產(chǎn)品主要從以下需求進行對比選型:
- 代碼的侵入性
- 探針的性能消耗
- 全面的調(diào)用鏈路數(shù)據(jù)分析
- 可擴展性
本文選擇skywalking作為微服務(wù)APM框架,skywalking相當(dāng)于其他APM產(chǎn)品在性能消耗方面更有優(yōu)勢,且代碼零入侵、可擴展性強并且其支持動態(tài)配置可以很好的和微服務(wù)框架進行集成。
1.2 分布式追蹤實現(xiàn)原理
1.2.1 術(shù)語
- Trace 一次分布式調(diào)用的鏈路
- Span 一次本地或者遠(yuǎn)程方法的調(diào)用
- Annotation 附加在 Span 上的日志信息
- Sampling 采樣率(客戶端按照比例將埋點信息提交給服務(wù)端
1.2.2 原理
分布式追蹤理論主要包括以下兩個關(guān)鍵點:
- 為了實現(xiàn)分布式追蹤,當(dāng)請求發(fā)送到分布式系統(tǒng)的入口端點時,分布式服務(wù)追蹤框架為該請求創(chuàng)建一個唯一的追蹤標(biāo)識,同時在分布式系統(tǒng)內(nèi)部流轉(zhuǎn)時,追蹤框架始終保持傳遞該唯一標(biāo)識,直到返回給請求方為止,這個唯一標(biāo)識就是Trace ID,而一次完整的調(diào)用鏈路就是Trace。通過Trace ID我們就能將請求鏈路進行關(guān)聯(lián)起來。
- 為了統(tǒng)計各單位的時間延遲,當(dāng)我們請求達到各個服務(wù)組件、或者處理邏輯到達某個狀態(tài)時,分布式服務(wù)追蹤框架創(chuàng)建一個唯一標(biāo)識來標(biāo)記它的開始、具體過程以及結(jié)束,該標(biāo)識就是Span。
1.3 OpenTracing?
1.3.1 OpenTracing簡介
OpenTracing:通過為流行的平臺提供一致的,富有表現(xiàn)力的,==供應(yīng)商中立==的API,OpenTracing使開發(fā)人員可以輕松地通過O(1)配置更改添加(或切換)跟蹤實現(xiàn)。OpenTracing還為OSS檢測和特定于平臺的跟蹤幫助程序庫提供了通用語言。OpenTracing盡力讓監(jiān)控一個分布式調(diào)用過程簡單化。通過OpenTracing可以快速配置一個監(jiān)控系統(tǒng)。

1.3.2 OpenTracing規(guī)范
OpenTracing 僅定義了一套開放規(guī)范和API用于在應(yīng)用開發(fā)中設(shè)置埋點,實際的抓取存儲日志、分析統(tǒng)計展現(xiàn)都由兼容 OpenTracing 的監(jiān)控產(chǎn)品完成。
數(shù)據(jù)模型
OpenTracing 設(shè)計圍繞兩個核心概念開展:
- Trace:一條完整的調(diào)用鏈,通過傳遞全局唯一的TraceID追蹤整個調(diào)用過程。
- Span: 記錄調(diào)用鏈某個具體服務(wù)或方法的執(zhí)行情況,通常包含調(diào)用開始時間、持續(xù)時間、調(diào)用方、被調(diào)用方、關(guān)鍵日志、異常、操作上下文等。
下圖為SpringCloud微服務(wù)一次完整調(diào)用鏈,Span的因果關(guān)系圖:

操作間引用
從上圖數(shù)據(jù)模型間關(guān)系可以看出,一個操作可能引用0個或多個具有觸發(fā)關(guān)系的操作上下文。OpenTracing定義了兩種類型的引用:
- ChildOf: 一個操作可以是另一個操作的子操作,在一個ChildOf引用中,父操作在一定程度上依賴于子操作。一下場景符合ChildOf關(guān)系:
- 一次Feign調(diào)用的服務(wù)端操作是客戶端操作的子操作
- 一個表示SQL插入的操作是一個ORM save方法的子操作
- 一個父操作可以有多個同步進行(可能是分布式)的父操作,該父操作會在執(zhí)行期限內(nèi)聚合所有子操作的結(jié)果返回給用戶
- FollowsFrom: 有些父操作不以任何方式依賴它們的子操作的結(jié)果。在這種場景下,子操作僅僅由父操作觸發(fā)。
二、skywalking
2.1 skywalking簡介
skywalking是分布式系統(tǒng)的應(yīng)用性能檢測工具,提供分布式追蹤、服務(wù)網(wǎng)格遙測分析、度量聚合和可視化一體化解決方案。
2.1.1 skywalking功能
skywalking提供如下核心功能:
- 多種監(jiān)控手?jǐn)?,支持語言探針和service mesh獲得監(jiān)控數(shù)據(jù)
- 支持多種語言探針,包括Java、NET、Python和NodeJS等
- 輕量高效,無需大數(shù)據(jù)平臺和服務(wù)支持
- 模塊化。UI、存儲、集群管理有多種機制可選
- 優(yōu)秀的可視化解決方案。
2.1.2 skywalking架構(gòu)

skywalking整體架構(gòu)可以分為四部分:
- Skywalking Agent:語言探針,通過無侵入的方式收集鏈路數(shù)據(jù),發(fā)送給OAP Server
- Skywalking OAP: 鏈路數(shù)據(jù)收集器,負(fù)責(zé)接收 Agent 發(fā)送的 數(shù)據(jù)信息,然后進行分析(Analysis Core) ,存儲到外部存儲器( Storage ),最終提供查詢( Query )功能。
- Storage :鏈路追蹤數(shù)據(jù)存儲。目前支持 ES、MySQL、Sharding Sphere、TiDB、H2 等多種存儲器。
- Skywalking UI:可視化平臺,提供一體化解決方案。
2.2 skywalking 安裝部署
我們使用apache-skywalking-apm-es7-8.7.0 + ES7.x + nacos 配置中心進行skywalking環(huán)境搭建。ES7.x + nacos安裝搭建這里不在贅述。
2.2.1 下載安裝
step1 下載skywalking APM包
# 創(chuàng)建目錄
$ mkdir -p /Users/skywalking
$ cd /Users/skywalking
# 下載
$ wget https://www.apache.org/dyn/closer.cgi/skywalking/java-agent/8.8.0/apache-skywalking-java-agent-8.8.0.tgz
step2 解壓
# 解壓
$ tar -zxvf apache-skywalking-apm-es7-8.7.0.tar.gz
$ cd apache-skywalking-apm-bin-es7
[root@localhost apache-skywalking-apm-bin-es7]# ll
drwxrwxr-x. 9 1001 1002 176 7月 30 21:15 agent # SkyWalking Agent語言探針插件
drwxr-xr-x. 2 root root 241 12月 24 17:13 bin # OAP Server和Web UI執(zhí)行腳本
drwxr-xr-x. 11 root root 4096 12月 29 16:51 config # OAP Server配置文件
drwxr-xr-x. 2 root root 68 12月 24 17:13 config-examples
-rwxrwxr-x. 1 1001 1002 31480 7月 30 20:32 LICENSE
drwxrwxr-x. 3 1001 1002 4096 12月 24 17:13 licenses
drwxr-xr-x. 2 root root 126 1月 12 11:40 logs # 日志目錄
-rwxrwxr-x. 1 1001 1002 32519 7月 30 20:32 NOTICE
drwxrwxr-x. 2 1001 1002 12288 7月 30 21:34 oap-libs # SkyWalking OAP Server
-rw-rw-r--. 1 1001 1002 1951 7月 30 20:32 README.txt
drwxr-xr-x. 3 root root 30 12月 24 17:13 tools
drwxr-xr-x. 2 root root 53 12月 24 17:31 webapp # SkyWalking UI
核心配置
OAP Server 核心配置
OAP Server 配置采用模塊化配置,從集群模式、核心Collector、存儲器、Collector Receiver、動態(tài)配置等提供了多種選擇:
-
集群模式(cluster)
Collector支持集群部署,zookeeper、kubernetes(如果你的應(yīng)用是部署在容器中的)、consul(GO語言開發(fā)的服務(wù)發(fā)現(xiàn)工具)、nacos是sw可選的集群管理工具,結(jié)合大家具體的部署方式進行選擇。
cluster:
selector: ${SW_CLUSTER:standalone}
standalone:
# Please check your ZooKeeper is 3.5+, However, it is also compatible with ZooKeeper 3.4.x. Replace the ZooKeeper 3.5+
# library the oap-libs folder with your ZooKeeper 3.4.x library.
zookeeper:
nameSpace: ${SW_NAMESPACE:""}
hostPort: ${SW_CLUSTER_ZK_HOST_PORT:localhost:2181}
# Retry Policy
baseSleepTimeMs: ${SW_CLUSTER_ZK_SLEEP_TIME:1000} # initial amount of time to wait between retries
maxRetries: ${SW_CLUSTER_ZK_MAX_RETRIES:3} # max number of times to retry
# Enable ACL
enableACL: ${SW_ZK_ENABLE_ACL:false} # disable ACL in default
schema: ${SW_ZK_SCHEMA:digest} # only support digest schema
expression: ${SW_ZK_EXPRESSION:skywalking:skywalking}
kubernetes:
namespace: ${SW_CLUSTER_K8S_NAMESPACE:default}
labelSelector: ${SW_CLUSTER_K8S_LABEL:app=collector,release=skywalking}
uidEnvName: ${SW_CLUSTER_K8S_UID:SKYWALKING_COLLECTOR_UID}
consul:
serviceName: ${SW_SERVICE_NAME:"SkyWalking_OAP_Cluster"}
# Consul cluster nodes, example: 10.0.0.1:8500,10.0.0.2:8500,10.0.0.3:8500
hostPort: ${SW_CLUSTER_CONSUL_HOST_PORT:localhost:8500}
aclToken: ${SW_CLUSTER_CONSUL_ACLTOKEN:""}
etcd:
# etcd cluster nodes, example: 10.0.0.1:2379,10.0.0.2:2379,10.0.0.3:2379
endpoints: ${SW_CLUSTER_ETCD_ENDPOINTS:localhost:2379}
namespace: ${SW_CLUSTER_ETCD_NAMESPACE:/skywalking}
serviceName: ${SW_SCLUSTER_ETCD_ERVICE_NAME:"SkyWalking_OAP_Cluster"}
authentication: ${SW_CLUSTER_ETCD_AUTHENTICATION:false}
user: ${SW_SCLUSTER_ETCD_USER:}
password: ${SW_SCLUSTER_ETCD_PASSWORD:}
nacos:
serviceName: ${SW_SERVICE_NAME:"SkyWalking_OAP_Cluster"}
hostPort: ${SW_CLUSTER_NACOS_HOST_PORT:localhost:8848}
# Nacos Configuration namespace
namespace: ${SW_CLUSTER_NACOS_NAMESPACE:"public"}
# Nacos auth username
username: ${SW_CLUSTER_NACOS_USERNAME:""}
password: ${SW_CLUSTER_NACOS_PASSWORD:""}
# Nacos auth accessKey
accessKey: ${SW_CLUSTER_NACOS_ACCESSKEY:""}
secretKey: ${SW_CLUSTER_NACOS_SECRETKEY:""}
-
Collector Core
Collector 核心配置主要提供通信、采樣率、TTL配置:- 通信方式:collector提供了gRPC和HTTP兩種通信方式,UI使用rest http通信,agent在大多數(shù)場景下使用grpc方式通信,在語言不支持的情況下會使用http通信。關(guān)于綁定IP和端口需要注意的一點是,通過綁定IP,agent和collector必須配置對應(yīng)ip才可以正常通信。
- downsampling: 采樣匯總統(tǒng)計維度,會分別按照分鐘、【小時、天、月】(可選)來統(tǒng)計各項指標(biāo)數(shù)據(jù)。
- TTL:通過設(shè)置TTL相關(guān)配置項可以對數(shù)據(jù)進行自動清理
詳細(xì)配置大家可以去Skywalking官網(wǎng)下載介質(zhì)包進行了解,這里就不貼出來了。
-
存儲器
SW支持ES、ES7、H2、Mysql、postgrsql等,項目可以根據(jù)實際情況選擇合適存儲器。
storage:
selector: elasticsearch7
elasticsearch7:
nameSpace: ${SW_NAMESPACE:"myyshop_monitor"}
clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:192.168.128.34:9200}
protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:"http"}
connectTimeout: ${SW_STORAGE_ES_CONNECT_TIMEOUT:500}
socketTimeout: ${SW_STORAGE_ES_SOCKET_TIMEOUT:30000}
// 省略許多
-
Collector Receiver
配置監(jiān)控的系統(tǒng)中接受指標(biāo)數(shù)據(jù)。支持上傳符合OpenTracing規(guī)范的自定義的監(jiān)控數(shù)據(jù)。 -
配置中心
按照Skywalking官網(wǎng)的客戶端搭建方式,基本采取配置agent.properties文件,或者通過java -D 帶參數(shù)方式(也可以直接使用環(huán)境變量進行配置),這些操作辦法都屬于靜態(tài)配置。如果在業(yè)務(wù)高峰期,可能需要調(diào)整采樣率 agent.sample_n_per_3_secs 的數(shù)值,只能通過重新啟動或者agent方式更新配置信息。
為了做到通過后臺,動態(tài)控制agent端的采樣率、鏈路跨度等配置信息,Skywalking提供了動態(tài)更新功能。Skywalking支持grpc、apollo、zookeeper、etcd、consul、k8s、nacos作為動態(tài)更新功能的配置中心。詳細(xì)配置大家可以去Skywalking官網(wǎng)下載介質(zhì)包進行了解
2.2.2 啟動 OAP Server 和 Web UI
項目采用 standalone模式 + ES7 + nacos配置中心搭建 OAP Server ,配置好application.yml后便可以啟動 OAP Server 和 UI。
$ cd apache-skywalking-apm-bin/bin
# 啟動
$ ./startup.sh
SkyWalking OAP started successfully!
SkyWalking Web Application started successfully!
訪問 http://xxxx:8081/ 即可看到如下頁面:

2.3 快速集成
skywalking支持傳統(tǒng)shell和docker模式集成
2.3.1 shell模式
skywalking agent軟件包明細(xì)
shell模式需要將agent軟件包拷貝到應(yīng)用部署服務(wù)器,這樣應(yīng)用才能夠使用skywalking agent。
drwxrwxr-x. 2 1001 1002 4096 12月 24 17:13 activations
drwxrwxr-x. 2 1001 1002 85 12月 24 17:13 bootstrap-plugins
drwxrwxr-x. 2 1001 1002 26 12月 24 17:13 config # 配置
drwxrwxr-x. 2 1001 1002 6 7月 30 20:35 logs # 日志
drwxrwxr-x. 2 1001 1002 4096 12月 24 17:13 optional-plugins # 可選插件,模式不會運行該目錄下插件,
如果需要運行,可把對應(yīng)插件拷貝到 /plugins 目錄
drwxrwxr-x. 2 1001 1002 45 12月 24 17:13 optional-reporter-plugins
drwxrwxr-x. 2 1001 1002 8192 12月 24 17:13 plugins # 插件
-rw-rw-r--. 1 1001 1002 19094336 7月 30 20:35 skywalking-agent.jar
如果應(yīng)用服務(wù)器只部署一個服務(wù),可以直接修改agent/config目錄下配置,正常情況不建議這樣做,而是通過設(shè)置系統(tǒng)變量(公共配置)或 啟動命名傳參(私有變量)方式。更多的變量詳情,可以在[/agent/config/agent.config查看。
java啟動腳本
# 通過系統(tǒng)變量方式設(shè)置 SkyWalking Agent 配置
export SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 # 配置 Collector 地址。
export SW_AGENT_SPAN_LIMIT=300# 配置鏈路的最大 Span 數(shù)量。默認(rèn)為 300 。
# 通過啟動shell命名傳遞SkyWalking Agent 配置
java -javaagent:D:apache-skywalking-apm-es7-8.8.0/apache-skywalking-apm-bin-es7/agentskywalking-agent.jar -Dskywalking.agent.service_name=當(dāng)前項目在skywalking顯示的名稱 -jar spring-demo-0.0.1-SNAPSHOT.jar
2.3.2 基于docker集成
基于docker集成skywalking agent,官方提供了docker鏡像代理,直接在構(gòu)建應(yīng)用服務(wù)鏡像時使用該鏡像代碼作為基礎(chǔ)鏡像構(gòu)建app。
FROM apache/skywalking-java-agent:8.5.0-jdk8
# ... build your java application

不過鏡像代理不是官方版本,只是為了方便提供,推薦的方式還是通過源代碼構(gòu)建鏡像。
通過源代碼方式構(gòu)建應(yīng)用鏡像
- 1. 拷貝/agent 軟件包到應(yīng)用目錄
軟件包和pom.xml在一級目錄,如下圖:

- 2. 修改軟件包/agent/config/agent.config
這里僅貼出項目實際配置的,更多詳情見軟件包/agent/config/agent.config
agent.service_name=ds-user-center-server
# Logging file_name
logging.file_name=${SW_LOGGING_FILE_NAME:skywalking-api.log}
# Logging level
logging.level=${SW_LOGGING_LEVEL:WARN}
# 歷史日志文件數(shù)量,這里配置保留一個防止日志文件過多,默認(rèn)-1 不限制
logging.max_history_files=${SW_LOGGING_MAX_HISTORY_FILES:1}
ps:軟件包/agent/config/agent.config配置可以不做任何配置,而是在dockerfile腳本中配置參數(shù)。
-
3. 添加DockerFile
這里僅貼出我自己項目的腳本,實際腳本根據(jù)加自己項目調(diào)整
FROM java:8
ADD /app.jar //
# copy arthas
# COPY --from=hengyunabc/arthas:latest /opt/arthas /opt/arthas
COPY agent /usr/local/agent
# 這里可以傳遞skywalking-agent 配置
ENTRYPOINT [ "sh", "-c", "java -javaagent:/usr/local/agent/skywalking-agent.jar -Dskywalking.agent.service_name=yourappname -Dskywalking.collector.backend_service=xx.xx.xx.xx:11800 -Dspring.profiles.active=dev -jar /app.jar" ]
- 4. 修改pom.xml打包程序
我們使用的是spotify maven docker插件
<build>
<finalName>app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.3</version>
<!--加入下面兩項配置-->
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.1.0</version>
<executions>
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<imageName>${project.artifactId}:${project.version}</imageName>
<!--鏡像構(gòu)建成果推送進行到指定存儲庫-->
<dockerHost>http://192.168.128.20:2375</dockerHost>
<imageTags>
<imageTag>latest</imageTag>
</imageTags>
<!--dockerDirectory 參數(shù)表示使用Dockerfile構(gòu)建鏡像,${basedir}表示Dockerfile與pom.xml同級目錄 -->
<dockerDirectory>${basedir}</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</build>
通過上述步驟構(gòu)建鏡像并運行服務(wù)便可以集成skywalking-agent


三、 Spring Cloud 微服務(wù)整合skywalking及應(yīng)用實戰(zhàn)
通過上述步驟我們基本能夠?qū)嶋HJava應(yīng)用集成skywalking-agent,下面我們將開始Spring Cloud 微服務(wù)整合skywalking-agent、微服務(wù)接入skywalking日志、動態(tài)配置、基于skywalking-ui托盤圖進行微服務(wù)調(diào)用關(guān)系分析、調(diào)用鏈分析、skywalking-agent探針優(yōu)化、開啟skywalking實戰(zhàn)之旅。
3.1 微服務(wù)整合skywalking-agent
3.1.1 SpringCoud Gateway 集成skywalking-agent
微服務(wù)整合skywalking-agent按照上述Docker集成skywalking-agent步驟便能完成大多數(shù)應(yīng)用的集成,只有SpringCoud Gateway需要進行額外的處理,需要將/optional-plugins可選插件包下apm-spring-cloud-gateway-xx.jar拷貝到/plugins插件包目錄下,具體版本根據(jù)自己微服務(wù)版本而定,本項目使用2.2.4版本,選擇apm-spring-cloud-gateway-2.1.x-plugin-8.7.0.jar即可,不要把所有版本插件全部移動到/plugins

如果SpringCoud Gateway不做上述操作,那么Gateway服務(wù)就和普通服務(wù)一樣,起點就是User,而看不到Gateway作為網(wǎng)關(guān)路由的效果。下圖為添加了apm-gateway插件后微服務(wù)全局拓?fù)鋱D,可以看到前端訪問微服務(wù)均是通過網(wǎng)關(guān)進行轉(zhuǎn)發(fā):

3.1.2 SpringCoud 日志接入skywalking
完成skywalking-agent探針集成后,我們便可以對微服務(wù)進行調(diào)用鏈和拓?fù)鋱D進行分析,但是我們有時還需要通過elk或者其他日志工具對調(diào)用鏈日志進行分析,skywalking提供無侵入式的trace ID、trace context、gRPC reporter,支持log4j、log4j2、logback。我們的項目使用logback,因此這里只介紹logback模式-Print trace ID in your logs。更多可以查看官方文檔
- step1 引入skywalking日志組件依賴
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>8.7.0</version>
</dependency>
- step2 修改應(yīng)用日志配置
配置layout,指定class為skywalking TraceIdPatternLogbackLayout,并且在模板中增加tid的錨點。
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<springProperty scope="context" name="springAppName" source="spring.application.name"/>
<springProperty scope="context" name="profilesActive" source="spring.profiles.active"/>
<appender name="consoleApp" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<!--SW layout 配置 -->
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<Pattern> %date{yyyy-MM-dd HH:mm:ss.SSS} [%tid] %-5level[%thread]%logger{56}.%method:%L -%msg%n</Pattern>
</layout>
</encoder>
</appender>
<appender name="fileInfoApp" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>ACCEPT</onMismatch>
</filter>
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<Pattern> %date{yyyy-MM-dd HH:mm:ss.SSS} [%tid] %-5level[%thread]%logger{56}.%method:%L -%msg%n</Pattern>
</layout>
<!-- 滾動策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 路徑 -->
<fileNamePattern>logs/${springAppName}.info.%d.log</fileNamePattern>
</rollingPolicy>
</appender>
<appender name="fileErrorApp" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<Pattern> %date{yyyy-MM-dd HH:mm:ss.SSS} [%tid] %-5level[%thread]%logger{56}.%method:%L -%msg%n</Pattern>
</layout>
<!-- 設(shè)置滾動策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 路徑 -->
<fileNamePattern>logs/${springAppName}.err.%d.log</fileNamePattern>
<!-- 控制保留的歸檔文件的最大數(shù)量,超出數(shù)量就刪除舊文件,假設(shè)設(shè)置每個月滾動,
且<maxHistory> 是1,則只保存最近1個月的文件,刪除之前的舊文件 -->
<MaxHistory>1</MaxHistory>
</rollingPolicy>
</appender>
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="consoleApp"/>
<appender-ref ref="fileInfoApp"/>
<appender-ref ref="fileErrorApp"/>
</root>
</springProfile>
<springProfile name="test">
<root level="INFO">
<appender-ref ref="consoleApp"/>
</root>
</springProfile>
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="consoleApp"/>
</root>
</springProfile>
</configuration>
修改完配置,啟動應(yīng)用,可以看到如下效果:當(dāng)應(yīng)用激活了SW agent,外部請求日志打印會攜帶tid,而內(nèi)部請求則打印N/A。
2022-02-07 00:00:00.031 [TID:N/A] INFO [xxl-rpc, EmbedServer bizThreadPool-71423529]com.xxl.job.core.executor.XxlJobExecutor.registJobThread:159 ->>>>>>>>>>> xxl-job regist JobThread success, jobId:144, handler:com.xxl.job.core.handler.impl.MethodJobHandler@c8567c9[class com.xxxx.saas.dsuserserver.job.VipActivePackagesExpiredStateJob$$EnhancerBySpringCGLIB$$72fe4b40#vipActivePackagesExpiredState]
2022-02-07 00:00:00.036 [TID:3344006a6a504161b199c17d5778a36d.3192.16441632000360001] INFO [Thread-1559]c.m.s.dsuserserver.job.VipActivePackagesExpiredStateJob.vipActivePackagesExpiredState:42 -================vipActivePackagesExpiredState start:1644163200036================
2022-02-07 00:00:00.112 [TID:3344006a6a504161b199c17d5778a36d.3192.16441632000360001] INFO [Thread-1559]c.m.s.dsuserserver.job.VipActivePackagesExpiredStateJob.vipActivePackagesExpiredState:46 -================vipActivePackagesExpiredState end:1644163200112================
2022-02-07 00:01:33.130 [TID:N/A] INFO [Thread-1559]com.xxl.job.core.thread.JobThread.run:212 ->>>>>>>>>>> xxl-job JobThread stoped, hashCode:Thread[Thread-1559,10,main]
2022-02-07 10:34:52.757 [TID:d9bb5cb491e34b5ca945c591a326ed8b.69.16442012921820337] INFO [http-nio-7004-exec-8]c.d.saas.common.feign.interceptor.MyOkhttpInterceptor.intercept:23 -OkHttp Feign 發(fā)送請求 [http://192.168.128.20:7001/oauth/token?password=a123456&grant_type=password&userType=1&username=sujingjun%40dhgate.com] on [null]
使用日志追蹤打印的tid,在SW UI 追蹤模塊可以追蹤到對應(yīng)的請求鏈路,如下圖:

源碼分析
- TraceIdPatternLogbackLayout:TraceIdPatternLogbackLayout繼承ch.qos.logback.classic.PatternLayout,添加了LogbackPatternConverter和LogbackSkyWalkingContextPatternConverter轉(zhuǎn)換器,支持%tid占位符。
ublic class TraceIdPatternLogbackLayout extends PatternLayout {
public TraceIdPatternLogbackLayout() {
}
static {
defaultConverterMap.put("tid", LogbackPatternConverter.class.getName());
defaultConverterMap.put("sw_ctx", LogbackSkyWalkingContextPatternConverter.class.getName());
}
}
// 這里實際僅是一個入口程序,實際日志打印實現(xiàn)是agent-activation包下定義的切面和攔截器處理
public class LogbackPatternConverter extends ClassicConverter {
public LogbackPatternConverter() {
}
public String convert(ILoggingEvent iLoggingEvent) {
return "TID: N/A";
}
}
- LogbackPatternConverterActivation: LogbackPatternConverterActivation定義了%tid占位符轉(zhuǎn)換器方法切面 和 攔截器。
public class LogbackPatternConverterActivation extends ClassInstanceMethodsEnhancePluginDefine {
public static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.toolkit.activation.log.logback.v1.x.PrintTraceIdInterceptor";
public static final String ENHANCE_CLASS = "org.apache.skywalking.apm.toolkit.log.logback.v1.x.LogbackPatternConverter";
public static final String ENHANCE_METHOD = "convert";
/**
* @return the target class, which needs active.
*/
@Override
protected ClassMatch enhanceClass() {
return byName(ENHANCE_CLASS);
}
/**
* @return null, no need to intercept constructor of enhance class.
*/
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return null;
}
/**
* @return the collection of {@link StaticMethodsInterceptPoint}, represent the intercepted methods and their
* interceptors.
*/
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
return new InstanceMethodsInterceptPoint[] {
new InstanceMethodsInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named(ENHANCE_METHOD).and(takesArgumentWithType(0, "ch.qos.logback.classic.spi.ILoggingEvent"));
}
@Override
public String getMethodsInterceptor() {
return INTERCEPT_CLASS;
}
@Override
public boolean isOverrideArgs() {
return false;
}
}
};
}
}
- PrintTraceIdInterceptor: PrintTraceIdInterceptor是%tid占位符轉(zhuǎn)換的實際處理攔截器,*.afterMethod() 方法調(diào)用 ContextManager#getGlobalTraceId() 方法,使用全局鏈路追蹤編號。
public class PrintTraceIdInterceptor implements InstanceMethodsAroundInterceptor {
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
}
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
Object ret) throws Throwable {
if (!ContextManager.isActive()) {
if (allArguments[0] instanceof EnhancedInstance) {
SkyWalkingContext skyWalkingContext = (SkyWalkingContext) ((EnhancedInstance) allArguments[0]).getSkyWalkingDynamicField();
if (skyWalkingContext != null) {
return "TID:" + skyWalkingContext.getTraceId();
}
}
}
return "TID:" + ContextManager.getGlobalTraceId();
}
@Override
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
Class<?>[] argumentsTypes, Throwable t) {
}
}
更多源碼詳情參考官方https://github.com/apache/skywalking
3.1.3 忽略追蹤某項節(jié)點url
SW支持忽略特點節(jié)點url,要支持節(jié)點忽略功能需要將apm-trace-ignore-plugin-8.7.0.jar插件包拷貝到../plugins目錄下,同時需要在../config目錄下增加配置文件apm-trace-ignore-plugin.config,并加入trace.ignore_path=${SW_AGENT_TRACE_IGNORE_PATH:/api-docs/**} 為例,來忽略請求url的追蹤,示例如下:
trace.ignore_path=/api-docs/**,/actuator/**
3.2 客戶端動態(tài)配置
按照Skywalking官網(wǎng)的客戶端搭建方式,基本采取配置文件,或者通過java -D 帶參數(shù)方式(也可以直接使用環(huán)境變量進行配置),這些操作辦法都屬于靜態(tài)配置。如果在生產(chǎn)環(huán)境需要調(diào)整采樣率、忽略url等,只能通過重新啟動agent方式更新配置信息。
Skywalking官方提供了動態(tài)更新功能,可以結(jié)合配置中心實現(xiàn)后臺動態(tài)更新agent采樣率、忽略url、調(diào)用鏈深度等配置。
3.2.1 動態(tài)配置說明
SW 支持以下動態(tài)配置:
| 配置key | 參數(shù)描述 | 參數(shù)示例 |
|---|---|---|
| agent-analyzer.default.slowDBAccessThreshold | 慢sql的閾值,覆蓋application.yml中receiver-trace/default/slowDBAccessThreshold | default:200,mongodb:50 |
| agent-analyzer.default.uninstrumentedGateways | 網(wǎng)關(guān)配置 | 參數(shù)參考gateways.yml
|
| alarm.default.alarm-settings | 告警配置 | 參考alarm-settings.yml
|
| core.default.endpoint-name-grouping | 端點分組配置 | 參考service-apdex-threshold.yml
|
| core.default.log4j-xml | log4j配置 | 參考log4j2.xml
|
| configuration-discovery.default.agentConfigurations | ConfigurationDiscovery 配置 | 參考configuration-discovery.md
|
更多動態(tài)配置說明參考Dynamic Configuration
3.2.2 集成nacos實現(xiàn)SW動態(tài)配置
SW動態(tài)配置依賴上游配置服務(wù),因此默認(rèn)是關(guān)閉的,在2.2 核心配置已經(jīng)介紹過配置中心配置說明,這里不再贅述。我們直接從nacos集成步驟開始
- step1 nacos配置中心安裝部署
我們直接使用SpringCloud nacos配置中心作為SW配置中心不再額外部署nacos,nacos安裝部署可以參考官方https://nacos.io/zh-cn/docs/deployment.html
- 修改OAP Server
修改OAP Server application.yml 開啟動態(tài)配置并使用nacos作為配置中心
configuration:
selector: nacos #開啟開啟動態(tài)配置并使用nacos作為配置中心
nacos:
# Nacos Server Host
serverAddr: ${SW_CONFIG_NACOS_SERVER_ADDR:192.168.128.20}
# Nacos Server Port
port: ${SW_CONFIG_NACOS_SERVER_PORT:8848}
# Nacos Configuration Group
group: ${SW_CONFIG_NACOS_SERVER_GROUP:skywalking}
# Nacos Configuration namespace
namespace: ${SW_CONFIG_NACOS_SERVER_NAMESPACE:1c53b59e-9f3d-44a4-b2d7-5faaea25b3c6}
# Unit seconds, sync period. Default fetch every 60 seconds.
period: ${SW_CONFIG_NACOS_PERIOD:60}
# Nacos auth username
username: ${SW_CONFIG_NACOS_USERNAME:""}
password: ${SW_CONFIG_NACOS_PASSWORD:""}
# Nacos auth accessKey
accessKey: ${SW_CONFIG_NACOS_ACCESSKEY:""}
secretKey: ${SW_CONFIG_NACOS_SECRETKEY:""}
nacos動態(tài)配置結(jié)果如下:


3.2.3 ConfigurationDiscovery 配置舉例說明
ConfigurationDiscovery 配置了服務(wù)發(fā)現(xiàn)配置,包含采樣率、不記錄鏈路信息url后綴、不記錄鏈路信息url、鏈路最大跨度。ConfigurationDiscovery 配置示例如下:
configurations:
ds-user-center-server:
agent.trace.ignore_path: Mysql/**,Lettuce/**,/941124188381/*,Lettuce/EXISTS/**,MongoDB/**
uaa-center-server:
agent.trace.ignore_path: /api-docs/**,/actuator/**,Mysql/**,Lettuce/**,Lettuce/EXISTS/**
gateway-server:
agent.trace.ignore_path: /api-docs/**,/actuator/**
ConfigurationDiscovery 配置模板
ConfigurationDiscovery 按服務(wù)維度進行配置
configurations:
//service name
serviceA:
// Configurations of service A
// Key and Value are determined by the agent side.
// Check the agent setup doc for all available configurations.
key1: value1
key2: value2
...
serviceB:
...
ConfigurationDiscovery 配置說明
| key | 參數(shù)描述 | 參數(shù)示例 | 依賴插件 |
|---|---|---|---|
| agent.sample_n_per_3_secs | 每3秒采集鏈路數(shù)據(jù),-1表示全部收集 | -1 | - |
| agent.ignore_suffix | 不記錄鏈路信息url后綴(第一個節(jié)點開始算起),如果多個,采用逗號,隔開 | .txt,.log | - |
| agent.trace.ignore_path | 記錄調(diào)用鏈的路徑,多個可以采用逗號,隔開 | /your/path/1/,/your/path/2/ | apm-trace-ignore-plugin |
| agent.span_limit_per_segment | 鏈路最大跨度 | 300 | - |