欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Mac开发-日志保存过滤以及上传

程序员文章站 2023-12-28 10:43:28
...


对于mac开发者来说,进行日志收集和上传尤为重要,没有日志就无法进行分析问题。通常来说我们会将日志文件.log存在~/library/logs/YourAppName的目录下,大部分app都是这样放置的,然后缓存文件存在~/library/Caches/YourAppName目录下。

简单的日志存储

使用stdout重定向来将控制台的日志写入日志文件.log中,从而达到日志存储的目的

freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);

通常来说,日志文件名称需要加上日志以及app名等相关信息,以便于筛选。

- (void)redirectNSlogToDocumentFolder{
    //这里判断是否连接xcode,如果连接xcode我们更希望是输出到控制台,这样我们能更能直接进行调试
#if DEBUG
#else
    if (isatty(STDOUT_FILENO)){
        NSLog(@"Appdelegate redirectNSlogToDocumentFolder failed : connected xcode mode");
        return;
    }
#endif
    NSString *genseePath = [self mylogPath];
    NSFileManager* manager = [NSFileManager defaultManager];
    [manager createDirectoryAtPath:genseePath withIntermediateDirectories:NO attributes:NULL error:NULL];
    NSString* bundleName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
    NSString* bundleID = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
    //这里使用的日期格式为 yyyyMMdd-HHmmss
    [dateFormatter setDateFormat:@"yyyyMMdd-HHmmss"];
    NSString *dateString = [dateFormatter stringFromDate:[NSDate date]];
    NSString *fileName = [NSString stringWithFormat:@"%@_%@_%@.log",bundleName,bundleID,dateString];
    _logPath = fileName;
    NSString *logFilePath = [genseePath stringByAppendingPathComponent:fileName];
    NSLog(@"[Appdelegate] redirectNSlogToDocumentFolder : %@",logFilePath);
    
    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
    //删除过期的日志信息
    [self clearUnusedlog];
}

- (NSString *)mylogPath {
    NSArray *array = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
    NSString *logsPath = [[array objectAtIndex:0] stringByAppendingPathComponent:@"Logs"];
    //这里webcast为app名称
    NSString *genseePath = [logsPath stringByAppendingPathComponent:@"Webcast"];
    return genseePath;
}

这里重定向的不好之处就是无法同时进行调试和日志记录,也无法选择只输出部分日志信息,还需要进行对NSLog宏定义。
其实有很多日志框架就解决了这些问题。 CocoaLumberjack 算是比较突出的一个

日志删除

日志文件没有上限限制,所以每次我们需要删除很久远的日志,可以是3天前,或者30天前的日志。避免不必要的空间浪费。

- (void)clearUnusedlog {
    NSString *genseePath = [self mylogPath];
    NSArray *tempArray = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:genseePath error:nil];
    for (NSString* filename in tempArray) {
        NSString *filetype = filename.pathExtension;
        if ([filetype isEqualToString:@"log"]){
            NSError *error = nil;
            NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[0-9]{8}-[0-9]{6}" options:NSRegularExpressionCaseInsensitive error:&error];
            NSArray* matches = [regex matchesInString:filename options:0
                                                   range:NSMakeRange(0, [filename length])];
            if (matches.count <= 0) continue;
            for (NSInteger i = matches.count - 1; i >= 0; i--) {
                NSTextCheckingResult *match = matches[i];
                if (match.numberOfRanges > 0) {
                    NSRange strRange = [match rangeAtIndex:0];
                    NSString *dataStr = [filename substringWithRange:strRange];
                    if ([self isBeforeDays:30 dataStr:dataStr]) {
                        NSError *error;
                        BOOL result = [[NSFileManager defaultManager] removeItemAtPath:[genseePath stringByAppendingPathComponent:filename] error:&error];
                        if (result) {
                            NSLog(@"[Appdelegate] clearUnusedlog : %@",filename);
                        }else{
                            NSLog(@"[Appdelegate] clearUnusedlog Error : %@",error.description);
                        }
                        
                    }
                }
            }
        }
        
        
    }
}

/**
 判断是否是30天以前的文件
 */
