比较全面的log4j配置

话不多说先来个properties版本的 我以前一直使用的是properties来配置的

#定义根级别
log4j.rootLogger=info,warn,error,console,mail

#定义项目输出日志级别
log4j.logger.org.system=debug
log4j.logger.org.main=info
log4j.logger.org.tools=info
log4j.logger.org.springframework=warn

#控制台输出生成阶段注释
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%-d{MM-ddHH:mm:ss}-[%p][%c{3}]%m%n

###info级别输出
log4j.appender.info=org.apache.log4j.DailyRollingFileAppender
#输出到Tomcat的logs目录下
log4j.appender.info.File=${catalina.home}/logs/${webapp.root}/infrastructure/info.log
log4j.appender.info.Append=true
log4j.appender.info.Threshold=info
log4j.appender.info.layout=org.apache.log4j.PatternLayout
log4j.appender.info.layout.ConversionPattern=%-d{MM-ddHH:mm:ss}-[%p][%c{3}]%m%n
log4j.appender.info.datePattern='.'yyyy-MM-dd
log4j.appender.info.BufferedIO=true
#Buffer单位为字节,默认是8K,IOBLOCK大小默认也是8K 这样配置就可以不每一条都写入文件 注意得结合上一句使用 但是性能提高了也会有其他问题到时候就知道了
log4j.appender.info.BufferSize=8192
#定制过滤器只过滤info级别
log4j.appender.info.filter.infoFilter=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.info.filter.infoFilter.LevelMin=info
log4j.appender.info.filter.infoFilter.LevelMax=info

###warn级别输出
log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender
log4j.appender.warn.File=${catalina.home}/logs/${webapp.root}/infrastructure/warn.log
log4j.appender.warn.Append=true
log4j.appender.warn.Threshold=warn
log4j.appender.warn.layout=org.apache.log4j.PatternLayout
log4j.appender.warn.layout.ConversionPattern=%-d{MM-ddHH:mm:ss}-[%p][%c]%m%n
log4j.appender.warn.datePattern='.'yyyy-MM-dd
log4j.appender.warn.BufferedIO=true
#Buffer单位为字节,默认是8K,IOBLOCK大小默认也是8K
log4j.appender.warn.BufferSize=8192
#定制过滤器只过滤warn级别
log4j.appender.warn.filter.warnFilter=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.warn.filter.warnFilter.LevelMin=WARN
log4j.appender.warn.filter.warnFilter.LevelMax=WARN

###error级别输出
log4j.appender.error=org.apache.log4j.DailyRollingFileAppender
log4j.appender.error.File=${catalina.home}/logs/${webapp.root}/infrastructure/error.log
log4j.appender.error.Append=true
log4j.appender.error.Threshold=error
log4j.appender.error.layout=org.apache.log4j.PatternLayout
log4j.appender.error.layout.ConversionPattern=%-d{MM-ddHH:mm:ss}-[%p][%c]%m%n
log4j.appender.error.datePattern='.'yyyy-MM-dd
#设置使用缓冲
log4j.appender.error.BufferedIO=true
#Buffer单位为字节,默认是8K,IOBLOCK大小默认也是8K
log4j.appender.error.BufferSize=8192
#定制过滤器只过滤error级别
log4j.appender.error.filter.errorFilter=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.error.filter.errorFilter.LevelMin=error
log4j.appender.error.filter.errorFilter.LevelMax=error

