亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > iOS > 正文

iOS中日志同步獲取NSLog重定向以及其他詳解

2019-10-21 18:43:12
字體:
來源:轉載
供稿:網友

前言

對于那些做后端開發的工程師來說,看LOG解Bug應該是理所當然的事,但我接觸到的移動應用開發的工程師里面,很多人并沒有這個意識,查Bug時總是一遍一遍的試圖重現,試圖調試,特別是對一些不太容易重現的Bug經常焦頭爛額。

我們在真機測試時經常會發現一個難題是無法查看真機的NSLog類型的實時日志,這時候需要RD復現問題來定位當時的日志,以方便查找問題。這個問題在測試中是非常常見的,也是功能測試會花費比較長時間的一個原因。

以下我們討論下能即時查看日志的幾種方案。

NSLog輸出到哪里?

在iOS開發中,我們經常會用到NSLog調試,但是我們卻不太了解它。在NSLog本質是一個C函數,它的函數聲明如下:
FOUNDATION_EXPORT void NSLog(NSString *format, ...)

系統對它的說明是:Logs an error message to the Apple System Log facility.。他是用來輸出信息到標準Error控制臺上去的,其內部其實是使用Apple System Log的API。在調試階段,日志會輸出到到Xcode中,而在iOS真機上,它會輸出到系統的/var/log/syslog這個文件中。

在iOS中,把日志輸出到文件中的句柄在unistd.h文件中有定義:

#define STDIN_FILENO 0 /* standard input file descriptor */#define STDOUT_FILENO 1 /* standard output file descriptor */#define STDERR_FILENO 2 /* standard error file descriptor */

NSLog輸出的是到STDERR_FILENO上,我們可以在iOS中使用c語言輸出到的文件的fprintf來驗證:

NSLog(@"iOS NSLog");fprintf (stderr, "%s/n", "fprintf log");

由于fprintf并不會像NSLog那樣,在內部調用ASL接口,所以只是單純的輸出信息,并沒有添加日期、進程名、進程id等,也不會自動換行。

ASL讀取日志

首先我們可以想到的是既然日志寫入系統的syslog中,那我們可以直接讀取這些日志。從ASL讀取日志的核心代碼如下:

#import <asl.h>// 從日志的對象aslmsg中獲取我們需要的數據+(instancetype)logMessageFromASLMessage:(aslmsg)aslMessage{ SystemLogMessage *logMessage = [[SystemLogMessage alloc] init]; const char *timestamp = asl_get(aslMessage, ASL_KEY_TIME); if (timestamp) {  NSTimeInterval timeInterval = [@(timestamp) integerValue];  const char *nanoseconds = asl_get(aslMessage, ASL_KEY_TIME_NSEC);  if (nanoseconds) {   timeInterval += [@(nanoseconds) doubleValue] / NSEC_PER_SEC;  }  logMessage.timeInterval = timeInterval;  logMessage.date = [NSDate dateWithTimeIntervalSince1970:timeInterval]; } const char *sender = asl_get(aslMessage, ASL_KEY_SENDER); if (sender) {  logMessage.sender = @(sender); } const char *messageText = asl_get(aslMessage, ASL_KEY_MSG); if (messageText) {  logMessage.messageText = @(messageText);//NSLog寫入的文本內容 } const char *messageID = asl_get(aslMessage, ASL_KEY_MSG_ID); if (messageID) {  logMessage.messageID = [@(messageID) longLongValue]; } return logMessage;}+ (NSMutableArray<SystemLogMessage *> *)allLogMessagesForCurrentProcess{ asl_object_t query = asl_new(ASL_TYPE_QUERY); // Filter for messages from the current process. Note that this appears to happen by default on device, but is required in the simulator. NSString *pidString = [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]]; asl_set_query(query, ASL_KEY_PID, [pidString UTF8String], ASL_QUERY_OP_EQUAL); aslresponse response = asl_search(NULL, query); aslmsg aslMessage = NULL; NSMutableArray *logMessages = [NSMutableArray array]; while ((aslMessage = asl_next(response))) {  [logMessages addObject:[SystemLogMessage logMessageFromASLMessage:aslMessage]]; } asl_release(response); return logMessages;}

