對Sentry功能擴充方法實踐

經過logback接入sentry,logback提供的原生ILoggingEvent對象相比於EventBuilder攜帶的信息也是有些不足的,而sentry對它進行了必定的加強 上一篇文章已經說了那些屬性分別表明什麼,這麼就看看究竟如何進行加強EventBuilder 下面代碼能夠看到這個方法處於發送到Sentry服務器上時對EventBuilder的加強,框架默認就兩個加強器ContextBuilderHelper,HttpEventBuilderHelper他們都實現了EventBuilderHelpervoid 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();
}
複製代碼
相關文章
相關標籤/搜索