iOS 開發(fā) 日志寫入文件_日志捕捉_日志上傳_升級版

【作者前言】:13年入圈,分享些本人工作中遇到的點點滴滴那些事兒,17年剛開始寫博客厦画,高手勿噴混坞!以分享交流為主,歡迎各路豪杰點評改進礼旅!

1.應(yīng)用場景:

很多時候,需要我們將項目的日志留存到文件中,或通過手機分享唾琼、Xcode調(diào)試、上傳服務(wù)器 等形式澎剥,對日志進行分析锡溯;
這里也是應(yīng)公司項目需要,寫了一個小工具類,沒那么長時間去寫-直接一個類處理了(不喜勿噴~~~??)祭饭,有需要的盆友可以參考芜茵、使用
如有幫助,歡迎點贊/收藏/留言??

2.實現(xiàn)目標(biāo):

通過自定義宏實現(xiàn)多形式的日志打印甜癞,debug環(huán)境下同步輸出控制臺夕晓,便于調(diào)試;可開啟是否寫入文件等
上一版在實際使用的過程中 - 發(fā)現(xiàn)了一些問題 - 后來進行了演進和修改

  1. 新增線程安全的兼容悠咱,考慮到多線程訪問日志類蒸辆,帶來的線程污染日志數(shù)據(jù)等問題,確蔽黾龋控制臺輸出與文件I/O完全一致
  2. 降低對文件I/O的操作頻率躬贡,提升穩(wěn)定性和性能
  3. 新增日志等級控制接口,取消以前的強制寫入眼坏,采用等級去控制拂玻,低于設(shè)置的日志等級不再加入文件寫入的隊列之中
  4. 新增動態(tài)配置日志存儲根目錄、配置存儲空間宰译、配置最大保留時長等接口檐蚜,新增可自定義設(shè)置日志標(biāo)識符,為檢索日志內(nèi)容提供便利
  5. 考慮到類的編譯加載的性能消耗等問題 - 去除多余類沿侈,采用單一類闯第,降低不必要的性能消耗
  6. 性能測試中,發(fā)現(xiàn)一些問題 - 繼續(xù)優(yōu)化缀拭,調(diào)整 -> 按需使用UI調(diào)試回顯咳短,避免過多的內(nèi)存占用,同時進一步優(yōu)化I/O機制蛛淋,通過任務(wù)降級+定時監(jiān)測+閾值觸發(fā)+同步寫入的接口咙好,進一步降低I/O頻率和內(nèi)存影響
懶得看?褐荷?勾效?demo直通車[1]

使用效果圖示:
Debug模式下,控制臺同步:


image.png

對應(yīng)的日志文件效果:


image.png

3.代碼說明:

主要通過自寫小工具類YPLogTool實現(xiàn)日志打印诚卸、同步文件寫入葵第、crash捕捉、日志上傳等

1)支持多用戶日志存取合溺,數(shù)據(jù)文件存儲結(jié)構(gòu)說明:
image.png
2)支持通過日志等級動態(tài)是否需要寫入文件流卒密、info/warn/error 多形式打印日志的 便捷宏
image.png
image.png
3)支持設(shè)置日志文件的最大存儲空間(Mb)
image.png
4)支持設(shè)置日志文件的保留天數(shù) (與3相比,具有優(yōu)先級)
image.png
5)支持日志脫敏的開關(guān)控制
image.png
7)支持獲取當(dāng)次打印的所有數(shù)據(jù)棠赛,可用于一些圖形化日志數(shù)據(jù)的回顯等
image.png
8)核心代碼實現(xiàn)(列舉下寫文件的吧哮奇,多了看了也煩??~~~具體看本文底部膛腐,我會附上demo地址)

YPLogTool.m

//MARK: - yp_printLogWithContentModel:  For Xcode
+ (void)yp_printLogWithContentModel:(YPLogContentModel *)contentModel {
    yp_printLogTimes ++;
    if (contentModel.keyIdentifierStr.length) {
        fprintf(contentModel.logLevel == YP_LOG_LEVEL_ERROR ? stderr : stdout,"%s [%s] [%s] %s [%s:%lu] [%s] [%s] :%s\n",contentModel.printLogLevelFlagUTF8, contentModel.timeStrUTF8, contentModel.fmtLogLevelStrUTF8, contentModel.keyIdentifierStrUTF8, contentModel.fileUTF8, (unsigned long)contentModel.line, contentModel.functionNameUTF8, contentModel.threadFlagUTF8, contentModel.formatUTF8);
    }else {
        fprintf(contentModel.logLevel == YP_LOG_LEVEL_ERROR ? stderr : stdout,"%s [%s] [%s] [%s:%lu] [%s] [%s] :%s\n",contentModel.printLogLevelFlagUTF8, contentModel.timeStrUTF8, contentModel.fmtLogLevelStrUTF8, contentModel.fileUTF8, (unsigned long)contentModel.line, contentModel.functionNameUTF8, contentModel.threadFlagUTF8, contentModel.formatUTF8);
    }
    if (!(yp_printLogTimes % 5000)) { //
        if (contentModel.logLevel == YP_LOG_LEVEL_ERROR) {
            fflush(stderr);
        }else {
            fflush(stdout);
        }
    }
}

//MARK: - yp_writeLogWithContentModel:  For File Save
+ (void)yp_writeLogWithContentModel:(YPLogContentModel *)contentModel multiLinesLog:(NSString *)linesLog{
    // 校驗下輪轉(zhuǎn)清理
    NSString *curDayStr = [[contentModel.timeStr componentsSeparatedByString:@" "][0] stringByReplacingOccurrencesOfString:@"-" withString:@""];
    if (yp_useBeginTimeDayStr && (![curDayStr isEqualToString:yp_useBeginTimeDayStr])) {// 當(dāng)啟用日志組件時的日期 與 當(dāng)前打印日志時的日期 不一致時  && 當(dāng)前打印日志時的日期有值 , 即跨夜了...
        YPLogWarn(@"少年好厲害,決戰(zhàn)到天亮鼎俘!跨夜時間:【%@->%@】", yp_useBeginTimeDayStr, curDayStr);
        yp_useBeginTimeDayStr = curDayStr;
        if (yp_tempNoClearUserDirectoryNames) {
            [yp_tempNoClearUserDirectoryNames removeAllObjects];
        }
    }
    
    NSError *error = nil;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    
    NSString *filePath = [yp_curUserDirectoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"YPLog_%@_%@.log", yp_curUserIdentifier, curDayStr]];
    if(![fileManager fileExistsAtPath:filePath]) {// 如果日志文件不存在 - 創(chuàng)建并寫入日志文件
        [linesLog writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
        if (error) {
            YPLogError(@"日志信息寫入文件失敗,errorInfo: %@ 對應(yīng)日志內(nèi)容:%@", error.domain, contentModel.fullLogContent);
        }
        
    }else {// 日志文件存在 - 則繼續(xù)追加寫入
        NSFileHandle *fileHandle;
        @try {
            fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
            [fileHandle seekToEndOfFile];
            NSData* stringData = [linesLog dataUsingEncoding:NSUTF8StringEncoding];
            [fileHandle writeData:stringData];
            [fileHandle synchronizeFile];
            
        } @catch (NSException *exception) {
            
        } @finally {
            // 確保句柄被有效關(guān)閉
            [fileHandle closeFile];
        }
    }
    
    // 打印日志寫入 次數(shù) ++
    yp_writeLogTimes ++;
    [[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithFormat:@"%lld", (long long)yp_writeLogTimes] forKey:@"yp_writeLogTimesKey"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    if (!(yp_writeLogTimes % 10000)) {// 每10000次打印哲身,校驗一回文件大小相關(guān)問題,降低頻率贸伐,增加性能   => 實測 10000次大概是1.*Mb左右
        FILE_SIZE_CHECK_LOOP: {
            
            float curFileSize = [YPLoggerTool yp_getTotalLogsSizeMb];
            if (curFileSize >= yp_maxFoldSize) {
                FILE_EARLIEST_LOOP: {
                    
                    NSString *earliestFilePath = [YPLoggerTool yp_getEarliestLogFilePath];
                    if ([earliestFilePath isEqualToString:@"NoFilePath"]) {
                        return;
                    }
                    
                    NSMutableArray *temp = [NSMutableArray arrayWithArray:[earliestFilePath componentsSeparatedByString:@"/"]];
                    
                    [temp removeLastObject];
                    
                    NSString *earliestUserPath = [temp componentsJoinedByString:@"/"];
                    
                    NSInteger userLogsCount = [YPLoggerTool yp_getUserPathLogsCount:earliestUserPath];
                    if (userLogsCount > yp_maxSaveDays) {
                        if ([fileManager fileExistsAtPath:earliestFilePath]) {
                            [fileManager removeItemAtPath:earliestFilePath error:nil];
                
                            goto FILE_SIZE_CHECK_LOOP;
                        }
                        
                    }else {
                        NSString *userDirectoryName = temp.lastObject;
                        
                        [yp_tempNoClearUserDirectoryNames addObject:userDirectoryName];
                        
                        goto FILE_EARLIEST_LOOP;
                    }
                }
            }
        }
    }
}

9)Demo地址

  1. Demo直通車 ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載勘天,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末捉邢,一起剝皮案震驚了整個濱河市脯丝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伏伐,老刑警劉巖宠进,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異藐翎,居然都是意外死亡材蹬,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門吝镣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來堤器,“玉大人,你說我怎么就攤上這事末贾『鹁桑” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵未舟,是天一觀的道長。 經(jīng)常有香客問我掂为,道長裕膀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任勇哗,我火速辦了婚禮昼扛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘欲诺。我一直安慰自己抄谐,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布扰法。 她就那樣靜靜地躺著蛹含,像睡著了一般。 火紅的嫁衣襯著肌膚如雪塞颁。 梳的紋絲不亂的頭發(fā)上浦箱,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天吸耿,我揣著相機與錄音,去河邊找鬼酷窥。 笑死咽安,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蓬推。 我是一名探鬼主播妆棒,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼沸伏!你這毒婦竟也來了糕珊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤馋评,失蹤者是張志新(化名)和其女友劉穎放接,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體留特,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡纠脾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蜕青。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苟蹈。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖右核,靈堂內(nèi)的尸體忽然破棺而出慧脱,到底是詐尸還是另有隱情,我是刑警寧澤贺喝,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布菱鸥,位于F島的核電站,受9級特大地震影響躏鱼,放射性物質(zhì)發(fā)生泄漏氮采。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一染苛、第九天 我趴在偏房一處隱蔽的房頂上張望鹊漠。 院中可真熱鬧,春花似錦茶行、人聲如沸躯概。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽娶靡。三九已至,卻和暖如春看锉,著一層夾襖步出監(jiān)牢的瞬間固蛾,已是汗流浹背结执。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留艾凯,地道東北人献幔。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像趾诗,于是被迫代替她去往敵國和親蜡感。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容