1. 下载Agents
https://archive.apache.org/dist/skywalking/java-agent/9.0.0/apache-skywalking-java-agent-9.0.0.tgz
2. 上传到服务器解压
在Spring Cloud项目中,每部署一个服务时,就拷贝一份skywalking的agent文件到该服务器上并解压。不管是部署注册中心、网关、还是其它服务,都这么做,无论一个服务器部署多少个微服务,有一份就行。
3. 集成链路追踪
3.1. 修改Spring Boot启动参数
springcloud/springboot 一般是通过 java -jar xxx.jar 进行启动。我们只需要在其中加上 -javaagent 参数即可,格式如下面
java -javaagent:上一步解压目录/agent/skywalking-agent.jar=agent.service_name=自定义服务名,collector.backend_service=服务ip:11800 -jar xxx.jar
说明:
-javaagent后是skywalking-agent.jar的绝对路径,在apache-skywalking-java-agent-9.0.0.tgz解压后的包中
-Dskywalking.agent.service_name是服务的名称,自定义,一般用spring.application.name
-Dskywalking.collector.backend_service是指skywalking oap服务器的ip和端口号
示例:
java -javaagent:F:\project\git\agent\skywalking-agent\skywalking-agent.jar -Dskywalking.agent.service_name=skywalking_demo_test -Dskywalking.collector.backend_service=192.168.110.155:11800 -jar skywaking_demo.jar
启动springboot项目,然后查看控制台或文件中的日志
bash start.sh
3.2. Gateway集成Skywalking链路追踪
其他服务已经完成链路追踪功能,只有gateway服务需要特殊配置一下。
问题:Spring Cloud Gateway是基于WebFlux实现的,Skywalking默认不支持,在链路上是不展示gateway组件的
链路图如下图所示
解决方案如下
把agent/optional-plugins下面的两个插件
apm-spring-cloud-gateway-x.x-plugin-x.0.0.jar、apm-spring-webflux-x.x-plugin-x.0.0.jar
根据你的Spring Cloud Gateway版本选择合适的版本拷贝到agent/plugin目录下
,比如我的gateway版本为3.1.4,则拷贝apm-spring-cloud-gateway-3.x-plugin-9.0.0.jar、apm-spring-webflux-5.x-plugin-9.0.0.jar到plugin目录下
然后重启gateway服务
重新发起请求,链路图如下
4. 配置日志收集
需要先完成第三步 配置链路追踪,不然无法打印日志。
4.1. 普通SpingBoot项目配置
这里的普通springboot项目是指除gateway服务以外的其他服务
参考:SpringBoot集成Skywalking日志收集-CSDN博客
4.2. Spring Cloud Gateway日志收集配置
目前Spring Cloud Gateway服务日志会丢失TraceId,现在的解决方案如下
4.2.1. 修改POM文件
日志更换为log4j2,引入skywalking相关的依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!--引入log4j2依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-log4j-2.x</artifactId>
<version>9.0.0</version>
</dependency>
<!--apm 工具包-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>9.0.0</version>
</dependency>
4.2.2. 增加如下两个配置类
LogHooks.java
package com.szc.gateway.logconfig;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Operators;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class LogHooks {
private static final String KEY = "logMdc";
@PostConstruct
@SuppressWarnings("unchecked")
public void setHook() {
reactor.core.publisher.Hooks.onEachOperator(KEY,
Operators.lift((scannable, coreSubscriber) -> new MdcSubscriber(coreSubscriber)));
}
@PreDestroy
public void resetHook() {
reactor.core.publisher.Hooks.resetOnEachOperator(KEY);
}
}
MdcSubscriber.java
package com.szc.gateway.logconfig;
import org.reactivestreams.Subscription;
import org.slf4j.MDC;
import org.springframework.cglib.beans.BeanMap;
import reactor.core.CoreSubscriber;
import reactor.util.context.Context;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class MdcSubscriber implements CoreSubscriber {
private static final String TRACE_ID = "traceId";
private static final String SKYWALKING_CTX_SNAPSHOT = "SKYWALKING_CONTEXT_SNAPSHOT";
private final CoreSubscriber<Object> actual;
public MdcSubscriber(CoreSubscriber<Object> actual) {
this.actual = actual;
}
@Override
public void onSubscribe(Subscription s) {
actual.onSubscribe(s);
}
@Override
public void onNext(Object o) {
Context c = actual.currentContext();
Optional<String> traceIdOptional = Optional.empty();
if (!c.isEmpty() && c.hasKey(SKYWALKING_CTX_SNAPSHOT)) {
traceIdOptional = Optional.of(c.get(SKYWALKING_CTX_SNAPSHOT)).map(MdcSubscriber::beanToMap)
.map(t -> t.get(TRACE_ID)).map(MdcSubscriber::beanToMap).map(t -> t.get("id")).map(Object::toString);
}
try (MDC.MDCCloseable cMdc = MDC.putCloseable(TRACE_ID, traceIdOptional.orElse("N/A"))) {
actual.onNext(o);
}
}
@Override
public void onError(Throwable throwable) {
actual.onError(throwable);
}
@Override
public void onComplete() {
actual.onComplete();
}
@Override
public Context currentContext() {
return actual.currentContext();
}
public static <T> Map<String, Object> beanToMap(T bean) {
Map<String, Object> map = new HashMap<>();
if (bean != null) {
BeanMap beanMap = BeanMap.create(bean);
for (Object key : beanMap.keySet()) {
if (key.equals("traceId") || key.equals("id")) {
map.put(key.toString(), beanMap.get(key));
}
}
}
return map;
}
}
4.2.3. 增加log4j2日志配置文件
log4j2.xml内容
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Properties>
<Property name="PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [TID: %equals{%X{traceId}}{}{N/A}] [%logger{36}] [%thread] [%-5level] %msg%n"/>
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${PATTERN}"/>
</Console>
<Async name="Async">
<AppenderRef ref="Console"/>
</Async>
<!--输出到日志文件,滚动分割日志文件,自动打包gz -->
<RollingFile name="INFO_FILE" fileName="logs/gateway-info1.log" filePattern="$logs/gateway-%d{yyyyMMdd}.log.%i">
<PatternLayout pattern="${PATTERN}"/>
<Policies>
<!--默认一天一个文件 -->
<TimeBasedTriggeringPolicy />
<!--一天内大于size就单独分隔-->
<SizeBasedTriggeringPolicy size="500MB"/>
</Policies>
</RollingFile>
<!--skywalking 日志收集 -->
<GRPCLogClientAppender name="APM_LOG">
<PatternLayout pattern="${PATTERN}"/>
</GRPCLogClientAppender>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="Console"/>
<AppenderRef ref="INFO_FILE"/>
<AppenderRef ref="APM_LOG"/>
</Root>
</Loggers>
</Configuration>
4.2.4. 启动
启动gateway服务,发起http请求,可以看到日志中已经出现traceId
要注意的是,在skywalking控制台看日志时,是没有traceid的,这一点和普通的springboot项目不同
如下图所示,gateway服务的日志 traceId只能在内容里面查看