#异常邮件发送配置
#log4j的邮件发送appender,如果有必要你可以写自己的appender
log4j.appender.mail=org.apache.log4j.net.SMTPAppender
#发送邮件的门槛,仅当等于或高于ERROR(比如FATAL)时,邮件才被发送
log4j.appender.mail.Threshold=error
#设置使用缓冲
log4j.appender.mail.BufferedIO=true
#Buffer单位为字节,默认是8K,IOBLOCK大小默认也是8K
log4j.appender.mail.BufferSize=8192
log4j.appender.mail.layout=org.apache.log4j.PatternLayout
log4j.appender.mail.layout.ConversionPattern=%-d{MM-ddHH:mm:ss}-[%p][%c]%m%n
#发送邮件的邮箱帐号
[email protected]
#SMTP邮件发送服务器地址
log4j.appender.mail.SMTPHost=smtp.163.com
#SMTP发送认证的帐号名
[email protected]
#SMTP发送认证帐号的密码
log4j.appender.mail.SMTPPassword=****************
#是否打印调试信息,如果选true,则会输出和SMTP之间的握手等详细信息
log4j.appender.mail.SMTPDebug=false
#邮件主题
log4j.appender.mail.Subject=服务异常日志
#发送到什么邮箱,如果要发送给多个邮箱,则用逗号分隔;
#如果需要发副本给某人,则加入下列行
#log4j.appender.mail.Bcc=*********@qq.com
log4j.appender.mail.To=**********@qq.com

这就是properties版本的 很多已经打了注释 但是遇上几个问题

1.发送邮件是采用同步发送的,这样将会遇到有错误日志发送时可能会很慢 实际上确实很慢

2.发送邮件是发现每次报ERROR及以上的错误日志时都会触发发送邮件的事件 好多帖子都说可以通过下面配置来缓冲数据 当达到这个数据量时才会发送

log4j.appender.mail.BufferedIO=true
#Buffer单位为字节,默认是8K,IOBLOCK大小默认也是8K
log4j.appender.mail.BufferSize=8192

然后测试并没有什么卵用(也随便吐槽下好多都是复制人家的博客,复制也罢你好歹也测试测试吧,真祸害人)不过也多亏了这些人才逼得自己去看了源码 然后在这里纠正一下  

BufferedIO=true这个没卵用自己也对应把之前发出来的配置修改一下 毕竟还是要知道问什么

BufferSize=8192这个的意思是log4j会帮你保持的日志Event的个数 默认是512并不是上面8192  拿默认的举个例子 你要是日志event超过512那么513就会覆盖之前的就是这个意思而已

然后看了网络上的好多文章真的是个字不差 说什么加下面配置就可以异步发送且是当内容大小达到某值时才发送(哎,害人不浅 异步倒是可以 摆脱看看源码 源码中的append中这一句)

	if (evaluator.isTriggeringEvent(event) ) {
			sendBuffer();
		}

然后跟踪evaluator.isTriggeringEvent(event)方法

  /**
     Is this event the e-mail triggering event?

     

This method returns true, if the event level has ERROR level or higher. Otherwise it returns false. */ public boolean isTriggeringEvent(LoggingEvent event) { return event.getLevel().isGreaterOrEqual(Level.ERROR); }


这尼玛分明是说只有日志级别达到了error及以上就返回true 所以你不管同步异步都是报错就来个邮件 


算了直接上解决方案 重写下面这个类 多加了errorSize参数 当错误日志数达到多少个在发送

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.main.log4j;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.helpers.CyclicBuffer;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.OptionHandler;
import org.apache.log4j.spi.TriggeringEventEvaluator;
import org.apache.log4j.xml.UnrecognizedElementHandler;
import org.w3c.dom.Element;

import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.InternetHeaders;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Date;
import java.util.Properties;

