欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

IOS本地日志記錄解決方案

 更新時間:2017年03月30日 10:53:36   作者:這酸爽!  
我們在項(xiàng)目中日志記錄這塊也算是比較重要的,有時候用戶程序出什么問題,光靠服務(wù)器的日志還不能準(zhǔn)確的找到問題。本文詳細(xì)介紹了IOS本地日志記錄解決方案。下面跟著小編一起來看下吧

我們在項(xiàng)目中日志記錄這塊也算是比較重要的,有時候用戶程序出什么問題,光靠服務(wù)器的日志還不能準(zhǔn)確的找到問題

現(xiàn)在一般記錄日志有幾種方式:

1、使用第三方工具來記錄日志,如騰訊的Bugly,它是只把程序的異常日志,程序崩潰日志,以及一些自定義的操作日志上傳到Bugly的后臺

2、我們把日志記錄到本地,在適合的時候再上傳到服務(wù)器

這里我要介紹的是第二種方法,第一種和第二種可以一起用。

假如現(xiàn)在有下面這樣的日志記錄要求

1、日志記錄在本地

2、日志最多記錄N天,N天之前的都需要清理掉

3、日志可以上傳到服務(wù)器,由服務(wù)器控制是否需要上傳

4、上傳的日志應(yīng)該壓縮后再上傳

實(shí)現(xiàn)思路

1、日志記錄在本地

也就是把字符串保存到本地,我們可以用 將NSString轉(zhuǎn)換成NSData然后寫入本地,但是NSData寫入本地會對本地的文件進(jìn)入覆蓋,所以我們只有當(dāng)文件不存在的時候第一次寫入的時候用這種方式,如果要將日志內(nèi)容追加到日志文件里面,我們可以用NSFleHandle來處理

2、日志最多記錄N天,N天之前的都需要清理掉

這個就比較容易了,我們可以將本地日志文件名定成當(dāng)天日期,每天一個日志文件,這樣我們在程序啟動后,可以去檢測并清理掉過期的日志文件

3、日志可以上傳到服務(wù)器,由服務(wù)器控制是否需要上傳

這個功能我們需要后臺的配合,后臺需要提供兩個接口,一個是APP去請求時返回當(dāng)前應(yīng)用是否需要上傳日志,根據(jù)參數(shù)來判斷,第二個接口就是上傳日志的接口

4、上傳的日志應(yīng)該壓縮后再上傳

一般壓縮的功能我們可以使用zip壓縮,OC中有開源的插件 ZipArchive 地址:http://code.google.com/p/ziparchive/ (需要FQ)

具體實(shí)現(xiàn)代碼

我們先將ZipArchive引入到項(xiàng)目中,注意還需要引入系統(tǒng)的 libz.tbd 動態(tài)庫,如下:

由于ZipArchive是使用C++編寫的,是不支持ARC的,所以我們需要在項(xiàng)目中把這個類的ARC關(guān)閉掉,不然會編譯不通過,如下:

給ZipArchive.mm文件添加一個 -fno-objc-arc 標(biāo)簽就可以了

然后就是代碼部分了,創(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

/**
 * 獲取單例實(shí)例
 *
 * @return 單例實(shí)例
 */
+ (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

/**
 * 獲取單例實(shí)例
 *
 * @return 單例實(shí)例
 */
+ (instancetype) sharedInstance{

 static LogManager* instance = nil;

 static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
 if (!instance) {
  instance = [[LogManager alloc]init];
 }
 });
 return instance;
}

// 獲取當(dāng)前時間
+ (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"];
 // 設(shè)置時區(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:@"【記錄日志異常】"];
 } @finally {
 // 將參數(shù)列表指針置空
 va_end(paramList);
 }

