日志打印會嚴重影響到性能


以前編程不喜歡對每個功能、重要的方法進行日志的格式化輸出,即使要輸出,也沒按照規范進行輸出。前段時間,隨着我們平

台用戶量不斷增加,出現些問題。當用戶遇到問題,就給我們客服打電話。然后客服毫無疑問就來找我們。當我們接收到這樣問題的時候,

我們首先要定位是什么原因造成的。當時就是因為自己方法中輸出的日志很少,而且不規范,所以根本找不到具體原因,雖然最后問題解

決了,但是花費很多不必要的時間。

 

那么我們怎樣使用好日志這一利器呢?

 

1.寫好日志:

 

我們先來看一下糟糕的日志打印:

 

 

[java]  view plain  copy
 
  1. @ResponseBody  
  2.     @RequestMapping(value = "unbind.do", method = RequestMethod.POST)  
  3.     public Object unbind(String bankId, String memberId) {  
  4.       
  5.         if (StringUtils.isBlank(bankId) || StringUtils.isBlank(memberId)) {  
  6.             return new Result(false, "解綁參數不合法!");  
  7.         }  
  8.         try {  
  9.             authPayTwoService.unbind(bankId, memberId);  
  10.         } catch (AppException e) {  
  11.             LOG.info("認證支付2.0(unbind)失敗:{}",e);  
  12.             return new Result(false, e.getMessage());  
  13.         } catch (Exception e) {  
  14.             LOG.info("認證支付2.0(unbind)]失敗:{}",  e);  
  15.             return new Result(false, "解綁銀行卡失敗,請重試");  
  16.         }  
  17.         return new Result(true, "解綁銀行卡成功!");  
  18.     }  



 

 

上面代碼是嚴格不符合規范的,每個公司都有自己的打印要求。首先日志的打印必須是以logger.error、logger.warn或logger.info

的方式打印出來。日志打印格式應包含[系統來源] 錯誤描述 [關鍵信息],日志信息要能打印出能看懂的信息,有前因和后果。方法的入參和

出參也要考慮打印出來。

好的日志格式打印

 

 

[java]  view plain  copy
 
  1. @ResponseBody  
  2.     @RequestMapping(value = "unbind.do", method = RequestMethod.POST)  
  3.     public Object unbind(String bankId, String memberId) {  
  4.         LOG.info("[解綁銀行卡--認證支付2.0][params:bankId=" + bankId + ",memberId="  
  5.                 + memberId + "]");  
  6.         if (StringUtils.isBlank(bankId) || StringUtils.isBlank(memberId)) {  
  7.             return new Result(false, "解綁參數不合法!");  
  8.         }  
  9.         try {  
  10.             authPayTwoService.unbind(bankId, memberId);  
  11.         } catch (AppException e) {  
  12.             LOG.error("[解綁銀行卡--認證支付2.0(unbind)-mas][error:{}",e);  
  13.             return new Result(false, e.getMessage());  
  14.         } catch (Exception e) {  
  15.             LOG.error("[解綁銀行卡--認證支付2.0(unbind)][error:{}",e);  
  16.             return new Result(false, "解綁銀行卡失敗,請重試");  
  17.         }  
  18.         return new Result(true, "解綁銀行卡成功!");  
  19.     }  


 

 

從上面的代碼可以很輕松的定位方法、傳入的參數及異常是調用接口拋出的異常還是系統級的異常。

 

2.良好的日志輸出格式

 

最近測試同事采用兩百並發對項目進行測試。

格式優化前:

經過測試查看每秒的並發量平均只有110多如下圖:

 

通過查看jvm 虛擬機發現很多日志線程出現BLOCKED,如下:

 

[java]  view plain  copy
 
  1. "http-saoma%2F192.168.6.162-8097-132" daemon prio=10 tid=0x00002aaab88e4800 nid=0x2c8e runnable [0x0000000045274000]  
  2.    java.lang.Thread.State: BLOCKED  
  3.     at java.lang.Throwable.getStackTraceElement(Native Method)  
  4.     at java.lang.Throwable.getOurStackTrace(Throwable.java:591)  
  5.     - locked <0x00000007691390d0> (a java.lang.Throwable)  
  6.     at java.lang.Throwable.getStackTrace(Throwable.java:582)  
  7.     at sun.reflect.GeneratedMethodAccessor29.invoke(Unknown Source)  
  8.     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)  
  9.     at java.lang.reflect.Method.invoke(Method.java:597)  
  10.     at org.apache.log4j.spi.LocationInfo.<init>(LocationInfo.java:139)  
  11.     at org.apache.log4j.spi.LoggingEvent.getLocationInformation(LoggingEvent.java:253)  
  12.     at org.apache.log4j.helpers.PatternParser$LocationPatternConverter.convert(PatternParser.java:500)  
  13.     at org.apache.log4j.helpers.PatternConverter.format(PatternConverter.java:65)  
  14.     at org.apache.log4j.PatternLayout.format(PatternLayout.java:506)  
  15.     at org.apache.log4j.WriterAppender.subAppend(WriterAppender.java:310)  
  16.     at org.apache.log4j.DailyRollingFileAppender.subAppend(DailyRollingFileAppender.java:369)  
  17.     at org.apache.log4j.WriterAppender.append(WriterAppender.java:162)  
  18.     at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:251)  
  19.     - locked <0x0000000780fb1e00> (a org.apache.log4j.DailyRollingFileAppender)  
  20.     at org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:66)  
  21.     at org.apache.log4j.Category.callAppenders(Category.java:206)  
  22.     - locked <0x00000007800020a0> (a org.apache.log4j.spi.RootLogger)  
  23.     at org.apache.log4j.Category.forcedLog(Category.java:391)  
  24.     at org.apache.log4j.Category.log(Category.java:856)  
  25.     at org.slf4j.impl.Log4jLoggerAdapter.error(Log4jLoggerAdapter.java:497)  


我們定位org.apache.log4j.spi.LocationInfo類的代碼如下:

 

 

[java]  view plain  copy
 
  1. String s;  
  2.      // Protect against multiple access to sw.  
  3.      synchronized(sw) {  
  4. t.printStackTrace(pw);  
  5. s = sw.toString();  
  6. sw.getBuffer().setLength(0);  
  7.      }  
  8.      //System.out.println("s is ["+s+"].");  
  9.      int ibegin, iend;  
  10.   
  11.      // Given the current structure of the package, the line  
  12.      // containing "org.apache.log4j.Category." should be printed just  
  13.      // before the caller.  
  14.   
  15.      // This method of searching may not be fastest but it's safer  
  16.      // than counting the stack depth which is not guaranteed to be  
  17.      // constant across JVM implementations.  
  18.      ibegin = s.lastIndexOf(fqnOfCallingClass);  



 

從上面可以看出在該方法中用了synchronized鎖,然后又通過打印堆棧來獲取行號。那么肯定會影響到性能的,我們通過看


此時log4j.properties日志文件配置的輸出格式為:


 %d %-5p %c:%L [%t] - %m%n

 

很明顯就是%l輸出行號的問題,那么我們把%l去掉結果會不會好很多呢?

把log4j.properties文件中輸出格式改為:

 %d %-5p %c [%t] - %m%n

輸出格式優化后:

 再看每秒的並發量如下圖:

 

從圖中我們可以看出並發量提高了40多,同時jvm線程日志中java.lang.Thread.State: BLOCKED也沒有了。看來程序的性能和日志的輸

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM