前言
这周学习了logback自定义日志格式、多线程基础、以及常见的定时器,本篇博客主要是结合以上知识实现一个简单的定时全部工单输出任务,再通过自定义的日志打印输出到控制台。
1.logback自定义日志
再说到logback自定义日志的时候,我们首先得了解它的三个主要类Logger(记录器)、Appender(附加器)、Layout(布局),首先Logger定义了我们的日志可以输出到哪几个地方,例如控制台、文件。appender是附加到记录器上面的,用来定义记录器的输出格式,例如输出到控制台的日志等级颜色,输出到硬盘的日志命名。Layout是将日志时间打印成字符串的组建。
1.1 自定义日志代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="log.path" value="H:/logs/com/ku/wo/logs" />
<!--<property name="log.path" value="/home/com/ku/logs" />-->
<!-- 日志输出格式 -->
<!--<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />-->
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level --- [%15.15(%thread)] %-40.40(%logger{20}) : %msg%n" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!--<immediateFlush>true</immediateFlush>-->
<encoder>
<!-- %msg:日志打印详情 -->
<!-- %n:换行符 -->
<!-- %highlight():转换说明符以粗体红色显示其级别为ERROR的事件,红色为WARN,BLUE为INFO,以及其他级别的默认颜色。 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) --- [%15.15(%thread)] %cyan(%-40.40(%logger{20})) : %msg%n</pattern>
<!-- 控制台也要使用UTF-8,不要使用GBK,否则会中文乱码 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- info 日志-->
<!-- RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
<!-- 以下的大概意思是:1.先按日期存日志,日期变了,将前一天的日志文件名重命名为XXX%日期%索引,新的日志仍然是project_info.log -->
<!-- 2.如果日期没有发生变化,但是当前日志的文件大小超过10MB时,对当前日志进行分割 重命名-->
<appender name="info_log" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日志文件路径和名称-->
<File>${log.path}/project_info.log</File>
<!--是否追加到文件末尾,默认为true-->
<append>true</append>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch><!-- 如果命中ERROR就禁止这条日志 -->
<onMismatch>ACCEPT</onMismatch><!-- 如果没有命中就使用这条规则 -->
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件的名字会根据fileNamePattern的值,每隔一段时间改变一次 -->
<!-- 文件名:logs/project_info.2017-12-05.0.log -->
<!-- 注意:TimeBasedRollingPolicy中 %d令牌是强制性的,必须存在,要不会报错 -->
<fileNamePattern>${log.path}/project_info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 每产生一个日志文件,该日志文件的保存期限为30天, ps:maxHistory的单位是根据fileNamePattern中的翻转策略自动推算出来的,例如上面选用了yyyy-MM-dd,则单位为天
如果上面选用了yyyy-MM,则单位为月,另外上面的单位默认为yyyy-MM-dd-->
<maxHistory>30</maxHistory>
<!-- 每个日志文件到10mb的时候开始切分,最多保留30天,但最大到20GB,哪怕没到30天也要删除多余的日志 -->
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<!--编码器-->
<encoder>
<!-- pattern节点,用来设置日志的输入格式 ps:日志文件中没有设置颜色,否则颜色部分会有ESC[0:39em等乱码-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level --- [%15.15(%thread)] %-40.40(%logger{20}) : %msg%n</pattern>
<!-- 记录日志的编码:此处设置字符集 - -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- error 日志-->
<!-- RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
<!-- 以下的大概意思是:1.先按日期存日志,日期变了,将前一天的日志文件名重命名为XXX%日期%索引,新的日志仍然是project_error.log -->
<!-- 2.如果日期没有发生变化,但是当前日志的文件大小超过10MB时,对当前日志进行分割 重命名-->
<appender name="error_log" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日志文件路径和名称-->
<File>${log.path}/project_error.log</File>
<!--是否追加到文件末尾,默认为true-->
<append>true</append>
<!-- ThresholdFilter过滤低于指定阈值的事件。 对于等于或高于阈值的事件,ThresholdFilter将在调用其decision()方法时响应NEUTRAL。 但是,将拒绝级别低于阈值的事件 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level><!-- 低于ERROR级别的日志(debug,info)将被拒绝,等于或者高于ERROR的级别将相应NEUTRAL -->
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 活动文件的名字会根据fileNamePattern的值,每隔一段时间改变一次 -->
<!-- 文件名:logs/project_error.2017-12-05.0.log -->
<!-- 注意:SizeAndTimeBasedRollingPolicy中 %i和%d令牌都是强制性的,必须存在,要不会报错 -->
<fileNamePattern>logs/project_error.%d.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
<maxFileSize>5KB</maxFileSize>
</rollingPolicy>
<!--编码器-->
<encoder>
<!-- pattern节点,用来设置日志的输入格式 ps:日志文件中没有设置颜色,否则颜色部分会有ESC[0:39em等乱码-->
<pattern>${log.pattern}</pattern>
<!-- 记录日志的编码:此处设置字符集 - -->
<charset>UTF-8</charset>
</encoder>
</appender>
<root level="INFO">
<!--控制台只有info及以上的才打印-->
<appender-ref ref="STDOUT" />
</root>
<!-- 指定项目中某个包,当有日志操作行为时的日志记录级别 -->
<!-- 级别依次为【从高到低】:FATAL > ERROR > WARN > INFO > DEBUG > TRACE -->
<logger name="com.ku.wo" level="INFO">
<appender-ref ref="info_log" />
<appender-ref ref="error_log" />
</logger>
<!-- 利用logback输入WomApplicationTests的sql日志,
注意:如果不加 additivity="false" 则此logger会将输出转发到自身以及祖先的logger中,就会出现日志文件中sql重复打印-->
<!--additivity="false"-->
<logger name="com.ku.wo.WomApplicationTests" level="DEBUG" additivity="false">
<appender-ref ref="info_log" />
<appender-ref ref="error_log" />
</logger>
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
</configuration>
logback自定义日志格式
1.2 应用标签讲解
接下来讲解一下我的日志配置,首先configuration标签概括logger和appender标签,以及一些properties标签。设置一些properties标签来赋值我们的日志输出路径、日志打印格式等等。
appender标签。
在输出日志中,我们主要使用appender的class指明RollingFileAppender来实现文件日志,它是FileAppender的子类,RollingFileAppender有两个重要的子组件,分别是RollingPolicy、TriggerPolicy,前者负责翻滚所需操作,后者负责啥时候翻滚所需操作。在文件日志中,RollingFileAppender需要同时设置RollingPolicy和TriggerPolicy。如果RollingPolicy中设置了TriggerPolicy,只需实现前者就行。
在滚动策略中,因为堆日志文档需求不同,延伸出了两种滚动策略分别是按天或月翻滚的TimeBaseRollingPolicy和按日期和限制文件大小的SizeAndTimeBaseRollingPolicy,两者都实现了RollingPolicy和TriggerPolicy。
- 其中appender标签包含name以及class属性,后者决定他属于哪种输出,如控制台输出、日志文件输出,前者是它的别名,用于后面附加给logger。
- encoder标签决定你打印的日志的输出格式以及编码,例如pattern标签是用来指明你的日志输出格式,chartset决定你的编码。
- File人标签指明你的日志文件存储路径以及名字
- append指明是否将新的日志内容追加到文件末尾,Ture表示追加,false则相反。
- filter标签可以用来过滤输出日志级别,当在该日志级别及以上就输出,在该日志级别以下就不输出。
- fileNamePattern为防止之前的文件日志被当前文件日志所覆盖所创建的标签,主要通过通过设置日期以及%d来表示使用每天日期进行命名。
- maxHistory表示文件日志存储最长时间
- totalSizeCap表示存储所有文件日志的大小总和。
- maxFileSize表示单个文件日志的最大存储内存。
- root表示根路径,可以将控制台附加器添加进去用于输出控制器日志。
- logger标签就是用来输出日志的,可以添加additivity属性来控制是否覆盖祖先日志,避免重复打印。
2.多线程实现全部工单输出
在学习了几天时间的多线程基础的时候,我通过创建线程以及sleep函数来实现一个过3秒获取所有工单信息打印在控制台的小作业,主要是通过sleep函数使我创建的当前线程休眠三秒,再使用使用synchronize锁锁住共享资源类,实现共享资源互斥访问。
2.1 实现代码
package com.ku.wo.timer;
import com.ku.wo.entity.WorkOrder;
import com.ku.wo.mapper.WorkOrderMapper;
import com.ku.wo.service.impl.WorkOrderServiceImpl;
import com.ku.wo.utils.GetBeanUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/*
想法是每天将每过6小时将工单推送给uid为1的人,即管理者,如果使用spring task其实很容易就可以实现,当然tread也可以
只不过是改参数的问题;消息推送应该要用到文websocket,暂时自己还不会,所以先就做每隔几秒钟获取全部工单信息把
*/
@Component//这个地方的注入还是可能存在问题
public class WorkOrderTimer {
//通过接口调用自定义logback日志
Logger logger = LoggerFactory.getLogger(WorkOrderTimer.class);
@Autowired//通过该注解实现对象注入
private WorkOrderServiceImpl workOrderService;
public WorkOrderTimer(){
getAllWorkOrders();
}
public void getAllWorkOrders(){
new Thread(() -> {
while(true){
synchronized (WorkOrderServiceImpl.class){
Integer uid = 9;
String username = "xiaoku";
List<WorkOrder> allWorkOrder = workOrderService.getAllWorkOrder(uid, username);
try {
Thread.sleep(3000);//休眠三秒再次查询
} catch (InterruptedException e) {
e.printStackTrace();
}
//System.out.println(allWorkOrder);
logger.info("result :{}", allWorkOrder);
}
}
},"t1").start();
}
}
多线程结合自定义日志实现定时器控制台打印
2.2 运行结果
上面的日志打印了输出时间,以及对应的线程名和该程序所在的包下面的类名,以及最后输出的所有工单信息。