#pragma mark - 寫入日志

 // 異步執(zhí)行
 dispatch_async(dispatch_queue_create("writeLog", nil), ^{

 // 獲取當(dāng)前日期做為文件名
 NSString* fileName = [self.dateFormatter stringFromDate:[NSDate date]];
 NSString* filePath = [NSString stringWithFormat:@"%@%@",self.basePath,fileName];

 // [時間]-[模塊]-日志內(nèi)容
 NSString* timeStr = [self.timeFormatter stringFromDate:[LogManager getCurrDate]];
 NSString* writeStr = [NSString stringWithFormat:@"[%@]-[%@]-%@\n",timeStr,module,parmaStr];

 // 寫入數(shù)據(jù)
 [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;
 // 獲取實(shí)體字典
 __block NSDictionary* resultDic = nil;
 // 請求的URL,后臺功能需要自己做
 NSString* url = [NSString stringWithFormat:@"%@/common/phone/logs",SERVIERURL];
 // 發(fā)起請求,從服務(wù)器上獲取當(dāng)前應(yīng)用是否需要上傳日志
 [[XGNetworking sharedInstance] get:url success:^(NSString* jsonData) {
 // 獲取實(shí)體字典
 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ù)據(jù)!"] code:500 userInfo:nil];
 }
 // 完成后的處理
 if (error == nil) {

  // 處理上傳日志
  [self uploadLog:resultDic];
 }else{
  LOGERROR(@"檢測日志返回結(jié)果有誤!data沒有數(shù)據(jù)!");
 }
 } 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í)行結(jié)果
 */
- (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) {
 // 關(guān)閉文件
 [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];
  }
 }
 }
 // 關(guān)閉文件
 [zip CloseZipFile2];
 return YES;
}

/**
 * 上傳日志到服務(wù)器
 *
 * @param returnBlock 成功回調(diào)
 * @param errorBlock 失敗回調(diào)
 */
- (void)uploadLogToServer:(BoolBlock)returnBlock errorBlock:(ErrorBlock)errorBlock{

 __block NSError* error = nil;
 // 獲取實(shí)體字典
 __block NSDictionary* resultDic;

 // 訪問URL
 NSString* url = [NSString stringWithFormat:@"%@/fileupload/fileupload/logs",SERVIERURL_FILE];

 // 發(fā)起請求,這里是上傳日志到服務(wù)器,后臺功能需要自己做
 [[XGNetworking sharedInstance] upload:url fileData:nil fileName:ZipFileName mimeType:@"application/zip" parameters:nil success:^(NSString *jsonData) {

 // 獲取實(shí)體字典
 resultDic = [Utilities getDataString:jsonData error:&error];

 // 完成后的處理
 if (error == nil) {
  // 回調(diào)返回?cái)?shù)據(jù)
  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];
 }
}

/**
 * 寫入字符串到指定文件,默認(rèn)追加內(nèi)容
 *
 * @param filePath 文件路徑
 * @param stringData 待寫入的字符串
 */
- (void)writeFile:(NSString*)filePath stringData:(NSString*)stringData{

 // 待寫入的數(shù)據(jù)
 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 用于處理文件內(nèi)容
 // 讀取文件到上下文,并且是更新模式
 NSFileHandle* fileHandler = [NSFileHandle fileHandleForUpdatingAtPath:filePath];

 // 跳到文件末尾
 [fileHandler seekToEndOfFile];

 // 追加數(shù)據(jù)
 [fileHandler writeData:writeData];

 // 關(guān)閉文件
 [fileHandler closeFile];
 }
}
@end

日志工具的使用

1、記錄日志

[[LogManager sharedInstance] logInfo:@"首頁" logStr:@"這是日志信息!",@"可以多參數(shù)",nil];

2、我們在程序啟動后,進(jìn)行一次檢測,看要不要上傳日志

// 幾秒后檢測是否有需要上傳的日志
[[LogManager sharedInstance] performSelector:@selector(checkLogNeedUpload) withObject:nil afterDelay:3];

這里可能有人發(fā)現(xiàn)我們在記錄日志的時候?yàn)槭裁醋詈竺嬉由蟦il,因?yàn)檫@個是OC中動態(tài)參數(shù)的結(jié)束后綴,不加上nil,程序就不知道你有多少個參數(shù),可能有人又要說了,NSString的 stringWithFormat 方法為什么不需要加 nil 也可以呢,那是因?yàn)閟tringWithFormat里面用到了占位符,就是那些 %@ %i 之類的,這樣程序就能判斷你有多少個參數(shù)了,所以就不用加 nil 了

