一個易用、現代、輕量且高效的 C++20 日誌庫 —— nlog

257天前 · 程式設計 · c++ · 527次阅读

Neko Logging

nlog 是我之前在開發Neko Launcher時所產生的需求而編寫的一個日誌程式庫。

下面就請讓我來講解一下這個用了絕對大吃一驚日誌庫的優點吧:

  • 沒有宏

你無需忍受C語言遺留下的糟粕 —— 宏。

  • 僅標頭檔 (無需構建/鏈接)

不需要構建或鏈接,新手小白福音。 —— 我太奶來了都會用 XD

  • Auto SrcLoc

自動捕獲原始碼位置 —— 阿嬤再也不用擔心報錯找不到位置啦

  • 支援多個輸出器(Console, File, Custom)

—— 無論多少輸出都能塞進去(你塞到uint64溢出的話當我沒說)

  • 支援自定格式化日誌訊息

——理所當然,你可以很容易的自定格式與輸出器

  • 支援異步日誌IO
  • 執行緒安全
  • 範圍日誌方式記錄(RAII)

Quick Start

  1. 克隆倉庫到你的主機
git clone https://github.com/moehoshio/nlog.git
  1. 將include資料夾拷貝到你的include目錄
  2. 在原始碼中引入標頭
#include "neko/log/nlog.hpp"

基礎範例

現在你可以開始記錄了,無需太多操作就能夠做到:

#include "neko/log/nlog.hpp"

int main() {
    using namespace neko;
    log::setCurrentThreadName("Main Thread"); //設定當前線程名稱
    log::setLevel(log::Level::Debug); //設定日誌等級

    log::info("This is an info message.");
    log::debug("This is a debug message.");
    log::warn("This is a warning message.");
    log::error("This is an error message.");
}

output:

[2025-09-16 01:53:58.678] [Info] [Main Thread] [main.cpp:9] This is an info message.
[2025-09-16 01:53:58.679] [Debug] [Main Thread] [main.cpp:10] This is a debug message.
[2025-09-16 01:53:58.679] [Warn] [Main Thread] [main.cpp:11] This is a warning message.
[2025-09-16 01:53:58.679] [Error] [Main Thread] [main.cpp:12] This is an error message.

使用

記錄

要記錄日誌很簡單,就和上面的例子一樣,
這些函式都有兩個版本。

單字串:

inline void debug(const std::string &message, const neko::SrcLocInfo &location = {});

debug("msg"); // (基礎格式)... msg

和格式化參數(透過std::format):

    template <typename... Args>
    void debug(const neko::SrcLocInfo &location, std::format_string<Args...> fmt, Args &&...args);

    debug( {} , "Hello , {} . 1 + 1 = {}", "World" , 1 + 1); // (基礎格式)... Hello , World . 1 + 1 = 2

其他等級的函式同上。

Tips: SrcLoc可以自動原始碼位置,只需要一個默認對象即可, 你可以透過 {} 或型參默認產生它。

Level

你可以設定記錄者的日誌級別,它會控制何等級的日誌該被記錄。

舉例來說,如果你設定setLevel爲 Info,那麼只有Info以上的日誌會被記錄。(此時Debug訊息會被丟棄)。

log::setLevel(log::Level::Info);

log::info("Info"); // 輸出 Info
log::warn("Warn"); // 輸出 Warn
log::debug("Debug"); // 更詳細的訊息將被丟棄

如果需要,你可以添加更多的日誌等級並透過log函式記錄。
列如:


// nlog.hpp
enum class Level : neko::uint8 {
        Debug = 1, ///< Debug
        Info = 2,  ///< General information
        Warn = 3,  ///< Potential issues
        Error = 4, ///< Error
        lv5 = 5, /// 自定的等級
        lv6 = 6,
        lv10 = 10,
        Off = 255  ///< Logging off
    };

// main.cpp
using namespace neko;

log::logger.log(log::Level::lv10,"Hello Lv10");

設定執行緒名稱

您可以透過

log::setCurrentThreadName

log::setThreadName

函式來設定不同執行緒在日誌中的名稱。

範例:

using namespace neko;

//設定目前的執行緒
log::setCurrentThreadName("Thread 1");
log::info(""); // ... [Thread 1] ...

//指定id
auto id = std::this_thread::get_id();
logsetThreadName(id, "Thread-1");

log::info(""); // ... [Thread-1] ...

Appenders

您可以同時新增多個 appender 來將日誌輸出到不同地方,默認提供了輸出到終端和寫入檔案的appender。

記錄到檔案中

// 新增檔案輸出器並覆寫檔案
addFileAppender("app.log", true); 

