CentOS 7.9 生產(chǎn)環(huán)境 C++ 日志庫 spdlog 編譯、集成與性能調(diào)優(yōu)實(shí)戰(zhàn)
1. 項(xiàng)目概述與核心價(jià)值最近在CentOS上折騰一個(gè)C的后臺(tái)服務(wù)調(diào)試的時(shí)候滿屏的printf和std::cout信息散亂不說還嚴(yán)重影響性能。這才下定決心要把日志模塊好好規(guī)整一下。在C的日志庫江湖里spdlog的名頭那是響當(dāng)當(dāng)?shù)乃俣瓤臁⒐δ苋?、頭文件庫用起來也方便。但真要在生產(chǎn)環(huán)境常用的CentOS系統(tǒng)上把它穩(wěn)穩(wěn)當(dāng)當(dāng)?shù)匮b好、集成到項(xiàng)目里還是有不少細(xì)節(jié)要注意的遠(yuǎn)不是一句yum install那么簡單。這篇文章我就把自己從環(huán)境準(zhǔn)備、編譯安裝、到項(xiàng)目集成、性能調(diào)優(yōu)的全過程以及踩過的坑和總結(jié)的經(jīng)驗(yàn)毫無保留地分享出來。無論你是剛接觸Linux C開發(fā)的新手還是正在為現(xiàn)有項(xiàng)目尋找一個(gè)可靠日志方案的老鳥這份從實(shí)戰(zhàn)中來的指南應(yīng)該都能讓你少走彎路。2. 環(huán)境準(zhǔn)備與依賴梳理在動(dòng)手安裝之前理清依賴和準(zhǔn)備好環(huán)境能避免后續(xù)一大堆編譯錯(cuò)誤和鏈接問題。CentOS的“穩(wěn)定”有時(shí)也意味著軟件包版本較舊我們需要有策略地應(yīng)對(duì)。2.1 系統(tǒng)環(huán)境確認(rèn)首先登錄你的CentOS服務(wù)器確認(rèn)基礎(chǔ)信息。我使用的是CentOS 7.9這也是目前企業(yè)環(huán)境中最常見的版本之一。cat /etc/redhat-release uname -r輸出類似CentOS Linux release 7.9.2009 (Core)和3.10.0-1160.el7.x86_64。記下你的系統(tǒng)版本和內(nèi)核雖然spdlog不直接依賴內(nèi)核但某些編譯工具鏈可能有要求。接下來更新系統(tǒng)基礎(chǔ)包到最新狀態(tài)這是一個(gè)好習(xí)慣sudo yum update -y sudo yum groupinstall -y Development ToolsDevelopment Tools這個(gè)軟件包組非常關(guān)鍵它包含了gcc、g、make、autoconf等一整套編譯構(gòu)建工具。沒有它后續(xù)的編譯步驟無從談起。2.2 核心依賴CMake與編譯器Spdlog本身是頭文件庫但其安裝、示例編譯和測(cè)試通常依賴CMake。此外spdlog需要支持C11標(biāo)準(zhǔn)的編譯器。安裝GCC/GCentOS 7自帶的gcc版本是4.8.5它完整支持C11。確認(rèn)一下gcc --version g --version如果版本足夠4.8.1則無需升級(jí)。若你因其他項(xiàng)目需要更高版本如C17可以考慮使用devtoolset系列如devtoolset-9來安裝更新的GCC但這會(huì)引入環(huán)境管理的復(fù)雜性對(duì)于僅使用spdlog而言非必需。安裝CMakeCentOS基礎(chǔ)倉庫的CMake版本如2.8太老無法滿足現(xiàn)代項(xiàng)目的需求。我們需要手動(dòng)安裝較新版本。這里采用從源碼編譯安裝的方式雖然步驟稍多但最可控。# 安裝編譯CMake所需的依賴 sudo yum install -y openssl-devel # 下載CMake源碼以3.22.0為例選一個(gè)穩(wěn)定版本 wget https://github.com/Kitware/CMake/releases/download/v3.22.0/cmake-3.22.0.tar.gz tar zxvf cmake-3.22.0.tar.gz cd cmake-3.22.0 # 配置、編譯、安裝 ./bootstrap --prefix/usr/local make -j$(nproc) # 使用所有CPU核心并行編譯加快速度 sudo make install # 驗(yàn)證安裝 cmake --version看到類似cmake version 3.22.0的輸出說明安裝成功。將CMake安裝到/usr/local下通??梢员苊馀c系統(tǒng)舊版本沖突。注意如果你在服務(wù)器上權(quán)限受限無法安裝到/usr/local可以將--prefix設(shè)置為你的家目錄下的某個(gè)路徑如$HOME/.local并記得將該路徑的bin子目錄加入PATH環(huán)境變量。2.3 可選依賴fmt庫Spdlog從v1.9.0版本開始將格式化功能從內(nèi)部實(shí)現(xiàn)遷移到了外部的{fmt}庫。這是一個(gè)非常優(yōu)秀的格式化庫后來甚至被采納為C20標(biāo)準(zhǔn)的一部分。使用{fmt}能帶來更好的類型安全和性能。有兩種方式處理這個(gè)依賴方式一推薦功能完整單獨(dú)安裝{fmt}庫。這樣spdlog在編譯時(shí)會(huì)自動(dòng)鏈接它。git clone https://github.com/fmtlib/fmt.git cd fmt mkdir build cd build cmake .. -DCMAKE_POSITION_INDEPENDENT_CODEON # 生成位置無關(guān)代碼便于作為共享庫使用 make -j$(nproc) sudo make install方式二最簡在編譯spdlog時(shí)通過編譯選項(xiàng)-DSPDLOG_FMT_EXTERNALOFF來使用其內(nèi)嵌的fmt實(shí)現(xiàn)。這種方式更簡單但可能無法獲得fmt庫的最新特性和優(yōu)化。我推薦方式一因?yàn)閧fmt}庫本身也值得在C項(xiàng)目中使用單獨(dú)安裝能為未來其他用途留有余地。3. spdlog的獲取與編譯安裝準(zhǔn)備工作就緒現(xiàn)在可以開始處理主角spdlog了。同樣我們選擇從源碼編譯安裝以獲得最大的靈活性和控制權(quán)。3.1 獲取源碼直接從GitHub倉庫克隆是最方便的方式能獲取到最新的代碼和修復(fù)。cd ~ git clone https://github.com/gabime/spdlog.git cd spdlog如果想使用某個(gè)特定穩(wěn)定版本生產(chǎn)環(huán)境推薦可以在克隆后切換標(biāo)簽git checkout v1.11.0 # 切換到v1.11.0版本3.2 編譯與安裝決策頭文件庫 vs. 靜態(tài)/動(dòng)態(tài)庫這是關(guān)鍵的一步。Spdlog官方宣稱是“頭文件庫header-only”這意味著在絕大多數(shù)情況下你只需要把include/spdlog目錄復(fù)制到你的項(xiàng)目里包含頭文件就能用了無需編譯。這非常適合快速原型開發(fā)和小型項(xiàng)目。但是在嚴(yán)肅的生產(chǎn)環(huán)境中尤其是大型項(xiàng)目我強(qiáng)烈建議將其編譯為靜態(tài)庫或動(dòng)態(tài)庫。原因有三編譯速度頭文件庫會(huì)導(dǎo)致包含它的每一個(gè)編譯單元.cpp文件都重新編譯一遍spdlog的模板代碼顯著增加項(xiàng)目的整體編譯時(shí)間。二進(jìn)制大小模板代碼在每個(gè)編譯單元實(shí)例化可能導(dǎo)致最終可執(zhí)行文件體積膨脹。啟用異步日志spdlog強(qiáng)大且高效的異步日志功能需要編譯成庫才能使用。因此我們選擇編譯安裝。3.3 使用CMake構(gòu)建與安裝在spdlog源碼目錄下使用CMake進(jìn)行構(gòu)建mkdir build cd build接下來是CMake配置。這里有幾個(gè)關(guān)鍵選項(xiàng)需要根據(jù)你的需求決定cmake .. \ -DCMAKE_BUILD_TYPERelease \ # 構(gòu)建Release版本追求性能 -DSPDLOG_BUILD_EXAMPLEOFF \ # 不編譯示例節(jié)省時(shí)間 -DSPDLOG_BUILD_TESTSOFF \ # 不編譯測(cè)試 -DSPDLOG_BUILD_SHAREDON \ # 構(gòu)建動(dòng)態(tài)庫(.so)。設(shè)為OFF則構(gòu)建靜態(tài)庫(.a) -DSPDLOG_FMT_EXTERNALON # 使用系統(tǒng)安裝的外部fmt庫。如果沒裝fmt設(shè)為OFF執(zhí)行完cmake后檢查輸出信息確認(rèn)是否找到了fmt庫如果設(shè)置了ON以及將要構(gòu)建的目標(biāo)。然后開始編譯和安裝make -j$(nproc) sudo make installmake install命令會(huì)將編譯好的庫文件如libspdlog.so、必要的頭文件以及CMake配置文件安裝到系統(tǒng)默認(rèn)路徑通常是/usr/local/lib和/usr/local/include。3.4 驗(yàn)證安裝安裝完成后進(jìn)行快速驗(yàn)證檢查庫文件ls /usr/local/lib/libspdlog*應(yīng)該能看到libspdlog.so動(dòng)態(tài)庫或libspdlog.a靜態(tài)庫等文件。檢查頭文件ls /usr/local/include/spdlog/應(yīng)該能看到一系列.h頭文件。讓系統(tǒng)找到庫動(dòng)態(tài)庫安裝到/usr/local/lib后需要更新動(dòng)態(tài)鏈接器的緩存。sudo ldconfig4. 在C項(xiàng)目中集成spdlog庫裝好了接下來就是如何在你的CMake項(xiàng)目中愉快地使用它。這里我演示一個(gè)最清晰、最符合現(xiàn)代CMake實(shí)踐的方法。4.1 項(xiàng)目結(jié)構(gòu)示例假設(shè)你有如下項(xiàng)目結(jié)構(gòu)my_project/ ├── CMakeLists.txt ├── include/ │ └── my_utils.h ├── src/ │ ├── main.cpp │ └── my_utils.cpp └── third_party/ # 通常放第三方依賴這里spdlog已系統(tǒng)安裝所以不需要4.2 編寫CMakeLists.txt關(guān)鍵點(diǎn)在于使用find_package來定位我們安裝的spdlog。因?yàn)閟pdlog的安裝包提供了良好的CMake支持。cmake_minimum_required(VERSION 3.10) # 確保CMake版本足夠 project(MySpdlogProject LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) # spdlog需要C11 set(CMAKE_CXX_STANDARD_REQUIRED ON) # 最重要的步驟查找spdlog包。 # 它會(huì)設(shè)置spdlog::spdlog這個(gè)target我們直接鏈接即可。 find_package(spdlog REQUIRED CONFIG) # 使用CONFIG模式尋找spdlog提供的配置文件 # 添加你的可執(zhí)行文件 add_executable(my_app src/main.cpp src/my_utils.cpp) target_include_directories(my_app PRIVATE include) # 將spdlog鏈接到你的目標(biāo) target_link_libraries(my_app PRIVATE spdlog::spdlog) # 如果你編譯的是靜態(tài)庫且你的項(xiàng)目也是靜態(tài)庫可能需要傳遞一些編譯定義 # target_compile_definitions(my_app PRIVATE SPDLOG_COMPILED_LIB)find_package(spdlog CONFIG)會(huì)在標(biāo)準(zhǔn)路徑如/usr/local/lib/cmake/spdlog下尋找spdlogConfig.cmake文件這個(gè)文件正是make install時(shí)安裝的。它定義了spdlog::spdlog這個(gè)target包含了所有必要的頭文件路徑、鏈接庫和編譯定義。4.3 編寫示例代碼 (main.cpp)現(xiàn)在可以在代碼中使用spdlog了。#include iostream #include spdlog/spdlog.h #include spdlog/sinks/basic_file_sink.h // 文件sink #include spdlog/sinks/stdout_color_sinks.h // 彩色控制臺(tái)sink #include spdlog/async.h // 異步日志 void setup_logger() { try { // 1. 創(chuàng)建一個(gè)控制臺(tái)logger同步 auto console_sink std::make_sharedspdlog::sinks::stdout_color_sink_mt(); console_sink-set_level(spdlog::level::info); console_sink-set_pattern([%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%n] %v); // 2. 創(chuàng)建一個(gè)文件logger同步 auto file_sink std::make_sharedspdlog::sinks::basic_file_sink_mt(logs/my_app.log, true); file_sink-set_level(spdlog::level::trace); // 文件記錄更詳細(xì)的trace級(jí)別 file_sink-set_pattern([%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%s:%#] %v); // 3. 將多個(gè)sink組合成一個(gè)logger std::vectorspdlog::sink_ptr sinks {console_sink, file_sink}; auto combined_logger std::make_sharedspdlog::logger(main, sinks.begin(), sinks.end()); combined_logger-set_level(spdlog::level::debug); // logger的全局級(jí)別 // 4. 注冊(cè)為全局logger方便在任何地方使用spdlog::info()等 spdlog::register_logger(combined_logger); spdlog::set_default_logger(combined_logger); } catch (const spdlog::spdlog_ex ex) { std::cerr Log initialization failed: ex.what() std::endl; throw; } } int main() { // 初始化日志系統(tǒng) setup_logger(); // 使用默認(rèn)logger記錄日志 spdlog::info(Welcome to spdlog version {}.{}.{}!, SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH); spdlog::warn(This is a warning message.); spdlog::error(This is an error message with arg: {}, 42); // 也可以獲取特定的logger進(jìn)行記錄 if(auto logger spdlog::get(main)) { logger-debug(This is a debug message from main logger.); logger-trace(This trace message wont appear on console due to level filter.); } // 刷新所有日志確保異步日志隊(duì)列被清空對(duì)于文件輸出很重要 spdlog::shutdown(); return 0; }4.4 編譯與運(yùn)行項(xiàng)目在你的項(xiàng)目build目錄中cmake .. make ./my_app你將在終端看到彩色的日志輸出同時(shí)在項(xiàng)目根目錄下的logs/my_app.log文件中找到更詳細(xì)的日志記錄。5. 高級(jí)配置與性能調(diào)優(yōu)基礎(chǔ)集成只是開始要讓spdlog在生產(chǎn)環(huán)境中真正發(fā)揮威力必須進(jìn)行配置和調(diào)優(yōu)。5.1 異步日志Async Logging配置異步日志是spdlog提升性能的利器。它將日志消息放入一個(gè)隊(duì)列由后臺(tái)線程負(fù)責(zé)寫入主線程無需等待I/O操作。#include spdlog/async.h #include spdlog/sinks/rotating_file_sink.h void setup_async_logger() { // 設(shè)置異步日志的參數(shù)隊(duì)列大小、后臺(tái)線程數(shù)、刷新策略 spdlog::init_thread_pool(8192, 1); // 隊(duì)列可存8192條消息使用1個(gè)后臺(tái)線程 auto async_file_sink std::make_sharedspdlog::sinks::rotating_file_sink_mt(logs/async_app.log, 1024*1024*10, 3); // 10MB一個(gè)文件保留3個(gè) auto async_logger std::make_sharedspdlog::async_logger(async_logger, async_file_sink, spdlog::thread_pool(), spdlog::async_overflow_policy::block); async_logger-set_level(spdlog::level::info); spdlog::set_default_logger(async_logger); }關(guān)鍵參數(shù)解析spdlog::init_thread_pool(queue_size, thread_count)初始化全局線程池。queue_size需要根據(jù)日志流量設(shè)置太小會(huì)導(dǎo)致生產(chǎn)者主線程阻塞太大消耗內(nèi)存。單線程通常足夠處理文件I/O。spdlog::async_overflow_policy::block當(dāng)隊(duì)列滿時(shí)主線程阻塞等待。這是最安全的選擇避免丟日志。另一個(gè)選項(xiàng)是overrun_oldest丟棄最老的日志適用于對(duì)日志完整性要求不高的場(chǎng)景。實(shí)操心得在高壓力的服務(wù)中我曾將queue_size設(shè)為32768并使用block策略。雖然極端情況下可能引起短暫阻塞但保證了在流量洪峰時(shí)所有關(guān)鍵錯(cuò)誤日志都能被記錄下來這對(duì)于事后排查至關(guān)重要。永遠(yuǎn)不要在生產(chǎn)環(huán)境使用overrun_oldest除非你完全清楚其后果。5.2 日志輪轉(zhuǎn)Rotating與文件管理日志文件不能無限增長。spdlog提供了rotating_file_sink和daily_file_sink。// 按大小輪轉(zhuǎn)每個(gè)文件最大5MB保留最近5個(gè)文件 auto rotating_sink std::make_sharedspdlog::sinks::rotating_file_sink_mt(logs/rotating.log, 1024*1024*5, 5); // 按天輪轉(zhuǎn)每天凌晨創(chuàng)建新文件保留最近30天的文件需手動(dòng)或腳本清理舊文件 auto daily_sink std::make_sharedspdlog::sinks::daily_file_sink_mt(logs/daily.log, 0, 0); // 第二個(gè)參數(shù)是旋轉(zhuǎn)時(shí)間小時(shí)第三個(gè)是最大文件數(shù)0為無限文件清理策略rotating_file_sink的保留文件數(shù)參數(shù)只控制它自己創(chuàng)建的文件。更通用的做法是結(jié)合Linux的logrotate工具或編寫簡單的cron定時(shí)任務(wù)按日期或大小歸檔和清理日志目錄。5.3 日志格式模式Pattern定制spdlog的格式字符串非常強(qiáng)大。上述例子中的[%Y-%m-%d %H:%M:%S.%e]是時(shí)間%^%l%$是帶顏色的級(jí)別%n是logger名%v是消息本身。幾個(gè)有用的格式標(biāo)志%s源文件名。%#行號(hào)。%!函數(shù)名。%t線程ID。%P進(jìn)程ID。在調(diào)試階段將%s:%# %!加入格式非常有用。但在生產(chǎn)環(huán)境考慮到性能獲取這些信息有開銷和隱私通常只保留時(shí)間、級(jí)別和消息。5.4 日志級(jí)別動(dòng)態(tài)調(diào)整一個(gè)優(yōu)秀的日志系統(tǒng)應(yīng)該支持運(yùn)行時(shí)動(dòng)態(tài)調(diào)整級(jí)別以便在不重啟服務(wù)的情況下排查問題。spdlog本身不直接提供HTTP接口但我們可以通過信號(hào)、簡單的TCP服務(wù)或修改配置文件并發(fā)送信號(hào)觸發(fā)重載來實(shí)現(xiàn)。一個(gè)簡單的思路是將logger的level設(shè)置為一個(gè)原子變量并提供一個(gè)接口如通過信號(hào)處理函數(shù)signal_handler來修改它。雖然spdlog的logger對(duì)象沒有直接提供線程安全的set_level原子操作但我們可以通過替換整個(gè)logger或sink的方式來實(shí)現(xiàn)類似效果不過這相對(duì)復(fù)雜。更常見的生產(chǎn)實(shí)踐是將日志級(jí)別配置放在外部文件如JSON主循環(huán)定期檢查文件修改時(shí)間如果變化則重新加載配置并調(diào)用logger-set_level(new_level)。注意在多sink的logger上設(shè)置級(jí)別是線程安全的但在極高并發(fā)下極短時(shí)間內(nèi)可能產(chǎn)生級(jí)別不一致的日志這點(diǎn)需要知曉。6. 常見問題排查與性能壓測(cè)即使按照步驟操作也可能會(huì)遇到問題。這里列出一些典型場(chǎng)景和解決方法。6.1 編譯與鏈接問題問題現(xiàn)象可能原因解決方案fatal error: spdlog/spdlog.h: No such file or directory編譯器找不到頭文件。1. 確認(rèn)find_package(spdlog)成功。2. 確認(rèn)CMake的target_link_libraries正確鏈接了spdlog::spdlog。3. 手動(dòng)添加包含目錄target_include_directories(my_app PRIVATE /usr/local/include)。undefined reference tospdlog::...鏈接器找不到庫文件。1. 確認(rèn)編譯安裝的是庫非頭文件模式。2. 確認(rèn)find_package找到了庫。3. 如果是動(dòng)態(tài)庫運(yùn)行sudo ldconfig更新緩存并檢查/usr/local/lib是否在/etc/ld.so.conf或LD_LIBRARY_PATH中。CMake報(bào)錯(cuò)Could not find a package configuration file...CMake找不到spdlog的配置文件。1. 確認(rèn)安裝時(shí)執(zhí)行了sudo make install。2. 檢查/usr/local/lib/cmake/spdlog/或/usr/lib/cmake/spdlog/目錄是否存在spdlogConfig.cmake。3. 在CMake中通過-DCMAKE_PREFIX_PATH/path/to/install指定安裝路徑。6.2 運(yùn)行時(shí)問題問題現(xiàn)象可能原因解決方案日志文件沒有生成或沒有內(nèi)容1. 目錄權(quán)限不足。2. sink的日志級(jí)別設(shè)置過高過濾了消息。3. 程序異常退出日志未刷新。1. 檢查logs/目錄是否存在且進(jìn)程有寫權(quán)限。2. 檢查logger和sink的set_level確保其低于或等于你打印日志的級(jí)別。3. 在main函數(shù)返回前或信號(hào)處理中調(diào)用spdlog::shutdown()或logger-flush()。異步日志丟失1. 隊(duì)列溢出策略為overrun_oldest。2. 程序崩潰隊(duì)列中未寫入磁盤的日志丟失。1. 使用block策略并適當(dāng)增大隊(duì)列大小。2. 對(duì)于關(guān)鍵日志如ERROR考慮使用同步sink或立即刷新spdlog::default_logger()-flush()。性能瓶頸1. 格式模式太復(fù)雜如包含%s、%!。2. 同步文件寫入I/O慢。3. 日志級(jí)別設(shè)置過低如TRACE產(chǎn)生海量日志。1. 生產(chǎn)環(huán)境簡化格式。2. 啟用異步日志。3. 合理設(shè)置日志級(jí)別通常INFO或WARN即可。使用動(dòng)態(tài)級(jí)別在需要時(shí)開啟DEBUG。6.3 簡易性能壓測(cè)與對(duì)比為了直觀感受同步/異步、不同格式的性能差異可以寫一個(gè)簡單的壓測(cè)程序#include chrono #include spdlog/spdlog.h #include spdlog/async.h #include spdlog/sinks/basic_file_sink.h void bench_logging(const std::string logger_name, int msg_count) { auto start std::chrono::steady_clock::now(); auto logger spdlog::get(logger_name); if(!logger) return; for(int i 0; i msg_count; i) { logger-info(Benchmark message #{:06d}: This is a test log message of moderate length., i); } logger-flush(); // 確保所有日志寫入 auto end std::chrono::steady_clock::now(); auto duration std::chrono::duration_caststd::chrono::milliseconds(end - start).count(); spdlog::info(Logger {} wrote {} messages in {} ms, {:.2f} msg/ms, logger_name, msg_count, duration, msg_count / (duration 1e-9)); } int main() { // 創(chuàng)建同步文件logger auto sync_logger spdlog::basic_logger_mt(sync_logger, logs/sync_bench.log); sync_logger-set_pattern(%v); // 最簡單的格式只記錄消息本身 // 創(chuàng)建異步文件logger spdlog::init_thread_pool(10000, 2); auto async_file_sink std::make_sharedspdlog::sinks::basic_file_sink_mt(logs/async_bench.log); auto async_logger std::make_sharedspdlog::async_logger(async_logger, async_file_sink, spdlog::thread_pool()); async_logger-set_pattern(%v); spdlog::register_logger(async_logger); const int msg_count 100000; spdlog::info(Starting benchmark...); bench_logging(sync_logger, msg_count); bench_logging(async_logger, msg_count); spdlog::shutdown(); return 0; }在我的測(cè)試機(jī)CentOS 7虛擬機(jī)機(jī)械硬盤上寫入10萬條簡單格式的日志異步模式相比同步模式有數(shù)十倍的性能提升。當(dāng)格式復(fù)雜加入時(shí)間、文件名、行號(hào)時(shí)同步模式的性能下降更為明顯。這個(gè)測(cè)試充分證明了在生產(chǎn)環(huán)境中使用異步日志和簡化格式的必要性。7. 生產(chǎn)環(huán)境部署建議與總結(jié)將spdlog集成為項(xiàng)目的一部分后在部署到生產(chǎn)服務(wù)器時(shí)還需要考慮以下幾點(diǎn)依賴打包如果你的應(yīng)用編譯成靜態(tài)二進(jìn)制并且spdlog也是靜態(tài)鏈接那么部署會(huì)非常簡單只需要一個(gè)可執(zhí)行文件。如果是動(dòng)態(tài)鏈接則需要確保目標(biāo)服務(wù)器上存在相同或兼容版本的libspdlog.so和libfmt.so。通常建議在構(gòu)建服務(wù)器與生產(chǎn)環(huán)境操作系統(tǒng)一致上編譯并將動(dòng)態(tài)庫隨應(yīng)用一起分發(fā)。日志目錄規(guī)劃不要將日志寫在當(dāng)前目錄或/tmp下。應(yīng)建立專門的日志目錄如/var/log/myapp/并確保運(yùn)行進(jìn)程的用戶如myapp對(duì)該目錄有寫權(quán)限。同時(shí)在/etc/logrotate.d/下配置logrotate規(guī)則定期輪轉(zhuǎn)和壓縮日志文件防止磁盤被撐滿。日志級(jí)別管理通過環(huán)境變量或配置文件來控制日志級(jí)別。例如設(shè)置環(huán)境變量MYAPP_LOG_LEVELINFO在程序啟動(dòng)時(shí)讀取并設(shè)置對(duì)應(yīng)的spdlog::level::level_enum。這樣運(yùn)維人員可以在不修改代碼和重啟服務(wù)的情況下動(dòng)態(tài)調(diào)整日志詳細(xì)程度。異常安全在main函數(shù)開頭就初始化日志確保在程序啟動(dòng)的任何階段異常都能被記錄下來。使用try-catch塊包裹main函數(shù)在catch中記錄致命錯(cuò)誤?;剡^頭看在CentOS上搭建spdlog環(huán)境從安裝依賴、編譯庫到項(xiàng)目集成每一步都需要清晰的思路和對(duì)細(xì)節(jié)的把控。它不僅僅是一個(gè)“安裝”動(dòng)作更是一次對(duì)項(xiàng)目日志架構(gòu)的審視和規(guī)劃。選擇編譯為庫而非頭文件模式啟用異步日志精心設(shè)計(jì)格式和輪轉(zhuǎn)策略這些決策共同構(gòu)成了一個(gè)健壯、高效、可維護(hù)的日志子系統(tǒng)的基礎(chǔ)。希望這份詳細(xì)的記錄能幫助你順利搭建起自己的C日志體系讓程序運(yùn)行時(shí)的每一個(gè)狀態(tài)變化都清晰可見有跡可循。

