IOS寫日誌文件並保存到Documents

Unity開發ios版的時候常要藉助xcode調試代碼

情景1

在項目接入推送的時候,要對通知欄點擊事件做統計打點,在app殺死的情形下,點擊通知欄啓動app,這種情形下是無法連接xcode進行調式的,這樣就拿不到我們提前寫下的nslog日誌

情景2

情景。。。

基於這種情況,我們就需要其他獲取日誌的手段來調式代碼,可以將日誌輸出到app的Documents目錄指定文本處。如下

方法一

說明

在Objective-c開發程序的時候,有專門的日誌操作類NSLog,它將指定的輸出,輸出到(stderr),我們可以利用Xcode的日誌輸出窗口,那麼既然是要記錄到具體日誌文件,我們就想輸出日誌寫入到具體的日誌文件即可。

 

代碼

1、  宏定義(下面是我在程序中常用到的日誌宏,用DEBUG開關管理,

也就是說只有在DEBUG模式下才讓日誌輸出 :)

 

#ifdef DEBUG  

#  define LOG(fmt, ...) do {                                            \  

        NSString* file = [[NSString alloc] initWithFormat:@"%s", __FILE__]; \  

        NSLog((@"%@(%d) " fmt), [file lastPathComponent], __LINE__, ##__VA_ARGS__); \  

        [file release];                                                 \  

    } while(0)  

#  define LOG_METHOD NSLog(@"%s", __func__)  

#  define LOG_CMETHOD NSLog(@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd))  

#  define COUNT(p) NSLog(@"%s(%d): count = %d\n", __func__, __LINE__, [p retainCount]);  

#  define LOG_TRACE(x) do {printf x; putchar('\n'); fflush(stdout);} while (0)  

#else  

#  define LOG(...)  

#  define LOG_METHOD  

#  define LOG_CMETHOD  

#  define COUNT(p)  

#  define LOG_TRACE(x)  

#endif

 

可以看到,除了標準的用戶定義輸出外,我還加入了許多有用的信息,

比如源程序文件位置,行號,類名,函數名等。具體的應用可以在具體的開發過程中添加、刪除。

 

2、  應用:

- (void)redirectNSLogToDocumentFolder{  

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);  

    NSString *documentsDirectory = [paths objectAtIndex:0];  

    NSString *fileName =[NSString stringWithFormat:@"%@.log",[NSDate date]];  

    NSString *logFilePath = [documentsDirectory stringByAppendingPathComponent:fileName];  

    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);  

}  

 

- (void)applicationDidFinishLaunching:(UIApplication *)application {  

    // 真機測試時保存日誌  

    if ([CDeviceInfo getModelType] != SIMULATOR) {  

        [self redirectNSLogToDocumentFolder];  

}  

}

 

真機測試的時候,可以利用freopen將標準錯誤輸出保存到指定的文件當中,

這樣就可以在問題發生後分析日誌文件。

 3、 設置DEBUG標誌是否正確定義

 

Xcode 一般會在 debug 運行配置項裏面已經定義號了DEBUG 標誌,如果沒定義我們就自己寫上,以我的 Xcode 4 爲例,在項目get Info中找到 PreProcessor Macros 這個屬性,對於 Debug 配置我們給他寫上 DEBUG,而在 Release 配置中把它留空。 這樣我們剛纔那段預處理命令就可以根據這個標誌來判斷我們編譯的時調試版本還是發佈版本,從而控制 NSLog 的輸出。 (因爲 Xcode 4會把 debug/release 兩個配置項同時對比展現出來,而 3.x 版本的只能分別設置,如果你用的時xcode 3.x 開發工具, 那麼就分別對 Debug/Release 都檢查一下)。

 

其他

              來源:

              iPhone開發技巧之日誌保存教程

              iPhone應用根據Debug和Release狀態變化來屏蔽日誌輸出

方法二

iOS - NSLog、UncaughtException日誌保存到文件

對於真機,日誌沒法保存,不好分析問題。所以有必要將日誌保存到應用的Docunment目錄下,方便取出分析。

 

