需求
在開發(fā)中我們經(jīng)常要通過Log(日志)來調(diào)試問題, 而上線后不需要打印Log,而且平常開發(fā)每做一個模塊如果可以將log按照級別打印出來,對分析問題幫助很大.
實現(xiàn)原理
通過導(dǎo)入一個頭文件以實現(xiàn)開啟或關(guān)閉Log模式,實現(xiàn)選擇Log級別.通過導(dǎo)入#include <syslog.h>
以實現(xiàn)打印不同級別的Log.
為什么使用syslog
? 因為它不僅可以在Xcode控制臺打印信息,還可能在Mac終端中打印出日志信息,這樣的好處是比如我們有一個app store的app出了問題,我們可以在app中調(diào)節(jié)log level到debug模式,然后使用數(shù)據(jù)線連接設(shè)備, 這樣再復(fù)現(xiàn)問題可以將相關(guān)的Log即時打印出來.對分析問題幫助很大.
簡要分析
1. Log模式
為什么設(shè)置log模式,很簡單,因為上線后的app不必打印log, 而且如果開發(fā)中想取消所有相關(guān)打印也可以直接通過該宏定義決定.
本例中如果在代碼中定義了kXDXDebugMode
則啟用打印log模式,否則關(guān)閉.
2. Log級別
本例中通過五個函數(shù)來打印不同級別log,如下
#define kModuleName "Hello"
...
int i = 1; int j = 2;
log4cplus_fatal(kModuleName, "world1 , %d,%d",i,j);
log4cplus_error(kModuleName, "world2");
log4cplus_warn(kModuleName, "world3");
log4cplus_info(kModuleName, "world4");
log4cplus_debug(kModuleName, "world5");
比如我們在開發(fā)中可能獨立承擔(dān)一個模塊,而此模塊單單做完如果出現(xiàn)問題就需要調(diào)試,但是如果我們的Log足夠清晰是可以迅速幫助我們找到問題所在,這里將Log分為5個級別
以下是個人對于打印Log的一些建議,并不是說一定要按照如下.
-
XDX_IOS_FLAG_FATAL
: 嚴(yán)重級別
此類錯誤出現(xiàn)時,表明程序因為某某問題已經(jīng)徹底無法運行下去,我們在使用此log時可以附加說明當(dāng)前碰到的重大問題及產(chǎn)生原因,以便調(diào)試.
-
XDX_IOS_FLAG_ERROR
: 出錯級別
此類打印可用于出現(xiàn)一般錯誤,比如某個方法調(diào)用返回失敗, 因為一般而言代碼預(yù)期是正確的, 所以此類Log不會打印的太頻繁, 打開此級別后我們可以清晰看到程序哪些地方出現(xiàn)問題.
-
XDX_IOS_FLAG_WARN
: 警告級別
此類錯誤一般較低于error級別,即在一些可能出錯的地方, 但實際并沒有出錯, 比如當(dāng)視頻幀數(shù)量小于0表示出錯情況,我們?yōu)榱祟A(yù)防,可以在視頻幀數(shù)量小于5時使用此類添加一條預(yù)防的Log.
-
XDX_IOS_FLAG_INFO
: 重要信息級別
此類級別一般用于打印模塊中一些重要的點, 比如我們可以在某個類初始化完成時打印此類中初始化好的一些重要信息,或者在使用某個功能前做一個打印,這樣對于追蹤代碼十分有效.
-
XDX_IOS_FLAG_DEBUG
: 調(diào)試級別
此類級別表明我們當(dāng)前正在臨時打印一些log為了去調(diào)試程序, 或者說我們?yōu)榱擞^察某個現(xiàn)象但是需要頻繁打印, 比如相機(jī)回調(diào)中打印時間戳,因為相機(jī)每秒鐘出來幾十幀數(shù)據(jù),所以打印十分頻繁,我們可以使用此級別在開發(fā)中作為調(diào)試信息,一般不建議在正常使用中開啟此級別.
綜上所述,即log級別越高,log數(shù)量應(yīng)該是越少,這樣我們可以在開發(fā)中根據(jù)如上所述的情況合理選擇使用哪個級別的log. Log級別是有包含關(guān)系的, 即低的log級別可以包含比自身高的log級別.
3. 模塊名
這里封裝的格式如下,即一條log分為兩部分,前面是模塊名,后面是具體log內(nèi)容,使用這種格式可以幫助我們通過過濾某個模塊中的Log, 十分直觀清晰
#define log4cplus_fatal(category, logFmt, ...) \
if(XDX_IOS_LOG_LEVEL & XDX_IOS_FLAG_FATAL) \
syslog(LOG_CRIT, "%s:" logFmt, category,##__VA_ARGS__); \
舉例如下:
#define kModuleName "Hello"
...
- (void)test {
int i = 0;
log4cplus_debug(kModuleName, "Hello World - %d",i);
}
4. 注意
因為我們使用的syslog
,所以O(shè)C中的格式符%@
是不可用的.但是任何OC中的字符串都可通過UTF8String
轉(zhuǎn)為C語言字符串,所以并不影響我們的使用.
5. 使用方式
- 每次編譯前設(shè)置log級別
如果不需要在項目中通過UI動態(tài)更改log級別,則可以直接使用如下方式將頭文件拖入項目,然后在需要使用log的地方將其導(dǎo)入.這種方式簡單方便,僅僅通過每次運行前更改宏定義即可轉(zhuǎn)變log級別.
- 在運行后通過UI更改
那么需要將頭文件的XDX_IOS_LOG_LEVEL
全局靜態(tài)變量換為外部變量,然后取消下面這段代碼,最后在控制UI的類中定義并通過交互更改此值即可.
#ifdef XDXLogLevelFatal
static const int XDX_IOS_LOG_LEVEL = XDX_IOS_FLAG_FATAL;
#elif defined(XDXLogLevelError)
static const int XDX_IOS_LOG_LEVEL = (XDX_IOS_FLAG_FATAL | XDX_IOS_FLAG_ERROR);
#elif defined(XDXLogLevelWarn)
static const int XDX_IOS_LOG_LEVEL = (XDX_IOS_FLAG_FATAL | XDX_IOS_FLAG_ERROR | XDX_IOS_FLAG_WARN);
#elif defined(XDXLogLevelInfo)
static const int XDX_IOS_LOG_LEVEL = (XDX_IOS_FLAG_FATAL | XDX_IOS_FLAG_ERROR | XDX_IOS_FLAG_WARN | XDX_IOS_FLAG_INFO);
#elif defined(XDXLogLevelDebug)
static const int XDX_IOS_LOG_LEVEL = (XDX_IOS_FLAG_FATAL | XDX_IOS_FLAG_ERROR | XDX_IOS_FLAG_WARN | XDX_IOS_FLAG_INFO | XDX_IOS_FLAG_DEBUG);
#endif
具體實現(xiàn)
#include <syslog.h>
#ifndef XDX_IOS
#define XDX_IOS
#endif
#pragma - Please select your log mode and log level
// Note: Only debug mode will print log. You could also set mode for log level.
#define kXDXDebugMode
// XDXLogLevelFatal , XDXLogLevelError , XDXLogLevelWarn , XDXLogLevelInfo , XDXLogLevelDebug
#define XDXLogLevelDebug
#pragma ----------------------------------
#ifdef kXDXDebugMode
static const int XDX_IOS_FLAG_FATAL = 0x10;
static const int XDX_IOS_FLAG_ERROR = 0x08;
static const int XDX_IOS_FLAG_WARN = 0x04;
static const int XDX_IOS_FLAG_INFO = 0x02;
static const int XDX_IOS_FLAG_DEBUG = 0x01;
#ifdef XDXLogLevelFatal
static const int XDX_IOS_LOG_LEVEL = XDX_IOS_FLAG_FATAL;
#elif defined(XDXLogLevelError)
static const int XDX_IOS_LOG_LEVEL = (XDX_IOS_FLAG_FATAL | XDX_IOS_FLAG_ERROR);
#elif defined(XDXLogLevelWarn)
static const int XDX_IOS_LOG_LEVEL = (XDX_IOS_FLAG_FATAL | XDX_IOS_FLAG_ERROR | XDX_IOS_FLAG_WARN);
#elif defined(XDXLogLevelInfo)
static const int XDX_IOS_LOG_LEVEL = (XDX_IOS_FLAG_FATAL | XDX_IOS_FLAG_ERROR | XDX_IOS_FLAG_WARN | XDX_IOS_FLAG_INFO);
#elif defined(XDXLogLevelDebug)
static const int XDX_IOS_LOG_LEVEL = (XDX_IOS_FLAG_FATAL | XDX_IOS_FLAG_ERROR | XDX_IOS_FLAG_WARN | XDX_IOS_FLAG_INFO | XDX_IOS_FLAG_DEBUG);
#endif
#define log4cplus_fatal(category, logFmt, ...) \
if(XDX_IOS_LOG_LEVEL & XDX_IOS_FLAG_FATAL) \
syslog(LOG_CRIT, "%s:" logFmt, category,##__VA_ARGS__); \
#define log4cplus_error(category, logFmt, ...) \
if(XDX_IOS_LOG_LEVEL & XDX_IOS_FLAG_ERROR) \
syslog(LOG_ERR, "%s:" logFmt, category,##__VA_ARGS__); \
#define log4cplus_warn(category, logFmt, ...) \
if(XDX_IOS_LOG_LEVEL & XDX_IOS_FLAG_WARN) \
syslog(LOG_WARNING, "%s:" logFmt, category,##__VA_ARGS__); \
#define log4cplus_info(category, logFmt, ...) \
if(XDX_IOS_LOG_LEVEL & XDX_IOS_FLAG_INFO) \
syslog(LOG_WARNING, "%s:" logFmt, category,##__VA_ARGS__); \
#define log4cplus_debug(category, logFmt, ...) \
if(XDX_IOS_LOG_LEVEL & XDX_IOS_FLAG_DEBUG) \
syslog(LOG_WARNING, "%s:" logFmt, category,##__VA_ARGS__); \
#else
#define log4cplus_fatal(category, logFmt, ...); \
#define log4cplus_error(category, logFmt, ...); \
#define log4cplus_warn(category, logFmt, ...); \
#define log4cplus_info(category, logFmt, ...); \
#define log4cplus_debug(category, logFmt, ...); \
#endif