相關(guān)新聞

AI論文寫作工具:智能文獻(xiàn)梳理與學(xué)術(shù)規(guī)范優(yōu)化

AI論文寫作工具:智能文獻(xiàn)梳理與學(xué)術(shù)規(guī)范優(yōu)化

1. 項(xiàng)目概述:AI論文寫作工具的崛起與痛點(diǎn)解決 去年幫導(dǎo)師審閱研究生論文時(shí),有個(gè)現(xiàn)象讓我印象深刻:超過60%的初稿存在文獻(xiàn)綜述結(jié)構(gòu)混亂、理論框架單薄的問題。這正是"千筆"這類專業(yè)論文工具要解決的核心痛點(diǎn)——不是簡單地替代寫作&…

2026/7/29 11:26:26 閱讀更多
DooTask輕量化AI協(xié)同工具的開發(fā)實(shí)戰(zhàn)與架構(gòu)解析

DooTask輕量化AI協(xié)同工具的開發(fā)實(shí)戰(zhàn)與架構(gòu)解析

1. 項(xiàng)目概述:當(dāng)開發(fā)團(tuán)隊(duì)遇上協(xié)同困局 2026年的開工季,開發(fā)團(tuán)隊(duì)面臨的協(xié)同挑戰(zhàn)比以往任何時(shí)候都更加復(fù)雜。隨著遠(yuǎn)程辦公的普及和項(xiàng)目規(guī)模的擴(kuò)大,傳統(tǒng)的項(xiàng)目管理工具已經(jīng)難以滿足現(xiàn)代開發(fā)團(tuán)隊(duì)的需求。我最近在帶領(lǐng)一個(gè)跨地域的敏捷團(tuán)隊(duì)時(shí)&#…

