经过logback
接入sentry,logback
提供的原生ILoggingEvent
对象相比于EventBuilder
携带的信息也是有些不足的,而sentry对它进行了必定的加强 上一篇文章已经说了那些属性分别表明什么,这么就看看究竟如何进行加强EventBuilder
下面代码能够看到这个方法处于发送到Sentry服务器上时对EventBuilder
的加强,框架默认就两个加强器ContextBuilderHelper
,HttpEventBuilderHelper
他们都实现了EventBuilderHelper
的void helpBuildingEvent(EventBuilder eventBuilder);
方法 因此下面的循环也是在调用的实现方法。java
public void runBuilderHelpers(EventBuilder eventBuilder) {
for (EventBuilderHelper builderHelper : builderHelpers) {
builderHelper.helpBuildingEvent(eventBuilder);
}
}
复制代码
若是这个时候又有其余需求,好比发个邮件或者添加一些属性信息,那么就能够以下代码:spring
public class DemoEventBuilderHelper implements EventBuilderHelper {
@Override
public void helpBuildingEvent(EventBuilder eventBuilder) {
//一顿操做
eventBuilder.withRelease("1.0.0");
}
}
复制代码
那么究竟怎么把手写的helper
加进builderHelpers
里面呢,其实他是在初始化SentryClient
的时候就已经肯定了,因此咱们在来看看初始化的代码:api
@Override
public SentryClient createSentryClient(Dsn dsn) {
SentryClient sentryClient = new SentryClient(createConnection(dsn), getContextManager(dsn));
//此方法就是对builderHelpers集合进行add
sentryClient.addBuilderHelper(new HttpEventBuilderHelper());
sentryClient.addBuilderHelper(new ContextBuilderHelper(sentryClient));
return configureSentryClient(sentryClient, dsn);
复制代码
由于咱们想要本身对EventBuilder进行加强,因此得控制建立SentryClient
的逻辑而不用默认的DefaultSentryClientFactory
因此该这样操做:bash
public class DemoSentryClientFactory extends DefaultSentryClientFactory {
@Override
public SentryClient createSentryClient(Dsn dsn) {
SentryClient sentryClient = new SentryClient(createConnection(dsn), getContextManager(dsn));
//一顿骚操做,把刚刚写好的helper添加进来
sentryClient.addBuilderHelper(new DemoEventBuilderHelper());
return sentryClient;
}
}
复制代码
逻辑已经写好了,怎么让框架自动加载咱们写好的DemoSentryClientFactory
类呢,String sentryClientFactoryName = Lookup.lookup("factory", realDsn);
这就是框架已经为咱们预留好的接口,只要让他查找到factory
这个key那么他就会经过全类名进行反射实例化,服务器
public static SentryClient sentryClient(String dsn, SentryClientFactory sentryClientFactory) {
//把数据源切割成Dsn对象
Dsn realDsn = resolveDsn(dsn);
//为空的是时候
if (sentryClientFactory == null) {
//查找key为factory的类,留的可扩展接口
String sentryClientFactoryName = Lookup.lookup("factory", realDsn);
if (Util.isNullOrEmpty(sentryClientFactoryName)) {
// 没有找到类就new一个默认工厂来建立SentryClient
sentryClientFactory = new DefaultSentryClientFactory();
} else {
// 找到key就经过类名反射实例化一个
Class<? extends SentryClientFactory> factoryClass = null;
try {
factoryClass = (Class<? extends SentryClientFactory>) Class.forName(sentryClientFactoryName);
sentryClientFactory = factoryClass.newInstance();
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
logger.error("Error creating SentryClient using factory class: '"
+ sentryClientFactoryName + "'.", e);
return null;
}
}
}
//拿到Dsn建立SentryClient
return sentryClientFactory.createSentryClient(realDsn);
}
复制代码
想让他获取到key咱们得先配置,在resource资源目录下新建sentry.properties
配置文件,这也是我上一篇提到过的几种参数配置方式之一。框架
dsn=http://e32a375c1ffd4dc0bc7510a65dba3143:928fedbef8b440b3bd2fe5dca2d8b56f@172.16.0.132:9000/50
factory=cn.yzw.superbox.swms.api.config.DemoSentryClientFactory
复制代码
这个时候就能够实例化本身的工厂。 ##Spring Boot HTTP数据 Spring Boot不会自动加载任何内容javax.servlet.ServletContainerInitializer
,这意味着Sentry SDK没有机会挂钩请求周期来收集有关HTTP请求的信息。为了在Spring Boot中向Sentry事件添加HTTP请求数据,须要io.sentry.spring.SentryServletContextInitializer
在应用程序中将该类注册为Bean。ide
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-spring</artifactId>
<version>1.7.27</version>
</dependency>
复制代码
@Bean
public ServletContextInitializer sentryServletContextInitializer() {
return new io.sentry.spring.SentryServletContextInitializer();
}
复制代码
来看一下里面写的什么内容ui
public class SentryServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
ctx.addListener(SentryServletRequestListener.class);
}
}
复制代码
依然仍是用的TheadLocal为每个线程存放属于本身的request,在request的生命周期结束时,能够看到就是清理了上下文,这些逻辑都是能够根据本身需求重写的,获取request的意义可不单单如此,经过request就能够在eventBuilder
里面添加不少用户信息以及上下文信息跟踪。spa
public class SentryServletRequestListener implements ServletRequestListener {
private static final Logger logger = LoggerFactory.getLogger(SentryServletRequestListener.class);
private static final ThreadLocal<HttpServletRequest> THREAD_REQUEST = new ThreadLocal<>();
public static HttpServletRequest getServletRequest() {
return THREAD_REQUEST.get();
}
@Override
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
THREAD_REQUEST.remove();
try {
SentryClient sentryClient = Sentry.getStoredClient();
if (sentryClient != null) {
sentryClient.clearContext();
}
} catch (Exception e) {
logger.error("Error clearing Context state.", e);
}
}
@Override
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
ServletRequest servletRequest = servletRequestEvent.getServletRequest();
if (servletRequest instanceof HttpServletRequest) {
THREAD_REQUEST.set((HttpServletRequest) servletRequest);
}
}
}
复制代码
上面那种方法是官网给出的,通过个人实验官网介绍那种方法可能由于版本比较高仍是什么缘由,老是监听器没生效,因此咱们能够换一种简单粗暴的方式,完美。线程
@Bean
public SentryServletRequestListener sentryServletRequestListener() {
return new SentryServletRequestListener();
}
复制代码