跳到主要内容SpringBoot 集成 SkyWalking 实现分布式链路追踪 | 极客日志Javajava
SpringBoot 集成 SkyWalking 实现分布式链路追踪
SpringBoot 集成 SkyWalking 实现分布式链路追踪。内容包括 SkyWalking 简介及 JDK 版本要求,APM 下载与 Elasticsearch 存储配置,服务启动流程,探针部署方式(IDEA、命令行、Shell 脚本)。重点讲解 SpringBoot 中启用日志收集并配置 TraceId,以及通过 Agent 配置、Filter 或 AOP 实现请求入参和返回参数的查看方法。
CloudNative2 浏览 SpringBoot 集成 SkyWalking 实现分布式链路追踪
一、SkyWalking 是什么?
SkyWalking 是一个开源的、用于观测分布式系统(特别是微服务、云原生和容器化应用)的平台。它提供了对分布式系统的追踪、监控和诊断能力。
二、SkyWalking 与 JDK 版本的对应关系
SkyWalking 8.x 版本要求 Java 版本至少为 8(即 JDK 1.8),
SkyWalking 9.x 版本则要求 Java 版本至少为 11(即 JDK 11)
选择时需要注意 JDK 版本。
三、SkyWalking 下载
注意点:
7.x 及以下版本 APM 包里面包括 Agents,但是 8.x 的就发现被分开了,所以 8.x 及以上的就需要 Agents 也得下载。
目前该文选择下载 APM 8.9.1 和 Agents 8.9.0 后解压。
四、SkyWalking 数据存储
- h2(默认的存储方式,重启后数据会丢失)
- Elasticsearch(最常用的数据存储方式)
- MySQL
- TiDB
- …
相关文件 OAP 配置文件(config/application.yml),关于设置存储方式的部分如下:
storage:
selector: ${SW_STORAGE:h2}
elasticsearch:
namespace: ${SW_NAMESPACE:""}
clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:localhost:9200}
protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:"http"}
connectTimeout: ${SW_STORAGE_ES_CONNECT_TIMEOUT:500}
socketTimeout: ${SW_STORAGE_ES_SOCKET_TIMEOUT:30000}
numHttpClientThread: ${SW_STORAGE_ES_NUM_HTTP_CLIENT_THREAD:0}
user: ${SW_ES_USER:""}
password: ${SW_ES_PASSWORD:""}
trustStorePath: ${SW_STORAGE_ES_SSL_JKS_PATH:""}
trustStorePass: ${SW_STORAGE_ES_SSL_JKS_PASS:""}
secretsManagementFile: ${SW_ES_SECRETS_MANAGEMENT_FILE:""}
dayStep: ${SW_STORAGE_DAY_STEP:1}
indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:1}
indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:1}
superDatasetDayStep: ${SW_SUPERDATASET_STORAGE_DAY_STEP:-1}
superDatasetIndexShardsFactor: ${SW_STORAGE_ES_SUPER_DATASET_INDEX_SHARDS_FACTOR:5}
superDatasetIndexReplicasNumber: ${SW_STORAGE_ES_SUPER_DATASET_INDEX_REPLICAS_NUMBER:0}
bulkActions: ${SW_STORAGE_ES_BULK_ACTIONS:5000}
flushInterval: ${SW_STORAGE_ES_FLUSH_INTERVAL:15}
concurrentRequests: ${SW_STORAGE_ES_CONCURRENT_REQUESTS:2}
resultWindowMaxSize: ${SW_STORAGE_ES_QUERY_MAX_WINDOW_SIZE:10000}
metadataQueryMaxSize: ${SW_STORAGE_ES_QUERY_MAX_SIZE:5000}
segmentQueryMaxSize: ${SW_STORAGE_ES_QUERY_SEGMENT_SIZE:200}
profileTaskQueryMaxSize: ${SW_STORAGE_ES_QUERY_PROFILE_TASK_SIZE:200}
oapAnalyzer: ${SW_STORAGE_ES_OAP_ANALYZER:"{"analyzer":{"oap_analyzer":{"type":"stop"}}}"}
oapLogAnalyzer: ${SW_STORAGE_ES_OAP_LOG_ANALYZER:"{"analyzer":{"oap_log_analyzer":{"type":"standard"}}}"}
advanced: ${SW_STORAGE_ES_ADVANCED:""}
五、SkyWalking 的启动
进入 apache-skywalking-apm-8.9.1/apache-skywalking-apm-bin/in,双击运行 startup.bat(用管理员方式启动),会开启两个命令行窗口。
- (1)Skywalking-Collector:追踪信息收集器,通过 gRPC/Http 收集客户端的采集信息。Http 默认端口 12800,gRPC 默认端口 11800。(如需要修改,可前往 apache-skywalking-apm-bin/config/application.yml 进行修改)
- (2)Skywalking-Webapp:管理平台页面 默认端口 8080(如需要修改,可前往 apache-skywalking-apm-bin/webapp/webapp.yml 进行修改)
六、部署探针
前提:Agents 8.9.0 放入项目工程
建议将 Agent 文件放入项目目录,后续使用更为便利。
方式一:IDEA 部署探针
修改启动类的 VM options(虚拟机选项)配置。
-javaagent:D:/ideaObject/reactBootspringboot-full/src/main/skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=woqu-ndy -Dskywalking.collector.backend_service=127.0.0.1:11800
javaagent:表示 skywalking-agent.jar 的本地磁盘的路径(建议放到项目里面了)
-Dskywalking.agent.service_name:表示在 SkyWalking 上显示的服务名
-Dskywalking.collector.backend_service:表示 SkyWalking 的 collector 服务的 IP 及端口
注意:-Dskywalking.collector.backend_service 可以指定远程地址,但是 javaagent 必须绑定你本机物理路径的 skywalking-agent.jar
方式二:Java 命令行启动方式
java -javaagent:D:/ideaObject/reactBootspringboot-full/src/main/skywalking-agent/skywalking-agent.jar=-Dskywalking.agent.service_name=service-myapp,-Dskywalking.collector.backend_service=localhost:11800 -jar service-myapp.jar
方式三:编写 sh 脚本启动(Linux 环境)
#!/bin/bash
AGENT_PATH="/home/yourusername/Desktop/apache-skywalking-apm-6.6.0/apache-skywalking-apm-bin/agent"
JAR_PATH="/path/to/your/service-myapp.jar"
SERVICE_NAME="service-myapp"
COLLECTOR_BACKEND_SERVICE="localhost:11800"
JAVA_AGENT="-javaagent:$AGENT_PATH/skywalking-agent.jar -Dskywalking.agent.service_name=$SERVICE_NAME -Dskywalking.collector.backend_service=$COLLECTOR_BACKEND_SERVICE"
java $JAVA_AGENT -jar $JAR_PATH
七、SpringBoot 的启动
IDEA 部署探针方式启动
启动后,控制台日志输出开头出现了以下的记录,就表示连接上 SkyWalking 了。
我们再请求一下 Controller 的接口,就会发现捕获了相关接口记录(但是目前,还是没有接口具体详细的日志入参或者出参的)。
SkyWalking 进行日志配置
为 log 日志增加 SkyWalking 的 traceId(追踪 ID)。便于排查。
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>9.0.0</version>
</dependency>
接着在 resources 文件夹下创建 logback-spring.xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<property name="LOG_HOME" value="D:/logs/" ></property>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<appender name="STDOUT">
<encoder>
<layout>
<pattern>%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} [%X{tid}] %clr([%-10.10thread]){faint} %clr(%-5level) %clr(%-50.50logger{50}:%-3L){cyan} %clr(-){faint} %msg%n</pattern>
</layout>
</encoder>
</appender>
<appender name="FILE">
<rollingPolicy>
<FileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}/pro.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder>
<layout>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%-10.10thread] %-5level %-50.50logger{50}:%-3L - %msg%n</pattern>
</layout>
</encoder>
<triggeringPolicy>
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<appender name="grpc">
<encoder>
<layout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n</Pattern>
</layout>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" ></appender-ref>
<appender-ref ref="FILE" ></appender-ref>
<appender-ref ref="grpc"/>
</root>
</configuration>
请求接口就可以发现 TID 的输出(在这里是 882c67dc859046c398fbfc5725df9de0.109.17288962842340001)。
然后把它放到 追踪 栏目的追踪 id,可以查到记录。
然后把它放到 日志 栏目的追踪 id,可以查到记录。
实现入参、返参都可查看
方式一:通过 Agent 配置实现(存在局限性)
首先,你需要确认 SkyWalking 的 Agent 配置。
SkyWalking 的 Agent 在启动时会读取配置文件,通常是 agent.config。
默认情况下,请求参数的采集是关闭的,你需要手动开启。
具体步骤如下:
在你的 SkyWalking Agent 配置文件 agent.config 中,找到 plugin 部分,确保以下配置项设置为 true:
plugin.tomcat.collect_http_params=${SW_PLUGIN_TOMCAT_COLLECT_HTTP_PARAMS:true}
plugin.springmvc.collect_http_params=${SW_PLUGIN_SPRINGMVC_COLLECT_HTTP_PARAMS:true}
plugin.httpclient.collect_http_params=${SW_PLUGIN_HTTPCLIENT_COLLECT_HTTP_PARAMS:true}
缺点:以上设置,只能开启 GET 请求的入参采集,POST 无法获取到,该方式存在局限性。
方式二:通过 trace 和 Filter 实现
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>9.0.0</version>
</dependency>
二、使用 HttpFilter 和 ContentCachingRequestWrapper
知识小贴士:为什么不用 HttpServletRequest?
如果直接把 HttpServletRequest 中的 InputStream 读取后输出日志,会导致后续业务逻辑读取不到 InputStream 中的内容,因为流只能读取一次。
package com.example.springbootfull.quartztest.Filter;
import lombok.extern.slf4j.Slf4j;
import org.apache.skywalking.apm.toolkit.trace.ActiveSpan;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Component
public class ApmHttpInfo extends HttpFilter {
private static final Set<String> IGNORED_HEADERS;
static {
Set<String> ignoredHeaders = new HashSet<>();
ignoredHeaders.addAll(
java.util.Arrays.asList(
"Content-Type", "User-Agent", "Accept", "Cache-Control", "Postman-Token", "Host", "Accept-Encoding", "Connection", "Content-Length"
).stream()
.map(String::toUpperCase)
.collect(Collectors.toList())
);
IGNORED_HEADERS = ignoredHeaders;
}
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
try {
filterChain.doFilter(requestWrapper, responseWrapper);
} finally {
try {
StringBuilder sb = new StringBuilder("curl")
.append(" -X ").append(request.getMethod())
.append(" ").append(request.getRequestURL().toString());
if (StringUtils.hasLength(request.getQueryString())) {
sb.append("?").append(request.getQueryString());
}
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
if (!IGNORED_HEADERS.contains(headerName.toUpperCase())) {
sb.append(" -H '").append(headerName).append(": ").append(request.getHeader(headerName)).append("'");
}
}
String body = new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
if (StringUtils.hasLength(body)) {
sb.append(" -d '").append(body).append("'");
}
ActiveSpan.tag("input", sb.toString());
String responseBody = new String(responseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
ActiveSpan.tag("output", responseBody);
} catch (Exception e) {
log.warn("fail to build http log", e);
} finally {
responseWrapper.copyBodyToResponse();
}
}
}
}
方式三:通过 trace 和 AOP 去实现
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online