commons-logger的原理和log4j.properties 配置

任何項目工程唁毒,日志的作用都毋庸置疑得重要蒜茴,監(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.loggingpackage中)中選擇一個漓摩。有各種整合方式。
網(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> 我們也是可以打印日志出來的,下面看下源碼:
找到

log入口.png

點擊<b>Log</b>進入源代碼

commons-logging.png

<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.Jdk14Loggerimpl包就有 遍歷的時候判斷了

result==null.png

第一次沒仔細看糾結(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的封裝類這樣這樣就沒法測試了

remove-log4j.jar.png

找個打印日志的類 我找的是FactoryBeanTests 自己加了條日志
這里有個小技巧
每次判斷級別的時候log.isDebugEnabled() 是可以少創(chuàng)建個類 假如你不判斷的話debug中

 public void debug(Object message) {
        getLogger().log(FQCN, Level.DEBUG, message, null);
    }

getLogger會多走邏輯和創(chuàng)建對象的瓦侮〖柙蓿看到基本開源的都是這種寫法算是個好習慣吧。

remove-test-pre.png

跑一下


remove-test-after.png

看到已經(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即可。

回家嘍 好晚啦欢际!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末母市,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子幼苛,更是在濱河造成了極大的恐慌窒篱,老刑警劉巖焕刮,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舶沿,死亡現(xiàn)場離奇詭異,居然都是意外死亡配并,警方通過查閱死者的電腦和手機括荡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來溉旋,“玉大人畸冲,你說我怎么就攤上這事。” “怎么了邑闲?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵算行,是天一觀的道長。 經(jīng)常有香客問我苫耸,道長州邢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任褪子,我火速辦了婚禮量淌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嫌褪。我一直安慰自己呀枢,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布笼痛。 她就那樣靜靜地躺著裙秋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缨伊。 梳的紋絲不亂的頭發(fā)上残吩,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音倘核,去河邊找鬼泣侮。 笑死,一個胖子當著我的面吹牛紧唱,可吹牛的內(nèi)容都是我干的活尊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼漏益,長吁一口氣:“原來是場噩夢啊……” “哼蛹锰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起绰疤,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤铜犬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后轻庆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體癣猾,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年余爆,在試婚紗的時候發(fā)現(xiàn)自己被綠了纷宇。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛾方,死狀恐怖像捶,靈堂內(nèi)的尸體忽然破棺而出上陕,到底是詐尸還是另有隱情,我是刑警寧澤拓春,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布释簿,位于F島的核電站,受9級特大地震影響硼莽,放射性物質(zhì)發(fā)生泄漏辕万。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一沉删、第九天 我趴在偏房一處隱蔽的房頂上張望渐尿。 院中可真熱鬧,春花似錦矾瑰、人聲如沸砖茸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凉夯。三九已至,卻和暖如春采幌,著一層夾襖步出監(jiān)牢的瞬間劲够,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工休傍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留征绎,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓磨取,卻偏偏與公主長得像人柿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子忙厌,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容