任何項目工程唁毒,日志的作用都毋庸置疑得重要蒜茴,監(jiān)控星爪,問題查找,統(tǒng)計粉私,大數(shù)據(jù)資源來源等顽腾。在閱讀spring源碼過程中開啟spring的日志對于閱讀的幫助也是非常大的。
先說說apache自帶的log組件 <b>commons-logging</b>
<em>commons-logging</em>提供的是一個日志的接口(<em>interface</em>),并有一個簡單的日志實現(xiàn)SimpleLog.class
诺核。它提供給中間件/日志工具開發(fā)者一個簡單的日志操作抽象抄肖,允許程序開發(fā)人員使用不同的具體日志實現(xiàn)工具。然后對各種實現(xiàn)的日志工具如Log4j窖杀、Avalon LogKit, and JDK(具體實現(xiàn)在java.util.logging
package中)中選擇一個漓摩。有各種整合方式。
網(wǎng)上看到的一個<em>commons-logging</em>出現(xiàn)的小故事
org.apache.commons.logging.Log 和org.apache.log4j.Logger 這兩個類入客,通過包名我們可以發(fā)現(xiàn)它們都是 apache 的項目管毙,既然如此,為何要動如此大的動作搞兩個東西(指的是 commons-logging 和 log4j)出來呢桌硫?事實上夭咬,在 sun 開發(fā) logger 前,apache 項目已經(jīng)開發(fā)了功能強大的 log4j 日志工具铆隘,并向 sun 推薦將其納入到 jdk 的一部分卓舵,可是 sun 拒絕了 apache 的提議,sun 后來自己開發(fā)了一套記錄日志的工具膀钠√屯澹可是現(xiàn)在的開源項目都使用的是 log4j裹虫,log4j 已經(jīng)成了事實上的標準,但由于又有一部分開發(fā)者在使用 sun logger忘巧,因此 apache 才推出 commons-logging恒界,使得我們不必關(guān)注我們正在使用何種日志工具
在不使用log4j的情況只使用<em>commons-logging</em> 我們也是可以打印日志出來的,下面看下源碼:
找到
點擊<b>Log</b>進入源代碼
<b>Log</b>是個interface
package org.apache.commons.logging;
public interface Log {
void debug(Object message);
void debug(Object message, Throwable t);
void error(Object message);
void error(Object message, Throwable t);
void fatal(Object message);
void fatal(Object message, Throwable t);
void info(Object message);
void info(Object message, Throwable t);
boolean isDebugEnabled();
boolean isErrorEnabled();
boolean isFatalEnabled();
boolean isInfoEnabled();
boolean isTraceEnabled();
boolean isWarnEnabled();
void trace(Object message);
void trace(Object message, Throwable t);
void warn(Object message);
void warn(Object message, Throwable t);
}
<b>LogFactory</b>是個抽象類public abstract class LogFactory
<b>LogSource</b>是以前創(chuàng)建<b>Log</b>的class 現(xiàn)在已被遺棄
<b>LogConfigurationException</b>是 <code>LogFactory</code>或者 <code>Log</code>實例創(chuàng)建失敗的時候拋出的異常
<b>impl</b>包下的<code>AvalonLogger</code><code>Jdk13LumberjackLogger</code><code>Log4JLogger</code><code>LogKitLogger</code><code>NoOpLog</code><code>SimpleLog</code>是<code>Log</code>接口的實現(xiàn)
<b>ServletContextCleaner</b>是<b>ServletContextListener</b>的實現(xiàn) 在contextDestroyed的時候把創(chuàng)建的<b>LogFactory</b>銷毀掉砚嘴。
public class ServletContextCleaner implements ServletContextListener {
private static final Class[] RELEASE_SIGNATURE = {ClassLoader.class};
public void contextDestroyed(ServletContextEvent sce) {
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
Object[] params = new Object[1];
params[0] = tccl;
…………………
} catch(InvocationTargetException ex) {
// This is not expected
System.err.println("LogFactory instance release method failed!");
loader = null;
}
}
LogFactory.release(tccl);
其中LogFactory.release(tccl)
在LogFactory中這樣是這樣實現(xiàn)
public static void release(ClassLoader classLoader) {
…………
factory.release();
factories.remove(classLoader);
}
}
}
}
其中factory是 protected static Hashtable factories = null;
在LogFactory實例化的時候 調(diào)用 static {……factories = createFactoryStore();……}
其中<b>createFactoryStore</b>創(chuàng)建了
private static final String WEAK_HASHTABLE_CLASSNAME ="org.apache.commons.logging.impl.WeakHashtable";
屬性 即WeakHashtable十酣。存放LogFactory
最后 factories.release的時候相當于清空了WeakHashtable,WeakHashtable其實是基于WeakReference 實現(xiàn)的 這種弱引用在一定情況下會被jvm優(yōu)先回收掉 所以節(jié)約內(nèi)存际长。時間久了不用jvm會回收掉后耸采,當再次用到的時候再創(chuàng)建即可。
<b>先說了銷毀再看下是怎么創(chuàng)建和選擇Log實現(xiàn)類的</b>
private static final Log log = LogFactory.getLog(FactoryBeanTests.class);
接著找到getLog
public static Log getLog(Class clazz) throws LogConfigurationException {
return getFactory().getInstance(clazz);
}
這里面有兩個重要的方法<b>getFactory()</b>和<b>getInstance(clazz)</b>
先說<b>getFactory()</b>
getFactory的時候會先取到ClassLoader 用戶加載所需要類的class文件
(基本各種判斷后獲得到的一般是當前類的classLoader classLoader = Thread.currentThread().getContextClassLoader()
)
接著LogFactory factory = getCachedFactory(contextClassLoader);
這是從上面說的WeakHashtable的實例factories中取工育,第一次肯定是沒有的 繼續(xù)往下會去讀取Properties文件
Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);
public static final String FACTORY_PROPERTIES = "commons-logging.properties";
用戶沒配置<em>commons-logging.properties</em>的情況下取到props==null
繼續(xù)往下回去系統(tǒng)啟動的屬性中去找
String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);
public static final String FACTORY_PROPERTY = "org.apache.commons.logging.LogFactory";
啟動的時候沒有設置則factoryClass==null 繼續(xù)往下
會讀取META-INF/services/org.apache.commons.logging.LogFactory這個文件
final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID);
protected static final String SERVICE_ID =
"META-INF/services/org.apache.commons.logging.LogFactory";
沒有配置這個文件的情況下繼續(xù)往下
到了
if (factory == null) {
factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);
}
public static final String FACTORY_DEFAULT = "org.apache.commons.logging.impl.LogFactoryImpl";
這個地方newFactory 方法去創(chuàng)建FACTORY_DEFAULT = "org.apache.commons.logging.impl.LogFactoryImpl"利用反射機制 默認創(chuàng)建了他的實現(xiàn)類<b>LogFactoryImpl</b>
再來看上面說的
這里面有兩個重要的方法<b>getFactory()</b>和<b>getInstance(clazz)</b>
的第二個方法getInstance 此時的getInstance是LogFactoryImpl的
public Log getInstance(Class clazz) throws LogConfigurationException {
return getInstance(clazz.getName());
}
接著往下走調(diào)用了<b>newInstance</b> 方法
if (logConstructor == null) {
instance = discoverLogImplementation(name);
}
第一次logConstructor 肯定為null所以繼續(xù)進去看
private Log discoverLogImplementation(String logCategory)
…………
initConfiguration();
String specifiedLogClassName = findUserSpecifiedLogClassName();
if (specifiedLogClassName != null) {
………………
for(int i=0; i<classesToDiscover.length && result == null; ++i) {
result = createLogFromClass(classesToDiscover[i], logCategory, true);
}
列出幾個的關(guān)鍵點initConfiguration初始化一些配置
findUserSpecifiedLogClassName 是去緩存找緩存的specifiedClass(具體的class)這里會查找這兩個虾宇,一個是以前的另一個是現(xiàn)在在用的
public static final String LOG_PROPERTY = "org.apache.commons.logging.Log";
protected static final String LOG_PROPERTY_OLD = "org.apache.commons.logging.log";
第一次所以沒有的話繼續(xù)到
for(int i=0; i<classesToDiscover.length && result == null; ++i) {
result = createLogFromClass(classesToDiscover[i], logCategory, true);
}
這個是最關(guān)鍵的部分
private static final String[] classesToDiscover = {
LOGGING_IMPL_LOG4J_LOGGER,
"org.apache.commons.logging.impl.Jdk14Logger",
"org.apache.commons.logging.impl.Jdk13LumberjackLogger",
"org.apache.commons.logging.impl.SimpleLog"
};
選擇默認找這三個
第一個org.apache.commons.logging.impl.Jdk14Logger
impl包就有 遍歷的時候判斷了
第一次沒仔細看糾結(jié)了半天 以為三個都創(chuàng)建了 其實值創(chuàng)建了一個
好了肯定是創(chuàng)建了org.apache.commons.logging.impl.Jdk14Logger 這個類
到這個類中看 就是封裝了JDK的日志實現(xiàn)
這個類Jdk14Logger 沒有找到設置日志級別的方法
繼續(xù)往jdk實現(xiàn)中找,在
java.util.logging.Logger
找到
private static Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
LogManager manager = LogManager.getLogManager();
SecurityManager sm = System.getSecurityManager();
if (sm != null && !SystemLoggerHelper.disableCallerCheck) {
if (caller.getClassLoader() == null) {
return manager.demandSystemLogger(name, resourceBundleName);
}
}
return manager.demandLogger(name, resourceBundleName, caller);
// ends up calling new Logger(name, resourceBundleName, caller)
// iff the logger doesn't exist already
}
其實決定權(quán)在LogManager manager = LogManager.getLogManager();
到LogManager 類中g(shù)etLogManager方法
public static LogManager getLogManager() {
if (manager != null) {
manager.ensureLogManagerInitialized();
}
return manager;
}
繼續(xù)找到ensureLogManagerInitialized
final void ensureLogManagerInitialized() {
final LogManager owner = this;
if (initializationDone || owner != manager) {
return;
}
………………
synchronized(this) {
assert rootLogger == null;
assert initializedCalled && !initializationDone;
// Read configuration.
owner.readPrimordialConfiguration();
…………………………
if (!owner.rootLogger.isLevelInitialized()) {
owner.rootLogger.setLevel(defaultLevel);
}
…………………………
} finally {
initializationDone = true;
}
}
}
最重要的一句owner.rootLogger.setLevel(defaultLevel);
這個defaultLevel是private final static Level defaultLevel = Level.INFO;
默認是INFO如绸,那么怎么打印DEBUG呢
其實在靜態(tài)初始化塊中還有個重要的方法owner.readPrimordialConfiguration()
其中owner是final LogManager owner = this;
即當前LogManager
繼續(xù)看<b>readPrimordialConfiguration</b>方法
private void readPrimordialConfiguration() {
……………………
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
readConfiguration();
// Platform loggers begin to delegate to java.util.logging.Logger
sun.util.logging.PlatformLogger.redirectPlatformLoggers();
return null;
……………………
}
}
看到<b>readConfiguration</b>方法,重點就是這個方法嘱朽,先會嘗試去加載 String cname = System.getProperty("java.util.logging.config.class");
java.util.logging.config.class 加載不到的話回去加載配置文件String fname = System.getProperty("java.util.logging.config.file");java.util.logging.config.file 如果還加載不到的話會去java home下面加載logging.properties ,哈哈 所以只要配置logging.properties即可了
源碼
public void readConfiguration() throws IOException, SecurityException {
checkPermission();
// if a configuration class is specified, load it and use it.
String cname = System.getProperty("java.util.logging.config.class");
if (cname != null) {
try {
// Instantiate the named class. It is its constructor's
// responsibility to initialize the logging configuration, by
// calling readConfiguration(InputStream) with a suitable stream.
try {
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
clz.newInstance();
return;
} catch (ClassNotFoundException ex) {
Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
clz.newInstance();
return;
}
} catch (Exception ex) {
System.err.println("Logging configuration class \"" + cname + "\" failed");
System.err.println("" + ex);
// keep going and useful config file.
}
}
String fname = System.getProperty("java.util.logging.config.file");
if (fname == null) {
fname = System.getProperty("java.home");
if (fname == null) {
throw new Error("Can't find java.home ??");
}
File f = new File(fname, "lib");
f = new File(f, "logging.properties");
fname = f.getCanonicalPath();
}
try (final InputStream in = new FileInputStream(fname)) {
final BufferedInputStream bin = new BufferedInputStream(in);
readConfiguration(bin);
}
}
那么怎么配置這個logging.properties呢 其實就是在readConfiguration(bin)方法中 進去看
public void readConfiguration(InputStream ins) throws IOException, SecurityException {
checkPermission();
reset();
// Load the properties
props.load(ins);
// Instantiate new configuration objects.
String names[] = parseClassNames("config");
for (int i = 0; i < names.length; i++) {
String word = names[i];
try {
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
clz.newInstance();
} catch (Exception ex) {
System.err.println("Can't load config class \"" + word + "\"");
System.err.println("" + ex);
// ex.printStackTrace();
}
}
// Set levels on any pre-existing loggers, based on the new properties.
setLevelsOnExistingLoggers();
// Notify any interested parties that our properties have changed.
// We first take a copy of the listener map so that we aren't holding any
// locks when calling the listeners.
Map<Object,Integer> listeners = null;
synchronized (listenerMap) {
if (!listenerMap.isEmpty())
listeners = new HashMap<>(listenerMap);
}
if (listeners != null) {
assert Beans.isBeansPresent();
Object ev = Beans.newPropertyChangeEvent(LogManager.class, null, null, null);
for (Map.Entry<Object,Integer> entry : listeners.entrySet()) {
Object listener = entry.getKey();
int count = entry.getValue().intValue();
for (int i = 0; i < count; i++) {
Beans.invokePropertyChange(listener, ev);
}
}
}
// Note that we need to reinitialize global handles when
// they are first referenced.
synchronized (this) {
initializedGlobalHandlers = false;
}
}
找到setLevelsOnExistingLoggers() 方法進去
synchronized private void setLevelsOnExistingLoggers() {
Enumeration<?> enum_ = props.propertyNames();
while (enum_.hasMoreElements()) {
String key = (String)enum_.nextElement();
if (!key.endsWith(".level")) {
// Not a level definition.
continue;
}
int ix = key.length() - 6;
String name = key.substring(0, ix);
Level level = getLevelProperty(key, null);
if (level == null) {
System.err.println("Bad level value for property: " + key);
continue;
}
for (LoggerContext cx : contexts()) {
Logger l = cx.findLogger(name);
if (l == null) {
continue;
}
l.setLevel(level);
}
}
}
哈哈加載!key.endsWith(".level") 以.level結(jié)尾的key 這樣你就知道怎么配置了吧
那么看看simpleConfig這個類吧
其實可以將這兩個
"org.apache.commons.logging.impl.Jdk14Logger",
"org.apache.commons.logging.impl.Jdk13LumberjackLogger",
刪除這樣就會創(chuàng)建org.apache.commons.logging.impl.SimpleLog
了怔接,我非常非常喜歡這個類 因為夠簡單
初始化的時候還是會讀取classpath下的simplelog.properties文件搪泳,這個文件主要讀取的是dateTimeFormat 方式 沒讀取到則采用默認的
看源碼
static {
// Add props from the resource simplelog.properties
InputStream in = getResourceAsStream("simplelog.properties");
if(null != in) {
try {
simpleLogProps.load(in);
in.close();
} catch(java.io.IOException e) {
// ignored
}
}
showLogName = getBooleanProperty(systemPrefix + "showlogname", showLogName);
showShortName = getBooleanProperty(systemPrefix + "showShortLogname", showShortName);
showDateTime = getBooleanProperty(systemPrefix + "showdatetime", showDateTime);
if(showDateTime) {
dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat",
dateTimeFormat);
try {
dateFormatter = new SimpleDateFormat(dateTimeFormat);
} catch(IllegalArgumentException e) {
// If the format pattern is invalid - use the default format
dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
dateFormatter = new SimpleDateFormat(dateTimeFormat);
}
}
}
static protected final String DEFAULT_DATE_TIME_FORMAT = "yyyy/MM/dd HH:mm:ss:SSS zzz";
這個類只能用作控制臺輸出 沒有做文件輸出 所以夠簡單 看源碼就是
就是調(diào)用了
protected void write(StringBuffer buffer) {
System.err.println(buffer.toString());
}
System的東西 太熟悉了 就和自己寫的一樣簡單 哈哈。但是只能用于控制臺打印 呵呵吧扼脐。
還提供了
public void setLevel(int currentLogLevel) {
this.currentLogLevel = currentLogLevel;
}
設置日志級別的岸军,所以我們可以改變?nèi)罩据敵黾墑e 不過我感覺沒有人會愿意使用這個類打印日志。
這樣我們發(fā)現(xiàn)即使沒有配置其他日志組件也是可以打印日志的
我們做個測試
build path將log4j從path中remove掉 不移除掉會創(chuàng)建到Log4JLogger這個類是和log4j的封裝類這樣這樣就沒法測試了
找個打印日志的類 我找的是FactoryBeanTests 自己加了條日志
這里有個小技巧
每次判斷級別的時候log.isDebugEnabled() 是可以少創(chuàng)建個類 假如你不判斷的話debug中
public void debug(Object message) {
getLogger().log(FQCN, Level.DEBUG, message, null);
}
getLogger會多走邏輯和創(chuàng)建對象的瓦侮〖柙蓿看到基本開源的都是這種寫法算是個好習慣吧。
跑一下
看到已經(jīng)是輸出日志的 這印證了我們的說法肚吏。
其實只要檢測到了log4j便會走Log4j的打印日志的方式 后續(xù)還想說說 slfj4 今天是說不玩了方妖。還是說下配置文件怎么配置吧。
主要配置的組件
- Loggers(記錄器)
Loggers組件在此系統(tǒng)中被分為五個級別:DEBUG罚攀、INFO党觅、WARN、ERROR和FATAL坞生。這五個級別是有順序的仔役,DEBUG < INFO < WARN < ERROR < FATAL,分別用來指定這條日志信息的重要程度是己,明白這一點很重要又兵,Log4j有一個規(guī)則:只輸出級別不低于設定級別的日志信息,假設Loggers級別設定為INFO,則INFO沛厨、WARN宙地、ERROR和FATAL級別的日志信息都會輸出,而級別比INFO低的DEBUG則不會輸出逆皮。 - Appenders (輸出源)
禁用和使用日志請求只是Log4j的基本功能宅粥,Log4j日志系統(tǒng)還提供許多強大的功能,比如允許把日志輸出到不同的地方电谣,如控制臺(Console)秽梅、文件(Files)等,可以根據(jù)天數(shù)或者文件大小產(chǎn)生新的文件剿牺,可以以流的形式發(fā)送到其它地方等等企垦。常使用的類如下:org.apache.log4j.ConsoleAppender(控制臺)org.apache.log4j.FileAppender(文件)org.apache.log4j.DailyRollingFileAppender(每天產(chǎn)生一個日志文件)org.apache.log4j.RollingFileAppender(文件大小到達指定尺寸的時候產(chǎn)生一個新的文件)org.apache.log4j.WriterAppender(將日志信息以流格式發(fā)送到任意指定的地方)配置模式:log4j.appender.appenderName = classNamelog4j.appender.appenderName.Option1 = value1…log4j.appender.appenderName.OptionN = valueN - Layouts(布局)
有時用戶希望根據(jù)自己的喜好格式化自己的日志輸出,Log4j可以在Appenders的后面附加Layouts來完成這個功能晒来。Layouts提供四種日志輸出樣式钞诡,如根據(jù)HTML樣式、自由指定樣式湃崩、包含日志級別與信息的樣式和包含日志時間荧降、線程、類別等信息的樣式攒读。常使用的類如下:org.apache.log4j.HTMLLayout(以HTML表格形式布局)org.apache.log4j.PatternLayout(可以靈活地指定布局模式)org.apache.log4j.SimpleLayout(包含日志信息的級別和信息字符串)org.apache.log4j.TTCCLayout(包含日志產(chǎn)生的時間朵诫、線程、類別等信息)配置模式:log4j.appender.appenderName.layout =classNamelog4j.appender.appenderName.layout.Option1 = value1…log4j.appender.appenderName.layout.OptionN = valueN
1整陌、配置根Logger:log4j.rootLogger = [ level ] , appenderName1, appenderName2, …log4j.additivity.org.apache=false:表示Logger不會在父Logger的appender里輸出拗窃,默認為true瞎领。level :設定日志記錄的最低級別泌辫,可設的值有OFF、FATAL九默、ERROR震放、WARN、INFO驼修、DEBUG殿遂、ALL或者自定義的級別,Log4j建議只使用中間四個級別乙各。通過在這里設定級別墨礁,您可以控制應用程序中相應級別的日志信息的開關(guān),比如在這里設定了INFO級別耳峦,則應用程序中所有DEBUG級別的日志信息將不會被打印出來恩静。appenderName:就是指定日志信息要輸出到哪里。可以同時指定多個輸出目的地驶乾,用逗號隔開邑飒。例如:log4j.rootLogger=INFO,A1,B2,C32、配置日志信息輸出目的地(appender):log4j.appender.appenderName = classNameappenderName:自定義appderName级乐,在log4j.rootLogger設置中使用疙咸;className:可設值如下:(1)org.apache.log4j.ConsoleAppender(控制臺)(2)org.apache.log4j.FileAppender(文件)(3)org.apache.log4j.DailyRollingFileAppender(每天產(chǎn)生一個日志文件)(4)org.apache.log4j.RollingFileAppender(文件大小到達指定尺寸的時候產(chǎn)生一個新的文件)(5)org.apache.log4j.WriterAppender(將日志信息以流格式發(fā)送到任意指定的地方)(1)ConsoleAppender選項:Threshold=WARN:指定日志信息的最低輸出級別,默認為DEBUG风科。ImmediateFlush=true:表示所有消息都會被立即輸出撒轮,設為false則不輸出,默認值是true贼穆。Target=System.err:默認值是System.out腔召。(2)FileAppender選項:Threshold=WARN:指定日志信息的最低輸出級別,默認為DEBUG扮惦。ImmediateFlush=true:表示所有消息都會被立即輸出臀蛛,設為false則不輸出,默認值是true崖蜜。Append=false:true表示消息增加到指定文件中浊仆,false則將消息覆蓋指定的文件內(nèi)容,默認值是true豫领。File=D:/logs/logging.log4j:指定消息輸出到logging.log4j文件中抡柿。(3)DailyRollingFileAppender選項:Threshold=WARN:指定日志信息的最低輸出級別,默認為DEBUG等恐。ImmediateFlush=true:表示所有消息都會被立即輸出洲劣,設為false則不輸出,默認值是true课蔬。Append=false:true表示消息增加到指定文件中囱稽,false則將消息覆蓋指定的文件內(nèi)容,默認值是true二跋。File=D:/logs/logging.log4j:指定當前消息輸出到logging.log4j文件中战惊。DatePattern='.'yyyy-MM:每月滾動一次日志文件,即每月產(chǎn)生一個新的日志文件扎即。當前月的日志文件名為logging.log4j吞获,前一個月的日志文件名為logging.log4j.yyyy-MM。另外谚鄙,也可以指定按周各拷、天、時闷营、分等來滾動日志文件烤黍,對應的格式如下:1)'.'yyyy-MM:每月2)'.'yyyy-ww:每周3)'.'yyyy-MM-dd:每天4)'.'yyyy-MM-dd-a:每天兩次5)'.'yyyy-MM-dd-HH:每小時6)'.'yyyy-MM-dd-HH-mm:每分鐘(4)RollingFileAppender選項:Threshold=WARN:指定日志信息的最低輸出級別,默認為DEBUG。ImmediateFlush=true:表示所有消息都會被立即輸出蚊荣,設為false則不輸出初狰,默認值是true。Append=false:true表示消息增加到指定文件中互例,false則將消息覆蓋指定的文件內(nèi)容奢入,默認值是true。File=D:/logs/logging.log4j:指定消息輸出到logging.log4j文件中媳叨。MaxFileSize=100KB:后綴可以是KB, MB 或者GB腥光。在日志文件到達該大小時,將會自動滾動糊秆,即將原來的內(nèi)容移到logging.log4j.1文件中武福。MaxBackupIndex=2:指定可以產(chǎn)生的滾動文件的最大數(shù),例如痘番,設為2則可以產(chǎn)生logging.log4j.1捉片,logging.log4j.2兩個滾動文件和一個logging.log4j文件。3汞舱、配置日志信息的輸出格式(Layout):log4j.appender.appenderName.layout=classNameclassName:可設值如下:(1)org.apache.log4j.HTMLLayout(以HTML表格形式布局)(2)org.apache.log4j.PatternLayout(可以靈活地指定布局模式)(3)org.apache.log4j.SimpleLayout(包含日志信息的級別和信息字符串)(4)org.apache.log4j.TTCCLayout(包含日志產(chǎn)生的時間伍纫、線程、類別等等信息)(1)HTMLLayout選項:LocationInfo=true:輸出java文件名稱和行號昂芜,默認值是false莹规。Title=My Logging: 默認值是Log4J Log Messages。(2)PatternLayout選項:ConversionPattern=%m%n:設定以怎樣的格式顯示消息
附上個簡單的demo
# Set root logger level to Debug and its only appender to A1
log4j.rootLogger=INFO, A1,UID
log4j.category.org.springframework = info
# A1 is set to be ConsoleAppender
log4j.appender.A1=org.apache.log4j.ConsoleAppender
# A1 uses PatternLayout
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%t] - %m%n
# A2 is set to be logfile
log4j.appender.A2=org.apache.log4j.RollingFileAppender
# Define the file name
log4j.appender.A2.File=ts.log
# Define the layout
log4j.appender.A2.layout=org.apache.log4j.PatternLayout
log4j.appender.A2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%t] %x - %m%n
log4j.appender.UID=org.apache.log4j.DailyRollingFileAppender
log4j.appender.UID.File=${catalina.base}/logs/uid.log
log4j.appender.UID.layout=org.apache.log4j.PatternLayout
log4j.appender.UID.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%t] %x - %m%n
log4j.logger.com.holly.wang=DEBUG
#unify log
log4j.logger.sysunifylog=debug, sysunifylog
log4j.appender.sysunifylog=org.apache.log4j.DailyRollingFileAppender
log4j.appender.sysunifylog.DatePattern='.'yyyy-MM-dd-HH
log4j.appender.sysunifylog.File=${catalina.base}/logs/sysunifylog.log
log4j.appender.sysunifylog.layout=org.apache.log4j.PatternLayout
log4j.appender.sysunifylog.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss.SSS}][%p] %m%n
log4j.additivity.sysunifylog = false
這里還要說個問題 看到同事在指定自定義類設置的時候還是用的
log4j.category.org.springframework = info
其實category已經(jīng)被拋棄了哈哈泌神,以后很有可能被刪除 所以不要用了 log4j.logger.com.holly.wang=DEBUG 這樣就可以了 就category改成logger既可良漱。還有自定義類設置輸出的時候經(jīng)常遇到重復輸出的問題 只需要設置log4j.additivity 為false即可。
回家嘍 好晚啦欢际!