目录
前言
Coding
输出到文件
输出到邮件
输出到数据库
自动加载配置
我们先来简单了解一下Logback,它是log4j的升级版,都是同一个作者。
Logback超轻量级日志框架,性能是老版log4j的10倍以上,占用内存更小。
Logback 的架构非常的通用,适用不同的使用场景。Logback 被分成三个不同的模块:logback-core,logback-classic,logback-access。
logback-core 是其它两个模块的基础。logback-classic 模块可以看作是 log4j 的一个优化版本,它天然的支持 SLF4J,所以你可以随意的从其它日志框架(例如:log4j 或者 java.util.logging)切回到 logack。
logback-access 可以与 Servlet 容器进行整合,例如:Tomcat、Jetty。它提供了 http 访问日志的功能。
本文将集成到现有项目中,采用Lombok+Logback+slf4j
将实现日志输出:邮件,数据库,控制台,文件
以及动态加载日志配置内容,无需重启项目
官网:http://www.logback.cn/
Maven
ch.qos.logback
logback-classic
1.2.3
无需单独再引入slf4j-api,logback-classic里面已经有了
logback配置文件
以下是 logback 的初始化步骤:
- logback 会在类路径下寻找名为 logback-test.xml 的文件。
- 如果没有找到,logback 会继续寻找名为 logback.groovy 的文件。
- 如果没有找到,logback 会继续寻找名为 logback.xml 的文件。
- 如果没有找到,将会通过 JDK 提供的 ServiceLoader 工具在类路径下寻找文件 META-INFO/services/ch.qos.logback.classic.spi.Configurator,该文件的内容为实现了
Configurator
接口的实现类的全限定类名。- 如果以上都没有成功,logback 会通过 BasicConfigurator 为自己进行配置,并且日志将会全部在控制台打印出来。
最后一步的目的是为了保证在所有的配置文件都没有被找到的情况下,提供一个默认的(但是是非常基础的)配置。
logback.xml
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
将日志信息打印到控制台,格式化时间,级别为debug,作用是针对大于等于debug级别的log内容。
默认就是debug,所以level="debug"可写可不写,但为了阅读性还是写上。
Test
package com.finance.www.log;
import lombok.extern.slf4j.Slf4j;
/**
* @author 954L
* @create 2020/4/30 10:55
*/
@Slf4j
public class LogbackTest {
public static void main(String[] args) {
log.trace("trace");
log.debug("debug");
log.info("info");
log.warn("warn");
log.error("error");
}
}
package com.finance.www.log;
import lombok.extern.slf4j.Slf4j;
/**
* @author 954L
* @create 2020/4/30 10:55
*/
@Slf4j
public class LogbackTest {
public static void main(String[] args) {
try {
System.out.println(1/0);
} catch (Exception e) {
log.error("程序异常", e);
}
}
}
@Slf4j注解是lombok里的,等同于:
public static final Logger log = LoggerFactory.getLogger(LogbackTest.class);
这也是目前最广泛的做法了,一天一个log文件,可以指定查看某天的日志文件内容,排查线上问题利器!优点就不具体说了。
%d{yyyy-MM-dd HH:mm:ss} %-5level %logger [%msg]%n
UTF-8
%d{yyyy-MM-dd HH:mm:ss} %-5level %logger [%msg]%n
UTF-8
ERROR
DENY
ACCEPT
${logbase}info/%d{yyyy-MM-dd}.log
30
%d{yyyy-MM-dd HH:mm:ss} %-5level %logger [%msg]%n
UTF-8
ERROR
ACCEPT
DENY
${logbase}error/%d{yyyy-MM-dd}.log
30
上述将info级别及以下跟error级别的日志区分在不同的文件里,方便我们针对性处理,因为你可不想以后在一堆debug日志里找一个error
Test
我们在web接口添加如下代码
try {
System.out.println(1/0);
} catch (Exception e) {
log.info("测试异常", e);
log.error("测试异常", e);
}
效果符合预期,堆栈信息均有打印
与其等出bug再看异常日志还不如直接在发生异常的时候就发送邮件到邮箱,这样就能快速发现解决问题。
这里就还是用网易爹的邮箱了,我几乎所有发送邮件都是用网易的....
发送邮件是用logback的SMTPAppender,依赖javax.mail
javax.mail
mail
1.4
SMTPAppender的属性如下表所示:
属性名 | 类型 | 描述 |
---|---|---|
smtpHost | String | SMTP 服务器的主机名。强制性的。 |
smtpPort | int | SMPT 服务监听的端口。默认为 25. |
to | String | 接收者的邮件地址。触发事件发送给接收者。多个收件人可以使用逗号(,)分隔,或者使用多个 元素来指定。 |
from | String | SMTPAppender 使用的发件人,格式遵循邮件通用格式,如果你想要包含发送者的名字,使用这种格式 " Adam Smith |
subject | String | 邮件的主题。它可以是通过 PatternLayout 转换后的有效值。关于 Layout 将在接下来的章节讨论。 邮件应该有一个主题行,对应触发的邮件信息。 假设 subject 的值为:"Log: %Logger - %msg",触发事件的 logger 名为 "com.foo.Bar",并且日志信息为 "Hello world"。那么发出的邮件信息将会有一个名为 "Log: com.foo.Bar - Hello World" 的主题行。默认情况下,这个属性的值为 "%logger{20} - %m" |
discriminator | Discriminator | 在 Discriminator 的帮助下,SMTPAppender 根据 discriminator 返回的值可以将不同日志事件分散到不同的缓冲区中。默认的 discriminator 将返回同一个值,所以所有的事件都使用同一个缓冲区。 |
evaluator | IEvaluator | 通过创建一个新的 元素来声明此选项。通过 class 属性指定 class 的名字表示用户希望通过哪个类来满足 SMTPAppender 的 Evaluator 的需要。如果没有指定此选项,当触发一个大于等于 ERROR 级别的事件时, SMTPAppender 将会被分配一个 OnErrorEvaluator 的实例。logback 配备了几个其它的 evaluator,分别叫 OnMarkerEvaluator (将在下面讨论),一个相对强大的 evaluator 叫 JaninoEventEvaluator (在其它章节讨论) 以及最近版本才有的一个更加强大的 evaluator 叫 GEventEvaluator 。 |
cyclicBufferTracker | CyclicBufferTracker |
从名字可以看出,是一个 CyclicBufferTracker 的实例追踪循环缓冲区。它基于 discriminator 返回的 key (见上)。如果你不想指定一个 cyclicBufferTracker,那么将会自动创建一个 CyclicBufferTracker 的实例。默认的,这个实例用来保留事件的循环缓冲区的大小为 256。你需要改变 bufferSize 选项的大小(见下面) |
username | String | 默认为 null |
password | String | 默认为 null |
STARTTLS | boolean | 如果为 true,那么 appender 将会发送 STARTTLS 命令(如果服务器支持)将连接变成 SSL 连接。注意,连接初始的时候是为加密的。默认为 false。 |
SSL | boolean | 如果为 true,将通过 SSL 连接服务器。默认为 false。 |
charsetEncoding | String | 邮件信息将会通过 charset]https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html) 进行编码。默认编码为 "UTF-8" |
localhost | String | 一旦 SMTP 客户端的主机名没有配置正确,例如客户端的 hostname 不是全限定的,那么服务端会拒绝客户端发送的 HELO/EHLO 命令。为了解决这个问题,你可以将 localhost 的值设置为客户端主机的全限定名。详情见 com.sun.mail.smtp 包文档中的 "mail.smtp.localhost" 属性。(这个网站已经关闭了...) |
asynchronousSending | boolean | 决定邮件传输是否是异步进行。默认为 'true'。但是,在某些特定的情况下,异步发送不怎么合适。例如,当发生一个严重错误时,你的应用使用 SMTPAppender 去发送一个警告,然后退出。但是相关线程可能没有时间去发送警告邮件。在这种情况下,你可以设置该属性的值为 'false'。 |
includeCallerData | boolean | 默认为 false。如果 asynchronousSending 的值为 true,并且你希望在日志中看到调用者的信息,你可以设置该属性的值为 true |
sessionViaJNDI | boolean | SMTPAppender 基于 javax.mail.Session 来发送邮件信息。默认情况下,该属性的值为 false,所以需要用户指定相关属性通过 SMTPAppender 来构建 javax.mail.Session 实例。如果设置为 true,javax.mail.Session 实例将会通过 JNDI 来获取。参见 jndiLocation 属性。通过 JNDI 获取 Session 实例可以减少需要配置的数量,使你的应用减少重复(dryer)的工作。更多关于在 Tomcat 配置 JNDI 的信息请参考 JNDI Resources How-to。注意 :通过 JNDI 获取 Session 的时候请移除 web 应用下 WEB-INF/lib 文件夹下的 mail.jar 与 activation.jar。 |
jndiLocation | String | JNDI 中放置 javax.mail.Session 的地方。默认为:" java:comp/env/mail/Session " |
使用网易邮箱需单独配置一下,我这轻车熟路了,就不再说了,附带一篇我以前的blog,里面有步骤
java利用网易邮箱批量发送邮件(带附件)
%d{yyyy-MM-dd HH:mm:ss} %-5level %logger [%msg]%n
UTF-8
%d{yyyy-MM-dd HH:mm:ss} %-5level %logger [%msg]%n
UTF-8
ERROR
DENY
ACCEPT
${logbase}info/%d{yyyy-MM-dd}.log
30
%d{yyyy-MM-dd HH:mm:ss} %-5level %logger [%msg]%n
UTF-8
ERROR
ACCEPT
DENY
${logbase}error/%d{yyyy-MM-dd}.log
30
ERROR
ACCEPT
DENY
smtp.163.com
[email protected]
[email protected]
xxx
password
[email protected]
false
TESTING Email Function: %logger{20} - %m
%d{yyyy-MM-dd HH:mm:ss} %-5level %logger [%msg]%n
- smtpHost:邮件服务器地址,网易固定值:smtp.163.com
- To:收件人邮箱地址,可配置多个
- username:发件人名称(非邮箱地址)
- password:邮箱授权码(非账号登录密码)
- From:发送方邮箱地址
- asynchronousSending:是否异步发送邮件(默认:true)
- Subject:邮件内容
效果图:
个人不是很喜欢这种方式,但是有提供的话,顺带尝试一下把
%d{yyyy-MM-dd HH:mm:ss} %-5level %logger [%msg]%n
UTF-8
%d{yyyy-MM-dd HH:mm:ss} %-5level %logger [%msg]%n
UTF-8
ERROR
DENY
ACCEPT
${logbase}info/%d{yyyy-MM-dd}.log
30
%d{yyyy-MM-dd HH:mm:ss} %-5level %logger [%msg]%n
UTF-8
ERROR
ACCEPT
DENY
${logbase}error/%d{yyyy-MM-dd}.log
30
ERROR
ACCEPT
DENY
smtp.163.com
[email protected]
xxxphone
HCNWVDYOBDPTBYHN
[email protected]
false
财税金结算平台告警: %logger{20} - %m
%d{yyyy-MM-dd HH:mm:ss} %-5level %logger [%msg]%n
ERROR
DENY
ACCEPT
com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/test
root
passw0rd
需要注意的是:
这里使用的DriverManagerConnectionSource是ConnectionSource的一个实现,使用传统JDBC的方式与DB交互。
它在每次获取db连接的时候都会重新建立一条新连接,所以建议改为内置连接池,示例:
com.mysql.jdbc.Driver jdbc:mysql://localhost:3306/test root passw0rd 用阿里druid的话,记得加入maven
com.alibaba druid 1.1.10
之后需要初始化一下数据库,创建表。logback不支持自动创建表,附带sql
/*
Navicat MySQL Data Transfer
Source Server : localhost
Source Server Version : 50728
Source Host : localhost:3306
Source Database : test
Target Server Type : MYSQL
Target Server Version : 50728
File Encoding : 65001
Date: 2020-04-30 15:46:49
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for logging_event
-- ----------------------------
DROP TABLE IF EXISTS `logging_event`;
CREATE TABLE `logging_event` (
`timestmp` bigint(20) NOT NULL,
`formatted_message` text NOT NULL,
`logger_name` varchar(254) NOT NULL,
`level_string` varchar(254) NOT NULL,
`thread_name` varchar(254) DEFAULT NULL,
`reference_flag` smallint(6) DEFAULT NULL,
`arg0` varchar(254) DEFAULT NULL,
`arg1` varchar(254) DEFAULT NULL,
`arg2` varchar(254) DEFAULT NULL,
`arg3` varchar(254) DEFAULT NULL,
`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(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`event_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for logging_event_exception
-- ----------------------------
DROP TABLE IF EXISTS `logging_event_exception`;
CREATE TABLE `logging_event_exception` (
`event_id` bigint(20) NOT NULL,
`i` smallint(6) NOT NULL,
`trace_line` varchar(254) NOT NULL,
PRIMARY KEY (`event_id`,`i`),
CONSTRAINT `logging_event_exception_ibfk_1` FOREIGN KEY (`event_id`) REFERENCES `logging_event` (`event_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for logging_event_property
-- ----------------------------
DROP TABLE IF EXISTS `logging_event_property`;
CREATE TABLE `logging_event_property` (
`event_id` bigint(20) NOT NULL,
`mapped_key` varchar(254) NOT NULL,
`mapped_value` text,
PRIMARY KEY (`event_id`,`mapped_key`),
CONSTRAINT `logging_event_property_ibfk_1` FOREIGN KEY (`event_id`) REFERENCES `logging_event` (`event_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
效果图:
这也是logback跟log4j的一点区别,我们在线上环境可以随时修改xml文件,利用logback的自动加载从而无需重启项目就可以生效我们的修改内容。
...
配置说明:
scan: 设置为true,代表会在指定的时间内重新加载日志配置文件
scanPeriod:当scan=true时,日志配置文件会在指定的单位时间内重新加载,默认是每分钟会重载一次。
scanPeriod的配置说明:
时间单位:milliseconds, seconds, minutes , hours
eg:
5分钟:
1小时: