springboot|SpringBoot2.x系列(二)生产环境日志及预警

在生产环境中,要求对日志进行分类切割及ERR异常类能及时预警,便于及时发现线上问题。
一、技术要求:
(1).日志按照以天为单位存储,超过一定大小后要另起文件,便于查阅,日志可设置过期时间,过期后系统可自动删除,避免海量存储空间
(2) 出现线上ERROR级别异常,需要通过钉钉或者邮件及时预警
(3)把ERROR级别异常信息存储到数据库,方便线上查询
(4)要能方便区分除生产环境及开发环境,开发环境不需要邮件及钉钉预警
二、技术解决思路
对应生产环境异常错误预警,大概有两种解决方
(1)采用全局拦截器,拦截所有异常,在拦截器里实现存储、推送等操作,这个需要考虑并发
(2)采用日志系统自带的相关功能及扩展
本论文介绍通过日志扩展来解决问题
Spring Boot 2.*默认采用了slf4j+logback的形式 ,slf4j是个通用的日志门面,logback就是个具体的日志框架了,我们记录日志的时候采用slf4j的方法去记录日志,底层的实现就是根据引用的不同日志jar去判定了。所以Spring Boot也能自动适配JCL、JUL、Log4J等日志框架,它的内部逻辑就是通过特定的JAR包去适配各个不同的日志框架。
logback日志集成了邮件发送、数据库存储、日志文件分类存储等功能,钉钉推送预警没有集成,需要去扩展
分环境编写配置文件,springboot已有解决方案,通过application.yml里面配置不同的对应文件,logback可读取当前的环境参数:
springboot|SpringBoot2.x系列(二)生产环境日志及预警
文章图片

解决步骤
1.在resources文件夹力创建logback-spring.xml文件,并在yml文件里声明
:注意默认文件名是logback-spring.xml,可省略
yml文件里声明

#日志信息 logging: config: classpath:logback-spring.xml #如果不配置config,默认查找logback-spring.xml path: D:/log

2.在logback.xml里配置控制台日志和文件输出
%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n ${LOG_HOME}/%d{yyyy-MM-dd}/MIXPAY_%d{yyyy-MM-s}.log50 %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n 50MB



3.配置插入数据库
pom.xml文件引入数据库相关驱动
org.springframework spring-jdbc com.alibaba druid-spring-boot-starter 1.1.10 mysql mysql-connector-java 5.1.48

yml里配置相关链接,这里日志数据库虽然用不到,但不配置要报启动错误
spring: profiles: active: prod datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: org.gjt.mm.mysql.Driver platform: mysql url: jdbc:mysql://127.0.0.1:3306/pmlog?useUnicode=true&characterEncoding=UTF-8&useSSL=true username: root password: 111111 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT1FROMDUAL testWhileIdle: true testOnBorrow: false testOnReturn: false filters: stat,wall,log4j logSlowSql: truelog

在logback.xml文件里配置
org.gjt.mm.mysql.Driver jdbc:mysql://127.0.0.1:3306/pmlog?useUnicode=true& characterEncoding=UTF-8& useSSL=true root111111 error ACCEPT DENY


初始化数据库表