使用以上方法的好處是不會影響Xcode控制臺的輸出,可以用非侵入性的方式來讀取日志。

NSLog重定向

另一種方式就是重定向NSLog,這樣NSLog就不會寫到系統的syslog中了。

dup2重定向

通過重定向,可以直接截取stdout,stderr等標準輸出的信息,然后保存在想要存儲的位置,上傳到服務器或者顯示到View上。

要做到重定向,需要通過NSPipe創建一個管道,pipe有讀端和寫端,然后通過dup2將標準輸入重定向到pipe的寫端。再通過NSFileHandle監聽pipe的讀端,最后再處理讀出的信息。

之后通過printf或者NSLog寫數據,都會寫到pipe的寫端,同時pipe會將這些數據直接傳送到讀端,最后通過NSFileHandle的監控函數取出這些數據。

核心代碼如下:

- (void)redirectStandardOutput{ //記錄標準輸出及錯誤流原始文件描述符 self.outFd = dup(STDOUT_FILENO); self.errFd = dup(STDERR_FILENO);#if BETA_BUILD stdout->_flags = 10; NSPipe *outPipe = [NSPipe pipe]; NSFileHandle *pipeOutHandle = [outPipe fileHandleForReading]; dup2([[outPipe fileHandleForWriting] fileDescriptor], STDOUT_FILENO); [pipeOutHandle readInBackgroundAndNotify]; stderr->_flags = 10; NSPipe *errPipe = [NSPipe pipe]; NSFileHandle *pipeErrHandle = [errPipe fileHandleForReading]; dup2([[errPipe fileHandleForWriting] fileDescriptor], STDERR_FILENO); [pipeErrHandle readInBackgroundAndNotify]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(redirectOutNotificationHandle:) name:NSFileHandleReadCompletionNotification object:pipeOutHandle]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(redirectErrNotificationHandle:) name:NSFileHandleReadCompletionNotification object:pipeErrHandle];#endif}-(void)recoverStandardOutput{#if BETA_BUILD dup2(self.outFd, STDOUT_FILENO); dup2(self.errFd, STDERR_FILENO); [[NSNotificationCenter defaultCenter] removeObserver:self];#endif}// 重定向之后的NSLog輸出- (void)redirectOutNotificationHandle:(NSNotification *)nf{#if BETA_BUILD NSData *data = [[nf userInfo] objectForKey:NSFileHandleNotificationDataItem]; NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; // YOUR CODE HERE... 保存日志并上傳或展示#endif [[nf object] readInBackgroundAndNotify];}// 重定向之后的錯誤輸出- (void)redirectErrNotificationHandle:(NSNotification *)nf{#if BETA_BUILD NSData *data = [[nf userInfo] objectForKey:NSFileHandleNotificationDataItem]; NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; // YOUR CODE HERE... 保存日志并上傳或展示#endif [[nf object] readInBackgroundAndNotify];}

文件重定向

另一種重定向的方式是利用c語言的freopen函數進行重定向,將寫往stderr的內容重定向到我們制定的文件中去,一旦執行了上述代碼那么在這個之后的NSLog將不會在控制臺顯示了,會直接輸出在指定的文件中。

在模擬器中,我們可以使用終端的tail命令(tail -f xxx.log)對這個文件進行實時查看,就如同我們在Xcode的輸出窗口中看到的那樣,你還可以結合grep命令進行實時過濾查看,非常方便在大量的日志信息中迅速定位到我們要的日志信息。

FILE * freopen ( const char * filename, const char * mode, FILE * stream );

具體代碼如下:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);NSString *documentsPath = [paths objectAtIndex:0];NSString *loggingPath = [documentsPath stringByAppendingPathComponent:@"/xxx.log"];//redirect NSLogfreopen([loggingPath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);

這樣我們就可以把可獲取的日志文件發送給服務端或者通過itunes共享出來。但是由于iOS嚴格的沙盒機制,我們無法知道stderr原來的文件路徑,也無法直接使用沙盒外的文件,所以freopen無法重定向回去,只能使用第1點所述的dup和dup2來實現。

// 重定向int origin1 = dup(STDERR_FILENO);FILE * myFile = freopen([loggingPath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);// 恢復重定向dup2(origin1, STDERR_FILENO);

使用GCD的dispatch Source重定向方式

具體代碼如下:

- (dispatch_source_t)_startCapturingWritingToFD:(int)fd { int fildes[2]; pipe(fildes); // [0] is read end of pipe while [1] is write end dup2(fildes[1], fd); // Duplicate write end of pipe "onto" fd (this closes fd) close(fildes[1]); // Close original write end of pipe fd = fildes[0]; // We can now monitor the read end of the pipe char* buffer = malloc(1024); NSMutableData* data = [[NSMutableData alloc] init]; fcntl(fd, F_SETFL, O_NONBLOCK); dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); dispatch_source_set_cancel_handler(source, ^{  free(buffer); }); dispatch_source_set_event_handler(source, ^{  @autoreleasepool {   while (1) {    ssize_t size = read(fd, buffer, 1024);    if (size <= 0) {     break;    }    [data appendBytes:buffer length:size];    if (size < 1024) {     break;    }   }   NSString *aString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];   //printf("aString = %s",[aString UTF8String]);   //NSLog(@"aString = %@",aString);   // Do something  } }); dispatch_resume(source); return source;}

日志同步/上傳

重定向或者存儲的數據可以傳到服務端或者通過server同步到網頁上,就可以更方便的看到這些數據了。

如果想再網頁端實時查看日志,可以在App內置一個小型http web服務器。GitHub上開源的項目有GCDWebServer,可以使用該工具,在APP開啟webserver服務,并在同一局域網下,使用http://localhost:8080來請求最新日志了。

上傳服務端的部分很簡單,實現簡單的網絡請求就可以,這兒不做敘述。

另外在實際項目中,可以設置一個開關來開啟或關閉這個重定向,在調試測試的過程中可以打開開關來查看程序當前的日志。

通過以上處理,真機測試中,日志就可以很方便的獲取和查看了,這樣能節省不少人力和時間成本。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


注:相關教程知識閱讀請移步到IOS開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产在线播放不卡| 一级做a爰片久久毛片美女图片| 91福利视频在线观看| 在线观看中文字幕亚洲| 国产精品黄页免费高清在线观看| 九九热最新视频//这里只有精品| 欧美极品少妇xxxxⅹ裸体艺术| 中文字幕日韩综合av| 日韩免费观看网站| 清纯唯美日韩制服另类| 九九九热精品免费视频观看网站| 精品亚洲va在线va天堂资源站| 亚洲国产精品va| 一本大道亚洲视频| 亚洲国产精品视频在线观看| 国产精品久久久久久网站| 这里只有视频精品| 欧美激情网站在线观看| 亚洲free性xxxx护士hd| 色综合久久中文字幕综合网小说| 欧美性xxxxx极品娇小| 韩日精品中文字幕| 狠狠做深爱婷婷久久综合一区| 在线精品视频视频中文字幕| 国产91精品久久久久| 成人福利在线视频| 欧美激情啊啊啊| 91亚洲va在线va天堂va国| 国产精品pans私拍| 韩国v欧美v日本v亚洲| 久久久久久亚洲| 亚洲人成电影网站| 91干在线观看| 久久精品免费电影| 亚洲精品久久久久国产| 中文字幕日韩免费视频| 91精品国产91久久久久久不卡| 日韩一区二区福利| 中文字幕免费精品一区高清| 久久久久久久成人| 久久久国产精品x99av| 国产精品都在这里| 国产在线观看一区二区三区| 欧美大码xxxx| 亚洲成人av片在线观看| 欧美另类xxx| 伦伦影院午夜日韩欧美限制| 亚洲www永久成人夜色| 亚洲精品视频播放| 草民午夜欧美限制a级福利片| 91热精品视频| 精品一区二区三区四区在线| 国产美女被下药99| 久久精品国产亚洲7777| 日韩欧美在线网址| 91精品国产综合久久久久久蜜臀| 久久精品在线视频| 91日本在线观看| 88xx成人精品| 日韩人在线观看| 欧美亚洲国产日本| 欧美日韩成人在线视频| 日韩成人网免费视频| 国产精品9999| 黑人巨大精品欧美一区二区| 中文日韩在线视频| 日韩av电影在线播放| 欧美高清视频在线| 青青草一区二区| 久久综合久久美利坚合众国| 亚洲成人免费网站| 亚洲福利在线看| 亚洲片在线观看| 成人福利在线观看| 久久99国产综合精品女同| 久久久久久国产精品三级玉女聊斋| 欧美俄罗斯乱妇| 黄色91在线观看| 日韩一区二区久久久| 国产一区二区三区在线看| 国产日韩在线一区| 一区二区亚洲精品国产| 精品动漫一区二区三区| 中文国产成人精品久久一| 久久99精品视频一区97| 日韩欧美有码在线| 麻豆乱码国产一区二区三区| 国产丝袜一区二区| 久久久精品影院| 97香蕉超级碰碰久久免费软件| 欧美激情视频在线免费观看 欧美视频免费一| 亚洲精品乱码久久久久久按摩观| 在线国产精品播放| 国产大片精品免费永久看nba| 欧美成年人视频网站欧美| 久久亚洲精品一区| 中文字幕国产日韩| 免费99精品国产自在在线| 国产精品久久久久久久电影| 国产精品狠色婷| 日韩hd视频在线观看| 亚洲女人天堂成人av在线| 欧美国产欧美亚洲国产日韩mv天天看完整| 欧美激情视频在线免费观看 欧美视频免费一| 亚洲精品理论电影| 亚洲美女中文字幕| 久青草国产97香蕉在线视频| 米奇精品一区二区三区在线观看| 亚洲性生活视频在线观看| 欧美成人手机在线| 欧美午夜性色大片在线观看| 深夜福利91大全| 亚洲性夜色噜噜噜7777| 久久国产精品久久国产精品| 欧美第一黄网免费网站| 精品国内产的精品视频在线观看| 91免费欧美精品| 在线亚洲午夜片av大片| 亚洲综合国产精品| 国产精品18久久久久久首页狼| 欧美激情视频网站| 精品国内自产拍在线观看| 久久中文字幕一区| 欧美成人黑人xx视频免费观看| 在线观看精品自拍私拍| 欧美极品少妇xxxxⅹ裸体艺术| 久久艹在线视频| 欧美性猛交xxxx免费看| 韩国美女主播一区| 欧美性生交大片免费| 日韩视频免费在线| 日韩av电影免费观看高清| 秋霞成人午夜鲁丝一区二区三区| 中文字幕免费精品一区| 国产成人精品视频在线观看| 中文亚洲视频在线| 国产福利精品视频| 日韩av中文字幕在线播放| 7m第一福利500精品视频| 黑人狂躁日本妞一区二区三区| 亚洲国内精品在线| 久久免费成人精品视频| 久久综合伊人77777蜜臀| 国产欧美婷婷中文| 日韩精品久久久久久福利| 黑人巨大精品欧美一区二区三区| 国产精品久久久av久久久| 欧美特级www| 91免费人成网站在线观看18| 91精品啪在线观看麻豆免费| 亚洲精品www久久久久久广东| 日韩美女视频免费看| 久久精品成人一区二区三区| 欧美一区二区影院| 亚洲最大av在线| 国产日韩精品在线| 国产精品久久97| 欧美日韩亚洲网| 大桥未久av一区二区三区| 92看片淫黄大片看国产片| 欧美电影在线播放| 97在线视频一区| 国产成人精品999| 91午夜理伦私人影院|