在實作中,當我們在debug時,筆者相信有許多人都會使用「printf大法」來追蹤或顯示程式內部運行的狀況吧,而相信大家接觸許多軟體也有看過日誌系統,用於調試、紀錄程式發生的事件,今天我們就來實現這個Logging system。
那麼我們要來使用spdlog來實現我們的Logging系統,他是一個快速且輕量的開元庫,而且功能豐富,還能使用fmt來實現自訂格式。
我們開啟終端機使用vcpkg尋找spdlog。
接著我們用vcpkg install spdlog。
vcpkg install spdlog:x64-windows-static
結束後會看見這個畫面,因為待會要將其加入CMake當中,所以我們可以先放著別關掉。
將engine.cmake做更改,加入這兩行
find_package(spdlog REQUIRED)
target_link_libraries(engine
spdlog::spdlog
spdlog::spdlog_header_only
)
這裡提供另一種做法,由於spdlog是header_only的函式庫,我們也可以簡單地將其作為submodule加進來專案中。
若是還沒初始化需要先init。
git init
接著將spdlog以submodule加進專案裡。
git submodule add https://github.com/gabime/spdlog engine/vendor/spdlog
如果使用這個做法的話就將engine.cmake的include資料夾加入spdlog的include資料夾。
target_include_directories(engine PRIVATE
${CMAKE_SOURCE_DIR}/engine
${CMAKE_SOURCE_DIR}/engine/src
${CMAKE_SOURCE_DIR}/engine/vendor/spdlog/include
)
我們在Core資料夾裡新增一個Core.h,裡面新增一個RuntimeModule class,這是我們將來要新增組件管理器的時候需要使用的class,他具備幾個虛擬函式。
startUp
用於啟動這個子系統。
shutDown
用於關閉這個子系統。
Tick
用於每幀更新。
這是Core.h的具體內容。
#pragma once
namespace Engine {
class RuntimeModule
{
public:
virtual ~RuntimeModule() {};
virtual void startUp() = 0;
virtual void shutDown() = 0;
virtual void Tick() = 0;
};
}
接著我們在engine的src資料夾裡新增一個Log的資料夾,並建立Log.h與Log.cpp。
定義LogManager並且繼承自Core的運行時組件。我們也能使用macro來簡化Log的使用。
#include "Core/Core.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/ostr.h"
namespace Engine {
class __declspec(dllexport) LogManager: public Engine::RuntimeModule
{
public:
~LogManager() {};
static void startUp() ;
static void shutDown() ;
void Tick();
inline static std::shared_ptr<spdlog::logger>& GetCoreLogger() { return s_CoreLogger; }
private:
static std::shared_ptr<spdlog::logger> s_CoreLogger;
};
// Core Log macro
#define ENGINE_CORE_TRACE(...) ::Engine::LogManager::GetCoreLogger()->trace(__VA_ARGS__);
#define ENGINE_CORE_INFO(...) ::Engine::LogManager::GetCoreLogger()->info(__VA_ARGS__);
#define ENGINE_CORE_WARN(...) ::Engine::LogManager::GetCoreLogger()->warn(__VA_ARGS__);
#define ENGINE_CORE_ERROR(...) ::Engine::LogManager::GetCoreLogger()->error(__VA_ARGS__);
#define ENGINE_CORE_FATAL(...) ::Engine::LogManager::GetCoreLogger()->fatal(__VA_ARGS__);
}
使用set_pattern來設定自定義的格式,具體文檔在這裡。
這裡稍微整理一些常用的。
flag | meaning | example |
---|---|---|
%v |
The actual text to log | "log text" |
%T or %X |
ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S | "23:55:59" |
%n |
Logger's name | "logger name" |
#include "Log.h"
#include "spdlog/sinks/stdout_color_sinks.h"
namespace Engine {
std::shared_ptr<spdlog::logger> LogManager::s_CoreLogger;
void LogManager::startUp()
{
// 自定義格式: [時間] Logger名稱:Log訊息
spdlog::set_pattern("%^[%T] %n: %v%$");
// Logger名稱
s_CoreLogger = spdlog::stdout_color_mt("ENGINE");
// 低於設定的Level級別的Log將不會被顯示
s_CoreLogger->set_level(spdlog::level::trace);
}
void LogManager::shutDown()
{
s_CoreLogger->flush();
spdlog::drop_all();
}
void LogManager::Tick()
{
}
}
我們可以在引擎啟動時使用Log來輸出訊息。就可以完成了!
#include "Application.h"
#include "Log/Log.h"
namespace Engine {
Application::Application() {}
Application::~Application() {}
void Application::Run()
{
ENGINE_CORE_TRACE("{0}","Engine Ver 1.0.0 !");
while (true)
{
/* code */
}
};
}