前言
作为一名合格的程序员,进行调试,找bug的时候不可能单单只靠断点或者进行printf输出,这种方式不仅不够准确与及时,比如解决一些不能够必现的问题,而且如果产品到了用户发生了问题,没有日志就很有可能遗漏了当时的场景。
所以我们就需要一种可以记录程序运行状态的日志,这里采用的是goole推出的glog,是一种跨windows、mac、ios、android、linux的强大开源工具。笔者为了方便书写代码,添加了部分宏定义。
本文只在windows下进行示范,其他平台基本一致。
一、glog
1.基本满足的功能
一个合格的日志在笔者看来最少要拥有日志类型、日期、时间、文件名、行号、线程号、日志内容才能够准确的帮助定位到一些问题的所在,也就是这样的格式:
IWEF | mmdd | hh:mm:ss.uuuuuu | threadid | file:line] | msg |
---|
其中类型含义:
I | W | E | F |
---|---|---|---|
INFO | WARNNING | ERROR | FATAL |
最终输出的日志看起来像是这样的
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中是看到可以进行加解密的(后面笔者如果使用到会再次添加)。
- 可以正常输出Unicode字符集(中文日志)
- 支持加/解密
- 支持多线程输出日志
- 控制日志文件大小
- 日志打满后自动新建文件
|版本声明:山河君,未经博主允许,禁止转载
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
头文件即可。
如果感觉对您有点用,请点个赞吧👍!