2026/7/29 11:26:26 閱讀更多
TI BOOSTXL-TLV8544PIR評(píng)估板:納安級(jí)運(yùn)放實(shí)現(xiàn)超低功耗PIR傳感器AFE設(shè)計(jì)

TI BOOSTXL-TLV8544PIR評(píng)估板:納安級(jí)運(yùn)放實(shí)現(xiàn)超低功耗PIR傳感器AFE設(shè)計(jì)

1. 項(xiàng)目概述與核心價(jià)值如果你正在設(shè)計(jì)一款需要長時(shí)間待機(jī)、靠電池供電的物聯(lián)網(wǎng)傳感器節(jié)點(diǎn),比如智能家居里的人體存在檢測(cè)、安防報(bào)警器,或者工業(yè)環(huán)境中的無線振動(dòng)監(jiān)測(cè)設(shè)備,那么功耗一定是懸在你頭頂?shù)倪_(dá)摩克利斯之劍。傳感器本身可能很省電&am…

2026/7/29 11:26:26 閱讀更多
BBWEYY · 教培增長解決方案,財(cái)會(huì)考證培訓(xùn)機(jī)構(gòu)GEO獲客與小程序轉(zhuǎn)化一體化策劃案,含零代碼SAAS、AI編程、源碼定制交付

