• SpringBoot(三) - Slf4j+logback 日志,异步请求,定时任务


    1、Slf4j+logback 日志

    SpringBoot框架的默认日志实现:slf4j + logback;

    默认日志级别:info,对应了实际生产环境日志级别;

    1.1 日志级别

    # 常见的日志框架中,日志级别都包含五种,优先级从低到高:
    trace < debug < info < warn < error 
    
    #日志输出规则是:
    自动设置的日志级及更高级别,进行日志输出;
    比如当前日志级别是info,那么日志输出的级别包含:info,warn,error,凡是业务的这三个级别,都会输出到日志文件
    

    1.1.1 trace 日志级别

    trace 日志级别,了解,实际开发中,几乎不会使用此级别;(配置文件指定为 trace 级别才会输出);

    logger.trace("------------ trace 日志级别,了解,实际开发中,几乎不会使用此级别 ------------------");
    

    1.1.2 debug 日志级别

    1. debug 日志级别,掌握,实际开发中,此日志级别作为调试日志使用,是线上调试问题定位的有效手段;
    2. 强调:线上一般默认是不开debug日志,因此日志级别记录的日志极为详情,会产生大量的日志内容及文件;
    3. 发现线上问题,不好定位时,临时开启debug;
    logger.debug("------------ {} 日志级别,{},实际开发中,此日志级别作为调试日志使用,是线上调试问题定位的有效手段 ------------------","debug","掌握");
    

    1.1.3 info 日志级别

    info 日志级别,掌握,实际开发中,此日志级别是核心业务环境日志,不需要记录特别详细,一般都是接口 入和出,方便版本上线查看业务是否正常使用;

    logger.info("------------ {} 日志级别,{},实际开发中,此日志级别时核心业务环境日志,不需要记录特别详细 ------------","info","掌握");
    

    1.1.4 warn 日志级别

    warn 日志级别,掌握,实际开发中,此日志级别是业务警告日志,警告日志不一定是错误,可能业务异常流程,或者数据错误判断;

    logger.warn("------------ {} 日志级别,{},实际开发中,此日志级别时业务警告日志 ------------","warn","掌握");
    

    1.1.5 error 日志级别

    error 日志级别,掌握,实际开发中,此日志级别是核心业务错误,凡是系统中出现了异常或者程序错误,都必须使用error日志,级别最高,确保必须输出,可以有效的记录线上业务的错误;

    logger.error("------------ {} 日志级别,{},实际开发中,此日志级别是核心业务错误 ------------","error","掌握");
    

    1.2 日志使用

    1.2.1 创建日志对象

    //创建日志对象
    Logger logger = LoggerFactory.getLogger(getClass());
    
    logger.trace("");
    logger.debug("");
    logger.info("");
    logger.warn("");
    logger.error("");
    

    1.2.2 @Slf4j 注解

    @Slf4j
    public class ChargeResultNotifySchedule{
    
        log.trace("");
        log.debug("");
        log.info("");
        log.warn("");
        log.error("");
        
    }
    

    1.3 配置文件配置日志 信息

    1.3.1 测试默认日志级别

    1.3.1.1 直接运行测试类

    1.3.1.2 测试类增加 @RunWith(SpringRunner.class) 注解

    测试类增加 @RunWith(SpringRunner.class) 注解 运行的是SpringBoot项目测试,会读取到配置文件;

    1.3.2 修改配置文件信息

    1.3.2.1 修改默认日志级别为 debug
    # 默认日志级别为 info ,更改默认日志级别debug
    logging:
      level:
        com:
          kgc:
            sbt: debug
    

    1.4 指定日志输出

    1.4.1 指定日志输出到指定文件

    1. 默认日志只输出到控制台;
    2. 指定输出到指定文件,默认会加载到根路径下;
    3. 所有的日志,都是追加记录,不会执行覆盖;
    logging:
    	file: kh96-logging.log
    

    日志输出到根目录下的指定文件名下:

    1.4.2 指定日志输入到指定目录下

    不指定文件名,SpringBoot中的logback会由默认的日志名spring.log;

    logging:
    	path: D:/KEGONGCHANG/DaiMa/IDEA/KH96/SpringBoot/SpringBoot/TempFile/kh96-logging2
    

    默认名:

    输出文件:

    1.4.3 指定输出格式

    # 了解 %d日期,%thread 线程名称,%-5leavel 日志级别 %logger{50} 日志类路径 %msg 日志内容
    logging:
    	pattern:
        console: "%d{yyyy-MM-dd HH:mm:ss} +++ [%thread] +++ %-5level +++ %logger{100} +++ %msg%n"
        file: "%d{yyyy-MM-dd HH:mm:ss} +++ [%thread] +++ %-5level +++ %logger{100} +++ %msg%n
    

    1.4.4 指定输入格式文件

    网上由很多;

    
    
    <configuration scan="true" scanPeriod="60 seconds" debug="false">
        
        <property name="LOG_HOME" value="D:/KEGONGCHANG/DaiMa/IDEA/KH96/SpringBoot/SpringBoot/TempFile/log" />
        
        <property name="appName" value="log-sbt-kh96"/>
        
        <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
            
            <layout class="ch.qos.logback.classic.PatternLayout">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{60} - %msg%npattern>
            layout>
        appender>
    
          
        <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
            
            <file>${LOG_HOME}/${appName}.logfile>
            
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                
                <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.logfileNamePattern>
                
                <MaxHistory>30MaxHistory>
                
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>30MBmaxFileSize>
                timeBasedFileNamingAndTriggeringPolicy>
            rollingPolicy>
                 
            <layout class="ch.qos.logback.classic.PatternLayout">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{60} : %line ] - %msg%npattern>
            layout>
        appender>
    
        
        
        <logger name="com.kgc.sbt" level="debug" />
        <logger name="org.springframework" level="debug"/>
    
        
        <root level="debug">
            <appender-ref ref="stdout" />
            <appender-ref ref="appLogAppender" />
        root>
    configuration> 
    

    2、异步请求

    2.1 异步请求处理实现类

    @Service
    @Slf4j
    public class ChargeServiceImpl implements ChargeService{
    
        @Override
        @Async  
        //开启异步线程注解,如果是同一个类中的其他方法,添加此异步处理注解,异步是不生效的(不能再同一个类中调用异步方法,解决方法,异步方法单独放在一个类中)
        //使用的是Spring内置的线程池
        public void executesAsynCharge(String chargeTel, Double chargeMoney) {
            try {
                TimeUnit.MILLISECONDS.sleep(2000);
            }catch (Exception e){
                e.printStackTrace();
            }
            log.info("********* 异步渠道 充值成功,充值手机号:{},充值金额:{} ************",chargeTel,chargeMoney);
    
        }
    }
    

    2.2 主启动类

    主启动类上必须增加@EnableAsync 注解,开启异步处理功能;

    @SpringBootApplication
    @EnableAsync //开启异步处理功能
    public class Springboot03AsyztimerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(Springboot03AsyzyimerApplication.class, args);
        }
    
    }
    

    2.3 请求调用异步处理方法

    @RestController
    @Slf4j
    public class ChargeController {
    
        @Autowired
        ChargeService chargeService;
        
        @GetMapping("/asyncCharge")
        public  String asyncCharge(@RequestParam("chargeTel") String chargeTel,@RequestParam("chargeMoney") Double chargeMoney){
    
            log.info("------ 开始充值,充值手机号:{},充值金额:{} 开始调用充值渠道充值 --------- ",chargeTel,chargeMoney);
    //        log startTimeMillis = System.currentTimeMillis(); //旧的获取当前时间毫秒数
            long startTimeMillis = Instant.now().toEpochMilli();
    
            //调用充值渠道 异步 充值
            chargeService.executesAsynCharge(chargeTel,chargeMoney);
    
            long finishTimeMillis = Instant.now().toEpochMilli();
            log.info("------ 结束充值,充值手机号:{},充值金额:{} ,充值总耗时:{}-----------",chargeTel,chargeMoney,finishTimeMillis-startTimeMillis);
    
            return String.format("%s充值%s成功!",chargeTel,chargeMoney);
    
        }
        
    }
    

    3、定时任务

    3.1 场景

    异步充值结果,定时批量回调订单;

    public void chargeResultNotifyMethodOne(){
    
            //模拟从数据库获取5笔需要回调的充值订单,进行批量回调结果
            List chargeOrderList = Arrays.asList("KH001","KH002","KH003","KH004","KH005");
    
            //循环处理需要回调的5笔订单
            log.info("---------- 开始 执行批量回调充值结果------------");
    
            chargeOrderList.forEach(chargeOrderNo->{
                log.info("***** 充值订单:{},回调重接成功! ******");
    
                try{
                    TimeUnit.MILLISECONDS.sleep(100);
                }catch (Exception e){
                    e.printStackTrace();
                }
    
                log.info("---------- 结束 执行批量回调充值结果 ------------");
            });
    
    }
    

    3.2 注解

    3.2.1 @Scheduled(fixedDelay = 5 * 1000)

    fixedDelay :计时规则: 从上一次执行结束 开始计时 到 下一次定时任务开始 ,不关心前一次定时任务耗时多久;

    3.2.2 @Scheduled(fixedRate = 5 * 1000)

    fixedRate: 计时规则:从上一次定时任务执行开始 开始计时 到 下一次定时任务开始,如果上一次定时任务 超过定时,上一次执行完后,下一次立即执行;

    3.2.3 @Scheduled(cron = "*/5 * * * * ?")

    cron表达式:既可以是实现循环时间间隔,执行定时任务,也可以执行某个时刻的定时任务,通过指定表达式实现的,灵活度是三种最高的
    1)循环定时: 每次执行的定时任务时间点,是由cron表达是决定的,其实都是预置好的;比如5秒,5秒执行一次定时
    2)定点定时:在指定的某个时刻,执行一次定时任务

    秒 分 时 日 月 周
    @Scheduled(cron = "*/5 * * * * ?")
    

    3.2.4 @EnableScheduling

    @SpringBootApplication
    @EnableScheduling //开启定时任务功能
    public class Springboot03AsyztimerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(Springboot03AsyzyimerApplication.class, args);
        }
    
    }
    
  • 相关阅读:
    如何让一个uniform variable在多级shader中都起作用(类似C语言的全局变量)?
    python项目练习——14.学生管理系统
    美国对人脸识别技术的法律规制及启示
    c++视觉处理---cv::Sobel()`算子
    2030:【例4.16】找素数 (信奥一本通)
    编程中常用的加密算法
    【不良事件上报源码】医疗安全(不良)事件管理系统
    Linux环境下(CentOS操作系统)如何修改MySQL数据库及Redis的密码?
    【JavaWeb】之文件上传与下载
    【go】字符串切片与字符串出入数据库转化
  • 原文地址:https://www.cnblogs.com/xiaoqigui/p/16790630.html