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可读取当前的环境参数:

SpringBoot2.x系列(二)生产环境日志及预警_第1张图片

解决步骤

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}.log
        
        50
    
    
        
        %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
            root
            111111
        
    

    
    
        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

  (

    timestmp         BIGINT NOT NULL,

    formatted_message  TEXT NOT NULL,

    logger_name       VARCHAR(254) NOT NULL,

    level_string      VARCHAR(254) NOT NULL,

    thread_name       VARCHAR(254),

    reference_flag    SMALLINT,

    arg0              VARCHAR(254),

    arg1              VARCHAR(254),

    arg2              VARCHAR(254),

    arg3              VARCHAR(254),

    caller_filename   VARCHAR(254) NOT NULL,

    caller_class      VARCHAR(254) NOT NULL,

    caller_method     VARCHAR(254) NOT NULL,

    caller_line       CHAR(4) NOT NULL,

    event_id          BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY

  );

COMMIT;

BEGIN;

CREATE TABLE logging_event_property

  (

    event_id       BIGINT NOT NULL,

    mapped_key        VARCHAR(254) NOT NULL,

    mapped_value      TEXT,

    PRIMARY KEY(event_id, mapped_key),

    FOREIGN KEY (event_id) REFERENCES logging_event(event_id)

  );

COMMIT;

BEGIN;

CREATE TABLE logging_event_exception

  (

    event_id         BIGINT NOT NULL,

    i                SMALLINT NOT NULL,

    trace_line       VARCHAR(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.com
    
    25
    
    [email protected]
    
    [email protected]
    ${ACTIVE_PROFILE_NAME}: %logger - %msg
    
    [email protected]
    
    xxxxxxx
    false
    
    true
    
        %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 {

@Override



public void append(ILoggingEvent eventObject) {



try {

 //节点输出内容
            String content = eventObject.getMessage();
            //异常的IP
            String  ip= 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 = "/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插件;

SpringBoot2.x系列(二)生产环境日志及预警_第2张图片                                                                                                                  

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

org.projectlombok

lombok

1.18.0

 

3.钉钉消息推送

参考:

https://blog.csdn.net/weixin_41158378/article/details/110749806

 

你可能感兴趣的:(springboot,spring,boot,java,spring)