BEGIN;
DROP TABLE IF EXISTS logging_event_property;
DROP TABLE IF EXISTS logging_event_exception;
DROP TABLE IF EXISTS logging_event;
COMMIT;
BEGIN;
CREATE TABLE logging_event
(
timestmpBIGINT NOT NULL,
formatted_messageTEXT NOT NULL,
logger_nameVARCHAR(254) NOT NULL,
level_stringVARCHAR(254) NOT NULL,
thread_nameVARCHAR(254),
reference_flagSMALLINT,
arg0VARCHAR(254),
arg1VARCHAR(254),
arg2VARCHAR(254),
arg3VARCHAR(254),
caller_filenameVARCHAR(254) NOT NULL,
caller_classVARCHAR(254) NOT NULL,
caller_methodVARCHAR(254) NOT NULL,
caller_lineCHAR(4) NOT NULL,
event_idBIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
COMMIT;
BEGIN;
CREATE TABLE logging_event_property
(
event_idBIGINT NOT NULL,
mapped_keyVARCHAR(254) NOT NULL,
mapped_valueTEXT,
PRIMARY KEY(event_id, mapped_key),
FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
);
COMMIT;
BEGIN;
CREATE TABLE logging_event_exception
(
event_idBIGINT NOT NULL,
iSMALLINT NOT NULL,
trace_lineVARCHAR(254) NOT NULL,
PRIMARY KEY(event_id, i),
FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
);
COMMIT;
4.配置发送到邮件
pom.xml引入mail包

org.codehaus.janino janino 3.1.2 javax.mail mail 1.4.5

logback.xml配置邮件信息
smtp.qiye.aliyun.com25jiangzengkui@lpcollege.comjyj_stuff@lpcollege.com ${ACTIVE_PROFILE_NAME}: %logger - %msgjyj_stuff@lpcollege.comxxxxxxx falsetrue %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 1 ERROR ACCEPT DENY


5.钉钉推送等其他操作
logback没有像邮件,数据库一样集成,只有继承UnsynchronizedAppenderBase去扩展
注:关于钉钉如何发预警消息,请参考钉钉和springboot的集成

(1)扩展类:
package com.jyj.soft.comm; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.UnsynchronizedAppenderBase; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; /*** @class: com.jyj.soft.comm.CustomizeApperder* @description:* logback的节点扩展类,获取输出,并进行异步处理* @author: jiangzengkui* @company: 教育家* @create: 2020-12-05 09:13*/public class CustomizeApperder extends UnsynchronizedAppenderBase {@Overridepublic void append(ILoggingEvent eventObject) {try { //节点输出内容 String content = eventObject.getMessage(); //异常的IP Stringip= InetAddress.getLocalHost().getHostAddress(); String run_machine=SpringContextUtil.getActiveProfile(); //运行服务器类型如在yml配置的生存、开发、测试等环境 //System.out.println("当前运行环境: " + run_machine); // System.out.println("content内容是: " + content); System.out.println("服务器IP:"+ip); if("prod".equals(run_machine)){//如果是生产环境 //1.可发邮件//2.可钉钉推送String title=">生产环境发生异常"; String markDown=">**服务器IP:**"+ip+"\n\n"; markDown+=">**异常原因:**"+content; RobotUtil.sendMarkdownMsg(RobotUtil.robot_name_test,null,title,markDown); //3.可插入数据库 }/** Map map = new HashMap(); map.put("LOG_LEVEL", eventObject.getLevel().levelStr); map.put("CONTENT", content.replace("'", "''")); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); map.put("CREATE_DATE", sdf.format(new Date())); **/// 拼接SQL语句,然后执行// … …} catch (Throwable e) {String errorMsg = e.getLocalizedMessage(); System.out.println(errorMsg); }}}

这里用到一个帮助类SpringContextUtil,通过非注解的方式或者bean实例和配置属性
package com.jyj.soft.comm; /** * @class: com.jyj.soft.comm.SpringContextUtil * @description: * 作用: *(1)不通过@Autowired注解来获得对象实例 *(2)直接读取propertie,yml文件里的配置值 * @author: jiangzengkui * @company: 教育家 * @create: 2020-12-05 11:05 */ import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import java.util.Map; /** * 获取Spring的ApplicationContext对象工具,可以用静态方法的方式获取spring容器中的bean * @author https://blog.csdn.net/chen_2890 * @date 2019/6/26 16:20 */ @Component public class SpringContextUtil implements ApplicationContextAware {private static ApplicationContext applicationContext; /** * 系统启动如tomcat时会执行这个方法 * @param applicationContext * @throws BeansException */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtil.applicationContext = applicationContext; }/** * 获取applicationContext */ public static ApplicationContext getApplicationContext() { return applicationContext; }/** * 通过name获取 Bean. */ public static Object getBean(String name) { Object o = null; try { o = getApplicationContext().getBean(name); } catch (NoSuchBeanDefinitionException e) { // e.printStackTrace(); } return o; }/** * 通过class获取Bean. */ public static T getBean(Class clazz) { return getApplicationContext().getBean(clazz); }/** * 通过name,以及Clazz返回指定的Bean */ public static T getBean(String name, Class clazz) { return getApplicationContext().getBean(name, clazz); }/** * 通过name获取 Bean. */ public static Map getBeansOfType(Class clazz) { return getApplicationContext().getBeansOfType(clazz); }/** * 获取配置文件配置项的值 * * @param key 配置项key,注意这个key支撑连写 * persoon: *name: jzk * 则key是persoon.name,不是name */ public static String getEnvironmentProperty(String key) { return getApplicationContext().getEnvironment().getProperty(key); }/** * 获取spring.profiles.active */ public static String getActiveProfile() { return getApplicationContext().getEnvironment().getActiveProfiles()[0]; } }

(2)配置logback.xml





error






5.日志在不同的环境运用
比如只有在生产环境推送钉钉预警和邮件等,开发和测试环境不需要
要用到logback.xml里这个这个标签,意思是对yml配置文件里的spring.profiles.active的数据
spring: profiles: active: prod

logback根据不同的值,调用不同的appender,如

7.其他知识

(1)logback读取yml配置文件的数据
先声明,在用{}引用
yml:
persoon:
name: jzk
在logback 里读取
定义

引用
${pro_name}: %logger - %msg
(3)日志简写
每个类都要写
LoggerFactory.getLogger(SbDemoApplicationTests.class); 很麻烦,可以省掉
//标签 @Slf4j @RestController public class HelloCtrol {@Autowired private Persoon persoon; @Autowired private Dage dage; //访问路径及方法 @RequestMapping(value = "https://www.it610.com/hello",method = RequestMethod.GET) public String hello(){ dage.h(); //直接用log log.error("error================"); log.warn("warn=============="); log.info("info=============="); log.debug("debug==================="); return "hello, "+persoon.getName()+",address:"+persoon.getAddress(); }

实现方式
1.使用idea首先需要安装Lombok插件;
springboot|SpringBoot2.x系列(二)生产环境日志及预警
文章图片

2..在pom文件加入lombok的依赖

org.projectlombok
lombok
1.18.0


3.钉钉消息推送
参考:
https://blog.csdn.net/weixin_41158378/article/details/110749806
【springboot|SpringBoot2.x系列(二)生产环境日志及预警】

    推荐阅读