对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();
}
复制代码
相关文章
相关标签/搜索