BBWEYY · 教培增長解決方案,財(cái)會(huì)考證培訓(xùn)機(jī)構(gòu)GEO獲客與小程序轉(zhuǎn)化一體化策劃案,含零代碼SAAS、AI編程、源碼定制交付

BBWEYY 教培增長解決方案 財(cái)會(huì)考證培訓(xùn)機(jī)構(gòu)GEO獲客與小程序 轉(zhuǎn)化一體化策劃案 從“被AI推薦”到“查詢報(bào)考條件或領(lǐng)取備考方案”的完整招生轉(zhuǎn)化閉環(huán) 項(xiàng)目定位 適用對(duì)象 方案版本 GEO獲客與招生轉(zhuǎn)化 財(cái)會(huì)考證培訓(xùn)機(jī)構(gòu) 策劃方案 V1.0|2026年7月 核心判斷 財(cái)會(huì)…

2026/7/29 12:36:28 閱讀更多
ssm 童裝銷售管理系統(tǒng)

ssm 童裝銷售管理系統(tǒng)

一、關(guān)鍵詞童裝銷售管理系統(tǒng)、童裝銷售、童裝銷售訂單管理、童裝銷售在線交易二、作品包含源碼數(shù)據(jù)庫萬字設(shè)計(jì)文檔PPT全套環(huán)境和工具資源本地部署教程三、項(xiàng)目技術(shù)前端技術(shù): Html、Css、Js、Vue2.6、Element-ui后端技術(shù):Java、SSM(Spring 5.0…

2026/7/29 12:26:27 閱讀更多
面試官大笑:“一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,不比 1 個(gè)快 5 倍?“我搖頭:“快不了,還可能更慢“

面試官大笑:“一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,不比 1 個(gè)快 5 倍?“我搖頭:“快不了,還可能更慢“

前兩個(gè)月,我在重構(gòu) AlgoMooc 網(wǎng)站過程中,發(fā)現(xiàn)一個(gè)問題:在 Claude Code 里把一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,結(jié)果可能比 1 個(gè) agent 從頭干到尾還慢? 大多數(shù)人的第一反應(yīng)是反過來的:活是并行干的&#…

2026/7/29 0:15:24 閱讀更多
# 鴻蒙 HarmonyOS 應(yīng)用開發(fā)實(shí)戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號(hào)與動(dòng)畫渲染精講

# 鴻蒙 HarmonyOS 應(yīng)用開發(fā)實(shí)戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號(hào)與動(dòng)畫渲染精講

一、應(yīng)用概述 骰子(Dice Roller) 是一款經(jīng)典的休閑娛樂應(yīng)用,模擬了真實(shí)擲骰子的過程。應(yīng)用投擲兩個(gè)骰子(六面標(biāo)準(zhǔn)骰),使用 Unicode 骰面符號(hào)直觀展示每個(gè)骰子的點(diǎn)數(shù),并伴有快速滾動(dòng)的動(dòng)畫效果。…

2026/7/29 0:15:24 閱讀更多