經過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();
}
複製代碼