我们在项目中日志记录这块也算是比较重要的,有时候用户程序出什么问题,光靠服务器的日志还不能准确的找到问题

现在一般记录日志有几种方式:

1、使用第三方工具来记录日志,如腾讯的Bugly,它是只把程序的异常日志,程序崩溃日志,以及一些自定义的操作日志上传到Bugly的后台

2、我们把日志记录到本地,在适合的时候再上传到服务器

这里我要介绍的是第二种方法,第一种和第二种可以一起用。

假如现在有下面这样的日志记录要求

1、日志记录在本地

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

3、日志可以上传到服务器,由服务器控制是否需要上传

4、上传的日志应该压缩后再上传

实现思路

1、日志记录在本地

  也就是把字符串保存到本地,我们可以用 将NSString转换成NSData然后写入本地,但是NSData写入本地会对本地的文件进入覆盖,所以我们只有当文件不存在的时候第一次写入的时候用这种方式,如果要将日志内容追加到日志文件里面,我们可以用NSFleHandle来处理

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

  这个就比较容易了,我们可以将本地日志文件名定成当天日期,每天一个日志文件,这样我们在程序启动后,可以去检测并清理掉过期的日志文件

3、日志可以上传到服务器,由服务器控制是否需要上传

  这个功能我们需要后台的配合,后台需要提供两个接口,一个是APP去请求时返回当前应用是否需要上传日志,根据参数来判断,第二个接口就是上传日志的接口

4、上传的日志应该压缩后再上传

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

具体实现代码

我们先将ZipArchive引入到项目中,注意还需要引入系统的 libz.tbd 动态库,好下:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

由于ZipArchive是使用C++编写的,是不支持ARC的,所以我们需要在项目中把这个类的ARC关闭掉,不然会编译不通过,如下:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

给ZipArchive.mm文件添加一个 -fno-objc-arc 标签就可以了

然后就是代码部分了,创建一个日志工具类,LogManager

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

////  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 日志信息,动态参数 */- (void)logInfo:(NSString*)module logStr:(NSString*)logStr, ...;/**
 *  清空过期的日志 */- (void)clearExpiredLog;/**
 *  检测日志是否需要上传 */- (void)checkLogNeedUpload;@end

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

////  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"// 日志保留最大天数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) {        
        // 创建日期格式化
        NSDateFormatter* dateFormatter = [[NSDateFormatter alloc]init];
        [dateFormatter setDateFormat:@"yyyy-MM-dd"];        // 设置时区,解决8小时
        [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
        self.dateFormatter = dateFormatter;        
        // 创建时间格式化
        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 日志信息,动态参数 */- (void)logInfo:(NSString*)module logStr:(NSString*)logStr, ...{    
#pragma mark - 获取参数
    
    NSMutableString* parmaStr = [NSMutableString string];    // 声明一个参数指针    va_list paramList;    // 获取参数地址,将paramList指向logStr    va_start(paramList, logStr);    id arg = logStr;    
    @try {        // 遍历参数列表
        while (arg) {
            [parmaStr appendString:arg];            // 指向下一个参数,后面是参数类似
            arg = va_arg(paramList, NSString*);
        }
        
    } @catch (NSException *exception) {

        [parmaStr appendString:@"【记录日志异常】"];
    } @finally {        
        // 将参数列表指针置空        va_end(paramList);
    }    
#pragma mark - 写入日志    
    // 异步执行
    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];        
        // 写入数据        [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];    // 发起请求,从服务器上获取当前应用是否需要上传日志
    [[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没有数据!"] code:500 userInfo:nil];
        }        
        // 完成后的处理
        if (error == nil) {            
            // 处理上传日志            [self uploadLog:resultDic];
        }else{
            LOGERROR(@"检测日志返回结果有误!data没有数据!");
        }
    } 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];    // 压缩文件是否创建成功
    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:@"日志上传失败!!Error:%@",errorInfo]));
        }];
    }
}/**
 *  压缩日志
 *
 *  @param dates 日期时间段,空代表全部
 *
 *  @return 执行结果 */- (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];    // 创建一个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];    
    // 发起请求,这里是上传日志到服务器,后台功能需要自己做
    [[XGNetworking sharedInstance] upload:url fileData:nil fileName:ZipFileName mimeType:@"application/zip" parameters:nil success:^(NSString *jsonData) {


作者:这酸爽! 
出处:http://www.cnblogs.com/xgao/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 
我的联系方式:

http://www.cnblogs.com/xgao/p/6553334.html