日志系統主要包含兩個部分
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