- (BOOL)isBeforeDays:(int)nday dataStr:(NSString*)dataStr
{
    NSDate *datenow = [NSDate date];
    long  nowTimeInterval=(long)[datenow timeIntervalSince1970];
    
    NSTimeInterval secondsPernDay = 24 * 60 * 60*nday;
    if (nday == 0) {
        secondsPernDay = 60*5; //for debug test
    }
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyyMMdd-HHmmss"];
    NSDate* data=[dateFormatter dateFromString:dataStr];
    long  beforeNdayTimeInterval=(long)[data timeIntervalSince1970];
    BOOL isNdayBefore=  nowTimeInterval-beforeNdayTimeInterval>secondsPernDay;
    return isNdayBefore;
}

关于日志删选

这里使用正则表达式进行匹配日志形式,也可以进行截取字符串指定后几位来进行。
或者我们也可以通过文件创建日期来进行筛选,使用NSCalendarNSDateComponents来计算创建日期和现在的时间间隔。

	NSDictionary* fileAttribs = [[NSFileManager defaultManager]attributesOfItemAtPath:filePath error:nil];
    NSDate* date = [fileAttribs objectForKey:NSFileCreationDate];
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    //这里的components决定了需要计算哪个单位的数值,若只需要天,则传入NSCalendarUnitDay即可
    NSDateComponents *components = [calendar components:NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond
                                               fromDate:date
                                                 toDate:[NSDate date]
                                                options:0];
    if(components.day <= 3){
        [_zipArchive addFileToZip:filePath newname:str];
    }		

日志上传

日志上传我们通常要经过3个步骤

  1. 过滤,选择仅3天内的日志
  2. 压缩,打包zip文件
  3. 上传到服务器

如果只需要压缩一个文件夹,我们可以使用终端命令来操作,如何在代码中进行终端操作?
如果我们需要在一个包含所有杂乱的文件夹中选择文件进行压缩,则需要一个一个加入到zip中,使用终端固然可以达到效果。这里推荐使用ZipArchive三方库来实现更加简单。

对于创建的zip文件,发送之后通常需要删除,我们可以放在/tmp/目录下,这样下次重启就会自动删除,还可以满足用户自己去查看自己发送的日志的case。

-(void)getSendData{
    NSString *targetZipPath = @"/tmp/webcast_log.zip";
    [_zipArchive CreateZipFile2:targetZipPath];
    [self getTraingLogs];
    [self postDataToService]; //这里即使用https协议将zip文件传输到服务器,使用系统的NSUrlSession即可
}

- (void)getTraingLogs{
    NSArray *array1 = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
    NSString *logsPath = [[array1 objectAtIndex:0] stringByAppendingPathComponent:@"Logs"];
    NSString *path = [logsPath stringByAppendingPathComponent:@"Webcast"];
    //    NSString* path = @"/tmp/Training";
    NSArray* array = [[NSFileManager defaultManager]contentsOfDirectoryAtPath:path error:nil];
    [self getNearThreeDaysLogs:array path:path];
}

- (void)getNearThreeDaysLogs:(NSArray*)array path:(NSString*)path{
    for(NSString* str in array){
        NSString* filePath  = [path stringByAppendingPathComponent:str];
        NSString* fileNmae = [filePath lastPathComponent];
        if([fileNmae hasPrefix:@"Webcast"]){
        //这里可以获取到文件创建的日期属性,并不是以命名来判断,如果是命名,使用NSDate+NSDataFormatter来处理即可
            NSDictionary* fileAttribs = [[NSFileManager defaultManager]attributesOfItemAtPath:filePath error:nil];
            NSDate* date = [fileAttribs objectForKey:NSFileCreationDate];
            NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
            NSDateComponents *components = [calendar components:NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond
                                                       fromDate:date
                                                         toDate:[NSDate date]
                                                        options:0];

            if(components.day <= 3){
                [_zipArchive addFileToZip:filePath newname:str];
            }

        }
    }
}

崩溃信息

一般来说,我们会在崩溃后,将崩溃信息附加到此次的日志信息最后,这样方便定位,不用匹配日志文件和崩溃文件。
关于崩溃信息如何收集,可以去看看 使用PLCrashReporter来保存崩溃日志信息

这样子简单的日志系统就算成立了,方便日后分析和处理问题

相关标签: Mac开发

上一篇:

下一篇: