# 日志
# 概述
- 用来记录程序运行过程中的信息,并可以进行永久存储
# 意义
- 可以将日志信息选择性的记录到指定位置(控制台、文件、数据库、日志中间件等)
- 支持以开关的方式进行控制是否记录,无需修改源码
# 常见日志框架
# Java Util Logging(原生)
# 使用示例
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author automannn
* @Date 2022/8/31
*/
public class JULTest {
public static void main(String[] args) {
Logger log = Logger.getLogger(JULTest.class.getName());
log.info("jul log info");
log.log(Level.WARNING,"fine msg");
log.log(Level.SEVERE,"severe msg");
log.config("config msg");
log.fine("fine msg");
log.finer("finer msg");
log.finest("finest msg");
}
}

# 日志级别
public class Level implements java.io.Serializable {
//xxx:严重
public static final Level SEVERE = new Level("SEVERE",1000, defaultBundle);
//xxx:警告
public static final Level WARNING = new Level("WARNING", 900, defaultBundle);
//xxx:消息
public static final Level INFO = new Level("INFO", 800, defaultBundle);
//xxx:配置
public static final Level CONFIG = new Level("CONFIG", 700, defaultBundle);
//xxx:详细
public static final Level FINE = new Level("FINE", 500, defaultBundle);
//xxx:较详细
public static final Level FINER = new Level("FINER", 400, defaultBundle);
//xxx: 非常详细
public static final Level FINEST = new Level("FINEST", 300, defaultBundle);
//xxx:全部记录
public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle);
//xxx:不记录
public static final Level OFF = new Level("OFF",Integer.MAX_VALUE, defaultBundle);
}
# 默认配置文件(jdk/jre/lib/logging.properties)
# xxx:默认将日志输出到控制台
handlers= java.util.logging.ConsoleHandler
# xxx: 默认的全局日志级别为 消息(消息,警告,严重会被打印)
.level= INFO
# xxx: 文件日志记录器 的默认配置
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
# xxx: 控制台日志记录器 的默认配置
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# xxx: 可以对其它日志记录器 进行配置,如下方 配置名称为 com.xyz.foo日志记录器的记录级别
com.xyz.foo.level = SEVERE
# 自定义配置
class Config{
public void customConfig(){
//xxx: 自定义配置文件
InputStream ins = this.getClass().getClassLoader().getResourceAsStream("logging.properties");
//xxx: LogManager 是一个单例对象
LogManager logManager = LogManager.getLogManager();
logManager.readConfiguration(ins);
}
}
# Log4J(apache)
# 使用示例
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
/**
* @author automannn
* @Date 2022/8/31
*/
public class Log4jTest {
public static void main(String[] args) {
Logger log = Logger.getLogger(Log4jTest.class);
//配置日志信息
BasicConfigurator.configure();
log.fatal("fatal msg");
log.error("error msg");
log.warn("warn msg");
log.info("info msg");
log.debug("debug msg"); //默认级别
log.trace("trace msg");
}
}