輸出到終端(默認已啟用)

addConsoleAppender(); //新增終端機輸出器

自定輸出器

你可以非常方便的添加自己的輸出器,以輸出到任何地方。

只需要繼承

log::IAppender

並覆寫 appendflush方法爲你的輸出即可。

範例:

using namespace neko;

class MyAppender : public log::IAppender {
    public:
    void append(const log::LogRecord &record) override {
        std::unique_ptr<log::IFormatter> formatter = std::make_unique<log::DefaultFormatter>(); //構建一個默認日誌格式化器
        auto formatted = formatter->format(record); //格式化日誌

        // 將字串輸出到你的目標
        yourOutput << formatted;
    }
    
    void flush() override {
        yourOutput.flush();
    }
};

// 添加自定的日誌輸出器
log::addAppender(std::make_unique<MyAppender>());

格式化日誌

格式化器是輸出器的一個輔助方法,用以格式化日誌。
其與每個appender獨立的,並由appender調用格式化器的方法來格式化日誌。
So,建議自定appender時內置一個格式化器對象。

默認格式化器

使用默認的格式化器格式爲:
[date time] [level] [thread] [file:line] [msg]

在構造時你可以指定要截斷的根路徑和是否使用全路徑,函式定義如下:

explicit DefaultFormatter(const std::string &rootPath = "", bool useFullPath = false)

當rootPath爲空字串時(默認), file = main.cpp。
當rootPath爲路徑,如 /to/path/, 且file位於 /to/path/src/main.cpp時,file = /src/main.cpp。
當 useFullPath爲 true時, 將忽略rootPath,始終顯示完全路徑。 file = /to/path/src/main.cpp。

自定格式化器

繼承自
IFormatter並覆寫 format函式。
你需要在format函式實現格式化傳入的記錄。

範例:

using namespace neko;

class MyFormatter : public log::IFormatter {
    public:
    std::string format(const log::LogRecord &record) override {
        //你可以使用任何方式組合record格式,std format ,oss ...
        
        std::ostringstream oss;
        oss << "lv: " << log::levelToString(record.level) << " , msg: "<< record.message ;
        return oss.str();
    }
};

//構建一個終端輸出器並指定格式化器爲MyFormatter
log::ConsoleAppender consoleAppender(std::make_unique<MyFormatter>());

log::info("Hello");

output:

lv: Info , msg: Hello

非同步日誌

默認情況下,記錄日誌由記錄者執行緒寫入IO。

為了獲得更好的效能,您可以啟用非同步模式。 讓日誌在背景執行緒中處理。

#include "neko/log/nlog.hpp"
#include <thread>
using namespace neko;

int main() {
    // 設定為非同步模式
    log::setMode(neko::SyncMode::Async);

    // 啟動日誌處理迴圈(通常在一個專用的執行緒中)
    std::thread logThread([]{ log::runLogLoop(); });

    // 主執行緒提交日誌
    log::info("This will be logged asynchronously.");

    // ... 應用程式執行 ...

    // 停止日誌迴圈並刷新剩餘的日誌
    log::stopLogLoop();
    logThread.join();
}

異步時日誌不會實時刷新,可能發生開始前提交了日誌,但來不及記錄便崩潰的情況。
建議Debug時關閉。

Tips: 使用異步模式時,必須有執行緒運行

neko::log::runLogLoop()

函式。
否則任何日誌都不會得到處理。

RAII 範圍日誌

使用 autoLog 來自動記錄一個範圍的開始和結束。

void someFunction() {
    // 當 someFunction 進入時記錄 "Start",離開時記錄 "End"
    log::autoLog log("Start", "End"); 
    
    // ... 函式內容 ...
}

結語

就是醬紫,你已經可以熟練使用 nlog 了,快去試試吧~
如果還有任何問題歡迎留言探討喔!😉

更新:
哇,正文含有命名空間會導致排版解析錯誤QQ, 所以看起來可能會有些奇怪。
對象的完整命名空間參考readme中的說明吧,或者閱讀原始碼~
(這不會很困難,該原始碼是易讀的)

👍 0

c++ cpp nlog programming

最后修改于94天前

评论

贴吧 狗头 原神 小黄脸
收起

贴吧

  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡
  • 贴吧泡泡

狗头

  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头
  • 狗头

原神

  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神
  • 原神

小黄脸

  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸
  • 小黄脸

目录

avatar

Hina

曇花一現

20

文章

96

评论

8

分类

初见

Modern C++ - Lambda

291天前

OwO

33

網站正在更新中...
站點正在更新功能與樣式,如有樣式錯誤,請嘗試刷新緩存