自定义同步日志系统

简介: 自定义同步日志系统

每个应用都是顺序执行的,在时间序列上都是串行的,每个具体应用在某个时刻只能有一个cpu正常处理,就是多线程也遵循cpu时间串行序列,只有时间一个线程调用中断函数(如:sleep)或一个处理事件完成才调度cpu,若调用了中断函数,线程被挂起,释放cpu控制权,仍保留部分自己线程的资源,以便与该线程再次获取到cpu继续后续处理。这就是我理解的多线程原理和普通应用在时间序列上的串行性。

同步处理就是该处理阻塞应用的其它操作,直到处理该应用处理完成;异步处理是起了另外的线程去处理自己的事情,不阻塞应用的其它处理。

同步处理的优缺点:实现简单便于管理,耗电量较少,可以立即看到处理结果,由于阻塞应用,所以处理要简单,处理不能耗费太长时间,否则严重影响系统性能;异步的优点:实现较复杂,因为起线程耗所以电量较大,得到结果不及时,由于不阻塞线程和结果不需要很及时,所以异步处理可以处理很复杂,可以加入相对较费时间的处理(发送邮件,上传文件到服务器等)。

CocoaLumberjack日志系统就是异步处理,所以它可以继续扩充功能,如:把日志以邮件的形式发送,把日志上传服务器。我设计的同步日志系统就是同步处理,同步日志系统最好别加入超耗时的处理(发送邮件,上传文件到服务器等),以免影响应用的性能。由于是同步日志,是影响性能的,快速打印它吃不消,只能进行间隔时间比较久的个别打印。如:拦截到app崩溃可以使用同步日志实时记录下来,若你用CocoaLumberjack等异步日志打印,由于它有延迟,app都挂了不可能记录下崩溃日志的。

异步日志系统CocoaLumberjack的使用见文章:http://blog.csdn.net/jia12216/article/details/44412697 。

简单的同步日志(就是对系统日志函数的NSLog进一步封装)可以只实现控制台日志打印,对系统性能基本没有影响(除非出现在循环里打印大量日志会影响系统性能),由于打印信息较少,它对开发人员同步定位问题有用,对普通的测试人员和其它使用人员帮助不大,稍微强过NSLog。

Macro.h实现代码:

//自定义日志开关
#ifdef DEBUG
static const int g_printLogLevel =  3;   // 仅当  isPrintLog 值大于0的时候才输出日志,关闭日志打印改为0即可
#else
static const int g_printLogLevel =  0;   // 仅当  isPrintLog 值大于0的时候才输出日志,关闭日志打印改为0即可
#endif

#ifndef LogInfo
#define LogInfo(format, ...)            \
{                                       \
if(2 < g_printLogLevel)                 \
{                                   \
NSLog((@"%@.m:%d Info:" format), NSStringFromClass([self class]), __LINE__, ## __VA_ARGS__); \
}                                   \
}
#endif

#ifndef LogDebug
#define LogDebug(format, ...)            \
{                                       \
if(1 < g_printLogLevel)                 \
{                                   \
NSLog((@"%@.m:%d Debug:" format), NSStringFromClass([self class]), __LINE__, ## __VA_ARGS__); \
}                                   \
}
#endif

#ifndef LogError
#define LogError(format, ...)            \
{                                       \
if(0 < g_printLogLevel)                 \
{                                   \
NSLog((@"%@.m:%d Error:" format), NSStringFromClass([self class]), __LINE__, ## __VA_ARGS__); \
}                                   \
}
#endif

PrefixHeader.pch文件

#ifndef MapDemoLocation_PrefixHeader_pch
#define MapDemoLocation_PrefixHeader_pch

#ifdef __OBJC__
#import "Macro.h"
#endif

#endif

使用例子:

LogError(@“LOGERR :%@”, @“TEST”);

自定义4级别同步日志系统实现控制台日志打印,写日志文件,可以指定写的日志文件的最大行数和最多的日志文件个数,超过制定的日志文件个数就删除最早的日志文件并且重新建立一个新的文件,可以自定义release版本是否写日志文件以及何种日志级别需要写日志文件。若功能有百度地图等用到.mm文件打印日志需要修改把Compile Sources As的选项选为Objective-C++(参考文章:http://blog.csdn.net/wangyuchun_799/article/details/7729222)。

WriteLog.h代码:

//
//  WriteLog.h
//  MapDemoLocation
//
//  Created by 郏国上 on 15/6/8.
//  Copyright (c) 2015年 gaos. All rights reserved.
//

#ifndef WriteLog_h
#define WriteLog_h
#define ERR_LOG 1 /* 应用程序无法正常完成操作,比如网络断开,内存分配失败等 */
#define WARN_LOG 2 /* 进入一个异常分支,但并不会引起程序错误 */
#define INFO_LOG 3 /* 日常运行提示信息,比如登录、退出日志 */
#define DEBUG_LOG 4 /* 调试信息,打印比较频繁,打印内容较多的日志 */

#ifndef LOGERR
#define LOGERR(format,...) WriteLog(ERR_LOG,__FUNCTION__,__LINE__,format,##__VA_ARGS__)
#endif

#ifndef LOGWARN
#define LOGWARN(format,...) WriteLog(WARN_LOG,__FUNCTION__,__LINE__,format,##__VA_ARGS__)
#endif

#ifndef LOGINFO
#define LOGINFO(format,...) WriteLog(INFO_LOG,__FUNCTION__,__LINE__,format,##__VA_ARGS__)
#endif

#ifndef LOGDEBUG
#define LOGDEBUG(format,...) WriteLog(DEBUG_LOG,__FUNCTION__,__LINE__,format,##__VA_ARGS__)
#endif

//#ifndef WRITELOGS
//#define WRITELOGS(format,...) WriteFileLog(NSString *string)
//#endif

void WriteLog(int ulErrorLevel, const char *func, int lineNumber, NSString *format, ...);

#endif

WriteLog.m代码:

#import <Foundation/Foundation.h>
#import “WriteLog.h”

NSString getTime(void)
{
NSDateFormatter * formatter = [[NSDateFormatter alloc]init];
[formatter setDateFormat:@“YYYY-MM-dd HH:mm:ss:SSS”];
NSString dateTime = [formatter stringFromDate:[NSDate date]];
return dateTime;
}

void WriteFileLog(NSString format, …)
{
va_list args;
va_start(args, format);
NSString string = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
if(!string)
{
return;
}
NSFileManager fm = [NSFileManager defaultManager];
NSString str = nil;
BOOL flag = NO;
NSArray myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString myDocPath = [myPaths objectAtIndex:0];
NSString* path = [myDocPath stringByAppendingPathComponent:@“Log”];
[fm fileExistsAtPath:path isDirectory:&flag];
NSArray dirarray = nil;
NSString filePath = nil;
NSArray *lines = nil;
NSError error = nil;
NSData data = nil;
NSInteger n = 0, i = 0, m = 0;
NSMutableArray *filesMutableArr = [NSMutableArray array];
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/jia12216/article/details/46425891
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSString *app_Name = [infoDictionary objectForKey:@"CFBundleDisplayName"];

if(flag)
{
    //        dirarray = [fm contentsOfDirectoryAtPath:filePath];
    //        FLDDLogDebug(@"%@ ",dirarray);
    dirarray = [fm contentsOfDirectoryAtPath:path error:nil];
    NSLog(@"%@ ",dirarray);
    n = dirarray.count;
    for(i = 0; i < n; i++)
    {
        filePath = [path stringByAppendingPathComponent:dirarray[i]];
        if ([fm fileExistsAtPath:filePath])
        {
            [filesMutableArr addObject:filePath];
        }
        
    }
    m = filesMutableArr.count;
    if(m > g_logFilesCount)
    {
        for(i = m - g_logFilesCount; i > 0; i++)
        {
            filePath = filesMutableArr[m - 1];
            [fm removeItemAtPath:filePath error:nil];
        }
    }
    else if(g_logFilesCount == m)
    {
        filePath = filesMutableArr[m - 1];
        lines = [[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]
                 componentsSeparatedByString:@"\n"];
        if(lines.count < g_logFileLines)
        {
            data = [NSData dataWithContentsOfFile:filePath];
            str =[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            str = [NSString stringWithFormat:@"%@\n%@",str,string];
            [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
        }
        else
        {
            [fm removeItemAtPath:filePath error:nil];
            str = [NSString stringWithFormat:@"%@ %@.text", app_Name, getTime()];
            filePath = [path stringByAppendingPathComponent:str];
            str = string;
            [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
        }
    }
    else if(m > 0)
    {
        filePath = filesMutableArr[m - 1];
        //            str =[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
        //            str = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
        //            NSLog(@"str :%@", str);
        lines = [[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]
                 componentsSeparatedByString:@"\n"];
        //            if(lines.count < 65535)
        if(lines.count < g_logFileLines)
        {
            data = [NSData dataWithContentsOfFile:filePath];
            str =[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            str = [NSString stringWithFormat:@"%@\n%@",str,string];
            [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
        }
        else
        {
            str = [NSString stringWithFormat:@"%@ %@.text", app_Name, getTime()];
            filePath = [path stringByAppendingPathComponent:str];
            str = string;
            [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
        }
        
    }
    else
    {
        str = [NSString stringWithFormat:@"%@ %@.text", app_Name, getTime()];
        filePath = [path stringByAppendingPathComponent:str];
        str = string;
        [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
    }
    
    
}
else
{
    BOOL res = [fm createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
    if (res) {
        //            NSLog(@"文件夹创建成功");
        str = [NSString stringWithFormat:@"%@ %@.text", app_Name, getTime()];
        filePath = [path stringByAppendingPathComponent:str];
        str = string;
        //            NSLog(@"filePath :%@", filePath);
        [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
        //            NSLog(@"error :%@", error);
    }
    else
    {
        NSLog(@"文件夹创建失败");
    }
    
}

}

void WriteLog(int ulErrorLevel, const char *func, int lineNumber, NSString *format, …)

{

va_list args;

va_start(args, format);

NSString *string = [[NSString alloc] initWithFormat:format arguments:args];

va_end(args);

NSString *strFormat = [NSString stringWithFormat:@"%@%s, %@%i, %@%@",@"Function: ",func,@"Line: ",lineNumber, @"Format: ",string];

NSString * strModelName = @"文件日志"; //模块名

NSString *strErrorLevel = [[NSString alloc] init];
NSString *str = nil;
switch (ulErrorLevel) {
    case ERR_LOG:
    {
        strErrorLevel = @"Error";
        str = [NSString stringWithFormat:@"ModalName: %@, ErrorLevel: %@, %@.",strModelName, strErrorLevel, strFormat];
        //写该级别日志,注释了就不写该级别日志了日志文件了

// WriteFileLog(str);

break;

}

case WARN_LOG:

{

strErrorLevel = @“Warning”;

str = [NSString stringWithFormat:@“ModalName: %@, ErrorLevel: %@, %@.”,strModelName, strErrorLevel, strFormat];

//写该级别日志,注释了就不写该级别日志了日志文件了

// WriteFileLog(str);

break;

}

case INFO_LOG:

{

strErrorLevel = @“INFO”;

str = [NSString stringWithFormat:@“ModalName: %@, ErrorLevel: %@, %@.”,strModelName, strErrorLevel, strFormat];

//写该级别日志,注释了就不写该级别日志了日志文件了

// WriteFileLog(str);

break;

}

case DEBUG_LOG:

{

strErrorLevel = @“Debug”;

str = [NSString stringWithFormat:@“ModalName: %@, ErrorLevel: %@, %@.”,strModelName, strErrorLevel, strFormat];

//写该级别日志,注释了就不写该级别日志了日志文件了

// WriteFileLog(str);

break;

}

default:

break;

}

str = [NSString stringWithFormat:@"ModalName: %@, ErrorLevel: %@, %@.",strModelName, strErrorLevel, strFormat];
NSLog(@"%@", str);
//打印全部级别日志,注释了就不写日志文件了
WriteFileLog(str);

}

PrefixHeader.pch文件

#ifndef MapDemoLocation_PrefixHeader_pch
#define MapDemoLocation_PrefixHeader_pch

// Include any system framework and library headers here that should be included in all compilation units.
// You will also need to set the Prefix Header build setting of one or more of your targets to reference this file.

#ifdef __OBJC__
#ifdef DEBUG
#import "WriteLog.h"
#else   //若想release版本也想打印日志就把下面12行注释了。
#ifndef LOGERR
#define LOGERR(format,...) {};
#endif
#ifndef LOGWARN
#define LOGWARN(format,...) {};
#endif
#ifndef LOGINFO
#define LOGINFO(format,...) {};
#endif
#ifndef LOGDEBUG
#define LOGDEBUG(format,...) {};
#endif
#endif
#endif
static const long g_logFilesCount = 10;
static const long g_logFileLines = 65535;
#endif

自定义同步日志函数和NSLOG完全相同,只是函数名不同。默认只debug版本写日志(工程的Scheme要是debug模式),release版本是否写日志。使用例子:

LOGERR(@“LOGERR :%@”, @“TEST”);

LOGWARN(@“LOGWARN :%@”, @“TEST”);

LOGINFO(@“LOGINFO :%@”, @“TEST”);

LOGDEBUG(@“LOGDEBUG :%@”, @“TEST”);

为了倒出日志文件需要在Info.plist文件里增加Application supports iTunes file sharing属性,类型是Boolean,值设置为YES(若设置为NO就不可以通过iTunes导出文件了,可以等发布正式版本时设置为NO).

demo的下载地址是:http://download.csdn.net/detail/jia12216/8787079

自定义同步日志文件目录:Log

日志文件的导出:

异步日志CocoaLumberjack文件目录:Log

百度地图位置信息csv格式记录目录:LngLat

相关实践学习
通过日志服务实现云资源OSS的安全审计
本实验介绍如何通过日志服务实现云资源OSS的安全审计。
目录
相关文章
WGLOG日志管理系统是怎么收集日志的
WGLOG通过部署Agent客户端采集日志,Agent持续收集指定日志文件并上报Server,Server负责展示与分析。Agent与Server需保持相同版本。官网下载地址:www.wgstart.com
|
5月前
|
Prometheus 监控 Cloud Native
基于docker搭建监控系统&日志收集
Prometheus 是一款由 SoundCloud 开发的开源监控报警系统及时序数据库(TSDB),支持多维数据模型和灵活查询语言,适用于大规模集群监控。它通过 HTTP 拉取数据,支持服务发现、多种图表展示(如 Grafana),并可结合 Loki 实现日志聚合。本文介绍其架构、部署及与 Docker 集成的监控方案。
517 122
基于docker搭建监控系统&日志收集
|
8月前
|
监控 API 开发工具
HarmonyOS Next的HiLog日志系统完全指南:从入门到精通
本文深入解析HarmonyOS Next的HiLog日志系统,涵盖日志级别、核心API、隐私保护与高级回调功能,助你从入门到精通掌握这一重要开发工具。
|
5月前
|
Ubuntu
在Ubuntu系统上设置syslog日志轮替与大小限制
请注意,在修改任何系统级别配置之前,请务必备份相应得原始档案并理解每项变更可能带来得影响。
683 2
|
存储 前端开发 数据可视化
Grafana Loki,轻量级日志系统
本文介绍了基于Grafana、Loki和Alloy构建的轻量级日志系统。Loki是一个由Grafana Labs开发的日志聚合系统,具备高可用性和多租户支持,专注于日志而非指标,通过标签索引而非内容索引实现高效存储。Alloy则是用于收集和转发日志至Loki的强大工具。文章详细描述了系统的架构、组件及其工作流程,并提供了快速搭建指南,包括准备步骤、部署命令及验证方法。此外,还展示了如何使用Grafana查看日志,以及一些基本的LogQL查询示例。最后,作者探讨了Loki架构的独特之处,提出了“巨型单体模块化”的概念,即一个应用既可单体部署也可分布式部署,整体协同实现全部功能。
4643 69
Grafana Loki,轻量级日志系统
|
10月前
|
消息中间件 运维 监控
智能运维,由你定义:SAE自定义日志与监控解决方案
通过引入 Sidecar 容器的技术,SAE 为用户提供了更强大的自定义日志与监控解决方案,帮助用户轻松实现日志采集、监控指标收集等功能。未来,SAE 将会支持 istio 多租场景,帮助用户更高效地部署和管理服务网格。
621 51
|
7月前
|
存储
WGLOG日志管理系统可以采集网络设备的日志吗
WGLOG日志审计系统提供开放接口,支持外部获取日志内容后发送至该接口,实现日志的存储与分析。详情请访问:https://www.wgstart.com/wglog/docs9.html
|
11月前
|
存储 消息中间件 缓存
MiniMax GenAI 可观测性分析 :基于阿里云 SelectDB 构建 PB 级别日志系统
基于阿里云SelectDB,MiniMax构建了覆盖国内及海外业务的日志可观测中台,总体数据规模超过数PB,日均新增日志写入量达数百TB。系统在P95分位查询场景下的响应时间小于3秒,峰值时刻实现了超过10GB/s的读写吞吐。通过存算分离、高压缩比算法和单副本热缓存等技术手段,MiniMax在优化性能的同时显著降低了建设成本,计算资源用量降低40%,热数据存储用量降低50%,为未来业务的高速发展和技术演进奠定了坚实基础。
508 1
MiniMax GenAI 可观测性分析 :基于阿里云 SelectDB 构建 PB 级别日志系统
|
11月前
|
存储 JSON Go
PHP 日志系统的最佳搭档:一个 Go 写的远程日志收集服务
为了不再 SSH 上去翻日志,我写了个 Go 小脚本,用来接收远程日志。PHP 负责记录日志,Go 负责存储和展示,按天存储、支持 API 访问、可远程管理,终于能第一时间知道项目炸了。
253 10
|
11月前
|
消息中间件 运维 监控
智能运维,由你定义:SAE自定义日志与监控解决方案
SAE(Serverless应用引擎)是阿里云推出的全托管PaaS平台,致力于简化微服务应用开发与管理。为满足用户对可观测性和运维能力的更高需求,SAE引入Sidecar容器技术,实现日志采集、监控指标收集等功能扩展,且无需修改主应用代码。通过共享资源模式和独立资源模式,SAE平衡了资源灵活性与隔离性。同时,提供全链路运维能力,确保应用稳定性。未来,SAE将持续优化,支持更多场景,助力用户高效用云。