iOS 將Log日誌輸出到檔案中儲存
對於那些做後端開發的工程師來說,看LOG解Bug應該是理所當然的事,但我接觸到的移動應用開發的工程師裡面,很多人並沒有這個意識,查Bug時總是一遍一遍的試圖重現,試圖除錯,特別是對一些不太容易重現的Bug經常焦頭爛額。而且iOS的異常機制比較複雜,Objective-C的語言駕馭也需要一定的功力,做出來的應用有時候挺容易產生崩潰閃退。一遍一遍的用XCode取應用崩潰記錄、解析符號,通常不勝其煩,有時還對著解析出來的呼叫棧發呆,因為程式當時的內部狀態常常難以看明白,只能去猜測。
對於真機,日誌沒法儲存,不好分析問題。所以有必要將日誌儲存到應用的Docunment目錄下,並設定成共享檔案,這樣才能取出分析。
首先是日誌輸出,分為c的printf和標準的NSLog輸出,printf會向標準輸出(sedout)列印,而NSLog則是向標準出錯(stderr),我們需要同時讓他們都將日誌列印到一個檔案中。
例子:
freopen(“xx.log”,”a+”,stdout);
freopen(“xx.log”,”a+”,stderr);
具體做法:
#pragma mark - 使用者方法,將nslog的輸出資訊寫入到dr.log檔案中
// 將NSlog列印資訊儲存到Document目錄下的檔案中
//此函式要在- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions中呼叫,這個函式在AppDelegate.m中實現的。
- (void)redirectNSlogToDocumentFolder
{
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES );
NSString *documentDirectory = [paths objectAtIndex:0];
NSString *fileName = [NSStringstringWithFormat:@"dr.log"];//注意不是NSData!
NSString *logFilePath = [documentDirectory stringByAppendingPathComponent:fileName];
//先刪除已經存在的檔案
NSFileManager *defaultManager = [NSFileManagerdefaultManager];
[defaultManagerremoveItemAtPath:logFilePath error:nil ];
// 將log輸入到檔案
freopen([logFilePathcStringUsingEncoding:NSASCIIStringEncoding],"a+", stdout);
freopen([logFilePathcStringUsingEncoding:NSASCIIStringEncoding],"a+", stderr);
}
此函式要在- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions
中呼叫,這個函式在AppDelegate.m中實現的。
/*********************************************************************/
//當真機連線Mac除錯的時候把這些註釋掉,否則log只會輸入到檔案中,而不能從xcode的監視器中看到。
// 如果是真機就儲存到Document目錄下的dr.log檔案中
UIDevice *device = [UIDevicecurrentDevice];
if (![[device model]isEqualToString:@”iPad Simulator”]) {
// 開始儲存日誌檔案
[selfredirectNSlogToDocumentFolder];
}
/*********************************************************************/
最後配置共享資料夾:
在應用程式的Info.plist檔案中新增UIFileSharingEnabled鍵,並將鍵值設定為YES。將您希望共享的檔案放在應用程式的 Documents目錄。一旦裝置插入到使用者計算機,iTunes9.1就會在選中裝置的Apps標籤中顯示一個File Sharing區域。此後,使用者就可以向該目錄新增檔案或者將檔案移動到桌面計算機中。如果應用程式支援檔案共享,當檔案新增到Documents目錄後,應用程式應該能夠識別並做出適當響應。例如說,應用程式可以將新檔案的內容顯示介面上。請不要向用戶展現目錄的檔案列表並詢問他們希望對檔案執行什麼操作。
就是說,一旦裝置連線上電腦,可以通過iTune檢視指定應用程式的共享資料夾,將檔案拷貝到你的電腦上看。
iOS中列印日誌顯示系統詳細時間,類名,行號及列印值
Q:如何列印當前的函式和行號?
A:我們可以在列印時使用一些預編譯巨集作為列印引數,來列印當前的函式和行號。如:
NSLog(@"%s:%d obj=%@", __func__, __LINE__, obj);
其中func和LINE都是預編譯的巨集,編譯時會分別替換為當前函式和當前行號。
下面是一些常用於列印日誌的巨集。
巨集 說明
__func__ 列印當前函式或方法,c字串
__LINE__ 列印當前行號,整數
__FILE__ 列印當前檔案路徑,c字串
__PRETTY_FUNCTION__ 列印當前函式或方法(在C++中會包含引數型別),c字串
Q:如何列印一個類名,訊息名,當前堆疊資訊?
A:你可以使用以下方法在執行時動態獲取這些資訊。
程式碼 說明
NSStringFromSelector(SEL) 獲取selector的名字
NSStringFromSelector(_cmd) 獲取當前方法名
NSStringFromClass([object class]) 獲取object的類名
NSThread callStackSymbols] 獲取當前執行緒的棧,是一個NSArry,包含堆疊中所有函式名。
Q:如何將日誌列印到一個檔案
A:可以使用freopen函式重定向標準輸出和標準出錯檔案。因為printf函式會向標準輸出(stdout)列印,而NSLog函式會向標準出錯(stderr)列印。重新定向標準輸出(stdout)和標準出錯(stderr)到一個檔案將會使他們列印日誌到一個檔案中。
freopen("/tmp/log.txt", "a+", stdout);
freopen("/tmp/log.txt", "a+", stderr);
#define NSLog(FORMAT, ...) {\
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];\
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];\
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];\
[dateFormatter setDateFormat:@"HH:mm:ss:SSSSSS"]; \
NSString *str = [dateFormatter stringFromDate:[NSDate date]];\
[dateFormatter release];\
fprintf(stderr,"[--%s--]*[--%s--]*[--%s:%d--]\n",[str UTF8String], [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String],[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__);\
}