iOS 记录本地日志文件,上传服务器

3,142 阅读3分钟

前言

鉴于项目中出现的一些问题,不好排查,光靠服务器的日志记录不太能准确的定位问题,本地日志的记录无疑是辅助性比较强的。

封装一个日志收集的类,实现日志信息文件保存本地,日志保存天数设置、过期日志自动删除,日志信息文件可压缩(提供压缩文件保存路径,可根据上传服务器方式单独配置)简单记录一下。

实现逻辑

1、日志记录保存在本地

首先明确一个问题,记录日志需要将字符串转成数据(NSData)保存在本地,NSData写入本地会对本地的文件进行覆盖,所以只有当文件不存在第一次写入的时候用这种方式,如果要将日志内容追加到日志文件里面,使用NSFleHandle来处理。

#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);
});


/**
 *  写入字符串到指定文件,默认追加内容
 *
 *  **@param** filePath   文件路径
 *  **@param** stringData 待写入的字符串
 */
- (**void**)writeFile:(NSString*)filePath stringData:(NSString*)stringData{
    // 待写入的数据
    NSData* writeData = [stringData dataUsingEncoding:NSUTF8StringEncoding];
    NSData *ungzipData = [writeData gunzippedData];
    // NSFileManager 用于处理文件
    BOOL createPathOk = YES;
    if (![[NSFileManager defaultManager] fileExistsAtPath:[filePath stringByDeletingLastPathComponent] isDirectory:&createPathOk]) {
        // 目录不存先创建
        [[NSFileManager defaultManager] createDirectoryAtPath:[filePath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
    }
    if(![[NSFileManager defaultManager] fileExistsAtPath:filePath]){
        // 文件不存在,直接创建文件并写入
        [writeData writeToFile:filePath atomically:NO];

    }else{
        // NSFileHandle 用于处理文件内容
        // 读取文件到上下文,并且是更新模式
        NSFileHandle* fileHandler = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
        // 跳到文件末尾
        [fileHandler seekToEndOfFile];
        // 追加数据
        [fileHandler writeData:ungzipData];
        // 关闭文件
        [fileHandler closeFile];
    }
}

2、日志记录保存时间限制

下面方法只是删除了未压缩前的文件,压缩后的文件过期同样需要删除,逻辑相同,此处不再赘述。


// 获取日志目录下的所有文件
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);
                }
            }
        }

3、日志文件压缩

一般的压缩功能采用zip压缩即可,本文使用ZipArchive。项目地址:github.com/ZipArchive/…

此处注意一个点:被压缩文件最好与压缩文件同名,否则会导致生成不了压缩文件。

建议:未压缩文件和压缩文件存放在不同的目录下,可能更方便管理

// 压缩包文件路径
    NSString *dateStr = [**self**.dateFormatter stringFromDate:[NSDate date]];
    NSString * zipFile = [NSString stringWithFormat:@"%@%@.zip",**self**.zipBasePath, dateStr];
    NSString* filePath = [**self** getLogPathWithDate:**nil**];
    BOOL createPathOk = **YES**;
    if (![[NSFileManager defaultManager] fileExistsAtPath:[zipFile stringByDeletingLastPathComponent] isDirectory:&createPathOk]) {
        // 目录不存先创建
        [[NSFileManager defaultManager] createDirectoryAtPath:[zipFile stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
    }
    // 创建一个zip包
    BOOL created = [SSZipArchive createZipFileAtPath:zipFile withFilesAtPaths:@[filePath]];
    if ([[NSFileManager defaultManager] fileExistsAtPath:zipFile]) {
//        NSLog(@"有文件");
    } else {
//        NSLog(@"没有文件");
    }
    return created;

4、写在最后

调用方式可以根据业务需求适当调整,所以在这里只放了核心代码。逻辑很简单,主要看业务想要收集哪些日志信息。