看到這里,可能大家覺得這個記錄日志的方法有點(diǎn)長,后面還加要nil,不方便,那能不能再優(yōu)化一些,讓它更簡單的調(diào)用呢?我可以用到宏來優(yōu)化,我們這樣定義一個宏,如下:

// 記錄本地日志
#define LLog(module,...) [[LogManager sharedInstance] logInfo:module logStr:__VA_ARGS__,nil]

這樣我們使用的時候就方便了,這樣調(diào)用就行了。

LLog(@"首頁", @"這是日志信息!",@"可以多參數(shù)");

以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!

相關(guān)文章

  • iOS DispatchSourceTimer 定時器的具體使用

    iOS DispatchSourceTimer 定時器的具體使用

    定時器在很多地方都可以用到,本文主要介紹了iOS DispatchSourceTimer 定時器的具體使用,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • IOS 下獲取 rootviewcontroller 的版本不同的問題解決辦法

    IOS 下獲取 rootviewcontroller 的版本不同的問題解決辦法

    這篇文章主要介紹了IOS 下獲取 rootviewcontroller 的版本不同的問題解決辦法的相關(guān)資料,希望通過本文能幫助到大家,讓大家遇到這種問題可以解決,需要的朋友可以參考下
    2017-10-10
  • ios開發(fā)Flutter之?dāng)?shù)據(jù)存儲

    ios開發(fā)Flutter之?dāng)?shù)據(jù)存儲

    這篇文章主要為大家介紹了ios開發(fā)Flutter之?dāng)?shù)據(jù)存儲的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • IOS開發(fā)用戶登錄注冊模塊所遇到的問題

    IOS開發(fā)用戶登錄注冊模塊所遇到的問題

    最近和另外一位同事負(fù)責(zé)公司登錄和用戶中心模塊的開發(fā)工作。通過本文給大家分享IOS開發(fā)用戶登錄注冊模塊所遇到的問題,感興趣的朋友一起學(xué)習(xí)吧
    2016-01-01
  • iOS指紋驗(yàn)證TouchID應(yīng)用學(xué)習(xí)教程2

    iOS指紋驗(yàn)證TouchID應(yīng)用學(xué)習(xí)教程2

    這篇文章主要為大家詳細(xì)iOS指紋驗(yàn)證TouchID應(yīng)用學(xué)習(xí)教程的第一篇,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • 詳解ios監(jiān)聽reloadData刷新列表完畢的時機(jī)

    詳解ios監(jiān)聽reloadData刷新列表完畢的時機(jī)

    這篇文章主要介紹了詳解ios監(jiān)聽reloadData刷新列表完畢的時機(jī),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-11-11
  • IOS 常見的循環(huán)引用總結(jié)

    IOS 常見的循環(huán)引用總結(jié)

    這篇文章主要介紹了IOS 常見的循環(huán)引用總結(jié)的相關(guān)資料,循環(huán)引用,指的是多個對象相互引用時,使得引用形成一個環(huán)形,導(dǎo)致外部無法真正是否掉這塊環(huán)形內(nèi)存。其實(shí)有點(diǎn)類似死鎖,需要的朋友可以參考下
    2017-03-03
  • 實(shí)例解析iOS app開發(fā)中音頻文件播放工具類的封裝

    實(shí)例解析iOS app開發(fā)中音頻文件播放工具類的封裝

    這篇文章主要介紹了iOS app開發(fā)中音頻文件播放工具類的封裝,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下
    2016-01-01
  • IOS實(shí)現(xiàn)基于CMPedometer的計(jì)步器

    IOS實(shí)現(xiàn)基于CMPedometer的計(jì)步器

    這篇文章主要為大家詳細(xì)介紹了IOS實(shí)現(xiàn)基于CMPedometer的計(jì)步器,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-06-06
  • iOS 沙盒圖片保存讀取實(shí)例

    iOS 沙盒圖片保存讀取實(shí)例

    下面小編就為大家分享一篇iOS 沙盒圖片保存讀取實(shí)例,具有很的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12

最新評論