一文輕鬆理解打印有效日誌

引言

在日常代碼測試或運行中,打印日誌檢測代碼運行狀態必不可少。先舉三個例子:

  1. 在物聯網系統中設備什麼時候上線,可以將上線的信息寫入數據庫,也可以將該設備上線的記錄打印到日誌中,以該設備的id爲文件名,查找日誌也比較方便,上線時間、IP地址等打印到文本中。

  2. 在複雜的多環節系統中,快速定位問題問題出錯的環節,將各個系統數據接口的數據打印日誌,如果有返回值可以判斷執行是否成功,可以只打印錯誤的日誌,出現問題時查看日誌文件就可以定位是那個環節。

  3. 在異常捕獲中將錯誤的信息打印成日誌文件,快速查看代碼或數據出現的問題。

日誌的作用

一般程序日誌出自下面幾個方面的需求:

  • 記錄用戶操作的審計日誌,甚至有的時候就是監管部門的要求。

  • 快速定位問題的根源

  • 追蹤程序執行的過程。

  • 追蹤數據的變化

  • 數據統計和性能分析

  • 採集運行環境數據

一般在程序上線之後,一旦發生異常,第一件事就是要弄清楚當時發生了什麼。用戶當時做了什麼操作,環境有無影響,數據有什麼變化,是不是反覆發生等,然後再進一步的確定大致是哪個方面的問題。確定是程序的問題之後再交由開發人員去重現、研究、提出解決方案。這時,日誌就給我們提供了第一手的資料。

撰寫日誌的要求

  • 日誌的可讀性

日誌是給人讀的,不僅僅是讓自己明白,也要讓沒有接觸過我們源代碼的其他程序員也能夠一目瞭然。有的同事在日誌中打印特殊的標識符號,例如“++++++++++”, “===========”,這些符號令人眼花繚亂。另外,把日誌分類輸出到不同的文件也有利於我們排除干擾,迅速找到我們需要的信息。而且,最好在打印日誌時輸出英文,防止中文不支持而打印出亂碼的情況。

  • 日誌的性能

無論我們把日誌寫到文件還是數據庫,都需要消耗IO資源。適當的控制日誌的輸出也有利於提高程序的性能。例如:儘量避免在在大的循環中打印意義不大的日誌內容。輸出日誌之前最好能判斷日誌的級別(例如. debug前先調用isDebugEnabled()作出判斷)。

  • 佔用磁盤空間

通常,我們都是把日誌寫入磁盤上的日誌文件中。適當的使用滾動日誌並且定時清除舊文件是有好處的。我見過這樣一個例子,程序運行幾次後就跑不起來了,前幾次都是正常的。怎麼都想不明白程序有什麼問題,最後才發現居然是日誌文件佔滿了磁盤空間。在實際的應用中出現上G的日誌文件也往往不少見。要在這樣規模的日誌文件中找出對解決問題有用的信息也是一大挑戰。

  • 日誌的時效性

有的時候我們並不能及時的發現問題。需要追溯之前的日誌。所以我們是需要保留一段時間以內的日誌便於追溯。

  • 日誌級別

通常我們在產品環境中日誌的級別都在INFO以上,所以我們必須保證在這樣的情況下程序仍然能夠輸出足夠我們作出判斷的信息。

  • 日誌內容

我們在寫日誌的時候,需要注意輸出適當的內容。首先,儘量使用業務相關的描述。我們的程序是實現某種業務的,那麼就最好能描述清楚這個時候走到了業務過程的哪一步。其次,避免在日誌中輸出一些敏感信息,例如用戶名和密碼。以及,要保持編碼的一致。如果不能保證就儘量使用英文而不是中文。這樣當我們拿到日誌之後就不會因爲看到一堆亂碼而不知所云了。

  • 日誌格式

常見的日誌格式中對於每一條日誌應含有的信息包括日期、時間、日誌級別、代碼位置、日誌內容、錯誤碼等信息。下面是一個工作中的日誌文件的一部分內容:

代碼實現


#include <iostream>
#include <afxtempl.h>     //CException
#include <ATLComTime.h>   //COleDateTime
#include <io.h>
#include <fcntl.h>
#include <stat.h>

using namespace std;

void PrintRunningLog(char *szbuff)
{

	//AfxMessageBox(szbuff);
	//打印
	int clientNumber = 1;
	CString strInfo;
	CString strCurrentTime;
	COleDateTime currentTime;
	currentTime = COleDateTime::GetCurrentTime();
	strCurrentTime=currentTime.Format("%Y-%m-%d %H:%M:%S");   //打印時間
	strInfo = strCurrentTime;
	strInfo +=" ";
	strInfo +=szbuff;
	strInfo +="\r\n";

	CString strLogTxtName;
	strLogTxtName.Format("log%d.txt",clientNumber);    //文件名

	FILE *stream;//文件流
	if ((stream = fopen(strLogTxtName,"r")) == NULL)
	{
		fopen(strLogTxtName,"w");//新建
	}
	try
	{
		int fh = _open(strLogTxtName,_O_RDWR|_O_CREAT,_S_IREAD|_S_IWRITE);
		if(fh != -1)
		{
			_lseek(fh,0L,SEEK_END);
			_write(fh,strInfo,strlen(strInfo));
			_close(fh);
		}
	}
	catch(CException *pe)   
	{
		pe->Delete();
	}
}

int main ()
{
    PrintRunningLog("test");
	return 0 ;
}

測試例子比較簡單,文件名爲:1

文件名稱截圖

文件名稱截圖

測試的日誌內容也比較簡單,根據實際情況,更換test內容

日誌內容

日誌內容

參考資料

https://blog.csdn.net/lk142500/article/details/80424945

最新原創推薦:

一文輕鬆理解內存對齊

一文讀懂C語言與C++動態內存

面試中常見的C語言與C++區別的問題

數據結構之線性表

深拷貝與淺拷貝到底是什麼

JSON封裝數據和解析數據

C/C++字符串操作的全面總結

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章