1. Журнал ошибок будильника боевой
1.1 Требования
Для того, чтобы удобнее было разбираться в ситуации с системной ошибкой в режиме реального времени, я начал искать аварийное решение
1.2. Идеи
1.2.1. Решения, которые не плохо
Если это не плохие деньги, более системное и более полное решение, первое, что приходит мне в голову, этоCAT
, он может не только реализовывать аварийные сигналы об ошибках, но и более интеллектуальный, интервал ошибок аварийных сигналов, содержание аварийных сигналов об ошибках, аварийные сигналы QPS и т. д. более разнообразны, а также он может просматривать трафик интерфейса QPS и т. д., но средства ограничены, так что сдавайся
1.2.2. Рассмотрите возможность реализации самостоятельно
- Подумайте, можно ли реализовать это самостоятельно
log.error
способ перехвата, поэтому я узнал, предоставляет ли logback фильтры перехватчиков и т. д., а затем нашел официальный сайт и обнаружил, что сам logback предоставляет метод appender-to-mail, который отлично подходит для прямой интеграции.
1.3 Файлы конфигурации
pom
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>2.7.8</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
<configuration>
<contextName>logback</contextName>
<!--配置文件中参数-->
<springProperty scope="context" name="applicationName" source="spring.application.name"/>
<springProperty scope="context" name="alertEmail" source="onegene.alert.email"/>
<springProperty scope="context" name="profile" source="spring.profiles.active"/>
<!-- 邮件 -->
<!-- SMTP server的地址,必需指定。如网易的SMTP服务器地址是: smtp.163.com -->
<property name="smtpHost" value="hwhzsmtp.qiye.163.com"/><!--填入要发送邮件的smtp服务器地址(问DBA或者经理啥的就知道)-->
<!-- SMTP server的端口地址。默认值:25 -->
<property name="smtpPort" value="465"/>
<!-- 发送邮件账号,默认为null -->
<property name="username" value="xxxx@163.com.cn"/><!--发件人账号-->
<!-- 发送邮件密码,默认为null -->
<property name="password" value="rVgkwPL4WsWmGV72"/><!--发件人密码-->
<!-- 如果设置为true,appender将会使用SSL连接到日志服务器。默认值:false -->
<property name="SSL" value="true"/>
<!-- 指定发送到那个邮箱,可设置多个<to>属性,指定多个目的邮箱 -->
<property name="email_to" value="${alertEmail}"/><!--收件人账号多个可以逗号隔开-->
<!-- 指定发件人名称。如果设置成“<ADMIN> ”,则邮件发件人将会是“<ADMIN> ” -->
<property name="email_from" value="xxxx@163.com"/>
<!-- 指定emial的标题,它需要满足PatternLayout中的格式要求。如果设置成“Log: %logger - %msg ”,就案例来讲,则发送邮件时,标题为“【Error】: com.foo.Bar - Hello World ”。 默认值:"%logger{20} - %m". -->
<property name="email_subject" value="【${applicationName}:${profile}:Error】: %logger"/>
<!-- ERROR邮件发送 -->
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>${smtpHost}</smtpHost>
<smtpPort>${smtpPort}</smtpPort>
<username>${username}</username>
<password>${password}</password>
<asynchronousSending>true</asynchronousSending>
<SSL>${SSL}</SSL>
<to>${email_to}</to>
<from>${email_from}</from>
<subject>${email_subject}</subject>
     <!-- html格式-->
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<Pattern>%date%level%thread%logger{0}%line%message</Pattern>
</layout>
     <!-- 这里采用等级过滤器 指定等级相符才发送 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker">
