【JAVA】SpringBoot + skywalking 将接口的入参、出参、异常等信息上报到skywalking 链路追踪服务器上
1.下载SkyWalking APM
https://skywalking.apache.org/downloads/
jdk8 不支持 SkyWalking APM 9.3.0以上版本,所以这里我们下载 9.3.0版本
2.下载 Java Agent
https://skywalking.apache.org/downloads/
Java Agent 我们现在最新版本的 9.2.0
3.启动 SkyWalking APM 服务
3.1 windows环境
3.1.1 解压apache-skywalking-apm-9.3.0.tar.gz
3.1.2 进入解压后的bin目录,点击 startup.bat 启动SkyWalking APM服务
3.1.3 访问 http://127.0.0.1:8080/
如果访问出现空白页面,等待1分钟左右
3.2 linux环境
3.2.1 上传apache-skywalking-apm-9.3.0.tar.gz到服务器并解压
tar -zxvf apache-skywalking-apm-9.3.0.tar.gz
3.2.2 开放端口 8080,11800,12800
firewall-cmd --zone=public --add-port=8080/tcp --permanent
firewall-cmd --zone=public --add-port=11800/tcp --permanent
firewall-cmd --zone=public --add-port=12800/tcp --permanent
## 开放或者关闭,需要刷新防火墙规则
firewall-cmd --reload
3.2.3 启动SkyWalking APM,进入解压后的bin目录
sh startup.sh
3.2.4 访问 http://IP:8080/
如果访问出现空白页面,等待1分钟左右
4.SpringBoot项目中引入以下依赖
<!-- lombok ↓ -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- lombok ↑ -->
<!-- hutool ↓ -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.28</version>
</dependency>
<!-- hutool ↑ -->
<!-- aspectjrt 切面编程 ↓ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<!-- aspectjrt 切面编程 ↑ -->
<!-- skywalking-trace skyWalking监控相关 ↓ -->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>9.1.0</version>
</dependency>
<!-- skywalking-trace ↑ -->
<!-- skywalking-logback skyWalking中的traceId记录到logback日志 ↓ -->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>9.1.0</version>
</dependency>
<!-- skywalking-logback ↑ -->
4.1 resources文件夹下创建 logback-spring.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 读取application.yml中的日志配置 -->
<springProperty scope="context" name="LOG_PATH" source="logging.file.path"/>
<springProperty scope="context" name="LOG_NAME" source="logging.file.log-name"/>
<springProperty scope="context" name="MAX_HISTORY" source="logging.file.days"/>
<!-- 定义控制台输出格式 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{50} - %msg%n</pattern>
</layout>
</encoder>
</appender>
<!--skywalking grpc 日志收集-->
<appender name="SKYWALKING" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{50} -%msg%n</Pattern>
</layout>
</encoder>
</appender>
<!-- 定义每天生成一个日志文件,并归档生成压缩包 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>./${LOG_NAME}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天生成一个日志文件 -->
<fileNamePattern>${LOG_PATH}/${LOG_NAME}.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 最多保留指定天数的日志文件 -->
<maxHistory>${MAX_HISTORY}</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{50} -%msg%n</Pattern>
</layout>
</encoder>
</appender>
<!-- 日志级别设置 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="SKYWALKING"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
4.2 application.yml 文件中增加
# 日志配置
logging:
file:
# 日志生成的目录
path: ./log
# 日志文件名称
log-name: log
# 日志文件保留天数
days: 3
level:
ROOT: info
# com.xhs 包名称
com.xhs: info
4.2 定义 接口日志切面
package com.xhs.aspect;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.skywalking.apm.toolkit.trace.ActiveSpan;
import org.apache.skywalking.apm.toolkit.trace.TraceContext;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.servlet.ServletRequest;
/**
* @desc: 接口日志切面,目的是将接口的入参、出参、异常等信息上报到SkyWalking链路追踪服务器上,以便我们进行日志分析
* @projectName: spring-boot-skywalking
* @author: xhs
* @date: 2024-06-17 017 10:24
* @version: JDK 1.8
*/
@Slf4j
@Aspect
@Component
public class ControllerLogAspect {
public static final String BIG_STR_SUB_PATTERN = "(:s*\"[^,\":]{8192,}?\")";
/**
* API的切点,拦截所有controller
*/
@Pointcut("execution(public * com.xhs.controller.*.*(..))")
public void apiPointcut() {
}
/**
* 在方法执行前进行参数判断、在方法执行后记录日志
*
* @param joinPoint 入参
*/
@Around("apiPointcut()")
public Object executeFaceAspect(ProceedingJoinPoint joinPoint) throws Throwable {
Object returnObj = null;
Object[] parameters = joinPoint.getArgs();
try {
returnObj = joinPoint.proceed();
} catch (Exception e) {
log.error("SkyWalking aspect catch exception {}", TraceContext.traceId(), e);
ActiveSpan.tag("异常", ExceptionUtil.getRootCauseMessage(e));
throw e;
} finally {
if (parameters != null && parameters.length > 0) {
for (int i = 0; i < parameters.length; i++) {
try {
Object nowObj = parameters[i];
if (nowObj instanceof ServletRequest) {
continue;
}
ActiveSpan.tag("入参" + i, replaceLog(JSON.toJSONString(nowObj)));
} catch (Exception ignore) {
}
}
}
if (returnObj != null) {
try {
ActiveSpan.tag("出参", replaceLog(JSON.toJSONString(returnObj)));
} catch (Exception ignore) {
}
}
}
return returnObj;
}
/*
* 格式化日志
*
* @param logStr 入参
* @return String
*/
public static String replaceLog(String logStr) {
if (StringUtils.hasLength(logStr)) {
return logStr.replaceAll(BIG_STR_SUB_PATTERN, ":\"*\"");
}
return StrUtil.EMPTY;
}
}
4.3 创建测试接口,并添加jvm参数启动
# D:\soft-ware\skywalking-agent\skywalking-agent.jar 解压apache-skywalking-java-agent-9.2.0.tgz 后的skywalking-agent.jar jar包的地址
-javaagent:D:\soft-ware\skywalking-agent\skywalking-agent.jar
# xhs1.0: skywalking 服务的 service groups 名称
# spring-boot-skywalking:service names 名称
-Dskywalking.agent.service_name=xhs1.0::spring-boot-skywalking
# 127.0.0.1 skywalking apm 服务地址
-Dskywalking.collector.backend_service=127.0.0.1:11800
linux环境下需要把 apache-skywalking-java-agent-9.2.0.tgz上传到服务器解压,然后在启动命令后面加上上面的三个参数
#!/bin/bash
# jar包名称
JAR_FILE="spring-boot-skywalking.jar"
# pid 名称
PID_FILE="spring-boot-skywalking.pid"
# skywalking-agent jar包地址
SKYWALKING_AGENT_URL="/root/skywalking-agent/skywalking-agent.jar"
# skywalking SERVICE_NAME
SKYWALKING_SERVICE_NAME="xhs1.0::spring-boot-skywalking"
# skywalking SERVICE_NAME
SKYWALKING_BACKEND_SERVICE="127.0.0.1:11800"
start() {
if [ -f "$PID_FILE" ]; then
echo "应用程序已在运行,PID: $(cat $PID_FILE) .........."
else
# 后台启动jar包,并将启动日志输出到log.log文件中
nohup java -javaagent:$SKYWALKING_AGENT_URL -Dskywalking.agent.service_name=$SKYWALKING_SERVICE_NAME -Dskywalking.collector.backend_service=$SKYWALKING_BACKEND_SERVICE -jar -Dloader.path=.,3rd-li $JAR_FILE >/dev/null 2>&1 &
echo $! > $PID_FILE
echo "应用程序已成功启动,PID: $(cat $PID_FILE) .........."
fi
}
stop() {
if [ -f "$PID_FILE" ]; then
kill -9 $(cat $PID_FILE)
rm $PID_FILE
echo "应用程序已成功停止.........."
else
echo "应用程序未运行.........."
fi
}
restart() {
echo "正在重启.........."
stop
start
echo "重启成功.........."
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
*)
echo "使用: $0 {start|stop|restart} 命令"
exit 1
;;
esac
4.3 访问创建的测试接口,刷新浏览器查看 SkyWalking APM 服务
4.3.1 SpringBoot 日志
4.3.2 SkyWalking APM 服务查看接口调用的情况及日志信息
5. 项目源码地址
https://gitee.com/xhs101/spring-boot-skywalking.git