原文:https://blog.csdn.net/giser_mxd/article/details/75557410

  在软件运行中,系统一般会采用一个持久化的日志系统来记录运行情况。本题你需要实现一个简易的 logger 日志类,这个类主要负责辅助调试,将日志信息输出到一个日志文件中。
  • 1
  • 2

  比赛预期的输出为同一目录下的 shiyanloulogger.log日志文件,文件的内容如下:

shiyanlou logger test start[2017.07.20 16:13:02 Thursday]
info info info[2017.07.20 16:13:02 Thursday]
shiyanlou logger test end[2017.07.20 16:13:02 Thursday]
  • 1
  • 2
  • 3

  程序的主要目标为: 
  1. 实现 Logger 简单的单例实现(自动垃圾回收) 
  2. 日志文件需要使用追加的方式添加日志

  C++单例模式也称为单件模式、单子模式。使用单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。有很多地方需要这样的功能模块,如系统的日志输出等。参考1

  在具体实现的时候需要考虑两个问题: 
  1. 单例模式的实现问题 
  这样的例子有很多,参考1,单例模式的好处就是保证这个类的实例是唯一的,从而不会产生冲突。 
  2. 日志文件流的生命周期 
  在进行日志输出的时候,当日志对象创建的时候,日志文件流就应该已经创建。当日志对象结束的时候,文件流也应当关闭。这样不用每次写一行日志的时候就要重新生成打开关闭文件,效率较高。 
  3. 数据释放问题 
  可以在日志类里面创建一个私有类,在程序运行结束时,自动调用私有类的析构函数,在析构函数中进行释放当前日志对象和关闭文件等操作。这样,不需要在外部函数中进行释放,不用关心日志对象的释放问题。 
  最终日志对象的头文件easyLogger.h如下:

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <memory>
#include <ctime>
#include <iostream>
#include <fstream>using namespace std;class easyLogger
{
public:static easyLogger *myInst(){if (NULL == _instance){_instance = new  easyLogger();// write to easyLogger.log_instance->ofs.open("logger.log",ios::app);}return _instance;}void Log(const string& logInfo);private:easyLogger(void) {}virtual ~easyLogger(void) {}friend class auto_ptr<easyLogger>;static easyLogger *_instance;char tmp[100];ofstream ofs;// 输出文件流class CGarbo // 它的唯一工作就是在析构函数中删除easyLogger的实例{public:~CGarbo(){if(easyLogger::_instance){easyLogger::_instance->ofs.close();//关闭文件流delete easyLogger::_instance;easyLogger::_instance = NULL;}}};static CGarbo Garbo;
};
easyLogger *easyLogger::_instance = NULL;
void easyLogger::Log(const string& logInfo)
{time_t t = time(0);strftime(tmp, sizeof(tmp), "[%Y.%m.%d %X %A]", localtime(&t));ofs << logInfo.c_str()<<tmp<<endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

  接下来我们进行日志写入的测试,代码如下:

#include "stdafx.h"
#include <iostream>
#include <ctime>
#include <cstdlib>
#include "easyLogger.h"#define NUM 100000  // 单次写入的日志条数using namespace std;char logdata[NUM][100];double random(double start,double end)
{return start+(end-start)*rand()/(RAND_MAX+1.0);
}// 测试日志写入的效率
int main()
{// 以当前的时间作为种子,这样每次生成的随机数都是不一样的,否则每次随机数都是一样的// srand(unsigned(time(0)));clock_t t1,t2;int sum_time =0;// 测试10次for (int k=0;k<10;k++){// 生成随机的数据t1 = clock();for(int i = 0;i<NUM;++i){for(int j =0;j<100-1;++j)logdata[i][j] = rand()%74+48;logdata[i][99] = '\0';}t2 = clock();cout<<"生成日志数据"<<NUM<<"条,耗费时间:"<<(t2-t1)<<"ms"<<endl;// 进行日志写入t1 = clock();for(int i = 0;i<NUM;++i)easyLogger::myInst()->Log(logdata[i]);t2 = clock();cout<<"写入日志数据"<<NUM<<"条,耗费时间:"<<(t2-t1)<<"ms"<<endl;sum_time += t2-t1;}cout<<"平均每次花费时间为:"<<sum_time/10<<"ms"<<endl;system("pause");return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

  最终在本平台(windows 10 64位+VS2010 +win32 debug模式下)运行结果如下: 

  程序其实还有需要改进的地方,比如说多线程环境下是否还能保证类的实例是唯一的、如何指定输出路径、以及如何能够进一步提升大量数据情况下的写入性能等等。


localtime()函数:获取当前时间和日期并转换为本地时间

1. time 函数返回1970-1-1, 00:00:00以来经过的秒数原型: time_t time(time_t *calptr) 结果可以通过返回值,也可以通过参数得到,见实例头文件 <time.h>返回值: 成功:秒数,从1970-1-1,00:00:00 可以当成整型输出或用于其它函数失败:-1例:time_t now;time(&now);// 等同于now = time(NULL)printf("now time is %d\n", now);2. localtime函数将时间数值变换成本地时间,考虑到本地时区和夏令时标志;原型: struct tm *localtime(const time_t * calptr); 头文件 <time.h>返回值:成功: struct tm *结构体, 原型如下:struct tm {int tm_sec;       /* 秒 – 取值区间为[0,59] */ int tm_min;       /* 分 - 取值区间为[0,59] */ int tm_hour;      /* 时 - 取值区间为[0,23] */ int tm_mday;     /* 一个月中的日期 - 取值区间为[1,31] */ int tm_mon;     /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */ int tm_year;     /* 年份,其值等于实际年份减去1900 */ int tm_wday;    /* 星期 – 取值区间为[0,6],其中0代表星期天,1代表星期一 */ int tm_yday;    /* 从每年1月1日开始的天数– 取值区间[0,365],其中0代表1月1日 */ int tm_isdst;    /* 夏令时标识符,夏令时tm_isdst为正;不实行夏令时tm_isdst为0 */    };此结构体空间由内核自动分配, 而且不要去释放它.失败: NULL例:time_t now ;struct tm *tm_now ;time(&now) ;tm_now = localtime(&now) ;printf("now datetime: %d-%d-%d %d:%d:%d\n", tm_now->tm_year+1900, tm_now->tm_mon+1, tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec) ;

void open (const char * filename, openmode mode);

这里filename 是一个字符串,代表要打开的文件名,mode 是以下标志符的一个组合:

ios::in 为输入(读)而打开文件
ios::out 为输出(写)而打开文件
ios::ate 初始位置:文件尾
ios::app 所有输出附加在文件末尾
ios::trunc 如果文件已存在则先删除该文件
ios::binary 二进制方式

这些标识符可以被组合使用,中间以”或”操作符(|)间隔。例如,如果我们想要以二进制方式打开文件"example.bin" 来写入一些数据,我们可以通过以下方式调用成员函数open()来实现:

ofstream file;
file.open ("example.bin", ios::out | ios::app | ios::binary);

ofstream, ifstream 和 fstream所有这些类的成员函数open 都包含了一个默认打开文件的方式,这三个类的默认方式各不相同:

参数的默认方式
ofstream ios::out | ios::trunc
ifstream ios::in
fstream ios::in | ios::out

只有当函数被调用时没有声明方式参数的情况下,默认值才会被采用。如果函数被调用时声明了任何参数,默认值将被完全改写,而不会与调用参数组合。

基于单例模式的日志输出(C++)相关推荐

  1. 动手造轮子:实现一个简单的基于 Console 的日志输出

    动手造轮子:实现一个简单的基于 Console 的日志输出 Intro 之前结合了微软的 Logging 框架和 Serilog 写了一个简单的日志框架,但是之前的用法都是基于 log4net.ser ...

  2. Mybatis指定日志输出实现

    10             Mybatis指定日志输出实现 在程序开发过程中,为了调试方便.了解程序的运行过程,进行必要的日志输出总是免不了的.对于使用Mybatis而言,我们常见的需求是希望可以在 ...

  3. php 单例类 mysql pdo_PHP实战:PHP基于单例模式编写PDO类的方法

    <PHP实战:PHP基于单例模式编写PDO类的方法>要点: 本文介绍了PHP实战:PHP基于单例模式编写PDO类的方法,希望对您有用.如果有疑问,可以联系我们. 一.单例模式简介 简单的说 ...

  4. PHP基于单例模式编写PDO类的方法

    一.单例模式简介简单的说,一个对象(在学习设计模式之前,需要比较了解面向对象思想)只负责一个特定的任务:二.为什么要使用PHP单例模式?1.php的应用主要在于数据库应用, 所以一个应用中会存在大量的 ...

  5. python日志输出到屏幕,python日志写入文件

    python日志输出到屏幕,python日志写入文件 日志 日志是跟踪软件运行时所发生的事件的一种方法.软件开发者在代码中调用日志函数,表明发生了特定的事件.事件由描述性消息描述,该描述性消息可以可选 ...

  6. Snort日志输出插件详解

    Snort日志输出插件详解 Snort是一款老×××的开源***检测工具,本文主要讨论他作为日志分析时的各种插件的应用.Snort的日志一般位于:/var/log/snort/目录下.可以通过修改配置 ...

  7. requirednew基于xml配置日志不回滚_Mybatis 系列 4:引入日志框架

    为什么要用日志? 我们以前要看一个信息,一般使用的是 System.out.println 来打印的,额,这种方式比较 low,需要在代码里各种写 System.out.println,是会受到鄙视的 ...

  8. unity 如何获取到屏幕中间_Unity通用渲染管线Shader日志输出工具

    这是侑虎科技第700篇文章,感谢作者邹春毅供稿.欢迎转发分享,未经作者授权请勿转载.如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨.(QQ群:793972859) 作者主页:https://w ...

  9. 给微软的日志框架写一个基于委托的日志提供者

    动手造轮子:给微软的日志框架写一个基于委托的日志提供者 Intro 微软的日志框架现在已经比较通用,有时候我们不想使用外部的日志提供者,但又希望提供一个比较简单的委托就可以实现日志记录,于是就有了后面 ...

最新文章

  1. CoGAN pytorch
  2. template might not exist or might not be accessible by any of the configured Template Resolvers
  3. 如何利用极致业务基础平台主界面容器工具配置出漂亮的业务系统
  4. postgresql根据字符分割数据为多列
  5. 反思避免只是小聪明:更加稳重 更加踏实
  6. 程序中保存状态的方式之Cookies
  7. NOIP2005普及组第3题 采药 (背包问题)
  8. 重磅!大数据知识总结和调参技巧开放下载了
  9. Session基础知识
  10. ubuntu 18.04 LTS 安装SecureCRT
  11. Java Builder模式(设计模式之Builder模式)
  12. Spring之FactoryBean的使用与源码解析
  13. 网站原创文章被盗用怎么办?
  14. 关于高速光耦6n137的使用总结_高速光耦6n137典型应用电路图汇总(多谐振荡/光电隔离器/光耦开关) - 全文...
  15. caniuse npm安装
  16. 【微信小程序-原生开发】添加自定义图标(以使用阿里图标库为例)
  17. js:数组转链表,链表转数组
  18. android 蓝牙ble调试助手,Android蓝牙调试助手源码分享
  19. 赢在中国 第二季 语录
  20. MySQL-Test-Run测试工具

热门文章

  1. 找出netstat -tan命令的结果中以'LISTEN'后跟0、1或多个空白字符结尾的行
  2. android ExpandableListView
  3. 用U盘安装VMware ESXi4
  4. 履带机器人运动控制器c++_敏捷得像猫,被抛出后能平稳着地,有意思的美国西点军校机器人!...
  5. .net thread操作串口_听说你不知道 RT-Thread 有个 ringbuffer
  6. android编程fragment,Android中关于FragmentA嵌套FragmentB的问题
  7. mysql注入攻击与防御word_SQL注入防御与绕过的几种姿势
  8. 树结构有什么好处_什么啊?面试官还在问HashMap了,老知识点了啊
  9. 简单介绍nginx 变量使用
  10. java 中的内部类介绍