美文网首页
Sentry-Spring Boot Starter的实现(适用

Sentry-Spring Boot Starter的实现(适用

作者: 天草二十六_简村人 | 来源:发表于2021-09-14 22:28 被阅读0次

一、背景

sentry官方已有提供sentry-spring-boot-starter支持spring boot项目,但是存在版本的差异。starter 1.7.30支持spring boot的版本是1.5.x, starter 3.0.0开始则要求你的spring boot版本必须大于2.1.x及以上。
差异主要是在spring boot的相关类,sentry、sentry-spring和sentry-logback等并没有版本要求,所以你想要使用sentry的高版本jar包,而又无法使用官方的starter。
本文则是出于此目标,提供一套简易的自定义starter,让你可以引入并支持高版本的sentry等jar包。

二、总体情况

想要实现一个starter,必不可少的两块是,首先是配置项,其次是configure类。

  • 配置项:SentryProperties.java
  • configure类:SentryLogbackAppenderAutoConfiguration.java和SentryConfiguration.java

下面是具体的代码实现。

三、具体实现

1、pom.xml

<!--sentry-->
<dependency>
    <groupId>io.sentry</groupId>
    <artifactId>sentry-spring</artifactId>
    <version>4.3.0</version>
</dependency>
<dependency>
    <groupId>io.sentry</groupId>
    <artifactId>sentry-logback</artifactId>
    <version>4.3.0</version>
</dependency>

2、配置项

  • application.yml
    应用的版本必须配置在application.yml中,mvn打包的时候替换为pom.xml中project的version值。
# sentry配置
sentry:
  # 应用的版本号
  release: @project.version@
  • 分布式配置
# sentry配置
sentry:
  dsn: http://1be0a59354f241e3a3956e6924d42cc5@192.168.10.109:9000/11
  # 多环境
  environment: dev

3、java代码

  • SentryProperties.java
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;
 
/**
 * sentry.property
 *
 * @author zwp
 */
@Data
@Configuration
@ConfigurationProperties("sentry")
public class SentryProperties {
    /**
     * Whether to enable sentry.
     */
    private boolean enabled = true;
 
    /**
     * is debug model
     */
    private Boolean debug = false;
    /**
     * dsn
     */
    private String dsn;
    /**
     * The application version that will be sent with each event.
     */
    private String release;
 
    /**
     * The application environment that will be sent with each event.
     */
    private String environment;
 
    /**
     * Tags that will be sent with each event.
     */
    private Map<String, String> tags = new LinkedHashMap<>();
 
}
  • SentryLogbackAppenderAutoConfiguration.java
import ch.qos.logback.classic.LoggerContext;
import io.sentry.logback.SentryAppender;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
/**
 * Auto-configures {@link SentryAppender}.
 */
@Configuration
@ConditionalOnClass({LoggerContext.class, SentryAppender.class})
@ConditionalOnProperty(name = "sentry.logging.enabled", havingValue = "true", matchIfMissing = true)
public class SentryLogbackAppenderAutoConfiguration {
 
    @Bean
    public SentryLogbackInitializer sentryLogbackInitializer(
            final SentryProperties sentryProperties) {
        return new SentryLogbackInitializer(sentryProperties);
    }
}
  • SentryLogbackInitializer.java

虽然构造函数引入了成员变量sentryProperties,但暂时没用上,待你自己实现。我这里是直接固定了值。
sentryAppender.setMinimumEventLevel(Level.ERROR);
sentryAppender.setMinimumBreadcrumbLevel(Level.INFO);

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import io.sentry.logback.SentryAppender;
import io.sentry.util.Objects;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
 
import java.util.Iterator;
 
/**
 * Registers {@link SentryAppender} after Spring context gets refreshed.
 */
class SentryLogbackInitializer implements ApplicationListener {
    private final SentryProperties sentryProperties;
 
    public SentryLogbackInitializer(final SentryProperties sentryProperties) {
        this.sentryProperties = Objects.requireNonNull(sentryProperties, "properties are required");
    }
 
    @Override
    public void onApplicationEvent(final ApplicationEvent event) {
        final Logger rootLogger = (Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
 
        if (!isSentryAppenderRegistered(rootLogger)) {
            final SentryAppender sentryAppender = new SentryAppender();
            sentryAppender.setName("SENTRY_APPENDER");
            sentryAppender.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
 
            sentryAppender.setMinimumEventLevel(Level.ERROR);
            sentryAppender.setMinimumBreadcrumbLevel(Level.INFO);
            sentryAppender.start();
 
            rootLogger.addAppender(sentryAppender);
        }
    }
 
    private boolean isSentryAppenderRegistered(final Logger logger) {
        final Iterator<Appender<ILoggingEvent>> it = logger.iteratorForAppenders();
        while (it.hasNext()) {
            final Appender<ILoggingEvent> appender = it.next();
 
            if (appender.getClass().equals(SentryAppender.class)) {
                return true;
            }
        }
        return false;
    }
}
  • SentryConfiguration.java
import io.sentry.Sentry;
import io.sentry.spring.SentryExceptionResolver;
import io.sentry.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
 
import javax.annotation.PostConstruct;
import java.util.Map;
 
/**
 * sentry配置初始化.
 *
 */
@ConditionalOnClass({HandlerExceptionResolver.class, SentryExceptionResolver.class})
@ConditionalOnWebApplication
@ConditionalOnProperty(name = "sentry.enabled", havingValue = "true", matchIfMissing = true)
@Configuration
public class SentryConfiguration {
    private final SentryProperties sentryProperties;
 
    public SentryConfiguration(final SentryProperties sentryProperties) {
        this.sentryProperties = Objects.requireNonNull(sentryProperties, "properties are required");
    }
 
    @PostConstruct
    public void init() {
        if (StringUtils.isEmpty(sentryProperties.getDsn())) {
            return;
        }
 
        Sentry.init(options -> {
            options.setDsn(sentryProperties.getDsn());
            options.setDebug(sentryProperties.getDebug() != null ? sentryProperties.getDebug() : false);
 
            if (StringUtils.isNotEmpty(sentryProperties.getRelease())) {
                options.setRelease(sentryProperties.getRelease());
            }
            if (StringUtils.isNotEmpty(sentryProperties.getEnvironment())) {
                options.setEnvironment(sentryProperties.getEnvironment());
            }
 
            String serverIp = System.getProperty("server.ip");
            if (StringUtils.isNotEmpty(serverIp)) {
                options.setTag("serverIp", serverIp);
            }
 
            if (sentryProperties.getTags() != null && !sentryProperties.getTags().isEmpty()) {
                for (Map.Entry<String, String> tag : sentryProperties.getTags().entrySet()) {
                    options.setTag(tag.getKey(), tag.getValue());
                }
            }
 
        });
    }
}

相关文章

网友评论

      本文标题:Sentry-Spring Boot Starter的实现(适用

      本文链接:https://www.haomeiwen.com/subject/rvocgltx.html