一、背景
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());
}
}
});
}
}
网友评论