首先是日誌輸出,分爲c的printf和標準的NSLog輸出,printf會向標準輸出(sedout)打印,而NSLog則是向標準出錯(stderr),我們需要同時讓他們都將日誌打印到一個文件中。 其次是Crash問題;Crash分爲兩種,一種是由EXC_BAD_ACCESS引起的,原因是訪問了不屬於本進程的內存地址,有可能是訪問已被釋放的內存;另一種是未被捕獲的Objective-C異常(NSException),導致程序向自身發送了SIGABRT信號而崩潰。其實對於未捕獲的Objective-C異常,我們是有辦法將它記錄下來的,如果日誌記錄得當,能夠解決絕大部分崩潰的問題。

 

我寫了兩個函數用於寫NSLog日誌和Crash日誌,這個兩個函數都必須在AppDelegate文件中下面的函數裏添加

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions



//連接xcode時可以從監視器中看日誌 沒連接時Log日誌會輸出到文件中,  
[self redirectNSLogToDocumentFolder];  


- (void)redirectNSLogToDocumentFolder  
{  
    //如果已經連接Xcode調試則不輸出到文件  
    if(isatty(STDOUT_FILENO)) {  
        return;  
    }  
      
    UIDevice *device = [UIDevice currentDevice];  
    if([[device model] hasSuffix:@"Simulator"]){ //在模擬器不保存到文件中  
        return;  
    }  
      
    //將NSlog打印信息保存到Document目錄下的Log文件夾下  
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
    NSString *logDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Log"];  
      
    NSFileManager *fileManager = [NSFileManager defaultManager];  
    BOOL fileExists = [fileManager fileExistsAtPath:logDirectory];  
    if (!fileExists) {  
        [fileManager createDirectoryAtPath:logDirectory  withIntermediateDirectories:YES attributes:nil error:nil];  
    }  
      
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];  
    [formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]];  
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; //每次啓動後都保存一個新的日誌文件中  
    NSString *dateStr = [formatter stringFromDate:[NSDate date]];  
    NSString *logFilePath = [logDirectory stringByAppendingFormat:@"/%@.log",dateStr];  
      
    // 將log輸入到文件  
    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);  
    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);  
      
    //未捕獲的Objective-C異常日誌  
    NSSetUncaughtExceptionHandler (&UncaughtExceptionHandler);  
}  
  
void UncaughtExceptionHandler(NSException* exception)  
{  
    NSString* name = [ exception name ];  
    NSString* reason = [ exception reason ];  
    NSArray* symbols = [ exception callStackSymbols ]; // 異常發生時的調用棧  
    NSMutableString* strSymbols = [ [ NSMutableString alloc ] init ]; //將調用棧拼成輸出日誌的字符串  
    for ( NSString* item in symbols )  
    {  
        [ strSymbols appendString: item ];  
        [ strSymbols appendString: @"\r\n" ];  
    }  
      
    //將crash日誌保存到Document目錄下的Log文件夾下  
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
    NSString *logDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Log"];  
      
    NSFileManager *fileManager = [NSFileManager defaultManager];  
    if (![fileManager fileExistsAtPath:logDirectory]) {  
        [fileManager createDirectoryAtPath:logDirectory  withIntermediateDirectories:YES attributes:nil error:nil];  
    }  
      
    NSString *logFilePath = [logDirectory stringByAppendingPathComponent:@"UncaughtException.log"];  
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];  
    [formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]];  
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];  
    NSString *dateStr = [formatter stringFromDate:[NSDate date]];  
      
    NSString *crashString = [NSString stringWithFormat:@"<- %@ ->[ Uncaught Exception ]\r\nName: %@, Reason: %@\r\n[ Fe Symbols Start ]\r\n%@[ Fe Symbols End ]\r\n\r\n", dateStr, name, reason, strSymbols];  
    //把錯誤日誌寫到文件中  
    if (![fileManager fileExistsAtPath:logFilePath]) {  
        [crashString writeToFile:logFilePath atomically:YES encoding:NSUTF8StringEncoding error:nil];  
    }else{  
        NSFileHandle *outFile = [NSFileHandle fileHandleForWritingAtPath:logFilePath];  
        [outFile seekToEndOfFile];  
        [outFile writeData:[crashString dataUsingEncoding:NSUTF8StringEncoding]];  
        [outFile closeFile];  
    }  
      
    //把錯誤日誌發送到郵箱  
    //    NSString *urlStr = [NSString stringWithFormat:@"mailto://[email protected]?subject=bug報告&body=感謝您的配合!


錯誤詳情:
%@",crashString ];  
    //    NSURL *url = [NSURL URLWithString:[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];  
    //    [[UIApplication sharedApplication] openURL:url];  
}  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章