<!-- 每个电子邮件只发送一个日志条目 -->
<bufferSize>1</bufferSize>
</cyclicBufferTracker>
</appender>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
<property name="log.path" value="log"/>
<!-- 彩色日志 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!--输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--输出到文件-->
<!-- 时间滚动输出 level为 DEBUG 日志 -->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/${applicationName}-log.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志归档 -->
<fileNamePattern>${log.path}/${applicationName}-log-%d{yyyyMMdd}.log.%i</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<logger name="com.onegene.platform" level="debug"/>
<logger name="com.onegene.platform.auth.authority" level="info"/>
<logger name="org.springframework.security.oauth2.provider.endpoint" additivity="false"/>
<springProfile name="local">
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DEBUG_FILE"/>
</root>
</springProfile>
<springProfile name="dev,pro">
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="EMAIL"/>
</root>
</springProfile>
</configuration>
1.4 Интерпретация конфигурационных файлов
- Суть конфигурационного файла
<springProperty scope="context" name="applicationName" source="spring.application.name"/>
<springProperty scope="context" name="alertEmail" source="onegene.alert.email"/>
<springProperty scope="context" name="profile" source="spring.profiles.active"/>
- Я вытащил большинство переменных параметров, которые можно извлечь.Файл конфигурации можно напрямую положить в любой проект, и имя журнала варьируется в зависимости от
bootstrap.yml
серединаspring.application.name
изменение параметра - Отправитель тревоги также может быть настроен в файле конфигурации,Обратите внимание здесь:
onegene.alert.email
а такжеspring.application.name
параметры лучше всего вbootstrap.yml
конфиг вместоapplication.yml
,потому чтоbootstrap.yml
имеет более высокий приоритет чтения, чемapplication.yml
, иначе эти два параметра могут быть не прочитаны
На данный момент, как только мы напечатаемlog.error
Журналы будут поставленыЖурналы ошибок отправляются на указанный адрес электронной почты, но этого конечно мало, надо сотрудничать@ControllerAdvice
сможет сделатьПока у вас есть исключение, вы можете унифицировать почту журнала., в то же время у нас есть особые требования, такие как отдельные журналы ошибок, частые и неизбежные, и их не нужно обрабатывать, тогда мы можемСделайте небольшое расширение, определите инъекцию интерфейса, в бизнес-коде разобраться, нет ли необходимости отправлять сообщение об ошибке
1.5 Код
- Обработка исключений
@ControllerAdvice
@Slf4j
public class SystemExceptionHandler {
@Autowired(required = false)
private IExceptionFilter exceptionFilter;
@ExceptionHandler(value = {DuplicateUniqueException.class, DuplicateKeyException.class})
@ResponseBody
public Result duplicateUniqueExceptionExceptionHandler(HttpServletRequest request, Exception e) {
return getExceptionResult(e, StatusCode.FAILURE_SYSTEM_CODE, "唯一主键重复(或联合唯一键)", false);
}
@ExceptionHandler(value = {FeignException.class, RuntimeException.class})
@ResponseBody
public Result FeignExceptionHandler(HttpServletRequest request, Exception e) throws Exception {
throw e;
}
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result commonExceptionHandler(HttpServletRequest request, Exception e) {
return getExceptionResult(e, StatusCode.FAILURE_CODE, true);
}
private Result getExceptionResult(Exception e, int statusCode, boolean ignoreAlert) {
return getExceptionResult(e, statusCode, e.getMessage(), ignoreAlert);
}
private Result getExceptionResult(Exception e, int statusCode, String msg, boolean ignoreAlert) {
e.printStackTrace();
String exceptionName = ClassUtils.getShortName(e.getClass());
StackTraceElement[] stackTrace = e.getStackTrace();
StringBuilder sb = new StringBuilder();
for (StackTraceElement stackTraceElement : stackTrace) {
sb.append(stackTraceElement.toString()).append("\n");
}
String message = e.getMessage();
if (ignoreAlert && filter(e)) {
log.error("ExceptionName ==> {},message:{},detail:{}", exceptionName, message, sb.toString());
}
return Result.failure(statusCode, msg);
}
private boolean filter(Exception e) {
if (exceptionFilter != null) {
return exceptionFilter.filter(e);
}
return true;
}
}
Интерфейс простой
public interface IExceptionFilter {
boolean filter(Exception e);
}
Обрабатывать исключения, которые не нужно обрабатывать здесь
/**
* @author: laoliangliang
* @description: 过滤不需要报警的异常
* @create: 2020/4/9 10:00
**/
@Component
@Slf4j
public class FilterAlert implements IExceptionFilter {
@Override
public boolean filter(Exception e) {
if (e instanceof ConnectException) {
return false;
}
return true;
}
}
1.6. Резюме
- На данный момент схема оповещения об ошибках реализована полностью, и дальнейшие действия направлены на оптимизацию работы.Эффект внедрения следующий:
Список рассылки ошибок
Содержание сообщения об ошибке