iOS 日志系統 本地日志打包上傳到服務器


日志系統主要包含兩個部分
1.本地保存
我們知道NSLog打印的日志一般都是直接輸出到控制台,開發人員可以在控制台直接看到實時打印的log,既然可以在控制台輸出,那么能否將日志輸出到其他地方呢,比如說自己定義的text文件?答案是肯定的 ,在iOS中可以通過一些方法將文件重定向到指定輸出位置:

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

實現了上述方法,就可以將log重定向到指定的filePath中了,既然文件中保存到了本地,那么怎么處理,怎么加密完全看你們公司的業務需求咯

2.日志上傳
日志上傳首先需要將本地日志打包成zip格式的文件,這樣可以減小上傳的size,通過ZipArchive將日志文件打包好,通過afnetworking的 formData將文件上傳了

    [formData appendPartWithFileURL:url name:@"zipFile" fileName:[NSString stringWithFormat:@"%@.zip",[self getCurrentTime]] mimeType:@"multipart/form-data" error:nil];

具體部分實現代碼如下:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface LogServer : NSObject

+(instancetype)shareInstance;

///開啟寫日志
-(void)writeLog;

///上傳日志
-(void)uploadLog;

///清空本地日志
-(void)clearLog;

@end

NS_ASSUME_NONNULL_END


#import "LogServer.h"
#import <ZipArchive/ZipArchive.h>

#define MaxCount   30    //只保留最近的30條數據
#define MaxSize    (1024*4)   //4M

@interface LogServer (){
    NSString *filePath_;  //文件存放路徑
}

@end

static LogServer * instance = nil;

@implementation LogServer

+(instancetype)shareInstance{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[LogServer alloc] init];
        [instance initLogServer];
    });
    return instance;
}

-(void)initLogServer{
    NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    //初始化log路徑
    filePath_ = [path stringByAppendingPathComponent:@"Log"];
    NSFileManager *manager = [NSFileManager defaultManager];
    if (![manager fileExistsAtPath:filePath_]) {
        //如果Log文件夾不存在,就創建文件夾
        [manager createDirectoryAtPath:filePath_ withIntermediateDirectories:YES attributes:nil error:nil];
    }
}

