1 日志介绍
因为程序最终要运行在服务器中,服务器里没有IDEA,没有控制台,只能通过查看日志文件,来定位bug的位置。
日志是软件应用必备的组件,是程序debug,或是数据收集管理的重要依据,方便我们监测生产环境的
变量值变化以及代码运行轨迹。
主要是为了方便我们监测生产环境的变量值变化以及代码运行轨迹等。
这些记录会被输出到我们指定的位置形成文件,帮助我们分析错误以及用户请求轨迹。
总而言之: 开发过程中,上线后运行过程中需要记录的信息,都归属于日志。
开发环境:向控制台输出变量值,为了调试程序。
生产环境:在使用过程中,记录服务产生的异常数据。
在用户过程中,记录用户的操作行为。为了用户的行为进行分析。
2 常用日志组件
常见的日志记录框架有log4j、logback、log4j2。
SpringBoot默认使用logback输出日志。
2.1. Log4j与log4j2.x
Log4j是目前最常用的一种日志组件,它本身是Apache的一个开放源代码项目。通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、数据库等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
Log4j目前两个版本,log4j与log4j2。Log4j2的前身是log4j,它吸收了logback的设计,重新推出的一款新组件,且使用.xml/.json文件来替换了之前.properties来配置。Log4j2采用异步日志器,基于LMAX Disruptor库,相比较log4j高出10倍吞吐量。
Log4j支持两种格式的配置文件:properties和xml。包含三个主要的组件:Logger、appender、Layout。
Log4j2 有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.
All:最低等级的,用于打开所有日志记录.
Trace:是追踪,就是程序推进以下,你就可以写个trace输出,所以trace应该会特别多,不过没关系,我们可以设置最低日志级别不让他输出.
Debug:指出细粒度信息事件对调试应用程序是非常有帮助的.
Info:消息在粗粒度级别上突出强调应用程序的运行过程.
Warn:输出警告及warn以下级别的日志.
Error:输出错误信息日志.
Fatal:输出每个严重的错误事件将会导致应用程序的退出的日志.
OFF:最高等级的,用于关闭所有日志记录.
程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少。
2.2. 日志门面
2.2.1. Common-logging
Common-logging简称JCL,Common-logging是Apache提供的一个通用日志API,可以让应用程序不再依赖于具体的日志实现工具。该日志接口对其它一些日志工具,包括Log4J、Avalon LogKit、JUL等,进行了简单的包装,可以让应用程序在运行时,直接将日志适配到对应的日志实现工具中。
Common-logging通过动态查找的机制,在程序运行时自动找出真正使用的日志库。这一点与slf4j不同,slf4j是在编译时静态绑定真正的Log实现库。
2.2.2. Slf4j
slf4j是对所有日志框架制定的一种规范、标准、接口,并不是一个框架的具体的实现,它的接口并不能独立使用,需要和具体的日志框架实现配合使用(如log4j、logback、log4j2)。
SLF4J全称The Simple Logging Facade for Java,简单日志门面,这个不是具体的日志解决方案,而是通过门面模式提供一些Java Logging API,类似于JCL。
使用SLF4J日志门面时,如果需要使用某一种日志实现,则需要选择正确的SLF4J的jar包(称为日志桥接)。SLF4J提供了统一的记录日志的接口,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现,因此可以在应用中灵活切换日志系统。
学习日志门面技术,就是为了在做应用程序开发或者中间件开发时,与具体的日志组件进行隔离。
-
导入依赖
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.11.0</version>
<scope>test</scope>
</dependency>
-
编写测试类
package com.shangma.demo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestDemo2 {
private static Logger l = LoggerFactory.getLogger(TestDemo2.class);
public static void main(String[] args) {
l.debug("debug...");
l.info("info...");
l.warn("warn...");
l.error("error...");
}
}
3 Log4j2
3.1 快速使用
-
导入依赖
<!-- Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions><!-- 去掉springboot默认配置logback --> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <!-- 引入log4j2依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> <!-- 异步日志 --> <dependency> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> <version>3.4.2</version> </dependency>
-
导入log4j2.xml到resources目录下
默认读取的配置文件名顺序
1.classpath下的名为log4j2-test.json 或者log4j2-test.jsn的文件.
2.classpath下的名为log4j2-test.xml的文件.
3.classpath下名为log4j2.json 或者log4j2.jsn的文件.
4.classpath下名为log4j2.xml的文件.
-
创建测试类测试
package com.shangma.demo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class TestDemo1 { private static Logger l = LogManager.getLogger(TestDemo1.class); public static void main(String[] args) { l.debug("debug..."); l.info("info..."); l.warn("warn..."); l.error("error..."); } }
3.2 配置文件解释
简单解释:
<appender>
用来定义输出位置
</appender>
<loggers>
全局日志级别与局部包的日志级别
<root>全局级别</root>
<logger>局部日志级别</logger>
</loggers>
整体配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appenders>
<!-- 控制台输出的日志格式 -->
<console name="Console" target="SYSTEM_OUT">
<PatternLayout
pattern="%d{HH:mm:ss} %-5level %class %L %M -- %msg%n" />
</console>
<!-- fileName:输出路径 filePattern:命名规则 -->
<!-- name 配置的名字,唯一 -->
<!-- 这个配置的作用,是指定 DEBUG级别的日志,输出到对应文件中-->
<RollingFile name="RollingFileDebug"
fileName="D:/logs/debug.log"
filePattern="D:/logs/$${date:yyyy-MM-dd}/debug-%d{yyyy-MM-dd}-%i.log">
<Filters>
<!--Filters决定日志事件能否被输出。过滤条件有三个值:ACCEPT(接受), DENY(拒绝) or NEUTRAL(中立).-->
<!--如果接受/拒绝,日志到这里就会记录,然后结束,如果是中立,则会继续记录.-->
<!--
level:将被过滤的级别。
onMatch:默认值是NEUTRAL
onMismatch:默认是DENY
-->
<!-- 过滤的日志级别是debug-->
<ThresholdFilter level="DEBUG"/>
<!-- 如果当前记录的日志级别>info则返回 onMatch 的值,小于info则onMismatch 的值-->
<ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL" />
</Filters>
<!-- 输出格式 -->
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %class{36} %L %M - %msg%n" />
<Policies>
<!-- 单个日志文件的大小限制 -->
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
<!-- 最多保留20个日志文件 -->
<DefaultRolloverStrategy max="20" />
</RollingFile>
<RollingFile name="RollingFileInfo"
fileName="D:/logs/info.log"
filePattern="D:/logs/$${date:yyyy-MM-dd}/info-%d{yyyy-MM-dd}-%i.log">
<Filters>
<ThresholdFilter level="INFO" />
<ThresholdFilter level="WARN" onMatch="DENY"
onMismatch="NEUTRAL" />
</Filters>
<!-- 输出格式 -->
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n" />
<Policies>
<!-- SizeBasedTriggeringPolicy单个文件的大小限制 -->
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
<!-- DefaultRolloverStrategy同一个文件下的最大文件数 -->
<DefaultRolloverStrategy max="20" />
</RollingFile>
<RollingFile name="RollingFileWarn"
fileName="D:/logs/warn.log"
filePattern="D:/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
<Filters>
<ThresholdFilter level="WARN" />
<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL" />
</Filters>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" />
<Policies>
<!--<TimeBasedTriggeringPolicy modulate="true" interval="1"/> -->
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
<!--最多保留20个日志文件 -->
<DefaultRolloverStrategy max="20" min="0" />
</RollingFile>
<RollingFile name="RollingFileError"
fileName="D:/logs/error.log"
filePattern="D:/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
<Filters>
<ThresholdFilter level="ERROR" />
<ThresholdFilter level="FATAL" onMatch="DENY" onMismatch="NEUTRAL" />
</Filters>
<PatternLayout attern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" />
<Policies>
<!--<TimeBasedTriggeringPolicy modulate="true" interval="1"/> -->
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
<!--最多保留20个日志文件 -->
<DefaultRolloverStrategy max="20" min="0" />
</RollingFile>
</appenders>
<loggers>
<!-- 设置当前的默认日志级别-->
<root level="INFO">
<!--配置生效的位置,如果不配置则不输出-->
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileDebug"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</root>
<!--额外配置的logger-->
<!--记录druid-sql的记录-->
<logger name="druid.sql.Statement" level="debug" additivity="false">
<appender-ref ref="druidSqlRollingFile"/>
</logger>
<!-- log4j2自带的过滤标签,额外指定 特定包的日志级别 -->
<logger name="org.springframework.web" level="error"></logger>
<logger name="org.springframework.core" level="error"></logger>
<logger name="org.springframework.beans" level="error"></logger>
<!--异步日志配置-->
<!--<AsyncLogger name="org.springframework" level="info" includeLocation="true">
<AppenderRef ref="RollingFileError"></AppenderRef>
</AsyncLogger>
<AsyncLogger name="org.mybatis" level="error" includeLocation="true">
<AppenderRef ref="RollingFileError"></AppenderRef>
</AsyncLogger>
<AsyncLogger name="com.alibaba.druid" level="error" includeLocation="true">
<AppenderRef ref="RollingFileError"></AppenderRef>
</AsyncLogger>
<AsyncRoot level="debug" includeLocation="true">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</AsyncRoot>-->
</loggers>
</configuration>
log4j2在开发中一般使用同步日志;上线改为异步日志。
由于开发的时候会不停重启服务器,这时异步线程可能会出现数据丢失。
4 符号说明
PatternLayout格式化符号说明:
%p 或 %level:输出日志信息的优先级,即DEBUG,INFO,WARN,ERROR,FATAL。
%d:输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,如:%d{yyyy/MM/dd HH:mm:ss,SSS}。
%r:输出自应用程序启动到输出该log信息耗费的毫秒数。
%t:输出产生该日志事件的线程名。
%class:输出日志信息所属的类目,通常就是所在类的全名。
%M:输出产生日志信息的方法名。
%F:输出日志消息产生时所在的文件名称。
%L:输出代码中的行号。
%m 或%msg或%message::输出代码中指定的具体日志信息。
%n:输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”。
%x:输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
%%:输出一个“%”字符。
另外,还可以在%与格式字符之间加上修饰符来控制其最小长度、最大长度、和文本的对齐方式。如:
1)%-20:“-”号表示左对齐,不满足20个字符则以空格代替。
2)%.30:指定输出category的名称,最大的长度是30,如果category的名称长度大于30的话,就会将左边多出的字符截掉,但小于30的话也不会补空格。
5 异常之后发送邮件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
yaml文件
spring:
mail:
protocol: smtp
host: smtp.163.com
port: 465
username: 19937782588@163.com
password: SYRZLRNOCSLFQJKA
properties:
mail:
smtp:
auth: true
ssl:
enable: true
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appenders>
<!-- name 标签名字
subject 邮件标题
to:发送给谁,多个人用,分隔 aaa@163.com,bbb@qq.com
from:发送邮件的账户 (需要在邮箱后台,设置中,开启SMTP服务)
smtpProtocol smtp 发送的协议
smtpUsername 发送账号
smtpPassword 发送密码
smtpHost 服务器地址:
POP3服务器: pop.163.com
SMTP服务器: smtp.163.com
IMAP服务器: imap.163.com
smtpPort 发送端口, 163的是465 加上会报错 Got bad greeting from SMTP host smtp.163.com, port 465 , response [EOF]
-->
<SMTP name="Mail"
subject="异常信息[%p]"
to="chengdujavasm@126.com"
from="19937782588@163.com"
smtpPassword="SYRZLRNOCSLFQJKA"
smtpUsername="19937782588@163.com"
smtpProtocol="smtp"
smtpHost="smtp.163.com"
bufferSize="10"
ignoreExceptions="false"
smtpDebug="true"
>
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout>
<pattern>%d %p [%C] [%t] [%l] %m%n</pattern>
</PatternLayout>
</SMTP>
</appenders>
<loggers>
<!-- 设置当前的默认日志级别-->
<!-- 默认是SpringBoot下所有包生效-->
<root level="INFO">
<!--配置生效的位置,如果不配置则不输出-->
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileDebug"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</root>
<!--单独配置 自己的包-->
<logger name="com.javasm" level="debug" additivity="true">
<!--输入日志到邮件-->
<appender-ref ref="Mail" level="error"/>
</logger>
</loggers>
</configuration>