您好,登錄后才能下訂單哦!
這篇文章主要介紹IOS本地日志記錄的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
我們在項目中日志記錄這塊也算是比較重要的,有時候用戶程序出什么問題,光靠服務器的日志還不能準確的找到問題
現(xiàn)在一般記錄日志有幾種方式:
1、使用第三方工具來記錄日志,如騰訊的Bugly,它是只把程序的異常日志,程序崩潰日志,以及一些自定義的操作日志上傳到Bugly的后臺
2、我們把日志記錄到本地,在適合的時候再上傳到服務器
這里我要介紹的是第二種方法,第一種和第二種可以一起用。
假如現(xiàn)在有下面這樣的日志記錄要求
1、日志記錄在本地
2、日志最多記錄N天,N天之前的都需要清理掉
3、日志可以上傳到服務器,由服務器控制是否需要上傳
4、上傳的日志應該壓縮后再上傳
實現(xiàn)思路
1、日志記錄在本地
也就是把字符串保存到本地,我們可以用 將NSString轉換成NSData然后寫入本地,但是NSData寫入本地會對本地的文件進入覆蓋,所以我們只有當文件不存在的時候第一次寫入的時候用這種方式,如果要將日志內容追加到日志文件里面,我們可以用NSFleHandle來處理
2、日志最多記錄N天,N天之前的都需要清理掉
這個就比較容易了,我們可以將本地日志文件名定成當天日期,每天一個日志文件,這樣我們在程序啟動后,可以去檢測并清理掉過期的日志文件
3、日志可以上傳到服務器,由服務器控制是否需要上傳
這個功能我們需要后臺的配合,后臺需要提供兩個接口,一個是APP去請求時返回當前應用是否需要上傳日志,根據參數(shù)來判斷,第二個接口就是上傳日志的接口
4、上傳的日志應該壓縮后再上傳
一般壓縮的功能我們可以使用zip壓縮,OC中有開源的插件 ZipArchive 地址:http://code.google.com/p/ziparchive/ (需要FQ)
具體實現(xiàn)代碼
我們先將ZipArchive引入到項目中,注意還需要引入系統(tǒng)的 libz.tbd 動態(tài)庫,如下:
由于ZipArchive是使用C++編寫的,是不支持ARC的,所以我們需要在項目中把這個類的ARC關閉掉,不然會編譯不通過,如下:
給ZipArchive.mm文件添加一個 -fno-objc-arc 標簽就可以了
然后就是代碼部分了,創(chuàng)建一個日志工具類,LogManager
// // LogManager.h // LogFileDemo // // Created by xgao on 17/3/9. // Copyright © 2017年 xgao. All rights reserved. // #import <Foundation/Foundation.h> @interface LogManager : NSObject /** * 獲取單例實例 * * @return 單例實例 */ + (instancetype) sharedInstance; #pragma mark - Method /** * 寫入日志 * * @param module 模塊名稱 * @param logStr 日志信息,動態(tài)參數(shù) */ - (void)logInfo:(NSString*)module logStr:(NSString*)logStr, ...; /** * 清空過期的日志 */ - (void)clearExpiredLog; /** * 檢測日志是否需要上傳 */ - (void)checkLogNeedUpload; @end
// // LogManager.m // LogFileDemo // // Created by xgao on 17/3/9. // Copyright © 2017年 xgao. All rights reserved. // #import "LogManager.h" #import "ZipArchive.h" #import "XGNetworking.h" // 日志保留最大天數(shù) static const int LogMaxSaveDay = 7; // 日志文件保存目錄 static const NSString* LogFilePath = @"/Documents/OTKLog/"; // 日志壓縮包文件名 static NSString* ZipFileName = @"OTKLog.zip"; @interface LogManager() // 日期格式化 @property (nonatomic,retain) NSDateFormatter* dateFormatter; // 時間格式化 @property (nonatomic,retain) NSDateFormatter* timeFormatter; // 日志的目錄路徑 @property (nonatomic,copy) NSString* basePath; @end @implementation LogManager /** * 獲取單例實例 * * @return 單例實例 */ + (instancetype) sharedInstance{ static LogManager* instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (!instance) { instance = [[LogManager alloc]init]; } }); return instance; } // 獲取當前時間 + (NSDate*)getCurrDate{ NSDate *date = [NSDate date]; NSTimeZone *zone = [NSTimeZone systemTimeZone]; NSInteger interval = [zone secondsFromGMTForDate: date]; NSDate *localeDate = [date dateByAddingTimeInterval: interval]; return localeDate; } #pragma mark - Init - (instancetype)init{ self = [super init]; if (self) { // 創(chuàng)建日期格式化 NSDateFormatter* dateFormatter = [[NSDateFormatter alloc]init]; [dateFormatter setDateFormat:@"yyyy-MM-dd"]; // 設置時區(qū),解決8小時 [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; self.dateFormatter = dateFormatter; // 創(chuàng)建時間格式化 NSDateFormatter* timeFormatter = [[NSDateFormatter alloc]init]; [timeFormatter setDateFormat:@"HH:mm:ss"]; [timeFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; self.timeFormatter = timeFormatter; // 日志的目錄路徑 self.basePath = [NSString stringWithFormat:@"%@%@",NSHomeDirectory(),LogFilePath]; } return self; } #pragma mark - Method /** * 寫入日志 * * @param module 模塊名稱 * @param logStr 日志信息,動態(tài)參數(shù) */ - (void)logInfo:(NSString*)module logStr:(NSString*)logStr, ...{ #pragma mark - 獲取參數(shù) NSMutableString* parmaStr = [NSMutableString string]; // 聲明一個參數(shù)指針 va_list paramList; // 獲取參數(shù)地址,將paramList指向logStr va_start(paramList, logStr); id arg = logStr; @try { // 遍歷參數(shù)列表 while (arg) { [parmaStr appendString:arg]; // 指向下一個參數(shù),后面是參數(shù)類似 arg = va_arg(paramList, NSString*); } } @catch (NSException *exception) { [parmaStr appendString:@"【記錄日志異?!?quot;]; } @finally { // 將參數(shù)列表指針置空 va_end(paramList); } #pragma mark - 寫入日志 // 異步執(zhí)行 dispatch_async(dispatch_queue_create("writeLog", nil), ^{ // 獲取當前日期做為文件名 NSString* fileName = [self.dateFormatter stringFromDate:[NSDate date]]; NSString* filePath = [NSString stringWithFormat:@"%@%@",self.basePath,fileName]; // [時間]-[模塊]-日志內容 NSString* timeStr = [self.timeFormatter stringFromDate:[LogManager getCurrDate]]; NSString* writeStr = [NSString stringWithFormat:@"[%@]-[%@]-%@\n",timeStr,module,parmaStr]; // 寫入數(shù)據 [self writeFile:filePath stringData:writeStr]; NSLog(@"寫入日志:%@",filePath); }); } /** * 清空過期的日志 */ - (void)clearExpiredLog{ // 獲取日志目錄下的所有文件 NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.basePath error:nil]; for (NSString* file in files) { NSDate* date = [self.dateFormatter dateFromString:file]; if (date) { NSTimeInterval oldTime = [date timeIntervalSince1970]; NSTimeInterval currTime = [[LogManager getCurrDate] timeIntervalSince1970]; NSTimeInterval second = currTime - oldTime; int day = (int)second / (24 * 3600); if (day >= LogMaxSaveDay) { // 刪除該文件 [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@/%@",self.basePath,file] error:nil]; NSLog(@"[%@]日志文件已被刪除!",file); } } } } /** * 檢測日志是否需要上傳 */ - (void)checkLogNeedUpload{ __block NSError* error = nil; // 獲取實體字典 __block NSDictionary* resultDic = nil; // 請求的URL,后臺功能需要自己做 NSString* url = [NSString stringWithFormat:@"%@/common/phone/logs",SERVIERURL]; // 發(fā)起請求,從服務器上獲取當前應用是否需要上傳日志 [[XGNetworking sharedInstance] get:url success:^(NSString* jsonData) { // 獲取實體字典 NSDictionary* dataDic = [Utilities getDataString:jsonData error:&error]; resultDic = dataDic.count > 0 ? [dataDic objectForKey:@"data"] : nil; if([resultDic isEqual:[NSNull null]]){ error = [NSError errorWithDomain:[NSString stringWithFormat:@"請求失敗,data沒有數(shù)據!"] code:500 userInfo:nil]; } // 完成后的處理 if (error == nil) { // 處理上傳日志 [self uploadLog:resultDic]; }else{ LOGERROR(@"檢測日志返回結果有誤!data沒有數(shù)據!"); } } faild:^(NSString *errorInfo) { LOGERROR(([NSString stringWithFormat:@"檢測日志失??!%@",errorInfo])); }]; } #pragma mark - Private /** * 處理是否需要上傳日志 * * @param resultDic 包含獲取日期的字典 */ - (void)uploadLog:(NSDictionary*)resultDic{ if (!resultDic) { return; } // 0不拉取,1拉取N天,2拉取全部 int type = [resultDic[@"type"] intValue]; // 壓縮文件是否創(chuàng)建成功 BOOL created = NO; if (type == 1) { // 拉取指定日期的 // "dates": ["2017-03-01", "2017-03-11"] NSArray* dates = resultDic[@"dates"]; // 壓縮日志 created = [self compressLog:dates]; }else if(type == 2){ // 拉取全部 // 壓縮日志 created = [self compressLog:nil]; } if (created) { // 上傳 [self uploadLogToServer:^(BOOL boolValue) { if (boolValue) { LOGINFO(@"日志上傳成功---->>"); // 刪除日志壓縮文件 [self deleteZipFile]; }else{ LOGERROR(@"日志上傳失?。?!"); } } errorBlock:^(NSString *errorInfo) { LOGERROR(([NSString stringWithFormat:@"日志上傳失?。。rror:%@",errorInfo])); }]; } } /** * 壓縮日志 * * @param dates 日期時間段,空代表全部 * * @return 執(zhí)行結果 */ - (BOOL)compressLog:(NSArray*)dates{ // 先清理幾天前的日志 [self clearExpiredLog]; // 獲取日志目錄下的所有文件 NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.basePath error:nil]; // 壓縮包文件路徑 NSString * zipFile = [self.basePath stringByAppendingString:ZipFileName] ; ZipArchive* zip = [[ZipArchive alloc] init]; // 創(chuàng)建一個zip包 BOOL created = [zip CreateZipFile2:zipFile]; if (!created) { // 關閉文件 [zip CloseZipFile2]; return NO; } if (dates) { // 拉取指定日期的 for (NSString* fileName in files) { if ([dates containsObject:fileName]) { // 將要被壓縮的文件 NSString *file = [self.basePath stringByAppendingString:fileName]; // 判斷文件是否存在 if ([[NSFileManager defaultManager] fileExistsAtPath:file]) { // 將日志添加到zip包中 [zip addFileToZip:file newname:fileName]; } } } }else{ // 全部 for (NSString* fileName in files) { // 將要被壓縮的文件 NSString *file = [self.basePath stringByAppendingString:fileName]; // 判斷文件是否存在 if ([[NSFileManager defaultManager] fileExistsAtPath:file]) { // 將日志添加到zip包中 [zip addFileToZip:file newname:fileName]; } } } // 關閉文件 [zip CloseZipFile2]; return YES; } /** * 上傳日志到服務器 * * @param returnBlock 成功回調 * @param errorBlock 失敗回調 */ - (void)uploadLogToServer:(BoolBlock)returnBlock errorBlock:(ErrorBlock)errorBlock{ __block NSError* error = nil; // 獲取實體字典 __block NSDictionary* resultDic; // 訪問URL NSString* url = [NSString stringWithFormat:@"%@/fileupload/fileupload/logs",SERVIERURL_FILE]; // 發(fā)起請求,這里是上傳日志到服務器,后臺功能需要自己做 [[XGNetworking sharedInstance] upload:url fileData:nil fileName:ZipFileName mimeType:@"application/zip" parameters:nil success:^(NSString *jsonData) { // 獲取實體字典 resultDic = [Utilities getDataString:jsonData error:&error]; // 完成后的處理 if (error == nil) { // 回調返回數(shù)據 returnBlock([resultDic[@"state"] boolValue]); }else{ if (errorBlock){ errorBlock(error.domain); } } } faild:^(NSString *errorInfo) { returnBlock(errorInfo); }]; } /** * 刪除日志壓縮文件 */ - (void)deleteZipFile{ NSString* zipFilePath = [self.basePath stringByAppendingString:ZipFileName]; if ([[NSFileManager defaultManager] fileExistsAtPath:zipFilePath]) { [[NSFileManager defaultManager] removeItemAtPath:zipFilePath error:nil]; } } /** * 寫入字符串到指定文件,默認追加內容 * * @param filePath 文件路徑 * @param stringData 待寫入的字符串 */ - (void)writeFile:(NSString*)filePath stringData:(NSString*)stringData{ // 待寫入的數(shù)據 NSData* writeData = [stringData dataUsingEncoding:NSUTF8StringEncoding]; // NSFileManager 用于處理文件 BOOL createPathOk = YES; if (![[NSFileManager defaultManager] fileExistsAtPath:[filePath stringByDeletingLastPathComponent] isDirectory:&createPathOk]) { // 目錄不存先創(chuàng)建 [[NSFileManager defaultManager] createDirectoryAtPath:[filePath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil]; } if(![[NSFileManager defaultManager] fileExistsAtPath:filePath]){ // 文件不存在,直接創(chuàng)建文件并寫入 [writeData writeToFile:filePath atomically:NO]; }else{ // NSFileHandle 用于處理文件內容 // 讀取文件到上下文,并且是更新模式 NSFileHandle* fileHandler = [NSFileHandle fileHandleForUpdatingAtPath:filePath]; // 跳到文件末尾 [fileHandler seekToEndOfFile]; // 追加數(shù)據 [fileHandler writeData:writeData]; // 關閉文件 [fileHandler closeFile]; } } @end
日志工具的使用
1、記錄日志
[[LogManager sharedInstance] logInfo:@"首頁" logStr:@"這是日志信息!",@"可以多參數(shù)",nil];
2、我們在程序啟動后,進行一次檢測,看要不要上傳日志
// 幾秒后檢測是否有需要上傳的日志 [[LogManager sharedInstance] performSelector:@selector(checkLogNeedUpload) withObject:nil afterDelay:3];
這里可能有人發(fā)現(xiàn)我們在記錄日志的時候為什么最后面要加上nil,因為這個是OC中動態(tài)參數(shù)的結束后綴,不加上nil,程序就不知道你有多少個參數(shù),可能有人又要說了,NSString的 stringWithFormat 方法為什么不需要加 nil 也可以呢,那是因為stringWithFormat里面用到了占位符,就是那些 %@ %i 之類的,這樣程序就能判斷你有多少個參數(shù)了,所以就不用加 nil 了
看到這里,可能大家覺得這個記錄日志的方法有點長,后面還加要nil,不方便,那能不能再優(yōu)化一些,讓它更簡單的調用呢?我可以用到宏來優(yōu)化,我們這樣定義一個宏,如下:
// 記錄本地日志 #define LLog(module,...) [[LogManager sharedInstance] logInfo:module logStr:__VA_ARGS__,nil]
這樣我們使用的時候就方便了,這樣調用就行了。
LLog(@"首頁", @"這是日志信息!",@"可以多參數(shù)");
以上是“IOS本地日志記錄的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業(yè)資訊頻道!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。