-(void)writeLog{
    // Do any additional setup after loading the view.
    //如果文件有zip格式則直接移除掉 可能是上次打包上傳的
    [self removeZipFile];
    
    //如果文件夾中的文件超過最大限制 咋移除舊數據
    [self removeExpireFile];
    
    //加密文件
    [self encryptLastFile];

    //將日志寫入到文件中
    NSString *filePath = [filePath_ stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.text",[self fileNameAtTime]]];
    freopen([filePath cStringUsingEncoding:NSASCIIStringEncoding],"a+", stdout);
    freopen([filePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
    
    NSLog(@"filePath:%@",filePath);
    //異常日志報告
    NSSetUncaughtExceptionHandler(&caughtExceptionHandler);
}

-(void)uploadLog{
    //將需要上傳的文件加密zip格式上傳到服務器
    NSArray *sortArray = [self sortFileAscending];
    if (sortArray.count > 0) {
        ZipArchive *zip = [[ZipArchive alloc] init];
        NSString *path = [filePath_ stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.zip",[self fileNameAtTime]]];
        BOOL ret = [zip CreateZipFile2:path];
        if (ret) {
            //便利所有文件內容
            for (int i = sortArray.count - 1; i>=0; i--) {
                NSString *fileName = sortArray[i];
                NSString *filePath = [filePath_ stringByAppendingPathComponent:fileName];
                [zip addFileToZip:filePath newname:fileName];
                //判斷zip文件大小 如果文件大小大於最大限制 則停止剩余文件的壓縮
                long long zipSize = [self getFileSize:path];
                if (zipSize > MaxSize) {
                    break;
                }
            }
            [zip CloseZipFile2];
            //將zip文件上傳到服務器
    
//            afnetworking  文件上傳到指定服務器即可
        }
    }
}

-(void)clearLog{
    NSFileManager *manager = [NSFileManager defaultManager];
    if ([manager fileExistsAtPath:filePath_]) {
        NSError *error = nil;
        [manager removeItemAtPath:filePath_ error:&error];
        if (error) {
            NSLog(@"日志清空失敗:%@",error);
        }else{
            NSLog(@"日志清空成功");
        }
    }
}

#pragma mark - private
#pragma mark 異常處理日志
void caughtExceptionHandler(NSException *exception){
    /**
     *  獲取異常崩潰信息
     */
    NSArray *callStack = [exception callStackSymbols];
    NSString *reason = [exception reason];
    NSString *name = [exception name];
    NSString *content = [NSString stringWithFormat:@"========異常錯誤報告========\nname:%@\nreason:\n%@\ncallStackSymbols:\n%@",name,reason,[callStack componentsJoinedByString:@"\n"]];
    NSLog(@"%@",content);
}

#pragma mark 獲取文件大小
-(long long)getFileSize:(NSString *)path{
    unsigned long long fileLength = 0;
    NSNumber *fileSize;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if([fileManager fileExistsAtPath:path]){
        NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:path error:nil];
        
        if ((fileSize = [fileAttributes objectForKey:NSFileSize])) {
            fileLength = [fileSize unsignedLongLongValue]; //單位是 B
        }
        return fileLength / 1024;
    }
    return 0;
}

#pragma mark 用時間創建文件名
-(NSString *)fileNameAtTime{
    NSDate *date = [NSDate date];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    formatter.dateFormat = @"yyyy-MM-dd-HH-mm-ss";
    NSString *timeStr = [formatter stringFromDate:date];
    
    return timeStr;
}

#pragma mark 加密文件內容
-(void)encryptLastFile{
    //這里根據自己的需要將文件內容進行加密
    NSFileManager *manager = [NSFileManager defaultManager];
    NSArray *sortArray = [self sortFileAscending];
    if (sortArray.count > 0) {
        NSString *lastPath = sortArray.lastObject;
        NSString *lastFilePath = [filePath_ stringByAppendingPathComponent:lastPath];
        NSData *data = [manager contentsAtPath:lastFilePath];
        NSString *base64String = [data base64EncodedStringWithOptions:0];
        //
        [base64String writeToFile:lastFilePath atomically:YES];
    }
}

#pragma mark 移除過期數據
-(void)removeExpireFile{
    NSFileManager *manager = [NSFileManager defaultManager];
    //將文件中數據排序
    NSArray *sortFileArray = [self sortFileAscending];
    if(sortFileArray.count > MaxCount){
        NSInteger count = sortFileArray.count - MaxCount;
        //因為是升序排在前面的都是比較老的數據 移除前面的count個數據即可
        for (int i = 0; i < count; i++) {
            NSString *path = sortFileArray[i];
            NSString *filePath = [filePath_ stringByAppendingPathComponent:path];
            [manager removeItemAtPath:filePath error:nil];
        }
    }
}

#pragma mark 移除zip格式文件
-(void)removeZipFile{
    NSFileManager *manager = [NSFileManager defaultManager];
    if ([manager fileExistsAtPath:filePath_]) {  //如果文件夾存在
        NSArray *filesArray = [manager contentsOfDirectoryAtPath:filePath_ error:nil];
        NSString *filePath = filePath_;
        [filesArray enumerateObjectsUsingBlock:^(NSString *  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if (![obj containsString:@".text"]) {
                NSString *nPath = [filePath stringByAppendingPathComponent:obj];
                [manager removeItemAtPath:nPath error:nil];
            }
        }];
    }
}

#pragma mark 將文件夾中的文件按照創建時間進行排序
-(NSArray *)sortFileAscending{
    //拿到文件
    NSFileManager *manager = [NSFileManager defaultManager];
    if ([manager fileExistsAtPath:filePath_]) {  //如果文件夾存在
        NSArray *filesArray = [manager contentsOfDirectoryAtPath:filePath_ error:nil];
        NSString *filePath = filePath_;
        //便利文件夾中的所有文件
        NSArray *array = [filesArray sortedArrayUsingComparator:^NSComparisonResult(NSString*  _Nonnull file1, NSString *  _Nonnull file2) {
            //file1 file2全路徑
            NSString *file1Path = [filePath stringByAppendingPathComponent:file1];
            NSString *file2Path = [filePath stringByAppendingPathComponent:file2];
            
            //file1 file2 Info
            NSDictionary *file1Dict = [[NSFileManager defaultManager] attributesOfItemAtPath:file1Path error:nil];
            NSDictionary *file2Dict = [[NSFileManager defaultManager] attributesOfItemAtPath:file2Path error:nil];
            
            //file1CreatTime file2CreatTime
            NSDate *file1Date = [file1Dict objectForKey:NSFileCreationDate];
            NSDate *file2Date = [file2Dict objectForKey:NSFileCreationDate];
            //升序
            return [file1Date compare:file2Date];
        }];
        return  array;
    }
    return nil;
}


@end


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM