如何优雅的跨平台输出log日志 C++


前言

作为一名合格的程序员,进行调试,找bug的时候不可能单单只靠断点或者进行printf输出,这种方式不仅不够准确与及时,比如解决一些不能够必现的问题,而且如果产品到了用户发生了问题,没有日志就很有可能遗漏了当时的场景。

所以我们就需要一种可以记录程序运行状态的日志,这里采用的是goole推出的glog,是一种跨windows、mac、ios、android、linux的强大开源工具。笔者为了方便书写代码,添加了部分宏定义。

本文只在windows下进行示范,其他平台基本一致。

一、glog

1.基本满足的功能

一个合格的日志在笔者看来最少要拥有日志类型、日期、时间、文件名、行号、线程号、日志内容才能够准确的帮助定位到一些问题的所在,也就是这样的格式:

IWEFmmddhh:mm:ss.uuuuuuthreadidfile:line]msg

其中类型含义:

IWEF
INFOWARNNINGERRORFATAL

最终输出的日志看起来像是这样的

Log file created at: 2022/04/06 17:37:49
Running on machine: A120282
Running duration (h:mm:ss): 0:00:00
Log line format: [IWEF]yyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg
I20220406 17:37:49.679339 20356 main.cpp:21] hello world

2.进阶的功能

在实际使用过程中,除了上面基本的功能,其实有的时候会遇到以下的问题:例如格式化输出日志,日志打满,如何多线程使用,更有的希望在用户那边使用的时候进行加密处理。笔者会接下来做一些测试证明以下glog都是支持的,除了加解密笔者自己没有进行尝试过,但是从glog的API中是看到可以进行加解密的(后面笔者如果使用到会再次添加)。

  1. 可以正常输出Unicode字符集(中文日志)
  2. 支持加/解密
  3. 支持多线程输出日志
  4. 控制日志文件大小
  5. 日志打满后自动新建文件

|版本声明:山河君,未经博主允许,禁止转载

2.glog源码编译

直接从github上下载,使用CMake编译就好了,如果想省事,可以直接下载别人编好的
github:https://github.com/google/glog
在这里插入图片描述
笔者这边编译的是动态库,注意有些头文件需要稍微移动一下,总体使用的头文件和编译成功的库如下:
在这里插入图片描述

二、使用

这边就直接省略工程创建和基本的配置拉…

1.初步使用

这里值得注意的是一些flag的配置,使用的拼接的语法,感兴趣的小伙伴可以自行跟踪代码进去查看。google的注释写的真的是没话说。

同时,如果采用不同的连接方式(静态/动态) 宏定义(如动态库为:GLOG_NO_ABBREVIATED_SEVERITIES)是不同的,不过在编译的时候有错误输出,可以很直观的看到,就不过多介绍了。

要注意宏定义要加载glog/logging.h 头文件之前


#define GLOG_NO_ABBREVIATED_SEVERITIES

#include "glog/logging.h"
#include <string>
#include <iostream>

int main()
{
	std::string strLogName = "test";
	std::string strLogPath = "E:/log/";
	std::string strDestination = strLogPath + strLogName;
	google::InitGoogleLogging(strLogName.c_str());
	google::SetStderrLogging(google::GLOG_FATAL);
	google::SetLogDestination(google::GLOG_INFO, strDestination.c_str());
	FLAGS_colorlogtostderr = true;	//如果输出到终端是否需要颜色标记
	FLAGS_logbufsecs = 0;		//设置日志输出等待时间
	FLAGS_max_log_size = 50;	//设置输出日志文件大小
	FLAGS_stop_logging_if_full_disk = true;	//磁盘满时是否停止

	LOG(INFO) << "hello " << "world";

	google::ShutdownGoogleLogging();

	return 0;
}

直接进行运行,可以看到磁盘E:/log/下有对应的文件生成

在这里插入图片描述

且文件内容为

Log file created at: 2022/04/06 17:37:49
Running on machine: A120282
Running duration (h:mm:ss): 0:00:00
Log line format: [IWEF]yyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg
I20220406 17:37:49.679339 20356 main.cpp:21] hello world

2.跨线程使用

跨线程使用核心其实就是全局使用,跟踪glog源码,我们可以很清楚的看到再每次输入日志,并刷新到log文件中,其实都会进行加锁,所以使用时不必担心跨线程使用时造成资源竞态。

glog源码节选:

inline void LogDestination::FlushLogFiles(int min_severity) {
  // Prevent any subtle race conditions by wrapping a mutex lock around
  // all this stuff.
  MutexLock l(&log_mutex);
  for (int i = min_severity; i < NUM_SEVERITIES; i++) {
    LogDestination* log = log_destination(i);
    if (log != NULL) {
      log->logger_->Flush();
    }
  }
}

所以我们只要在真正使用之前进行创建就可以了。

这里为了方便,新建一个Logger类
头文件:

#pragma once
#define GLOG_NO_ABBREVIATED_SEVERITIES

#include "glog/logging.h"
#include <string>
#include <iostream>

//方便输出采用宏定义方式

#define LOG_DEBUG DLOG(INFO)
#define LOG_INFO LOG(INFO)
#define LOG_WARNING LOG(WARNING)
#define LOG_ERROR LOG(ERROR)
#define LOG_FATAL LOG(FATAL)

class Logger
{
public:
	static Logger* GetInstance(const std::string &strLogPath, const std::string &strLogName);
	~Logger();

private:
	Logger(const std::string &strLogPath, const std::string &strLogName);
	static Logger* m_pInstance;
};

源文件:

#include "Logger.h"
Logger* Logger::m_pInstance = nullptr;

Logger* Logger::GetInstance(const std::string &strLogPath, const std::string &strLogName)
{
	if (m_pInstance == nullptr)
	{
		m_pInstance = new Logger(strLogPath, strLogName);
		LOG_INFO << "The Logger is new init";
	}
	else
	{
		LOG_INFO << "The logger has init";
	}

	return m_pInstance;
}

Logger::Logger(const std::string &strLogPath, const std::string &strLogName)
{
	std::string strDestiona = strLogPath + strLogName + "_";
	google::InitGoogleLogging(strLogName.c_str());
	google::SetStderrLogging(google::GLOG_FATAL);
	google::SetLogDestination(google::GLOG_INFO, strDestiona.c_str());
	FLAGS_colorlogtostderr = true;
	FLAGS_logbufsecs = 0;
	FLAGS_max_log_size = 50;
	FLAGS_stop_logging_if_full_disk = true;
}

Logger::~Logger()
{
	LOG_INFO << "The logger is over";
	google::ShutdownGoogleLogging();
}

main函数(测试):

#include "Logger.h"
#include <string>
#include <iostream>
#include <thread>

void Thread1()
{
	int i = 100;
	while (i--)
	{
		LOG_INFO << "this is thread one";
	}
}

void Thread2()
{
	int i = 100;
	while (i--)
	{
		LOG_INFO << "this is thread two";
	}
}

int main()
{
	Logger* pLogger = Logger::GetInstance("E:/Log/", "LogTest");
	std::thread thread1(Thread1);
	std::thread thread2(Thread2);

	thread1.join();
	thread2.join();
	delete pLogger;
	return 0;
}

我们可以看到,多线程输出并没有杂乱的现象

Log file created at: 2022/04/06 18:25:39
Running on machine: A120282
Running duration (h:mm:ss): 0:00:00
Log line format: [IWEF]yyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg
I20220406 18:25:39.786847 6324 logger.cpp:9] The Logger is new init
I20220406 18:25:39.790838 23468 main.cpp:11] this is thread one
I20220406 18:25:39.790838 23468 main.cpp:11] this is thread one
I20220406 18:25:39.790838 23468 main.cpp:11] this is thread one
I20220406 18:25:39.790838 23468 main.cpp:11] this is thread one
I20220406 18:25:39.790838 23468 main.cpp:11] this is thread one
I20220406 18:25:39.790838 23468 main.cpp:11] this is thread one
I20220406 18:25:39.790838 23468 main.cpp:11] this is thread one
I20220406 18:25:39.790838 16488 main.cpp:20] this is thread two
I20220406 18:25:39.790838 23468 main.cpp:11] this is thread one
I20220406 18:25:39.790838 16488 main.cpp:20] this is thread two
I20220406 18:25:39.790838 23468 main.cpp:11] this is thread one
I20220406 18:25:39.790838 16488 main.cpp:20] this is thread two

3.测试疯狂打中文日志日志

测试main.cpp

#include "Logger.h"
#include <string>
#include <iostream>
#include <thread>

void Thread1()
{
	int i = 100;
	while (true)
	{
		LOG_INFO << "你好";
	}
}


int main()
{
	Logger* pLogger = Logger::GetInstance("E:/Log/", "LogTest");
	std::thread thread1(Thread1);

	thread1.join();
	delete pLogger;
	return 0;
}

由输出日志看,每到50M之后,会新建一个日志,同时中文也没有乱码

Log file created at: 2022/04/06 18:34:25
Running on machine: A120282
Running duration (h:mm:ss): 0:00:00
Log line format: [IWEF]yyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg
I20220406 18:34:25.704986 11584 logger.cpp:9] The Logger is new init
I20220406 18:34:25.708976 19268 main.cpp:11] 你好
I20220406 18:34:25.708976 19268 main.cpp:11] 你好
I20220406 18:34:25.708976 19268 main.cpp:11] 你好
I20220406 18:34:25.708976 19268 main.cpp:11] 你好
I20220406 18:34:25.708976 19268 main.cpp:11] 你好

在这里插入图片描述

总结

如果想在工程任意地方使用,只要包含Logger.h头文件即可。

如果感觉对您有点用,请点个赞吧👍!

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值