- 目录:
- 一、日志的概念
- 二、Java日志框架
- 三、JUL日志框架
-
四、JUL日志框架代码实现
1、默认配置日志输出
2、直接对应日志级别输出
3、自定义编码形式日志输出
4、Logger对象的父子关系
5、加载自定义配置文件
- 五、JUL日志配置文件
1、JDK默认的日志配置文件
2、自定义日志配置文件
3、自定义配置文件的详细解读
tips:Ctrl+F快速定位到自己所需内容阅读吧。
一、日志的概念
日志文件是用于记录系统操作事件的文件集合,可分为事件日志和消息日志。
①调试日志
软件开发中,我们经常需要去调试程序,做一些信息,状态的输出便于我们查询程序的运行状况。日志主要是为了更方便的去重现问题。
②系统日志
系统日志是记录系统中硬件、软件和系统问题的信息,同时还可以监视系统中发生的事件。系统日志包括系统日志、应用程序日志和安全日志。
二、Java日志框架
JUL(java util logging)、logback、log4j、log4j2
JCL(Jakarta Commons Logging)、slf4j( Simple Logging Facade for Java)
①日志门面
JCL、slf4j
②日志实现
JUL、logback、log4j、log4j2
三、JUL日志框架
①Loggers
应用程序访问日志系统的入口程序,用户使用Logger来进行日志记录;
②Appenders(Handlers)
每个Logger都会关联一组Handlers,Logger会将日志交给关联Handlers处理,由Handlers负责将日志做记录;
Handlers的具体实现决定了日志存放位置(如:FileHandler对应文件、ConsoleHandler对应控制台,等等);
③Layouts(Formatters)
负责对日志事件中的数据进行转换和格式化,它决定了数据在一条日志记录中的最终形式;
④Level
每条日志消息都有一个关联的日志级别,可以将Level和Loggers,Handlers做关联以便于我们过滤消息;
⑤Filters
过滤器,根据需要定制哪些信息会被记录,哪些信息会被放过。
四、JUL日志框架代码实现
①获取 Logger 对象
②日志记录输出
info(String msg)//直接输出
log(Level level, String msg)//设置日志级别输出
log(Level level, String msg, Object params[])//通过占位符 输出变量值
//1.获取日志记录器对象
//命名:通常使用当前类的全限定类名(包名+类名)
Logger logger = Logger.getLogger("com.stone.JULTest");
//2.日志记录输出
//2.1.直接输出日志
logger.info("hello jul");
//2.2.设置级别 输出日志
logger.log(Level.INFO, "level info msg");
//2.3.通过占位符方式 输出变量值
String msg = "hello world";
Integer num = 123;
logger.log(Level.INFO, "user message:{0}, {1}", new Object[]{msg, num});
public void severe(String msg) {
log(Level.SEVERE, msg);
}
//1.获取日志记录器对象
Logger logger = Logger.getLogger("com.stone.JULTest");
//2.日志记录输出
logger.severe("severe");
logger.warning("warning");
logger.info("info");//默认日志输出级别
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
①获取日志记录对象Logger
②用该对象关闭其系统默认配置
③创建Handler
④创建Formatter
⑤通过Handler设置Formatter
⑥通过Logger对象设置Handler(一个Logger对象可以设置多个Handler)
⑦设置Handler、Logger日志级别
//1.获取日志记录器对象
Logger logger = Logger.getLogger("com.stone.JULTest");
//1.1.关闭系统默认配置
logger.setUseParentHandlers(false);
//1.2.自定义配置日志级别(关联处理器&转换器)
//1.2.1.创建ConsoleHandler
ConsoleHandler consoleHandler = new ConsoleHandler();
//1.2.2.创建简单格式转换对象
SimpleFormatter simpleFormatter = new SimpleFormatter();
//1.2.3.进行关联
consoleHandler.setFormatter(simpleFormatter);
logger.addHandler(consoleHandler);
//1.3.设置日志具体级别
logger.setLevel(Level.ALL);
consoleHandler.setLevel(Level.ALL);
//场景FileHandler 文件输出
FileHandler fileHandler = new FileHandler("D:/logs/jul.log");
//进行关联
fileHandler.setFormatter(simpleFormatter);
logger.addHandler(fileHandler);
//2.日志记录输出
logger.severe("severe");
logger.warning("warning");
logger.info("info");//默认日志输出级别
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
①三个对象:RootLogger、loggerParent 、loggerChild
②loggerParent.setUseParentHandlers(false);//关闭使用系统默认配置
③loggerChild.severe("severe");//日志记录输出
注意:由于loggerChild并没有setUseParentHandlers(false),故而此处loggerChild会默认使用loggerParent的自定义配置。
父子关系:由命名的层级决定,如com是com.stone的父对象。
RootLogger名称:默认其name为""。
Logger loggerChild = Logger.getLogger("com.stone");
Logger loggerParent = Logger.getLogger("com");
//默认按照 命名目录层级关系 来设置父子关系
System.out.println(loggerParent == loggerChild.getParent());//true
//所有日志记录器的*父元素 LogManager$RootLogger,默认的name:""。
System.out.println("loggerParent's default parent: " + loggerParent.getParent() + ", name: "
+ loggerParent.getParent().getName());
//1.1.关闭系统默认配置
loggerParent.setUseParentHandlers(false);
//1.2.自定义配置日志级别(关联处理器&转换器)
//1.2.1.创建ConsoleHandler
ConsoleHandler consoleHandler = new ConsoleHandler();
//1.2.2.创建简单格式转换对象
SimpleFormatter simpleFormatter = new SimpleFormatter();
//1.2.3.进行关联
consoleHandler.setFormatter(simpleFormatter);
loggerParent.addHandler(consoleHandler);
//1.3.设置日志具体级别
loggerParent.setLevel(Level.ALL);
consoleHandler.setLevel(Level.ALL);
//2.日志记录输出
//loggerChild会按照loggerParent设置的日志级别输出对应的日志
loggerChild.severe("severe");
loggerChild.warning("warning");
loggerChild.info("info");//默认日志输出级别
loggerChild.config("config");
loggerChild.fine("fine");
loggerChild.finer("finer");
loggerChild.finest("finest");
①通过类加载器 读取配置文件
②创建LogManager对象
③通过LogManger加载配置文件
④创建日志输出类
⑤日志记录输出
//1.通过类加载器 读取配置文件
InputStream inputStream = JULTest.class.getClassLoader().getResourceAsStream("logging.properties");
//2.创建LogManager
LogManager logManager = LogManager.getLogManager();
//3.通过LogManger加载配置文件
logManager.readConfiguration(inputStream);
//创建日志记录器
Logger logger = Logger.getLogger("com.stone.JULTest");
//日志记录输出
logger.severe("severe");
logger.warning("warning");
logger.info("info");//默认日志输出级别
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
五、JUL日志配置文件
可以通过LogManager#readConfiguration()方法进行定位,可以在IDEA中将断电打在以下代码处:
// row:1298
f = new File(f, "logging.properties");
然后我们运行代码,获取Logger对象的时候就能定位到JDK默认配置文件位置。Windows系统下默认目录地址为:C:\Program Files\Java\jdk1.8.0_311\jre\lib
断点调试定位logging.properties文件.png
核心思想其实跟上述编码的思想一致,将配置文件抽离出来可以做到一定程度的解耦,也提高了配置文件复用性。
关键步骤:
①配置handlers
②配置.level
③关联handlers与formatter
④指定日志消息格式
⑤自定义日志形式
# RootLogger 配置*父元素指定的默认处理器为: ConsoleHandler
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
# RootLogger *父元素默认的日志级别为:ALL
.level= ALL
# 向日志文件输出的 handler 对象
# 指定日志文件输出路径:D:/logs/java%u.log
java.util.logging.FileHandler.pattern = D:/logs/java%u.log
# 指定日志文件内容大小
java.util.logging.FileHandler.limit = 50000
# 指定日志文件数量
java.util.logging.FileHandler.count = 1
# 指定 handler 对象日志消息格式对象 默认为:XMLFormatter,此处修改为:SimpleFormatter
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
# 指定以追加的方式添加日志内容
java.util.logging.FileHandler.append = true
# 向控制台输出的 handler 对象
# 指定 handler 对象的日志级别
java.util.logging.ConsoleHandler.level = ALL
# 指定 handler 对象的日志消息格式对象 SimpleFormatter
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 指定 handler 对象的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
# 指定日志消息格式
java.util.logging.SimpleFormatter.format = %1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%n
# 此处设置会使以 com.stone. 开头命名的 Logger 对象采用此配置
# 自定义 Logger 使用
# For example, set the com.stone logger to use ConsoleHandler as handlers
com.stone.handlers = java.util.logging.ConsoleHandler
# For example, set the com.stone logger to only log CONFIG
com.stone.level = CONFIG
# 关闭默认配置
com.stone.useParentHandlers = false
①、配置RootLogger的handlers
Logger内部使用CopyOnWriteArrayList集合存储handlers,具体源码如下:
private final CopyOnWriteArrayList<Handler> handlers =
new CopyOnWriteArrayList<>();
所以我们可以给Logger对象设置多个handlers,只需用","分隔开即可,如下:
# RootLogger 配置*父元素指定的默认处理器为: ConsoleHandler
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
②、配置RootLogger的日志级别level
RootLogger对象的默认名称为"",具体源码如下:
private RootLogger() {
// We do not call the protected Logger two args constructor here,
// to avoid calling LogManager.getLogManager() from within the
// RootLogger constructor.
super("", null, null, LogManager.this, true);
}
Logger(String name, String resourceBundleName, Class<?> caller, LogManager manager, boolean isSystemLogger) {
this.manager = manager;
this.isSystemLogger = isSystemLogger;
setupResourceInfo(resourceBundleName, caller);
this.name = name;
levelValue = Level.INFO.intValue();
}
另外需要注意的是,此处需要使用ALL大写字母形式,因为Level类的具体实现是如此,源码如下:
/**
* ALL indicates that all messages should be logged.
* This level is initialized to <CODE>Integer.MIN_VALUE</CODE>.
*/
public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle);
所以我们给RootLogger对象的level属性设置日志级别时使用如下配置:
# RootLogger *父元素默认的日志级别为:ALL
.level= ALL
③、配置FileHandler相关属性
pattern 输出路径
limit 内容大小
count 文件数量
formatter 编辑器
append 内容追加
# 向日志文件输出的 handler 对象
# 指定日志文件输出路径:D:/logs/java%u.log
java.util.logging.FileHandler.pattern = D:/logs/java%u.log
# 指定日志文件内容大小
java.util.logging.FileHandler.limit = 50000
# 指定日志文件数量
java.util.logging.FileHandler.count = 1
# 指定 handler 对象日志消息格式对象 默认为:XMLFormatter,此处修改为:SimpleFormatter
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
# 指定以追加的方式添加日志内容
java.util.logging.FileHandler.append = true
④、配置ConsoleHandler相关属性
level 日志级别
formatter 编辑器
encoding 字符集
# 向控制台输出的 handler 对象
# 指定 handler 对象的日志级别
java.util.logging.ConsoleHandler.level = ALL
# 指定 handler 对象的日志消息格式对象 SimpleFormatter
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 指定 handler 对象的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
⑤指定日志消息格式
配置SimpleFormatter的format属性,以下为JDK官方样例:
Some example formats:
java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"
This prints 1 line with the log level (4$), the log message (5$) and the timestamp (1$) in a square bracket.
WARNING: warning message [Tue Mar 22 13:11:31 PDT 2011]
java.util.logging.SimpleFormatter.format="%1$tc %2$s%n%4$s: %5$s%6$s%n"
This prints 2 lines where the first line includes the timestamp (1$) and the source (2$); the second line includes the log level (4$) and the log message (5$) followed with the throwable and its backtrace (6$), if any:
Tue Mar 22 13:11:31 PDT 2011 MyClass fatal
SEVERE: several message with an exception
java.lang.IllegalArgumentException: invalid argument
at MyClass.mash(MyClass.java:9)
at MyClass.crunch(MyClass.java:6)
at MyClass.main(MyClass.java:3)
java.util.logging.SimpleFormatter.format="%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%n"
This prints 2 lines similar to the example above with a different date/time formatting and does not print the throwable and its backtrace:
Mar 22, 2011 1:11:31 PM MyClass fatal
SEVERE: several message with an exception
我们可以自定义相关内容,格式就像:String.format(format, date, source, logger, level, message, thrown);
或者我们也可以选择官方样例其一做配置,如下:
# 指定日志消息格式
java.util.logging.SimpleFormatter.format = %1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%n
⑥、对指定Logger对象做配置
使用Logger的名称,以及其属性来具体配置其内容即可,如下:
# 此处设置会使以 com.stone. 开头命名的 Logger 对象采用此配置
# 自定义 Logger 使用
# For example, set the com.stone logger to use ConsoleHandler as handlers
com.stone.handlers = java.util.logging.ConsoleHandler
# For example, set the com.stone logger to only log CONFIG
com.stone.level = CONFIG
# 关闭默认配置
com.stone.useParentHandlers = false
关键的步骤是com.stone.useParentHandlers = false,否则会失效。
六、结尾
以上即为JUL日志技术的基础内容