CocoaLumberjack数据库存储并在PC端web访问

CocoaLumberjack数据库存储并在PC端web访问

这个题目存在两个问题:1、使用CocoaLumberjack将文件日志存储在数据库;2、使用PC端使用web访问;那接下来我们分别解决两个问题。

1 使用CocoaLumberjack将日志文件存储到数据库

CocoaLumberjack的使用其他的地方都有就不在表述了,详细使用CocoaLumberjack github地址

CocoaLumberjack的代码的流程,所有的log发送给DDlog对象,运行在自己的GCD队列(GlobalLoggingQueue),之后DDLog会将日志分发到其下注册的一个或多个Logger(并发),每个Logger处理受到的log,此时是在自己的GCD队列(loggerQueue)下操作的,然后分别执行其下的formatter,获取log应有的格式,最终将消息分发到不同的地方。

我们直奔主题,要存数据库,我们看文件目录:


image.png

这是我们需要注意的,这个文件说明我们需要将数据存储到数据库上的时候需要我们继承这个类,并将其中的方法重写。
我们定义自己的数据库存储文件:

//DDDBLogger.h

#import "DDAbstractDatabaseLogger.h"

@interface DDDBLogger : DDAbstractDatabaseLogger

@property (readwrite, assign) NSInteger saveDBTotalNum;

@end

实现:

#import "DDDBLogger.h"
#import "DBConnect.h"
#import "logSqlModel.h"


@interface DDDBLogger ()

@property (nonatomic, strong) DDLogMessage *saveDBLogMessage;


@end


@implementation DDDBLogger

//设置数据库存储的条数
- (void)setsaveDBTotalNum:(NSUInteger)threshold {
    dispatch_block_t block = ^{
        @autoreleasepool {
            if (_saveDBTotalNum != threshold) {
                _saveDBTotalNum = threshold;
            }
        }
    };
//判断是否在自己logger GCD队列中
    if ([self isOnInternalLoggerQueue]) {
        block();
    } else {
        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
        
        dispatch_async(globalLoggingQueue, ^{
            dispatch_async(self.loggerQueue, block);
        });
    }
}



-(instancetype)init
{
    self = [super init];
    if (self)
    {
        self.deleteOnEverySave = NO;
        self.saveInterval = 1;         //1 seconds,1秒发送一次
        self.saveThreshold = 1;       //每一条就发送
        self.saveDBTotalNum = 2000;   //数据库总共存2000 条
    }
    return  self;
};


- (BOOL)db_log:(DDLogMessage *)logMessage
{
    //没有日志信息
    if(!_logFormatter)
    {
        return NO;
    }
    _saveDBLogMessage = logMessage;
    return YES;
}

- (void)db_save {
    if ([self isOnGlobalLoggingQueue]) {
        NSAssert(NO, @"db_saveAnddelete should only be executed on the internaLoggerQueue thread, if you are seeing this, your doing it wrong");
    }

    [self saveLogEntries:self.saveDBLogMessage];
}


-(void)saveLogEntries:(DDLogMessage *)logMessage
{
    logSqlModel *logSql = [[logSqlModel alloc] init];
    
    //获取当前的数据库中村的数据的条数
    //统计数据库数据条数的sql
    NSInteger saveDBNum = [[DBConnect shareConnect] getDBDataCount: logSql.countSqlStr];
    NSString *logTimeStr = [self stringFromDate:logMessage.timestamp];
    NSString *line = [NSString stringWithFormat:@"%lu", (unsigned long)logMessage.line];
    NSString *level = [NSString stringWithFormat:@"%lu", (unsigned long)logMessage.level];
    
    if (_saveDBTotalNum < saveDBNum)
    {
        //数据库中存储的数据条数 大于 要求存储的条数
        //删除的数据条数
        NSInteger deleteNum = saveDBNum - _saveDBTotalNum;
        NSString *deleteDataSqlStr = [NSString stringWithFormat:@"DELETE FROM logMessageHistory order by lastUpdateTime limit %ld", (long)deleteNum];
        [[DBConnect shareConnect] executeUpdateSql:deleteDataSqlStr];
    }else if (_saveDBTotalNum > saveDBNum) {
        //数据库能保存的数据多余现在的数据,就是直接插入数据库
        [[DBConnect shareConnect].dataBase executeUpdate:logSql.insertSqlStr,logTimeStr, logMessage.fileName, line, logMessage.function, logMessage.message, level, logMessage.timestamp];
    }
    else{
        //数据库已经存满,此时需要update时间最早的数据
        [[DBConnect shareConnect].dataBase executeUpdate: logSql.updateDataSqlStr,logTimeStr, logMessage.fileName, line, logMessage.function, logMessage.message, level, logMessage.timestamp];
    }
}

- (NSString *)stringFromDate:(NSDate *)date{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSString *destDateString = [dateFormatter stringFromDate:date];
    return destDateString;
}
@end

我们封装完Logger对象之后,我们需要实现LogFormatter,那么我们的DDDBLogFormatter的声明和实现:

#import <Foundation/Foundation.h>
#import "DDLog.h"
@interface DDDBLogFormatter : NSObject<DDLogFormatter>
@end

实现:

#import "DDDBLogFormatter.h"

@implementation DDDBLogFormatter

//实现代理协议format方法
-(NSString *)formatLogMessage:(DDLogMessage *)logMessage
{
    NSMutableDictionary *logDic = [NSMutableDictionary  dictionary];
    
    NSString *locationString;
    NSArray *parts = [logMessage->_file componentsSeparatedByString:@"/"];
    if (parts.count > 0)
    {
        locationString = [parts lastObject];
    }
    if (parts.count == 0)
    {
        locationString = @"no file";
    }
    
    logDic[@"gampTech"] = [NSString stringWithFormat:@"%@:%lu(%@):%@", locationString, (unsigned long)logMessage.line, logMessage.function, logMessage.message];
    //NSMutableDictionary 转成 NSString
    NSError *error;
    NSData *outputJsonData = [NSJSONSerialization dataWithJSONObject:logDic options:0 error:&error];
    if (error) {
        return @"{\"gampTech\":\"error\"}";
    }
    NSString *outputJsonStr = [[NSString alloc] initWithData:outputJsonData encoding:NSUTF8StringEncoding];
    if (outputJsonStr) {
        return outputJsonStr;
    }
    return @"{\"gampTech\":\"error\"}";
}
@end

其中logSqlModel.h是我将sql封装之后的语句demo。
我们在需要CocoaLumberjack的同时还需要FMDB和GCDWebserver,前者数据库存储,后者建立网络访问。

2 PC端通过网络访问

在本地资源文件bundle中创建一个html,此HTML是返回的界面。

<!doctype html>
<html>
<head>
<script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>

</head>
<body ng-app="myApp" ng-controller="mainCtrl">

<button ng-click="refresh()">refresh</button>
<div  style="background: #07242E; color: #708284;height: auto;overflow: auto;min-height: 600px;max-height:700px" >

<ul>
<li ng-repeat="x in items" style='font-size:15px'>
    {{ x.logtime }} &nbsp; [{{x.level}}]-{{x.filename}}:{{x.line}}/{{x.function}} &nbsp;=> {{x.message}}
</li>
</ul>

</div>

<script>
var app = angular.module('myApp', []);

app.controller('mainCtrl', function($scope, $http) {
    $http.get(location+'logs').success(function(response) {
                                 $scope.items = response;
                             });
    $scope.refresh = function(){
                    $http.get(location+'logs').success(function(response){
                                                  $scope.items = response;
                                              });
    };
});

</script>
</body>
</html>

接下来我们看GCDWebServer的调用本地方法列表:

#import <Foundation/Foundation.h>

@interface ExLogger : NSObject
+(void)prepare;
+(BOOL)startService;
+(void)stopService;
+(BOOL)isStarted;
@end

实现:

#import "ExLogger.h"
#import "GCDWebServer.h"
#import "GCDWebServerDataResponse.h"

@implementation ExLogger

static BOOL isStarted;
static GCDWebServer *_webServer;

+(void)prepare{
    //TODO 初始化DDLog
    [DDLog addLogger:[DDTTYLogger sharedInstance]];
    //声明我们自己的Logger
    DDDBLogger *logger = [[DDDBLogger alloc] init];
    [logger setLogFormatter:[DDDBLogFormatter new]];
    [logger setSaveDBTotalNum:2000];
    [DDLog addLogger:logger];
    //捕获异常数据
    NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
}

//从数据库查询日志
+(NSArray*)logsFromDB{
    logSqlModel *logSql = [[logSqlModel alloc] init];
    return [[DBConnect shareConnect] getDBlist:logSql.queryDataSqlStr];
}

+(BOOL)startService{
    NSLog(@"start service! _webserver=%@",_webServer);
    if (isStarted) {
        return NO;
    }
    if(!_webServer)
        _webServer = [[GCDWebServer alloc] init];
    
    //首页请求
    [_webServer addDefaultHandlerForMethod:@"GET" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse*(GCDWebServerRequest *request){
        NSError *err;
        NSString *path = [[NSBundle mainBundle].resourcePath stringByAppendingFormat:@"/%@/template.html", kBundleName];
        NSLog(@"path:%@",path);
        GCDWebServerResponse *response = [GCDWebServerDataResponse responseWithHTML:[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&err]];
        return response;
    }];
    //点击refresh按钮时请求日志数据
    [_webServer addHandlerForMethod:@"GET" path:@"/logs" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse*(GCDWebServerRequest *request){
        NSArray *array  = [self logsFromDB];
        
        return [GCDWebServerDataResponse responseWithJSONObject:array];
    }];
    
    isStarted = [_webServer startWithPort:8089 bonjourName:nil];
    return isStarted;

}

+(void)stopService{
    [_webServer stop];
    [DDLog removeAllLoggers];
    isStarted= NO;
    _webServer = nil;

}

+(BOOL)isStarted{
    return isStarted;
}

@end

此时我们就可以在手机的IP加上端口8089,在PC端查看。
(我的demo地址)[https://github.com/yanduhantan563/CocoaLogToWebServer]

-> CocoaLogToWebServer (1.0.0)
   通过使用CocoaLumberjack 和GCDWebServer实现日志NSLog打印的重定向,并且可以使用浏览器访问日志
   pod 'CocoaLogToWebServer', '~> 1.0.0'
   - Homepage: https://github.com/yanduhantan563/CocoaLogToWebServer
   - Source:   https://github.com/yanduhantan563/CocoaLogToWebServer.git
   - Versions: 1.0.0 [master repo]
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容