引言:
日志大家都再熟悉不過了,日常開發中經常要用到,甲方接口人最喜歡對我說的話就是“趕緊去查一下日志,快點!”,說話的同時瞪著個銅鈴大的眼睛。
就你接口人知道日志嗎?還要你指揮啊,我心里可真來勁,但是沒辦法誰叫我是乙方的,我想大口喘氣,也得拿手捂著,兄弟們都指著我好好說話呢,我這話一出口,我這一眾兄弟這個月的績效恐怕就打水漂了。
就在我進行心里斗爭的時候,接口人又說了“什么時候能定位到問題myeclipse控制臺沒有日志,啥時候能解決?”。(甲方規定:超1小時通報、超過2小時扣分、超4小時約談領導、24小時沒搞定打包回家)。
下面恭迎今天的主角“日志”大神出場。
什么是日志
日志:記錄程序的運行軌跡,打印一下設定的信息,比如錯誤,很方便查找關鍵信息,快速定位解決問題。
Java程序員在開發項目時都是依賴debug來調試錯誤、跟蹤代碼來解決bug,比如我們開發工具/就有很強大錯誤調試功能,比如我自己就有4個版本的:
(我懷疑你在做廣告)
當然在開發環境下,要調試這些錯誤很容易,打好斷點跟蹤代碼就行了,但項目發布到了測試、生產環境怎么辦?不好意思你調試不了,甲方會斷了一切你可以這么做的念想。所以日志就派上用場了,日志打印的好,就能根據日志的軌跡快速定位并解決線上問題,反之你光憑分析代碼、分析數據,腦細胞胡亂打架是沒法快速解決問題的,日志記錄的信息能夠很好的幫助開發人家快速定位到問題,并且解決。
記錄日志的方式
有很多記錄日志的方式,比如 log4j、、-、slf4j 等等,這里呢我就說說我們做項目經常用到的幾種。
log4j
配置文章可以看我的文章Log4j配置詳解
我用的是log4j-1.2.15.jar 這個包,將其放在lib下
輸出到控制臺
log4j.配置
# DEBUG 日志優先級,stdout(自定義名字) 代表日志輸出到那個地方
log4j.rootLogger= ERROR, stdout
# 設置日志輸出類型 appender負責控制日志記錄操作的輸出 ConsoleAppender:日志信息輸出到控制臺
log4j.appender.stdout= org.apache.log4j.ConsoleAppender
# 日志自定義格式
log4j.appender.stdout.layout= org.apache.log4j.PatternLayout
# 日志輸出格式為 優先級、 [產生日志事件的線程名]、日志信息、換行
log4j.appender.stdout.layout.ConversionPattern= %5p [%t] %m%n
測試代碼
package com.log;
import org.apache.log4j.Logger;
public class Log4jTest
{
Logger logger = Logger.getLogger(this.getClass());
public void test()
{
logger.debug("debug print");
logger.info("info print");
logger.warn("warn print");
logger.error("error print");
}
/**
* @param args
*/
public static void main(String[] args)
{
new Log4jTest().test();
}
}
因配置的是ERROR,所以只輸出error的信息
輸出到日志文件
log4j.配置
# DEBUG 日志優先級,stdout(自定義名字) 代表日志輸出到那個地方
log4j.rootLogger= ERROR, stdout ,myLog
# 設置日志輸出類型 appender負責控制日志記錄操作的輸出 ConsoleAppender:日志信息輸出到控制臺
log4j.appender.stdout= org.apache.log4j.ConsoleAppender
# 日志自定義格式
log4j.appender.stdout.layout= org.apache.log4j.PatternLayout
# 日志輸出格式為 優先級、 [產生日志事件的線程名]、日志信息、換行
log4j.appender.stdout.layout.ConversionPattern= %5p [%t] %m%n
### 輸出到日志文件 ###
log4j.appender.myLog = org.apache.log4j.DailyRollingFileAppender
log4j.appender.myLog.File = E:\\testlog\\log4j\\server.log
log4j.appender.myLog.Append = true
## 只輸出DEBUG級別以上的日志
log4j.appender.myLog.Threshold = DEBUG
#'.'yyyy-MM-dd: 每天產生一個新的文件
log4j.appender.myLog.DatePattern = '.'yyyy-MM-dd
log4j.appender.myLog.layout = org.apache.log4j.PatternLayout
log4j.appender.myLog.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [%t:%r] - [%p] [%c{1}:%L] [%M] %m%n
log4j.additivity.myLog = false
測試代碼還是之前的,運行結果如下:
自定義文本方式
每天記錄一個日志文件,這個日志文件把今天比較重要的日志記錄下來。
每個日志文件記錄的形式(時間+組裝好的內容),如下 :
是不是有個疑問,如果有多個服務器,日志是記錄到代碼執行的這個服務器上了,外面訪問進來肯定會訪問到不同的服務器上了,日志文件也肯定存在多個咯myeclipse控制臺沒有日志,查問題的時候不就得一個個服務器的日志都翻一遍,煩人嗎不是,這個時候你請搞工程的兄弟吃個夜宵他就會告訴你,其實服務器是可以搞成共享一個目錄的,多個服務器訪問同一個日志文件也是可以的(具體我就不講了),但說實話這種方式我們現在也只是偶爾用一下。
示意代碼
package com.log;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileLog {
public static void writeLog(String file, String conent) {
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(file, true)));
out.write(conent);
out.newLine();//換行
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @param args
*/
public static void main(String[] args) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat dfTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
//當前時間 方便日志查看
String curTime = dfTime.format(date);
//yyyy-MM-dd格式的,為了每日更換日志文件
String curDay = df.format(date);
//組裝日志內容
String text="日志內容"+org.apache.commons.lang.RandomStringUtils.randomNumeric(4);
String logStr="["+curTime+"]"+text;
//指定日志文件路徑
String fileName="E:/testlog/"+curDay+".txt";
System.out.println("start");
//打印日志
writeLog(fileName, logStr);
System.out.println("end");
}
}
記錄到數據庫
我們用的最多的場景是接口,把收發報文記錄下來,如果接口發生錯誤的話,將錯誤的信息記錄成一條數據。
一旦發生接口調用爭議的實話,這個就派上用場了,場景:
對端是個大廠很大的那種(甲方都不吊的那種),你送了數據過去,然后對端說沒收到或者說收到的有問題,但人家又不說具體什么問題,也不給報文,就做甩手掌柜,接口人只好找你了(你懂的,柿子都找軟的捏),你如果沒有調用報文和返回報文的記錄的話,你就等著哭吧,挨罵你只能忍著,就算你確定你的程序沒問題也沒有用,你得有證據,你就得想方設法的找報文,但如果要從日志找的話不是那么好找,甚至有時候找不到。
記錄到庫就很好找,根據工單號、主鍵或者發生時間等等,可以很容易發現問題、定位問題,然后迅速把發送的報文、返回的報文發給接口人,手一甩就剩下你心情愉悅的摸魚了。
我一般會記錄這些信息
來源系統
對端系統/自己的系統代號
業務
不同的模塊寫不同的編碼,方便區分
工單號
唯一標示即可,看自己系統的需求
方法名
調用的方法
類型
接收或者發送
狀態
成功或者失敗
報文
具體的報文信息
操作時間
創建時間
當然有可能會涉及到比較長的報文,所以我們設計的是有好幾個字段來存,不夠的就往后一個字段放,比如:
String xmlInfo = vo.getXmlInfo();
int xmlLength = xmlInfo.length();
int count = xmlLength / 1000;
if (count * 1000 < xmlLength)
count++;
String xmlArr[] = new String[count];
for (int i = 0; i < count; i++)
{
int endLength = (i + 1) * 1000;
if (endLength > xmlLength)
endLength = xmlLength;
xmlArr[i] = xmlInfo.substring(i * 1000, endLength);
if (i == 0)
vo.setXmlInfo1(xmlArr[i]);
else if (i == 1)
vo.setXmlInfo2(xmlArr[i]);
else if (i == 2)
vo.setXmlInfo3(xmlArr[i]);
else if (i == 3)
vo.setXmlInfo4(xmlArr[i]);
else if (i == 4)
vo.setXmlInfo5(xmlArr[i]);
else if (i == 5)
vo.setXmlInfo6(xmlArr[i]);
else if (i == 6)
vo.setXmlInfo7(xmlArr[i]);
else if (i == 7)
vo.setXmlInfo8(xmlArr[i]);
}
最后
通常情況下在程序日志里記錄一些比較有意義的數據:程序啟動、登錄、退出、重要變量的狀態變化、重要數據的執行記錄、接口調用報文的記錄、定時執行相關任務的狀況 等等。