# 日志级别
public class Level extends Priority implements Serializable {
//xxx:不输出
public static final Level OFF = new Level(2147483647, "OFF", 0);
//xxx:致命
public static final Level FATAL = new Level(50000, "FATAL", 0);
//xxx:错误
public static final Level ERROR = new Level(40000, "ERROR", 3);
//xxx:警告
public static final Level WARN = new Level(30000, "WARN", 4);
//xxx:消息
public static final Level INFO = new Level(20000, "INFO", 6);
//xxx:调试
public static final Level DEBUG = new Level(10000, "DEBUG", 7);
//xxx:详细
public static final Level TRACE = new Level(5000, "TRACE", 7);
//xxx:所有输出
public static final Level ALL = new Level(-2147483648, "ALL", 7);
}
# 配置(有多种配置方式)
BasicConfigurator
可用于辅助快捷配置org.apache.log4j.spi.Configurator
可通过SPI接口
对配置器进行扩展PropertyConfigurator
自带DOMConfigurator
自带- 自己扩展
# Appender(输出端)组件分类
名称 | 作用 |
---|---|
ConsoleAppender | 日志输出到控制台 |
FileAppender | 日志输出到文件 |
DailyRollingFileAppender | 日志输出到文件,并且每天输出到一个新的文件 |
RollingFileAppender | 日志输出到文件,并指定大小,大小到达阈值后,将文件改名,并产生一个新文件 |
JDBCAppender | 日志输出到数据库中 |
# Layouts(格式)占位符说明
形式 | 含义 |
---|---|
%m | 输出代码中指定的日志信息 |
%p | 日志级别 |
%n | 换行符 |
%r | 应用启动到输出该信息耗费的毫秒数 |
%c | 打印语句所属类的全名 |
%t | 产生该日志的线程全名 |
%d | 服务器当前时间 |
%l | 日志发送的位置,包括类名、线程、及代码中的行数(【不推荐】影响性能) |
%F | 日志消息产生时,所在的文件名称(【不推荐】影响性能) |
%L | 代码中的行号(【不推荐】影响性能) |
- 最小宽度,最大宽度,对其方式配置
形式 含义 %5c 最小宽度5,小于5时默认情况下右对齐 %-5c 最小宽度5,小于5时 ‘-’ 指定左对齐 %.5c 最大宽度5,大于5时,将左边多出的字符截掉 %20.30c 小于20补空格且右对齐,大于30就将左边超出的字符截掉
# 配置文件
public class LogManager {
//xxx:源码中 已经不推荐使用
static public final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
//xxx: 在 classpath配置,自动读取
static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
static {
//xxx: 加载配置,略
//xxx: 默认通过配置器: PropertyConfigurator
}
}
# 配置文件示例
#xxx: 第一个参数配置 rootLogger的级别,第二个及以后的参数 配置Appender
log4j.rootLogger = trace,console
# 指定appender
#xxx: 指定的appender,可以指定 Threshold属性,配置输出级别 (注意,不能比rootLogger的级别低,否则以rootLogger为准)
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定格式化信息
log4j.appender.console.layout.conversionPattern = %r [%5t] %p %l - %m%n
log4j.appender.file = org.apache.log4j.FileAppender
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern = %r [%5t] %p %l - %m%n
# 来源于SetFile方法
log4j.appender.file.file = ./log4j.log
log4j.appender.file.encoding = UTF-8
# Commons-Logging
# 使用示例
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.BasicConfigurator;
/**
* @author automannn
* @Date 2022/8/31
*/
public class CommonsLoggingTest {
public static void main(String[] args) {
//xxx: 默认使用 JUL作为日志底层实现,当导入log4j的时候,自动进行转换适配
Log log = LogFactory.getLog(CommonsLoggingTest.class);
BasicConfigurator.configure();
log.fatal("fatal msg");
log.error("error msg");
log.warn("warn msg");
log.info("info msg");
log.debug("debug msg");
log.trace("trace msg");
}
}
# 原理
final class LogAdapter {
private static final String LOG4J_SPI = "org.apache.logging.log4j.spi.ExtendedLogger";
private static final String LOG4J_SLF4J_PROVIDER = "org.apache.logging.slf4j.SLF4JProvider";
private static final String SLF4J_SPI = "org.slf4j.spi.LocationAwareLogger";
private static final String SLF4J_API = "org.slf4j.Logger";
private static final LogAdapter.LogApi logApi;
/*xxx: 检测日志环境,具有本身的优先级*/
static {
if (isPresent("org.apache.logging.log4j.spi.ExtendedLogger")) {
if (isPresent("org.apache.logging.slf4j.SLF4JProvider") && isPresent("org.slf4j.spi.LocationAwareLogger")) {
logApi = LogAdapter.LogApi.SLF4J_LAL;
} else {
logApi = LogAdapter.LogApi.LOG4J;
}
} else if (isPresent("org.slf4j.spi.LocationAwareLogger")) {
/*xxx: slf4j第一优先级*/
logApi = LogAdapter.LogApi.SLF4J_LAL;
} else if (isPresent("org.slf4j.Logger")) {
/*xxx: log4j第二优先级*/
logApi = LogAdapter.LogApi.SLF4J;
} else {
/*xxx: jul第三优先级*/
logApi = LogAdapter.LogApi.JUL;
}
}
}
# slf4j日志(slf4j, simple logging facade for java)
# 使用案例
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author automannn
* @Date 2022/8/31
*/
public class Slf4jLogTest {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(Slf4jLogTest.class);
logger.error("error msg");
logger.warn("warn msg");
logger.info("info msg"); //默认日志级别
logger.debug("debug msg");
logger.trace("trace msg");
}
}
# 原理
public final class LoggerFactory {
//xxx: 获取日志实现
public static ILoggerFactory getILoggerFactory() {
return StaticLoggerBinder.getSingleton().getLoggerFactory();
}
}
//xxx: slf4j用什么个实现,主要取决于 import org.slf4j.impl.StaticLoggerBinder; 位于哪个包, logback,slf4j-simple,以及一些桥接包都有该实现
public class StaticLoggerBinder implements LoggerFactoryBinder {
public ILoggerFactory getLoggerFactory() {
return this.contextSelectorBinder.getContextSelector().getLoggerContext();
}
}
存在多个slf4j实现时,不会报错,而是会选择其中一个作为实现
# logback日志(ch.qos)
# 使用案例
<!-- loback本身自带 lobback-core 以及 slf4j-api依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
/**
* @author automannn
* @Date 2022/8/31
*/
public class LogbackLogTest {
public static void main(String[] args) {
//使用方式同 slf4j,略
}
}
# 配置文件(logback.xml)
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<property name="log.path" value="F:\\logback.log" />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- <filter class="com.example.logback.filter.MyFilter" /> -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<appender name="file"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}.%d{yyyy-MM-dd}.zip</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
<logger name="com.example.logback" level="warn" />
</configuration>
# 日志框架在常用框架中的使用
# lombok
@Slf4j
使用slf4j
,需要依赖至少一个slf4j实现类@Log4j
使用log4j
作为日志实现,直接使用即可@Log4j2
使用log4j2
,直接使用即可
# springboot
默认使用logback进行实现
# springboot配置
logging.level.con.automannn = debug
logging.pattern.console=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%p] %c -%m%n
logging.pattern.file=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%p] %c -%m%n
# 两者只能留下一个path默认文件名是spring.log
#logging.file.name=./logs/springboot.log
logging.file.path=./logs
logging.logback.rollingpolicy.file-name-pattern=${logging.file.path}/roll_logback.%d{yyyy-MM-dd-HH-mm-ss}.log%i.zip
logging.logback.rollingpolicy.max-file-size=1KB
logging.logback.rollingpolicy.max-history=30
# 自定义配置
在类路径下,放上每个日志框架自己的配置文件,SpringBoot就不使用默认配置
日志框架 | 配置文件 |
---|---|
Logback | logback-spring.xml,logback.xml |
Log4j2 | log4j2-spring.xml, log4j2.xml |
JUL | logging.properties |
# 切换至log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 排除logging依赖:logback依赖-->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<!-- 引入log4j2的依赖,底层依然使用slf4j-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>