/**
 * Send an e-mail when a specific logging event occurs, typically on errors or
 * fatal errors.
 * 
 * 

* The number of logging events delivered in this e-mail depend on the value of * BufferSize option. The SMTPAppender keeps only the last * BufferSize logging events in its cyclic buffer. This keeps * memory requirements at a reasonable level while still delivering useful * application context. * * By default, an email message will be sent when an ERROR or higher severity * message is appended. The triggering criteria can be modified by setting the * evaluatorClass property with the name of a class implementing * TriggeringEventEvaluator, setting the evaluator property with an instance of * TriggeringEventEvaluator or nesting a triggeringPolicy element where the * specified class implements TriggeringEventEvaluator. * * This class has implemented UnrecognizedElementHandler since 1.2.15. * * Since 1.2.16, SMTP over SSL is supported by setting SMTPProtocol to "smpts". * * @author Ceki Gülcü * @since 1.0 */ public class SMTPAppender extends AppenderSkeleton implements UnrecognizedElementHandler { private String to; /** * Comma separated list of cc recipients. */ private String cc; /** * Comma separated list of bcc recipients. */ private String bcc; private String from; /** * Comma separated list of replyTo addresses. */ private String replyTo; private String subject; private String smtpHost; private String smtpUsername; private String smtpPassword; private String smtpProtocol; private int smtpPort = -1; private boolean smtpDebug = false; private int bufferSize = 512; private int errorSize = 512; private boolean locationInfo = false; private boolean sendOnClose = false; protected CyclicBuffer cb = new CyclicBuffer(bufferSize); protected Message msg; protected TriggeringEventEvaluator evaluator; /** * The default constructor will instantiate the appender with a * {@link TriggeringEventEvaluator} that will trigger on events with level * ERROR or higher. */ public SMTPAppender() { this(new DefaultEvaluator()); } /** * Use evaluator passed as parameter as the * {@link TriggeringEventEvaluator} for this SMTPAppender. */ public SMTPAppender(TriggeringEventEvaluator evaluator) { this.evaluator = evaluator; } public int getErrorSize() { return errorSize; } public void setErrorSize(int errorSize) { this.errorSize = errorSize; } /** * Activate the specified options, such as the smtp host, the recipient, * from, etc. */ public void activateOptions() { Session session = createSession(); msg = new MimeMessage(session); try { addressMessage(msg); if (subject != null) { try { msg.setSubject(MimeUtility.encodeText(subject, "UTF-8", null)); } catch (UnsupportedEncodingException ex) { LogLog.error("Unable to encode SMTP subject", ex); } } } catch (MessagingException e) { LogLog.error("Could not activate SMTPAppender options.", e); } if (evaluator instanceof OptionHandler) { ((OptionHandler) evaluator).activateOptions(); } } /** * Address message. * * @param msg * message, may not be null. * @throws MessagingException * thrown if error addressing message. * @since 1.2.14 */ protected void addressMessage(final Message msg) throws MessagingException { if (from != null) { msg.setFrom(getAddress(from)); } else { msg.setFrom(); } // Add ReplyTo addresses if defined. if (replyTo != null && replyTo.length() > 0) { msg.setReplyTo(parseAddress(replyTo)); } if (to != null && to.length() > 0) { msg.setRecipients(Message.RecipientType.TO, parseAddress(to)); } // Add CC receipients if defined. if (cc != null && cc.length() > 0) { msg.setRecipients(Message.RecipientType.CC, parseAddress(cc)); } // Add BCC receipients if defined. if (bcc != null && bcc.length() > 0) { msg.setRecipients(Message.RecipientType.BCC, parseAddress(bcc)); } } /** * Create mail session. * * @return mail session, may not be null. * @since 1.2.14 */ protected Session createSession() { Properties props = null; try { props = new Properties(System.getProperties()); } catch (SecurityException ex) { props = new Properties(); } String prefix = "mail.smtp"; if (smtpProtocol != null) { props.put("mail.transport.protocol", smtpProtocol); prefix = "mail." + smtpProtocol; } if (smtpHost != null) { props.put(prefix + ".host", smtpHost); } if (smtpPort > 0) { props.put(prefix + ".port", String.valueOf(smtpPort)); } Authenticator auth = null; if (smtpPassword != null && smtpUsername != null) { props.put(prefix + ".auth", "true"); auth = new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(smtpUsername, smtpPassword); } }; } Session session = Session.getInstance(props, auth); if (smtpProtocol != null) { session.setProtocolForAddress("rfc822", smtpProtocol); } if (smtpDebug) { session.setDebug(smtpDebug); } return session; } /** * Perform SMTPAppender specific appending actions, mainly adding the event * to a cyclic buffer and checking if the event triggers an e-mail to be * sent. */ public void append(LoggingEvent event) { if (!checkEntryConditions()) { return; } event.getThreadName(); event.getNDC(); event.getMDCCopy(); if (locationInfo) { event.getLocationInformation(); } event.getRenderedMessage(); event.getThrowableStrRep(); cb.add(event); if (evaluator.isTriggeringEvent(event) && cb.length() >= errorSize) { sendBuffer(); } } /** * This method determines if there is a sense in attempting to append. * *

* It checks whether there is a set output target and also if there is a set * layout. If these checks fail, then the boolean value false * is returned. */ protected boolean checkEntryConditions() { if (this.msg == null) { errorHandler.error("Message object not configured."); return false; } if (this.evaluator == null) { errorHandler.error("No TriggeringEventEvaluator is set for appender [" + name + "]."); return false; } if (this.layout == null) { errorHandler.error("No layout set for appender named [" + name + "]."); return false; } return true; } synchronized public void close() { this.closed = true; if (sendOnClose && cb.length() > 0) { sendBuffer(); } } InternetAddress getAddress(String addressStr) { try { return new InternetAddress(addressStr); } catch (AddressException e) { errorHandler.error("Could not parse address [" + addressStr + "].", e, ErrorCode.ADDRESS_PARSE_FAILURE); return null; } } InternetAddress[] parseAddress(String addressStr) { try { return InternetAddress.parse(addressStr, true); } catch (AddressException e) { errorHandler.error("Could not parse address [" + addressStr + "].", e, ErrorCode.ADDRESS_PARSE_FAILURE); return null; } } /** * Returns value of the To option. */ public String getTo() { return to; } /** * The SMTPAppender requires a {@link org.apache.log4j.Layout * layout}. */ public boolean requiresLayout() { return true; } /** * Layout body of email message. * * @since 1.2.16 */ protected String formatBody() { // Note: this code already owns the monitor for this // appender. This frees us from needing to synchronize on 'cb'. StringBuffer sbuf = new StringBuffer(); String t = layout.getHeader(); if (t != null) sbuf.append(t); int len = cb.length(); for (int i = 0; i < len; i++) { // sbuf.append(MimeUtility.encodeText(layout.format(cb.get()))); LoggingEvent event = cb.get(); sbuf.append(layout.format(event)); if (layout.ignoresThrowable()) { String[] s = event.getThrowableStrRep(); if (s != null) { for (int j = 0; j < s.length; j++) { sbuf.append(s[j]); sbuf.append(Layout.LINE_SEP); } } } } t = layout.getFooter(); if (t != null) { sbuf.append(t); } return sbuf.toString(); } /** * Send the contents of the cyclic buffer as an e-mail message. */ protected void sendBuffer() { try { String s = formatBody(); boolean allAscii = true; for (int i = 0; i < s.length() && allAscii; i++) { allAscii = s.charAt(i) <= 0x7F; } MimeBodyPart part; if (allAscii) { part = new MimeBodyPart(); part.setContent(s, layout.getContentType()); } else { try { ByteArrayOutputStream os = new ByteArrayOutputStream(); Writer writer = new OutputStreamWriter(MimeUtility.encode(os, "quoted-printable"), "UTF-8"); writer.write(s); writer.close(); InternetHeaders headers = new InternetHeaders(); headers.setHeader("Content-Type", layout.getContentType() + "; charset=UTF-8"); headers.setHeader("Content-Transfer-Encoding", "quoted-printable"); part = new MimeBodyPart(headers, os.toByteArray()); } catch (Exception ex) { StringBuffer sbuf = new StringBuffer(s); for (int i = 0; i < sbuf.length(); i++) { if (sbuf.charAt(i) >= 0x80) { sbuf.setCharAt(i, '?'); } } part = new MimeBodyPart(); part.setContent(sbuf.toString(), layout.getContentType()); } } Multipart mp = new MimeMultipart(); mp.addBodyPart(part); msg.setContent(mp); msg.setSentDate(new Date()); Transport.send(msg); } catch (MessagingException e) { LogLog.error("Error occured while sending e-mail notification.", e); } catch (RuntimeException e) { LogLog.error("Error occured while sending e-mail notification.", e); } } /** * Returns value of the EvaluatorClass option. */ public String getEvaluatorClass() { return evaluator == null ? null : evaluator.getClass().getName(); } /** * Returns value of the From option. */ public String getFrom() { return from; } /** * Get the reply addresses. * * @return reply addresses as comma separated string, may be null. * @since 1.2.16 */ public String getReplyTo() { return replyTo; } /** * Returns value of the Subject option. */ public String getSubject() { return subject; } /** * The From option takes a string value which should be a e-mail * address of the sender. */ public void setFrom(String from) { this.from = from; } /** * Set the e-mail addresses to which replies should be directed. * * @param addresses * reply addresses as comma separated string, may be null. * @since 1.2.16 */ public void setReplyTo(final String addresses) { this.replyTo = addresses; } /** * The Subject option takes a string value which should be a the * subject of the e-mail message. */ public void setSubject(String subject) { this.subject = subject; } /** * The BufferSize option takes a positive integer representing the * maximum number of logging events to collect in a cyclic buffer. When the * BufferSize is reached, oldest events are deleted as new * events are added to the buffer. By default the size of the cyclic buffer * is 512 events. */ public void setBufferSize(int bufferSize) { this.bufferSize = bufferSize; cb.resize(bufferSize); } /** * The SMTPHost option takes a string value which should be a the * host name of the SMTP server that will send the e-mail message. */ public void setSMTPHost(String smtpHost) { this.smtpHost = smtpHost; } /** * Returns value of the SMTPHost option. */ public String getSMTPHost() { return smtpHost; } /** * The To option takes a string value which should be a comma * separated list of e-mail address of the recipients. */ public void setTo(String to) { this.to = to; } /** * Returns value of the BufferSize option. */ public int getBufferSize() { return bufferSize; } /** * The EvaluatorClass option takes a string value representing the * name of the class implementing the {@link TriggeringEventEvaluator} * interface. A corresponding object will be instantiated and assigned as * the triggering event evaluator for the SMTPAppender. */ public void setEvaluatorClass(String value) { evaluator = (TriggeringEventEvaluator) OptionConverter.instantiateByClassName(value, TriggeringEventEvaluator.class, evaluator); } /** * The LocationInfo option takes a boolean value. By default, it is * set to false which means there will be no effort to extract the location * information related to the event. As a result, the layout that formats * the events as they are sent out in an e-mail is likely to place the wrong * location information (if present in the format). * *

* Location information extraction is comparatively very slow and should be * avoided unless performance is not a concern. */ public void setLocationInfo(boolean locationInfo) { this.locationInfo = locationInfo; } /** * Returns value of the LocationInfo option. */ public boolean getLocationInfo() { return locationInfo; } /** * Set the cc recipient addresses. * * @param addresses * recipient addresses as comma separated string, may be null. * @since 1.2.14 */ public void setCc(final String addresses) { this.cc = addresses; } /** * Get the cc recipient addresses. * * @return recipient addresses as comma separated string, may be null. * @since 1.2.14 */ public String getCc() { return cc; } /** * Set the bcc recipient addresses. * * @param addresses * recipient addresses as comma separated string, may be null. * @since 1.2.14 */ public void setBcc(final String addresses) { this.bcc = addresses; } /** * Get the bcc recipient addresses. * * @return recipient addresses as comma separated string, may be null. * @since 1.2.14 */ public String getBcc() { return bcc; } /** * The SmtpPassword option takes a string value which should be the * password required to authenticate against the mail server. * * @param password * password, may be null. * @since 1.2.14 */ public void setSMTPPassword(final String password) { this.smtpPassword = password; } /** * The SmtpUsername option takes a string value which should be the * username required to authenticate against the mail server. * * @param username * user name, may be null. * @since 1.2.14 */ public void setSMTPUsername(final String username) { this.smtpUsername = username; } /** * Setting the SmtpDebug option to true will cause the mail session * to log its server interaction to stdout. This can be useful when debuging * the appender but should not be used during production because username * and password information is included in the output. * * @param debug * debug flag. * @since 1.2.14 */ public void setSMTPDebug(final boolean debug) { this.smtpDebug = debug; } /** * Get SMTP password. * * @return SMTP password, may be null. * @since 1.2.14 */ public String getSMTPPassword() { return smtpPassword; } /** * Get SMTP user name. * * @return SMTP user name, may be null. * @since 1.2.14 */ public String getSMTPUsername() { return smtpUsername; } /** * Get SMTP debug. * * @return SMTP debug flag. * @since 1.2.14 */ public boolean getSMTPDebug() { return smtpDebug; } /** * Sets triggering evaluator. * * @param trigger * triggering event evaluator. * @since 1.2.15 */ public final void setEvaluator(final TriggeringEventEvaluator trigger) { if (trigger == null) { throw new NullPointerException("trigger"); } this.evaluator = trigger; } /** * Get triggering evaluator. * * @return triggering event evaluator. * @since 1.2.15 */ public final TriggeringEventEvaluator getEvaluator() { return evaluator; } /** * {@inheritDoc} * * @since 1.2.15 */ public boolean parseUnrecognizedElement(final Element element, final Properties props) throws Exception { if ("triggeringPolicy".equals(element.getNodeName())) { Object triggerPolicy = org.apache.log4j.xml.DOMConfigurator.parseElement(element, props, TriggeringEventEvaluator.class); if (triggerPolicy instanceof TriggeringEventEvaluator) { setEvaluator((TriggeringEventEvaluator) triggerPolicy); } return true; } return false; } /** * Get transport protocol. Typically null or "smtps". * * @return transport protocol, may be null. * @since 1.2.16 */ public final String getSMTPProtocol() { return smtpProtocol; } /** * Set transport protocol. Typically null or "smtps". * * @param val * transport protocol, may be null. * @since 1.2.16 */ public final void setSMTPProtocol(final String val) { smtpProtocol = val; } /** * Get port. * * @return port, negative values indicate use of default ports for protocol. * @since 1.2.16 */ public final int getSMTPPort() { return smtpPort; } /** * Set port. * * @param val * port, negative values indicate use of default ports for * protocol. * @since 1.2.16 */ public final void setSMTPPort(final int val) { smtpPort = val; } /** * Get sendOnClose. * * @return if true all buffered logging events will be sent when the * appender is closed. * @since 1.2.16 */ public final boolean getSendOnClose() { return sendOnClose; } /** * Set sendOnClose. * * @param val * if true all buffered logging events will be sent when appender * is closed. * @since 1.2.16 */ public final void setSendOnClose(final boolean val) { sendOnClose = val; } } class DefaultEvaluator implements TriggeringEventEvaluator { /** * Is this event the e-mail triggering event? * *

* This method returns true, if the event level has ERROR level * or higher. Otherwise it returns false. */ public boolean isTriggeringEvent(LoggingEvent event) { return event.getLevel().isGreaterOrEqual(Level.ERROR); } }


然后异步用xml方式配置

配置如下 这样你就能愉快的玩耍了




    
    
    
        
            
        
    
    
    
    
        
        
        
        
        
        
        
        
        
      
        
        
            
        
        
        
            
            
            
            
        
    
    
    
    
        
        
        
        
        
        
        
        
        
        
        
        
            
        
        
        
            
            
            
            
        
    
    
    
    
        
        
        
        
        
        
        
        
        
        
        
        
            
        
        
        
            
            
            
            
        
    
    
    
	
		
		
		
		
		 
		 
		 
		
		
		
		
		
		
		
		
		
		
		
		
		
		 
		
        
            
        
	

	
	
		
	  
 
 	
  	
        
    
  	
        
    

	
    
        
        
        
        
        
        
    
    


顺带一提 结合slf4j效率更好

你可能感兴趣的:(Log4j)