我们如何去设计一个简单的log系统呢,在看着个项目之前还真是没有好好的想过,当然也不是说这个项目有多么多么屌,它也没有实现什么我们完全意想不到的设计,只是我觉得通过它我们可以去可以思考如何做,这才是最重要的。
第一步我们要做什么麽呢,我觉得就是应该清楚我们到底要做什么,我觉得一个简单的日志系统主要是记录程序的运行情况,崩溃情况,那么KKLog都做了什么呢?
- 它设置了两种不同的log方式,一种是记录崩溃日志,一种是记录普通的日志,而普通的日志有需要被分为不同的等级,以便于记录不同等级的日志
typedef enum
{
LOGLEVELV = 0, //wend
LOGLEVELD = 1, //Debug
LOGLEVELI = 2, //Info
LOGLEVELW = 3, //Warning
LOGLEVELE = 4, //Error
} KKLogLevel;
/**
* 设置要记录的log级别
*
* @param level level 要设置的log级别
*/
+ (void)setLogLevel:(KKLogLevel)level;
/**
* 记录系统crash的Log函数
*
* @param exception 系统异常
*/
+ (void)logCrash:(NSException*)exception;
特别需要说明的一点是在iOS中我们可以通过一下的方式捕获崩溃
void uncaughtExceptionHandler(NSException *exception)
{
[KKLog logCrash:exception];
}
//注册程序 Crash后的处理函数
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
2 . 那么我们第一步已经知道需要做两种不同的日志了,那么下一步就是两种不同的日志具体到底要记录什么,怎么记录
那么我们先说第一种 记录崩溃, 我们单独把崩溃拿出来,就是因为它真的是十分的重要,需要特别注意,而当我们记录它的时候就需要把每一个崩溃都单独的变成一个日志文件,最好文件名字包含崩溃的时间,这样我们就可以清晰明了的知道都有多少崩溃,崩溃集中在什么时间点,当然,如果我们能再进一步的话,可以简单的把不同的崩溃分成几大类,然后体现在文件名中,这样在总览崩溃日志的时候就会更加明确了。
我们在记录崩溃日志的时候最重要的就是希望知道崩溃的原因 所以我们需要在记录的时候去获取这些信息,还有我们也需要去知道我们具体是崩溃在什么样的设备和系统中的,那么KKLog也就是向下面这样组合崩溃日志信息的
NSString *fileName = [NSString stringWithFormat:@"CRASH_%@.log", [[KKLog nowBeijingTime] description]];
NSString *filePath = [crashDic stringByAppendingString:fileName];
NSString *content = [[NSString stringWithFormat:@"CRASH: %@\n", exception] stringByAppendingString:[NSString stringWithFormat:@"Stack Trace: %@\n", [exception callStackSymbols]]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *languages = [defaults objectForKey:@"AppleLanguages"];
NSString *phoneLanguage = [languages objectAtIndex:0];
content = [content stringByAppendingString:[NSString stringWithFormat:@"iPhone:%@ iOS Version:%@ Language:%@",[KKLog platformString], [[UIDevice currentDevice] systemVersion],phoneLanguage]];
NSError *error = nil;
[content writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
那么下面说第二种 针对普通的日志 其实思路就是完全不一样了 首先就不可能把每一个日志都变成一个文件,我们只能根据时间,简单来说就是可以每天一个文件,或者每12小时一个文件,把新的log写在文件的末尾,还有一个也许是我们都不会想的功能,我们可以设置需要记录什么等级的日志,什么意思呢,就是在前面说到,我们为普通的日志写了一个枚举,而枚举的排序也是由不重要到重要,所以在记录的时候我们可以对需要记录的日志加以限制
if (level >= LogLevel)
{
formatTmp = [[KKLog KKLogFormatPrefix:level] stringByAppendingString:formatTmp];
NSString *contentStr = [[NSString alloc] initWithFormat:formatTmp arguments:args];
NSString *contentN = [contentStr stringByAppendingString:@"\n"];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSString *content = [NSString stringWithFormat:@"%@ %@", [dateFormatter stringFromDate:[KKLog nowBeijingTime]], contentN];
//append text to file (you'll probably want to add a newline every write)
NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:logFilePath];
[file seekToEndOfFile];
[file writeData:[content dataUsingEncoding:NSUTF8StringEncoding]];
[file closeFile];
}
3 那么好 现在日志我们都记录下来了 那么我们还需要做点什么呢 ,对就是响应的清理日志,我们只是做一个简单的日志系统,不会记录太久的日志,因为这样反而会占用巨大的存储空间,所以我们需要设置一个过期时间,然后来清除缓存,KKLog写了一个初始化的方法,需要在开始调用一次,这里面就对缓存进行了处理
//删除过期的日志
NSDate *prevDate = [[NSDate date] dateByAddingTimeInterval:-60*60*24*k_preDaysToDelLog];
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:prevDate];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
//要删除三天以前的日志(0点开始)
NSDate *delDate = [[NSCalendar currentCalendar] dateFromComponents:components];
NSArray *logFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:logDic error:nil];
for (NSString *file in logFiles)
{
NSString *fileName = [file stringByReplacingOccurrencesOfString:@".logtraces.txt" withString:@""];
fileName = [fileName stringByReplacingOccurrencesOfString:@"KK_log_" withString:@""];
NSDate *fileDate = [dateFormatter dateFromString:fileName];
if (nil == fileDate)
{
continue;
}
if (NSOrderedAscending == [fileDate compare:delDate])
{
[[NSFileManager defaultManager] removeItemAtPath:[logDic stringByAppendingString:file] error:nil];
}
}
4其他一些需要注意的东西, 其实大家看到这发现,哎,真是没有什么高深的东西,是,好像这些我想想也都能简单的写出来,但是其实就我个人而言,真的发现,我没办法想的这么全面,不能把每个细节都做到最好。
在做IO操作的时候,尤其是简单的IO操作,我们往往都忽略了需要异步处理这一关键的概念,当然,这也许只是我个人的错误
static dispatch_queue_t k_operationQueue;
...
dispatch_once(&logQueueCreatOnce, ^{
k_operationQueue = dispatch_queue_create("com.coneboy.app.operationqueue", DISPATCH_QUEUE_SERIAL);
});
...
dispatch_async(k_operationQueue, ^{
...
}
还有就是这两个函数,我也学到了很多,具体的大家自己查去吧
va_start(args, format);
va_end(args);