在Java应用开发中,日志记录是不可或缺的一环,它为我们提供了追踪程序运行状态、排查错误的关键信息,log4j作为Apache基金会旗下的一款强大日志框架,被广泛使用,许多开发者,尤其是初学者,可能会遇到一个问题:在没有创建任何配置文件(如
log4j2.xml
或
log4j2.properties
)的情况下,为什么程序依然能在控制台打印出日志?这背后就是Log4j的“默认配置”在发挥作用,本文将深入探讨Log4j的默认配置机制、其核心组成部分以及它在实际开发中的意义。
默认配置的核心机制
Log4j的默认配置,是一种“自动配置”或“紧急回退”机制,当Log4j在启动过程中,按照其预定义的查找顺序遍历所有可能的配置文件位置后,仍然找不到任何有效的配置文件时,它不会直接瘫痪或抛出致命错误,而是启用一个内置的、最简化的配置来保证基本的日志功能可用,这个设计理念至关重要,它确保了即使在开发初期或配置文件缺失的意外情况下,开发者依然能够获得关键的错误信息,而不是面对一个“沉默”的应用。
这个默认配置主要服务于两个目的:一是提供最低限度的日志输出,二是避免因日志系统初始化失败而导致整个应用程序崩溃。
默认配置的组成与表现
Log4j的默认配置(以Log4j 2.x为例)虽然简单,但其构成依然遵循Log4j的核心架构:Logger(日志记录器)、Appender(输出器)和Layout(布局),其具体配置如下:
这个模式布局可以解读为:
让我们通过一个简单的Java代码示例来观察默认配置的实际效果,假设有一个如下:
import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;public class Main {private static final Logger logger = LogManager.getLogger(Main.class);public static void main(String[] args) {logger.debug("这是一条DEBUG级别的信息,默认不会显示。");logger.info("这是一条INFO级别的信息,默认不会显示。");logger.warn("这是一条WARN级别的信息,默认不会显示。");logger.error("这是一条ERROR级别的信息,将会被显示。");logger.fatal("这是一条FATAL级别的信息,将会被显示。");}}
如果在项目下没有任何
log4j2-*.xml
或
log4j2-*.properties
文件,运行上述代码,你将在控制台看到类似以下的输出:
12:34:56.789 [main] ERROR Main - 这是一条ERROR级别的信息,将会被显示。12:34:56.791 [main] FATAL Main - 这是一条FATAL级别的信息,将会被显示。
这清晰地展示了默认配置的行为:只输出和级别的日志到控制台,并使用了预设的格式。
如何覆盖默认配置
Log4j 2.x会按照特定的优先级顺序自动查找配置文件,下表列出了常见的配置文件类型及其查找优先级(从高到低):
|
优先级
|
文件类型
|
文件名格式
|
|
log4j2-test.xml
|
|
log4j2-test.json
|
|
log4j2-test.yml
|
|
Properties
|
log4j2-test.properties
|
|
log4j2.xml
|
|
log4j2.json
|
|
log4j2.yml
|
|
Properties
|
log4j2.properties
|
一旦找到任何一个配置文件,Log4j就会使用它来构建自己的日志上下文,后续的配置文件将被忽略,一个最简单的
log4j2.xml
配置文件,将根日志级别改为,就可以完全覆盖默认配置:
有了这个文件,之前示例中的和级别的日志也将会被输出到控制台。
理解默认配置的重要性
深入理解Log4j的默认配置并非多此一举,它在多个方面都具有实际价值:
相关问答FAQs
问题1:为什么我明明没有配置文件,控制台却有日志输出?这是否意味着我的Log4j工作正常?
答:这种现象的出现,恰恰是因为Log4j的自动默认配置机制在起作用,它表明Log4j库已成功加载,但它未能找到你自定义的配置文件(如
log4j2.xml
),它启用了一个内置的“紧急”配置:仅将及以上级别的日志输出到控制台,这虽然证明了Log4j本身在运行,但对于生产环境或需要精细化日志管理的应用来说,这通常是一个警示信号,提示你应该检查配置文件的路径、命名或语法是否正确。
问题2:Log4j 1.x和Log4j 2.x的默认配置有什么区别?
答:两者有显著区别,Log4j 2.x拥有一个非常健壮和定义明确的默认配置机制,如上文所述,它会提供一个基本的
ConsoleAppender
和
PatternLayout
,而Log4j 1.x(现已停止维护)则相对脆弱,在找不到
log4j.properties
或文件时,Log4j 1.x通常会向标准错误流输出一条类似”log4j:WARN No appenders could be found for logger…”的警告信息,并且可能导致任何日志记录都无法输出,可以说,Log4j 2.x的默认配置是设计上的一个巨大进步,它极大地改善了开发者的初次使用体验和问题排查效率。
linux系统中在查看systemout.log时log中有许多内容,用什么命令可以快速的跳到想要的信息那里?
用gedit打开,ctrl+f搜索一下你想要的内容
一个java Web 项目,导入log4j.jar 包时有一个log4j.properties文件一起导入……
导入 包时有一个文件一起导入. 至于是干嘛的。
楼上的大大们,都说的很清楚了。
包,只是MS SQLServer2005的JDBC驱动包,这个驱动包不需要相对应的配置文件,如不需要** 需要**做配置的,并不多,log4j算是其中之一~~~ log4j包,是为了输出日志用的,文件是初始化了很多变量~ 在工程被服务器(如TOMCAT)加载时,必须初始化log4j相关配置。
python里面test.log 是什么意思
1. logging介绍Python的logging模块提供了通用的日志系统,可以方便第三方模块或者是应用使用。
这个模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP GET/POST,SMTP,Socket等,甚至可以自己实现具体的日志记录方式。
logging模块与log4j的机制是一样的,只是具体的实现细节不同。
模块提供logger,handler,filter,formatter。
logger:提供日志接口,供应用代码使用。
logger最长用的操作有两类:配置和发送日志消息。
可以通过(name)获取logger对象,如果不指定name则返回root对象,多次使用相同的name调用getLogger方法返回同一个logger对象。
handler:将日志记录(log record)发送到合适的目的地(destination),比如文件,socket等。
一个logger对象可以通过addHandler方法添加0到多个handler,每个handler又可以定义不同日志级别,以实现日志分级过滤显示。
filter:提供一种优雅的方式决定一个日志记录是否发送到handler。
formatter:指定日志记录输出的具体格式。
formatter的构造方法需要两个参数:消息的格式字符串和日期字符串,这两个参数都是可选的。
与log4j类似,logger,handler和日志消息的调用可以有具体的日志级别(Level),只有在日志消息的级别大于logger和handler的级别。
[python] view plain copy print?import loggingimport _FILE = = (LOG_FILE, maxBytes = 1024*1024, backupCount = 5) # 实例化handler fmt = %(asctime)s - %(filename)s:%(lineno)s - %(name)s - %(message)sformatter = (fmt) # 实例化(formatter)# 为handler添加formatterlogger = (tst)# 获取名为tst的(handler) # 为logger添加()(first info message)(first debug message)输出:[plain] view plain copy print?2012-03-04 23:21:59,682 - log_:16 - tst - first info message 2012-03-04 23:21:59,682 - log_:17 - tst - first debug message关于formatter的配置,采用的是%()s的形式,就是字典的关键字替换。
提供的关键字包括:FormatDescription%(name)sName of the logger (logging channel).%(levelno)sNumeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL).%(levelname)sText logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL).%(pathname)sFull pathname of the Source file where the logging call was issued (if available).%(filename)sFilename portion of pathname.%(module)sModule (name portion of filename).%(funcName)sName of function containing the logging call.%(lineno)dSource line number where the logging call was issued (if available).%(created)fTime when the LogRecord was created (as returned by ()).%(relativeCreated)dTime in milliseconds when the LogRecord was created, relative to the time the logging module was loaded.%(asctime)sHuman-readable time when the LogRecord was created. By default this is of the form “2003-07-08 16:49:45,896” (the numbers after the comma are millisecond portion of the time).%(msecs)dMillisecond portion of the time when the LogRecord was created.%(thread)dThread ID (if available).%(threadName)sThread name (if available).%(process)dProcess ID (if available).%(message)sThe logged message, computed as msg % args.这个是摘自官网,提供了很多信息。
2. logging的配置logging的配置可以采用python代码或是配置文件。
python代码的方式就是在应用的主模块中,构建handler,handler,formatter等对象。
而配置文件的方式是将这些对象的依赖关系分离出来放在文件中。
比如前面的例子就类似于python代码的配置方式。
这里看一下采用配置文件的方式。
[python] view plain copy print?import loggingimport ()# 采用配置文件# create loggerlogger = (simpleExample)# application (debug message)(info message)(warn message)(error message)(critical message)采用了模式匹配的方式进行配置,正则表达式是r^[(.*)]$,从而匹配出所有的组件。
对于同一个组件具有多个实例的情况使用逗号‘,’进行分隔。
对于一个实例的配置采用componentName_instanceName配置块。
使用这种方式还是蛮简单的。
[plain] view plain copy print?[loggers]keys=root,simpleExample[handlers]keys=consoleHandler[formatters]keys=simpleFormatter[logger_root]level=DEBUGhandlers=consoleHandler[logger_simpleExample]level=DEBUGhandlers=consoleHandlerqualname=simpleExamplepropagate=0[handler_consoleHandler]class=StreamHandlerlevel=DEBUGformatter=simpleFormatterargs=(,)[formatter_simpleFormatter]format=%(asctime)s - %(name)s - %(levelname)s - %(message)sdatefmt=在指定handler的配置时,class是具体的handler类的类名,可以是相对logging模块或是全路径类名,比如需要RotatingFileHandler,则class的值可以为:RotatingFileHandler或者。
args就是要传给这个类的构造方法的参数,就是一个元组,按照构造方法声明的参数的顺序。
输出:[plain] view plain copy print?2012-03-06 00:09:35,713 - simpleExample - DEBUG - debug message2012-03-06 00:09:35,713 - simpleExample - INFO - info message2012-03-06 00:09:35,714 - simpleExample - WARNING - warn message2012-03-06 00:09:35,714 - simpleExample - ERROR - error message2012-03-06 00:09:35,714 - simpleExample - CRITICAL - critical message这里还要明确一点,logger对象是有继承关系的,比如名为a.b和a.c的logger都是名为a的子logger,并且所有的logger对象都继承于root。
如果子对象没有添加handler等一些配置,会从父对象那继承。
这样就可以通过这种继承关系来复用配置。
3. 多模块使用logginglogging模块保证在同一个python解释器内,多次调用(log_name)都会返回同一个logger实例,即使是在多个模块的情况下。
所以典型的多模块场景下使用logging的方式是在main模块中配置logging,这个配置会作用于多个的子模块,然后在其他模块中直接通过getLogger获取Logger对象即可。
这里使用上面配置文件:[plain] view plain copy print?[loggers]keys=root,main[handlers]keys=consoleHandler,fileHandler[formatters]keys=fmt[logger_root]level=DEBUGhandlers=consoleHandler[logger_main]level=DEBUGqualname=mainhandlers=fileHandler[handler_consoleHandler]class=StreamHandlerlevel=DEBUGformatter=fmtargs=(,)[handler_fileHandler]class==DEBUGformatter=fmtargs=(,a,,5,)[formatter_fmt]format=%(asctime)s - %(name)s - %(levelname)s - %(message)sdatefmt=主模块:[python] view plain copy print?import loggingimport ()root_logger = (root)root_(test root logger...)logger = (main)(test main logger)(start import module \mod\...)import (let\s test ())()root_(finish test...)子模块:[python] view plain copy print?import loggingimport submodlogger = ()(logger of mod say something...)def testLogger()(this is ...)()子子模块:[python] view plain copy print?import logginglogger = ()(logger of submod say something...)def tst()(this is ()...)然后运行python ,控制台输出:[plain] view plain copy print?2012-03-09 18:22:22,793 - root - DEBUG - test root logger...2012-03-09 18:22:22,793 - main - INFO - test main logger2012-03-09 18:22:22,809 - main - INFO - start import module mod...2012-03-09 18:22:22,809 - - INFO - logger of submod say something...2012-03-09 18:22:22,809 - - INFO - logger say something...2012-03-09 18:22:22,809 - main - DEBUG - lets test ()2012-03-09 18:22:22,825 - - DEBUG - this is ...2012-03-09 18:22:22,825 - - INFO - this is ()...2012-03-09 18:22:22,841 - root - INFO - finish test...可以看出,和预想的一样,然后在看一下,logger配置中的输出的目的地:[plain] view plain copy print?2012-03-09 18:22:22,793 - main - INFO - test main logger2012-03-09 18:22:22,809 - main - INFO - start import module mod...2012-03-09 18:22:22,809 - - INFO - logger of submod say something...2012-03-09 18:22:22,809 - - INFO - logger say something...2012-03-09 18:22:22,809 - main - DEBUG - lets test ()2012-03-09 18:22:22,825 - - DEBUG - this is ...2012-03-09 18:22:22,825 - - INFO - this is ()中没有root logger输出的信息,因为中配置了只有main logger及其子logger使用RotatingFileHandler,而root logger是输出到标准输出。
发表评论