Log4j是Apache的一个开源项目,通过使用Log4j我们可以控制日志信息输送的目的地(控制台/文件/GUI组件/套接字服务器/NT事件记录器等),也可以控制每一条日志的输出格式,通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程,最重要的是我们可以通过一个配置文件来灵活的进行动态配置,而不需要修改应用代码
官网:https://logging.apache.org/log4j/1.2/
Log4j的产生比Java原生的日志框架JUL还要早,原生的JUL借鉴了Log4j而产生,所以在组件结构和内部逻辑非常相似
1. 组件说明
Log4j重要的三大组件:
- Loggers:日志记录器,它控制日志输出以及日志级别
- Appenders:输出控制器,指定日志的输出位置(控制台/文件/GUI组件等等)
- Layout:日志格式化器,指定日志信息的输出格式
它们分别对标了JUL的loggers,Handler,Formatter
1)Loggers
日志记录器,负责收集处理日志记录,实例的命名就是类的全限定名,如:com.kxj.log4j.xxx。Logger的名称大小写敏感,其命名和JUL一样具有继承机制,例如:com.kxj.log4j会继承com.kxj的配置信息,具体的全限定名继承抽象的全限定名
和JUL一样Log4j同样是树结构存储,有一个根Logger(RootLogger),其他所有的Logger都会直接或间接的继承自root,自定义的Logger可以用getRootLogger()方法获取
- com.kxj.log4j.wql(儿子节点)
- com.kxj.log4j(父节点)
- com.kxj(爷节点)
- RootLogger(根节点)
注:自log4j 1.2版以来,Logger类已经取代了Category类。在早期的Log4j版本,Logger类可以被视为Category类的别名
关于日志级别信息,例如DEBUG、INFO、WARN、ERROR……级别是分大小的,Debug<Info<Warn<Error,分别用来指定这条日志信息的重要程度,Log4j输出日志的规则是:只输出级别不低于设定级别的日志信息,假设Logger级别设置为Info,则Info、Warn、Error级别的日志信息都会输出,而日志级别比Info低的debug则不会输出(和JUL是一样的原理)
2)Appenders
记录日志以及定义日志的级别仅仅是Log4j的基本功能,Log4j日志系统还提供了许多强大的功能,比如允许把日志输出到不同的地方(Console/File/GUI组件等等),可以根据天数或者文件大小产生新的文件,可以以流的形式发送到其他地方等等
常用的Appenders:
- ConsoleAppender:将日志输出到控制台
- FileAppender:将日志输出到文件
- DailyRollingFileAppender:按照时间将日志输出到一个日志文件,并且每天输出到一个新的文件
- RollingFileAppender:将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会把文件改名,同时产生一个新的文件
- JDBCAppender:把日志信息保存到数据库
3)Layouts
格式输出日志,Layouts提供了四种日志输出样式,如根据Html样式、自由指定样式、包含日志级别与信息的样式与信息的样式和包含日志时间、线程、类别等信息的样式
常用的Layouts:
- HTMLLayout:格式化日志输出为HTML表格形式
- SimpleLayout:简单的日志输出格式化,打印的日志格式如默认的INFO级别的消息
- PatternLayout:最强大的格式化组件,可以根据自定义格式输出日志,如果没有指定转换格式,就是用默认的转换格式
2. 入门案例
导入依赖:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
1)和JUL操作一样直接输出(错误示范)
@Test public void test1(){ //logger入口程序 Logger logger = Logger.getLogger(Log4jTest1.class); logger.info("wdasdwq"); }
报错:
log4j:WARN No appenders could be found for logger (com.kxj.log4j.test.Log4jTest1). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
原因:没有对Logger进行初始化,没有寻找到appenders,它和JUL不一样,JUL配置文件配置了默认Handler
2)初始化后进行输出
@Test public void test1(){ //加载初始化配置 BasicConfigurator.configure(); //logger入口程序 Logger logger = Logger.getLogger(Log4jTest1.class); logger.info("wdasdwq"); }
输出:
0 [main] INFO com.kxj.log4j.test.Log4jTest1 - wdasdwq
加载初始化配置底层源码:默认加入了一个rootLogger根节点,并配置了默认的ConsoleAppender输出和PatternLayout格式化
static public void configure() { Logger root = Logger.getRootLogger(); root.addAppender(new ConsoleAppender( new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN))); }
3. 日志级别说明
Log4j提供了8个级别的日志等级:由低到高
- ALL:最低等级,用于打开所有级别的日志记录
- TRACE:跟踪信息,等级也非常低,一般情况下不使用
- DEBUG(默认级别):指出细粒度信息事件对调试应用程序是非常有帮助的,主要配合开发,在开发过程中打印一些重要信息
- INFO:消息的粗粒度级别运行信息
- WARN:警告信息,程序在运行过程中会出现的有可能发生的隐形的错误
- ERROR:系统的错误信息,如果不想要输出过多的日志设置该级别即可
- FATAL:表示有种错误,一旦系统发生就不能运行的严重错误
- OFF:最高等级的级别,用户关闭所有的日志记录
注:
- ERROR:这个错误的发生不会影响整个系统的运行,最多是某个模块错误,比如:找不到数据库等
- FATAL:这个错误会直接终结这个系统
- 和JUL一样,只会输出比当前日志等级更高的日志信息
例:输出各个日志等级
@Test public void test1(){ BasicConfigurator.configure(); Logger logger = Logger.getLogger(Log4jTest1.class); logger.trace("输出trace日志等级"); logger.debug("输出debug日志等级"); logger.info("输出info日志等级"); logger.warn("输出warn日志等级"); logger.error("输出error日志等级"); logger.fatal("输出fatal日志等级"); }
输出:默认debug,只输出debug以上的日志等级信息
0 [main] DEBUG com.kxj.log4j.test.Log4jTest1 - 输出debug日志等级 4 [main] INFO com.kxj.log4j.test.Log4jTest1 - 输出info日志等级 4 [main] WARN com.kxj.log4j.test.Log4jTest1 - 输出warn日志等级 4 [main] ERROR com.kxj.log4j.test.Log4jTest1 - 输出error日志等级 4 [main] FATAL com.kxj.log4j.test.Log4jTest1 - 输出fatal日志等级
4. 日志管理器分析配置文件
1)BasicConfigurator.configure()源码解析
static public void configure() { Logger root = Logger.getRootLogger(); root.addAppender(new ConsoleAppender( new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN))); }
- 创建了根节点对象,Logger root = Logger.getRootLogger()
- 根节点添加了ConsoleAppender对象
- 定义了PatternLayout格式化对象
这是Log4j默认提供的初始化,假如需要自定义从配置文件中获取配置并加载,就不能使用默认初始化
2)分析Logger.getLogger()源码
① 进入该方法
static public Logger getLogger(Class clazz) { //返回日志管理器 return LogManager.getLogger(clazz.getName()); }
② 点击LogManager对象
static public final String DEFAULT_CONFIGURATION_FILE = "log4j.properties"; static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml"; static final public String DEFAULT_CONFIGURATION_KEY="log4j.configuration"; static final public String CONFIGURATOR_CLASS_KEY="log4j.configuratorClass"; public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride";
LogManager管理着全部的Log4j配置文件,开发中最常用的是log4j.properties属性文件
③ 加载配置文件的代码
static { …………………… if(configurationOptionStr == null) { url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE); if(url == null) { url = Loader.getResource(DEFAULT_CONFIGURATION_FILE); } } else { try { url = new URL(configurationOptionStr); } catch (MalformedURLException ex) { url = Loader.getResource(configurationOptionStr); } } if(url != null) { LogLog.debug("Using URL ["+url+"] for automatic log4j configuration."); try { OptionConverter.selectAndConfigure(url, configuratorClassName, LogManager.getLoggerRepository()); } catch (NoClassDefFoundError e) { LogLog.warn("Error during default initialization", e); } } else { LogLog.debug("Could not find resource: ["+configurationOptionStr+"]."); } } else { LogLog.debug("Default initialization of overridden by " + DEFAULT_INIT_OVERRIDE_KEY + "property."); }} }
提供静态代码块加载配置文件,提供Loader.getResource()获取配置文件
- Loader.getResource说明只能在类路径下寻找并加载配置文件
④ 配置文件读取解析,通过OptionConverter.selectAndConfigure()方法
static public void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) { Configurator configurator = null; String filename = url.getFile(); if(clazz == null && filename != null && filename.endsWith(".xml")) { clazz = "org.apache.log4j.xml.DOMConfigurator"; } if(clazz != null) { LogLog.debug("Preferred configurator class: " + clazz); configurator = (Configurator) instantiateByClassName(clazz, Configurator.class, null); if(configurator == null) { LogLog.error("Could not instantiate configurator ["+clazz+"]."); return; } } else { configurator = new PropertyConfigurator(); } configurator.doConfigure(url, hierarchy); }
通过new PropertyConfigurator()进行映射
public class PropertyConfigurator implements Configurator { /** Used internally to keep track of configured appenders. */ protected Hashtable registry = new Hashtable(11); private LoggerRepository repository; protected LoggerFactory loggerFactory = new DefaultCategoryFactory(); static final String CATEGORY_PREFIX = "log4j.category."; static final String LOGGER_PREFIX = "log4j.logger."; static final String FACTORY_PREFIX = "log4j.factory"; static final String ADDITIVITY_PREFIX = "log4j.additivity."; static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory"; static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger"; static final String APPENDER_PREFIX = "log4j.appender."; static final String RENDERER_PREFIX = "log4j.renderer."; static final String THRESHOLD_PREFIX = "log4j.threshold"; private static final String THROWABLE_RENDERER_PREFIX = "log4j.throwableRenderer"; private static final String LOGGER_REF = "logger-ref"; private static final String ROOT_REF = "root-ref"; private static final String APPENDER_REF_TAG = "appender-ref"; ………………
- log4j.rootLogger和log4j.appender、rootLogger是必须要进行配置的
综上所述,假如配置了Log4j.properties文件,logger会自动进行加载,不需要进行初始化配置
5. 配置文件编写格式
Log4j的配置文件和一般其他的配置文件编写格式不一样,需要按照固定格式书写
1)Appender的编写格式
底层源码:
String prefix = APPENDER_PREFIX + appenderName;
- APPENDER_PREFIX = log4j.appender.
以log4j.appender为前缀在加上自定义名称(appenderName)
2)Layouter的编写格式
底层源码:
String layoutPrefix = prefix + ".layout";
- 以Appender为前缀在加上loyout进行设置
例:
#配置appender输出方式 log4j.appender.myconsole=org.apache.log4j.ConsoleAppender #配置输出的格式 log4j.appender.myconsole.layout=org.apache.log4j.SimpleLayout
3)RootLogger的编写格式
- 通过log4j.rootLogger在类中搜索
- 找到void configureRootCategory方法
观察该方法,找到new StringTokenizer(value,","),这个方法表示要用逗号的方式来切割字符串,证明了log4j.rootLogger的取值,其中可以有多个值,使用逗号进行分隔
① 通过代码st.nextToken()表示切割后的第一个值是日志的级别
② while(st.hasMoreTokens())表示接下来的值,可以通过while循环遍历得到2~n个值,这就是配置的其他信息,这个信息就是appenderName
综上所述,RootLogger的配置格式为:
Log4j.rootLogger=日志级别,appenderName1,appenderName2,…………
例:配置文件并进行输出
① 配置文件
#配置appender输出方式 log4j.appender.myconsole=org.apache.log4j.ConsoleAppender #配置输出的格式 log4j.appender.myconsole.layout=org.apache.log4j.SimpleLayout #配置根节点 log4j.rootLogger=trace,myconsole
② 代码
@Test public void test1(){ Logger logger = Logger.getLogger(Log4jTest1.class); logger.trace("输出trace日志等级"); logger.debug("输出debug日志等级"); logger.info("输出info日志等级"); logger.warn("输出warn日志等级"); logger.error("输出error日志等级"); logger.fatal("输出fatal日志等级"); }
③ 输出
TRACE - 输出trace日志等级 DEBUG - 输出debug日志等级 INFO - 输出info日志等级 WARN - 输出warn日志等级 ERROR - 输出error日志等级 FATAL - 输出fatal日志等级
输出了TRACE说明设置成功
6. Layout自定义格式
Layouter格式化:
- HTMLLayout:格式化日志输出为HTML表格形式
- SimpleLayout:简单的日志输出格式化,打印的日志格式如默认的INFO级别的消息
- PatternLayout:最强大的格式化组件,可以根据自定义格式输出日志,如果没有指定转换格式,就是用默认的转换格式
其中最常用的是PatternLayout,
这种格式化输出采用类似于C语言的printf函数的打印格式化日志消息,具体的占位符及其含义如下:
- %m:输出代码中指定的日志信息
- %p:输出优先级,及DEBUG、INFO等
- %n:换行符(Windown平台的换行符"\n",UNIX平台为"\n")
- %r:输出自应用启动到输出该log信息耗费的毫秒数
- %c:输出打印语句所属的类的全名
- %t:输出产生该日志的线程全名
- %d:输出服务器当前时间,默认为ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日 HH:mm:ss}
- %l:输出日志时间发生的位置,包括类名、线程、及在代码中的行数,如:Test.main(Test.java:10)
- %F:输出日志消息产生时所在的文件名称
- %L:输出代码中的行号
- %%:输出一个"%"字符,可以在%与字符之间加上修饰来控制最小宽度、最大宽度和文本的对其方式
可以在%与字符之间加上修饰符来控制最小宽度,最大宽度和文本对其方式:
- %5c:输出category名称,最小宽度是5,category<5,默认的情况下右对齐
- %-5c:输出category名称,最小宽度是5,category<5,"-"号指定左对齐,会有空格
- %.5c:输出category名称,最大宽度是5,category>5,就会将左边多出的字符截掉,<5不会有空格
- %20.30c:名称<20补空格,并且右对齐,>30字符,就从左边交远削出的字符截断
配置文件开启自定义格式:
#配置输出的格式 log4j.appender.appenderName.layout=org.apache.log4j.PatternLayout #配置具体的PatternLayout log4j.appender.appenderName.layout.conversionPattern = 自定义格式
例:配置自定义PatternLayout
① 配置文件
#配置appender输出方式 log4j.appender.myconsole=org.apache.log4j.ConsoleAppender #配置输出的格式 log4j.appender.myconsole.layout=org.apache.log4j.PatternLayout #配置根节点 log4j.rootLogger=trace,myconsole #配置具体的PatternLayout格式 log4j.appender.myconsole.layout.conversionPattern=[%p]%r %c %t %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
② 代码
@Test public void test1(){ Logger logger = Logger.getLogger(Log4jTest1.class); logger.trace("输出trace日志等级"); logger.debug("输出debug日志等级"); logger.info("输出info日志等级"); logger.warn("输出warn日志等级"); logger.error("输出error日志等级"); logger.fatal("输出fatal日志等级"); }
③ 输出
[TRACE]0 com.kxj.log4j.test.Log4jTest1 main 2022-11-04 11:26:50:948 输出trace日志等级 [DEBUG]1 com.kxj.log4j.test.Log4jTest1 main 2022-11-04 11:26:50:949 输出debug日志等级 [INFO]1 com.kxj.log4j.test.Log4jTest1 main 2022-11-04 11:26:50:949 输出info日志等级 [WARN]1 com.kxj.log4j.test.Log4jTest1 main 2022-11-04 11:26:50:949 输出warn日志等级 [ERROR]1 com.kxj.log4j.test.Log4jTest1 main 2022-11-04 11:26:50:949 输出error日志等级 [FATAL]1 com.kxj.log4j.test.Log4jTest1 main 2022-11-04 11:26:50:949 输出fatal日志等级
7. 自定义Logger
默认创建的Logger对象,默认都是继承RootLogger,在开发中可以自定义Logger,让其他Logger来继承中国logger
这种继承关系和JUL中的继承一样,都是按包结构的关系进行指定的,更具体的包名Logger继承更抽象的包名Logger,最顶级的父类依然是RootLogger
如:Logger logger = Logger.getLogger(Log4jTest1.class);
- Log4jTest1.class的包路径:com.kxj.log4j.test.Log4jTest1
- 它的父logger就是它更上层的路径
- 父logger有:com -> com.kxj -> com.kxj.log4j -> com.kxj.log4j.test
- 这些父logger彼此也有继承关系
自定义logger的配置项:
log4j.logger.父路径 = 日志级别,AppenderName1,AppenderName2,…………
例:配置Log4jTest1.class的父路径
#自定义logger log4j.logger.com.kxj.log4j.test = info,myfile log4j.appender.myfile=org.apache.log4j.FileAppender #输出格式 log4j.appender.myfile.layout=org.apache.log4j.SimpleLayout #文件输出 log4j.appender.myfile.file=D:\\mylog4j.log #文件编码 log4j.appender.myfile.encoding=UTF-8
同时配置多个父logger的问题:
- 如果多个父Logger配置的内容不相同,取它们配置并集,如:ParentLogger1配置了FileAppender,rootLogger配置了ConsolAppender,那么最终结果是控制台和文件都会输出
- 如果多个父Logger配置内容不相同,那么按照直接的父logger的配置执行,如:ParentLogger1的日志等级是Error,RootLogger日志等级为Info,那么最终以ParentLogger1日志级别为准
例:配置父logger(PrantLogger和RootLogger)
① 配置文件
log4j.appender.myconsole=org.apache.log4j.ConsoleAppender log4j.appender.myconsole.layout=org.apache.log4j.PatternLayout log4j.appender.myconsole.layout.conversionPattern=[%p]%r %c %t %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n log4j.appender.myfile=org.apache.log4j.FileAppender log4j.appender.myfile.layout=org.apache.log4j.SimpleLayout log4j.appender.myfile.file=D:\\mylog4j.log log4j.appender.myfile.encoding=UTF-8 #rootLoggeer log4j.rootLogger=info,myfile #父logger log4j.logger.com.kxj.log4j.test=trace,myconsole
② 代码
@Test public void test1(){ logger.trace("输出trace日志等级"); logger.debug("输出debug日志等级"); logger.info("输出info日志等级"); logger.warn("输出warn日志等级"); logger.error("输出error日志等级"); logger.fatal("输出fatal日志等级"); }
③ 输出
8. 其他
使用LogLog.setInternalDebugging(true)设置输出日志的详细信息
例:
@Test public void test1(){ //设置输出日志的详细信息 LogLog.setInternalDebugging(true); Logger logger = Logger.getLogger(Log4jTest1.class); logger.trace("输出trace日志等级"); logger.debug("输出debug日志等级"); logger.info("输出info日志等级"); logger.warn("输出warn日志等级"); logger.error("输出error日志等级"); logger.fatal("输出fatal日志等级"); }
输出:
log4j: Trying to find [log4j.xml] using context classloader sun.misc.Launcher$AppClassLoader@18b4aac2. log4j: Trying to find [log4j.xml] using sun.misc.Launcher$AppClassLoader@18b4aac2 class loader. log4j: Trying to find [log4j.xml] using ClassLoader.getSystemResource(). log4j: Trying to find [log4j.properties] using context classloader sun.misc.Launcher$AppClassLoader@18b4aac2. log4j: Using URL [file:/F:/Dome/Log-Dome/Log4j-Dome/target/classes/log4j.properties] for automatic log4j configuration. log4j: Reading configuration from URL file:/F:/Dome/Log-Dome/Log4j-Dome/target/classes/log4j.properties log4j: Parsing for [root] with value=[trace,myconsole]. log4j: Level token is [trace]. log4j: Category root set to TRACE log4j: Parsing appender named "myconsole". log4j: Parsing layout options for "myconsole". log4j: End of parsing for "myconsole". log4j: Parsed "myconsole" options. log4j: Finished configuring. TRACE - 输出trace日志等级 DEBUG - 输出debug日志等级 INFO - 输出info日志等级 WARN - 输出warn日志等级 ERROR - 输出error日志等级 FATAL - 输出fatal日志等级
Comments | NOTHING
Warning: Undefined variable $return_smiles in /www/wwwroot/wql_luoqin_ltd/wp-content/themes/Sakura/functions.php on line 1109