前言
日志系统是所有程序中的刚需,本章节将开源的spdlog集成到引擎中。
spdlog是一个只有头文件,性能很好的C++日志库
集成spdlog
spdlog以submodule的形式集成到项目中。
所有的第三方库我们放到/Sanbox/Hazel/vendor下面
vendor供应商的意思,也可以表示第三方库
download spdlog
在项目根目录下执行下面命令:
bash
git submodule add https://github.com/gabime/spdlog.git Sanbox/Hazel/vendor/spdlog
在根目录下会生成一个.gitmodules文件,而spdlog并不会加入到本项目的git记录中。
.gitmodules
ini
[submodule "Sandbox/Hazel/vendor/spdlog"]
path = Sandbox/Hazel/vendor/spdlog
url = https://github.com/gabime/spdlog.git
增加了一些ignore
Hazel/.gitignore
bash
/Sandbox/cmake-build-debug/
.idea/
build
更新Hazel引擎的CMake
更新CMake文件,增加spdlog引用
参考diff,修改的地方一目了然
实现Log封装
Log.h
声明两个静态的log实例,分别用于引擎内核的日志打印,和业务层的日志打印。这样做可以设置不同的tag,以及设置不同的日志策略。
两个Log对应两个静态的获取实例的函数。
c++
namespace Hazel {
class Log {
public:
static void Init();
inline static std::shared_ptr<spdlog::logger>& GetCoreLogger(){
return s_CoreLogger;
}
inline static std::shared_ptr<spdlog::logger>& GetClientLogger(){
return s_ClientLogger;
};
private:
static std::shared_ptr<spdlog::logger> s_CoreLogger;
static std::shared_ptr<spdlog::logger> s_ClientLogger;
};
}
Log.cpp中初始化spdlog。
spdlog的使用可以参考官方文档,有详细的说明,此处不做展开spdlog文档
c++
#include "Log.h"
namespace Hazel{
std::shared_ptr<spdlog::logger> Log::s_CoreLogger;
std::shared_ptr<spdlog::logger> Log::s_ClientLogger;
void Log::Init() {
spdlog::set_pattern("%^[%T] %n : %v%$");
Log::s_CoreLogger = spdlog::stdout_color_mt("HAZEL", spdlog::color_mode::always);
Log::s_CoreLogger->set_level(spdlog::level::trace);
Log::s_ClientLogger = spdlog::stdout_color_mt("APP", spdlog::color_mode::always);
Log::s_ClientLogger->set_level(spdlog::level::trace);
}
}
这里我们两个日志等级都设置成trace级别,tag分别设置为"HAZEL"和"APP"
注意,C++中,静态变量定义和声明需要分开写。
如Log::s_CoreLogger和Log::s_ClientLogger声明和定义。
Hazel.h文件中增加Log.h的引用
c++
#ifndef SANBOX_HAZEL_H
#define SANBOX_HAZEL_H
#include <stdio.h>
#include "Hazel/Application.h"
#include "Hazel/Log.h"
// -----Entry Point-------
#include "Hazel/EntryPoint.h"
#endif //SANBOX_HAZEL_H
Log的使用及优化
回到EntryPoint.h文件中,使用Log打印日志
c++
int main(int argc, char** argv) {
Hazel::Log::Init();
Hazel::Log::GetCoreLogger()->warn("Initialized Log!");
Hazel::Log::GetClientLogger()->info("Hello!");
auto app = Hazel::CreateApplication();
app->Run();
delete app;
}
可以看到虽然可以用了,但是打印一行日志的代码有点啰嗦,用宏定义简化。
回到Log.h中,在末尾增加宏定义
c++
// Core log macros
#define HZ_CORE_TRACE(...) ::Hazel::Log::GetCoreLogger()->trace(__VA_ARGS__)
#define HZ_CORE_INFO(...) ::Hazel::Log::GetCoreLogger()->info(__VA_ARGS__)
#define HZ_CORE_WARN(...) ::Hazel::Log::GetCoreLogger()->warn(__VA_ARGS__)
#define HZ_CORE_ERROR(...) ::Hazel::Log::GetCoreLogger()->error(__VA_ARGS__)
#define HZ_CORE_FATAL(...) ::Hazel::Log::GetCoreLogger()->fatal(__VA_ARGS__)
// Client log macros
#define HZ_TRACE(...) ::Hazel::Log::GetClientLogger()->trace(__VA_ARGS__)
#define HZ_INFO(...) ::Hazel::Log::GetClientLogger()->info(__VA_ARGS__)
#define HZ_WARN(...) ::Hazel::Log::GetClientLogger()->warn(__VA_ARGS__)
#define HZ_ERROR(...) ::Hazel::Log::GetClientLogger()->error(__VA_ARGS__)
#define HZ_FATAL(...) ::Hazel::Log::GetClientLogger()->fatal(__VA_ARGS__)
EntryPoint.h中的调用就简单多了
c++
int main(int argc, char** argv) {
Hazel::Log::Init();
// Hazel::Log::GetCoreLogger()->warn("Initialized Log!");
// Hazel::Log::GetClientLogger()->info("Hello!");
HZ_CORE_WARN("Initialed Log!");
int a = 5;
HZ_INFO("Hello! Var={0}", a);
auto app = Hazel::CreateApplication();
app->Run();
delete app;
}
基于宏定义的日志除了简化调用逻辑,还有个重要的作用,区分不同包做不同的逻辑,比如在正式版本不希望打印调试日志,可以增加宏定义:
#define HZ_TRACE(...)
这样预编译的时候替换成空,相当于什么都没干。
完整代码commit参考: