議到rsyslog實戰(zhàn)避坑)
簡介面向Linux/Unix系統(tǒng)開發(fā)者的syslog編程學(xué)習(xí)包聚焦系統(tǒng)日志的采集、過濾與上報場景適合正在學(xué)習(xí)系統(tǒng)編程或需要為應(yīng)用集成日志功能的工程師。壓縮包內(nèi)僅有2個文件分別是C源文件與配套頭文件整體容量僅5KB屬于輕量級示例代碼便于直接閱讀和修改。目前已有270人學(xué)習(xí)下載可作為快速上手syslog接口的參考。借助syslog.h中的函數(shù)聲明與syslog.c的實現(xiàn)示例可掌握openlog、syslog、closelog等核心調(diào)用的參數(shù)配置與優(yōu)先級設(shè)置并理解如何將自定義消息按不同級別寫入系統(tǒng)日志。同時資源也展示了與syslogd協(xié)同工作的基本模式有助于開發(fā)者根據(jù)實際需求擴展日志處理邏輯提升應(yīng)用的健壯性與可維護性。1. syslog.rar_Linux/Unix編程_Unix_Linux_ 里裝的到底是什么先說結(jié)論再動手如果你在一個項目交接包里見到 syslog.rar 這個名字大概率不是病毒而是一份 Linux/Unix 編程的 syslog 示例源碼或講課工程。它要解決的是個很樸素卻容易翻車的問題程序里的日志怎么才能統(tǒng)一送進系統(tǒng)日志體系而不是散在各自的 stdout 文件里。syslog 在 Unix 時代就是標(biāo)準(zhǔn)通道到今天 rsyslog、systemd-journal 和各類集中日志平臺都還在兼容它。下面我從一個壓縮包怎么解壓、怎么編譯講起把 syslog 的協(xié)議、代碼、參數(shù)和坑一次說清。適合正在寫 C 服務(wù)、嵌入式 Linux 程序或者要搭日志采集的讀者。2. syslog 協(xié)議與編程選型先把“日志發(fā)到哪里”這件事拆清楚2.1 三種接入方式libc 封裝、UDP socket 與 Unix domain socket寫 Linux/Unix 日志程序第一件事不是寫代碼而是選入口。常見做法是三種很多新手只會用第一種等日志要跨機器時就卡住了。接入方式目標(biāo)可靠性典型延遲適用場景syslog() / openlog()本地 /dev/log本地緩存滿了會丟微秒級單機調(diào)試、常規(guī)服務(wù)日志UDP socket遠端 rsyslog 的 514/5514盡力而為丟包不重傳網(wǎng)絡(luò) RTT 量級跨機器集中采集Unix domain socket/var/run/log、/dev/log本機可靠但緩沖區(qū)有限微秒級高性能本地日志、代理轉(zhuǎn)發(fā)TCP/TLS socket遠端 6514 等可靠但可能頭阻塞略高審計、支付等高價值日志syslog() 是 glibc 對 /dev/log 的封裝開發(fā)者不用管 socket 生命周期調(diào)用 openlog 設(shè)置程序名然后 syslog() 一條條發(fā)。缺點是它只往本機送程序要跨機器上報日志還是得自己建 socket。嵌入式環(huán)境如果用的是 musl、uClibcsyslog() 也存在但有些裁剪版 libc 可能不帶這時直接 socket 反而是最穩(wěn)的。直接 socket 發(fā) UDP 不復(fù)雜但要注意默認系統(tǒng)日志服務(wù)只監(jiān)聽 /dev/log并不監(jiān)聽 514 端口。你往內(nèi)網(wǎng)某臺機器的 514 塞數(shù)據(jù)對方?jīng)]開 imudp 模塊就靜默丟棄TCP 會立刻被 resetUDP 連報錯都沒有這是后面排障時最典型的黑匣子。2.2 facility 與 severity消息為什么會“消失”在系統(tǒng)側(cè)syslog 消息的第一部分叫 PRI由 facility 乘以 8 加 severity 得到。facility 決定消息歸屬哪類子系統(tǒng)severity 決定緊急程度。C 代碼里你寫LOG_USER|LOG_INFOfinally 到達 rsyslog 時規(guī)則匹配的就是這兩個維度。facility 值關(guān)鍵字常見用途0LOG_KERN內(nèi)核日志用戶態(tài)不要用1LOG_USER用戶進程默認歸屬5LOG_AUTH認證安全相關(guān)12LOG_NTP時間同步服務(wù)16~23LOG_LOCAL0~LOCAL7留給應(yīng)用自定義severity 從 0 到 7 分別是 EMERG、ALERT、CRIT、ERR、WARNING、NOTICE、INFO、DEBUG。這個順序很關(guān)鍵rsyslog 的*.info表示“info 及以上”如果規(guī)則寫user.*user 下所有級別都收如果只寫user.infodebug 和 notice 的處理方式就完全取決于下一條規(guī)則可能被丟。很多人以為 syslog() 返回 0 就等于日志落盤了其實它只代表消息進了內(nèi)核 socket 緩沖區(qū)。之后 rsyslog 按配置決定寫哪個文件、轉(zhuǎn)給誰或者直接丟棄。我在項目里見過最隱蔽的一次程序里打LOG_LOCAL0|LOG_INFO系統(tǒng)默認規(guī)則把 LOCAL0 定向到一個沒建好的目錄日志全被 rsyslog 靜默吞掉應(yīng)用側(cè)完全無感知。2.3 RFC3164 與 RFC5424日志頭里藏著年份和時區(qū)的坑老式 syslog 消息長這樣PRIOct 11 22:14:15 hostname tag[pid]: message這是 RFC3164 格式。問題在于沒有年份、沒有時區(qū)??缒昱耪蠒r日志一多你分不清去年十月和今年十月跨時區(qū)轉(zhuǎn)發(fā)接收端拿到的“22:14:15”到底按哪邊解釋全看接收端配置。你要做日志審計或者對接集中平臺我建議直接用 RFC5424 頭1651 2025-10-11T22:14:15.123Z myhost myapp 1234 - - message它帶版本號、ISO8601 時間戳、帶時區(qū)還有結(jié)構(gòu)化數(shù)據(jù)段。代價是很多老系統(tǒng)日志分析器不認但現(xiàn)代 rsyslog、journald、ELK 都兼容。自己寫發(fā)送端時別偷懶用 RFC3164除非對方平臺明確只支持老格式。2.4 系統(tǒng)日志服務(wù)rsyslog、syslog-ng 與 busybox syslogd 的配合方式應(yīng)用層 syslog() 只負責(zé)把消息丟給本機日志守護進程真正落盤、轉(zhuǎn)發(fā)、過濾的是那三個服務(wù)。Debian/Ubuntu 默認 rsyslogRHEL 早期常帶 syslog-ng嵌入式設(shè)備多半是 busybox syslogd。它們的協(xié)議一致但配置語言完全不同。rsyslog 在 Debian 系只監(jiān)聽 /dev/log不監(jiān)聽網(wǎng)絡(luò)端口這是近二十年安全默認。要收遠端 UDP 日志得在配置里顯式打開# /etc/rsyslog.d/remote.conf module(loadimudp) input(typeimudp port5514)其中imudp是 rsyslog 的 UDP 輸入模塊port是監(jiān)聽端口。注意 Linux 上進程綁定小于 1024 的端口需要 root 權(quán)限或文件 capabilities測試階段用 5514 這類高位端口不要一上來就 sudo 跑程序。改成 514 后重啟systemctl restart rsyslog用ss -lunp | grep 514確認監(jiān)聽成功這是最直接的驗證。syslog-ng 的對應(yīng)配置則是source s_net { udp(ip(0.0.0.0) port(5514)); };語法差很遠。busybox 更簡單syslogd -R 192.168.1.10:5514就能把日志轉(zhuǎn)發(fā)出去但基本沒有規(guī)則過濾能力。3. 把 syslog.rar 里的工程跑起來Linux 下最小 C 收發(fā) Demo 與三處參數(shù)3.1 解包與工具鏈準(zhǔn)備解壓 rar 常用的幾個 linux 命令拿到 syslog.rar先別急著改代碼把環(huán)境檢查做干凈否則后面查問題會讓你懷疑人生。解壓這一步在坑里排第一因為 Windows 壓縮包常帶 CRLF 換行符和中文文件名。# 1. 查看壓縮包內(nèi)容 unrar l syslog.rar # 2. 完整解壓 unrar x syslog.rar # 3. 如果發(fā)行版沒帶 unrar裝 unar 也能解 # sudo apt install unar # unar syslog.rar # 4. 看工程結(jié)構(gòu) ls -l syslog/ # 5. 確認編譯器 gcc --version make --versionunrar l只列出內(nèi)容不釋放文件適合先看包內(nèi)目錄是否混亂。unrar x解壓時保留完整路徑e則把所有文件攤到當(dāng)前目錄千萬別用錯。解壓后第一件事是看文件行尾file src/*.c cat -v src/syslog_sender.c | head -5如果看到大量^M這樣的字符說明文件是 CRLF 行尾gcc 能編過但字符串字面量如果跨行或者消息拼接處帶\r日志解析會出怪問題。批量處理# 把當(dāng)前工程下所有 .c/.h 的 Windows 行尾轉(zhuǎn)成 Unix find syslog -name *.c -o -name *.h | xargs sed -i s/\r$//這步做完再編譯。很多 Unix 上編譯失敗其實不是代碼問題是行尾和 Makefile 的 Tab 字符被改壞Makefile 要求命令行以 Tab 開頭Windows 編輯器常把 Tab 轉(zhuǎn)成空格。3.2 發(fā)送端syslog() 一行接入的最小 C 代碼最簡發(fā)送端其實不長核心是 openlog 的三個參數(shù)。我一般會在每個工程里單獨寫一個 log.c避免業(yè)務(wù)代碼到處裸調(diào) syslog。#define _GNU_SOURCE #include syslog.h #include stdio.h #include string.h #include errno.h int main(void) { /* ident 是程序名LOG_PID 讓 tag 帶上進程號 */ openlog(myapp, LOG_PID | LOG_CONS | LOG_NDELAY, LOG_USER); syslog(LOG_INFO, hello syslog, seq%d, 1); syslog(LOG_ERR, something failed: %s, strerror(ENOENT)); closelog(); return 0; }編譯運行g(shù)cc -Wall -O2 -o sender sender.c ./senderopenlog 的第二個參數(shù)里L(fēng)OG_PID會把進程號打印成myapp[1234]:多實例部署時靠它區(qū)分是哪個進程。LOG_CONS表示本地 /dev/log 通路失敗時把消息直接寫到控制臺調(diào)試期有用生產(chǎn)上開了可能干擾終端交互。LOG_NDELAY讓 openlog 立刻連接 /dev/log否則第一次 syslog() 時才會連接那一次的延遲會略高。第三個參數(shù)是默認 facility。注意 syslog() 的第一個參數(shù)既包含 facility 又包含 severity你可以寫syslog(LOG_DAEMON|LOG_ERR, ...)臨時覆蓋 openlog 里設(shè)定的 facility但一般建議固定一個 facility方便 rsyslog 規(guī)則歸類。3.3 接收端能跑在用戶態(tài)的 UDP 最小 C 實現(xiàn)把工程里的接收端核心邏輯抽出來一個最小 UDP receiver 就這么長#include stdio.h #include string.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include unistd.h #define PORT 5514 int main(void) { int fd socket(AF_INET, SOCK_DGRAM, 0); struct sockaddr_in addr; memset(addr, 0, sizeof(addr)); addr.sin_family AF_INET; addr.sin_addr.s_addr htonl(INADDR_ANY); addr.sin_port htons(PORT); if (bind(fd, (struct sockaddr *)addr, sizeof(addr)) 0) { perror(bind); return 1; } char buf[4096]; /* 一次 recvfrom 只收一條 UDP 數(shù)據(jù)報 */ int n recvfrom(fd, buf, sizeof(buf) - 1, 0, NULL, NULL); if (n 0) { buf[n] \0; printf(%s\n, buf); } close(fd); return 0; }這里的SOCK_DGRAM對應(yīng) UDP收到的是完整數(shù)據(jù)報如果改成SOCK_STREAM就是 TCPrecvfrom 要換成 recv。bind 到INADDR_ANY表示監(jiān)聽所有網(wǎng)卡地址生產(chǎn)環(huán)境建議 bind 到業(yè)務(wù)內(nèi)網(wǎng) IP。端口用 5514 而不是 514原因是 1024 以下端口綁定需要特權(quán)測試階段沒必要往 sudo 火坑里跳。bind 失敗最常見兩個原因端口被占用可以用ss -lunp | grep 5514查另一個是權(quán)限換成 5514 基本就能避開。別在生產(chǎn)環(huán)境為了綁定 514 給整個程序 setcap那是給日志接收服務(wù)權(quán)限過大風(fēng)險不值。驗證一下./receiver printf 14Oct 11 22:14:15 myapp[1]: test\n | nc -u -w1 127.0.0.1 5514如果終端打印出消息說明網(wǎng)絡(luò)通路通了接下來可以接系統(tǒng)的 rsyslog。3.4 從自定義收端換回 rsyslog把消息真正落到系統(tǒng)日志最小 receiver 只是給你驗證協(xié)議格式真正干活的是 rsyslog。改配置# /etc/rsyslog.d/remote.conf module(loadimudp) input(typeimudp port5514)重啟并確認監(jiān)聽systemctl restart rsyslog ss -lunp | grep 5514 logger -n 127.0.0.1 -P 5514 remote logger test然后另一個終端看日志tail -f /var/log/syslog看到 “remote logger test” 說明全鏈路通了。這里有三處參數(shù)要盯住imudp模塊有沒有加載成功rsyslog 日志里會有模塊加載報錯port和發(fā)送端目標(biāo)端口必須一致ruleset 如果沒有單獨指定默認規(guī)則決定消息落哪個文件。很多系統(tǒng)里/var/log/messages和/var/log/syslog兩個文件的規(guī)則不一樣別只盯一個文件。注意 rsyslog 的 legacy 語法在老版本配置里很常見$ModLoad imudp、$UDPServerRun 514。如果接手的項目配置還是這種寫法改的時候要保留原樣不能混用新舊語法兩套rsyslog 對混用會給出警告但行為可能不符合預(yù)期。4. 從 Demo 到可用日志服務(wù)多線程、隊列與 UDP 丟包的取舍4.1 多線程程序里的 syslog線程安全不等于不丟不亂glibc 的 syslog() 內(nèi)部有鎖多線程調(diào)用不會崩但別高興太早。日志風(fēng)暴時全局鎖會讓所有業(yè)務(wù)線程排隊等日志線程業(yè)務(wù)延遲突然飆升更隱蔽的是syslog() 只記錄進程號多個線程寫日志你分不清消息來自哪個線程。常見做法是包一層#include pthread.h #include syslog.h #include stdarg.h #include stdio.h static pthread_mutex_t log_lock PTHREAD_MUTEX_INITIALIZER; void app_log(int prio, const char *fmt, ...) { char buf[512]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); pthread_mutex_lock(log_lock); syslog(prio, %s, buf); pthread_mutex_unlock(log_lock); }這個封裝把線程號帶進消息里比如調(diào)用前拼一個pthread_self()進去同時用鎖保證一行消息不會被其他線程截斷。但鎖本身是性能瓶頸所以更靠譜的結(jié)構(gòu)是隊列。4.2 隊列設(shè)計業(yè)務(wù)線程不阻塞日志線程慢慢發(fā)生產(chǎn)級日志庫的思路通常是業(yè)務(wù)線程把日志塞進一個有界環(huán)形隊列專門的日志線程批量發(fā)送。隊列滿就丟丟之前計數(shù)。我常用下面這個結(jié)構(gòu)#define LOG_QUEUE_SIZE 8192 #define LOG_MSG_MAX 256 struct log_msg { char data[LOG_MSG_MAX]; int len; }; static struct log_msg ring[LOG_QUEUE_SIZE]; static _Atomic unsigned int head, tail; static _Atomic unsigned long dropped; int log_enqueue(const char *data, int len) { unsigned int h atomic_load_explicit(head, memory_order_relaxed); unsigned int t atomic_load_explicit(tail, memory_order_relaxed); if (len LOG_MSG_MAX || h - t LOG_QUEUE_SIZE) { atomic_fetch_add_explicit(dropped, 1, memory_order_relaxed); return -1; } struct log_msg *slot ring[h (LOG_QUEUE_SIZE - 1)]; memcpy(slot-data, data, len); slot-len len; atomic_store_explicit(head, h 1, memory_order_relaxed); return 0; }隊列參數(shù)有兩個隊列長度 8192 條、單條消息上限 256 字節(jié)算下來約 2MB 內(nèi)存對服務(wù)器和嵌入式主控都算可控。隊列滿時返回 -1上層可以選擇忽略也可以臨時降級成把消息寫到本地文件。注意這個無鎖隊列是單生產(chǎn)者多消費者才會真正無鎖多線程并發(fā)寫還是需要原子 CAS 或者直接退化成互斥鎖別把注釋里的簡化當(dāng)真。日志線程從 tail 取消息再用 UDP socket 批量發(fā)送。批量發(fā)送的意思是攢 10 條或 10 毫秒發(fā)一次減少系統(tǒng)調(diào)用。日志不能因為發(fā)送超時反過來把業(yè)務(wù)卡死所以 send 要用非阻塞模式int flags fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK);這樣 socket 緩沖區(qū)滿時 send 返回 EAGAIN日志線程可以把這批消息繼續(xù)留在隊列里或者丟棄。沒有這個設(shè)置UDP 在極端情況下也會把進程拖住。4.3 UDP 丟包是常態(tài)用序列號打出第一個“空洞”UDP 514 傳日志默認就是丟。網(wǎng)上很多集中日志方案都遇到過機房間 0.1% 到 1% 的丟包率這不是網(wǎng)線問題是網(wǎng)卡緩沖、socket 緩沖和接收端處理速度三者不匹配造成。把日志都改 TCP日志流量一大TCP 的隊頭阻塞反而會讓堆積更嚴(yán)重。先做個壓測看清楚丟包。發(fā)送端用 bash 的 linux 腳本就行#!/bin/bash # 發(fā)送 20000 條 UDP 日志到本機 5514 total20000 start$(date %s%N) for ((i1; itotal; i)); do printf 14$(date %b %e %T) perf seq%d\n $i \ | nc -u -w1 127.0.0.1 5514 done end$(date %s%N) echo sent $total in $(( (end-start)/1000000 )) ms接收端用 python 統(tǒng)計序號空洞import socket import re s socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind((127.0.0.1, 5514)) seen set() while True: data, _ s.recvfrom(2048) m re.search(rbseq(\d), data) if m: seen.add(int(m.group(1))) if len(seen) % 1000 0: missing len(seen) and (max(seen) - min(seen) 1 - len(seen)) print(fgot {len(seen)}, missing {missing})參數(shù)里nc -u走 UDP-w1是等待收尾的超時防止循環(huán)卡死。這個腳本會把內(nèi)核 socket 緩沖打滿丟包率會真實暴露。看 missing 值如果穩(wěn)定增長說明要么發(fā)送端太快要么接收端處理不過來。先調(diào)接收端緩沖參數(shù)sysctl -w net.core.rmem_max8388608 sysctl -w net.core.rmem_default8388608再不行就要考慮在發(fā)送端做批量合并。真正關(guān)鍵的高價值日志直接走 TCP 或者 RELP不要指望 UDP 重傳日志場景下重傳只會加劇擁塞。4.4 嵌入式 Linux 下的 syslog 差異嵌入式系統(tǒng)資源少rsyslog 太重常見的是 busybox syslogd。它沒有 rsyslog 的規(guī)則引擎facility 和 severity 過濾能力很弱所以程序內(nèi)自己做級別裁剪更重要。busybox syslogd 常用參數(shù)syslogd -n -s 64 -b 1 -R 192.168.1.10:5514-n表示不 fork 到后臺適合交給 init 或 systemd 管理-s 64限制單個日志文件最大 64KB-b 1保留 1 個輪轉(zhuǎn)備份-R指定遠端日志服務(wù)器。嵌入式 Flash 存儲有限別貪心設(shè)大日志文件否則設(shè)備跑幾個月后壞塊先找上門。內(nèi)核日志和用戶日志在嵌入式上很容易混在一起。dmesg看的是內(nèi)核日志來自 /proc/kmsg用戶的 printk 由 console_loglevel 控制落不落串口。程序里的 syslog 消息別往 /dev/kmsg 寫那是內(nèi)核專屬權(quán)限要求也高。調(diào)試串口日志太多時調(diào)dmesg -n而不是去停業(yè)務(wù)程序的日志輸出。5. syslog 編程避坑5 個讓日志丟在路上的真實場景5.1 syslog() 返回 0系統(tǒng)日志里卻什么都沒有現(xiàn)象C 程序調(diào)用 syslog() 一切正常返回值是 0但 tail /var/log/syslog 看不到一條自己的消息。原因rsyslog 的規(guī)則把該 facility 定向到了別的文件*.info的默認規(guī)則根本沒匹配上你寫的 facility或者等級低于規(guī)則門檻比如規(guī)則只收 info你打的是 debug。日志從應(yīng)用進 /dev/log 只是第一步后面 rsyslog 過濾是第二個黑匣子。解決先用 logger 做對照實驗區(qū)分是應(yīng)用問題還是系統(tǒng)配置問題。logger -p user.info probe from logger logger -p local0.info probe local0 tail -f /var/log/syslog如果 logger 的 user.info 能看見local0 看不見就去查 local0 被哪條規(guī)則接管grep -rn local0 /etc/rsyslog.conf /etc/rsyslog.d/常見處理是把規(guī)則指到業(yè)務(wù)自己的日志文件比如local0.* /var/log/myapp.log。改了配置記得systemctl restart rsyslog。5.2 跨年后時間錯亂遠程日志相差 8 小時現(xiàn)象日志里時間顯示無年份跨年的兩天日志翻起來順序顛倒內(nèi)網(wǎng)多臺機器日志時間差一個時區(qū)。原因RFC3164 的頭部沒有年份和時區(qū)。設(shè)備時間如果是 UTC接收端按本地時間解讀東八區(qū)自然差 8 小時。這個坑在 syslog.rar 這類老工程里幾乎是必踩的。解決盡量讓發(fā)送端直接發(fā) RFC5424 格式時間戳帶時區(qū)或者高價值日志在消息體里自己拼一個 epoch 字段。統(tǒng)一所有服務(wù)器系統(tǒng)時區(qū)為 UTC展示時再轉(zhuǎn)本地時間這是集中日志平臺的標(biāo)準(zhǔn)做法。不要在應(yīng)用層把時間先轉(zhuǎn)成字符串再發(fā)接收端格式化才是正路。5.3 日志一多業(yè)務(wù)調(diào)用跟著變慢現(xiàn)象日志量從每分鐘幾百條漲到幾萬條后業(yè)務(wù)接口耗時漲了三分之一strace 發(fā)現(xiàn)卡在 sendto 附近。原因syslog() 默認是阻塞寫 /dev/log內(nèi)核緩沖滿后寫操作等待glibc 內(nèi)部還有鎖日志風(fēng)暴時所有調(diào)用 syslog() 的線程互相排隊。rsyslog 接收端如果解析慢連鎖反應(yīng)又讓緩沖堆積。解決先看實際緩沖壓力。cat /proc/net/unix | grep /dev/log ss -lnp | grep :5514臨時調(diào)內(nèi)核緩沖只能緩解sysctl -w net.core.wmem_max4194304 sysctl -w net.core.wmem_default4194304根治是 4.2 的隊列方案業(yè)務(wù)線程只入隊發(fā)送線程批量處理發(fā)送 socket 設(shè) O_NONBLOCK緩沖滿直接丟并記 dropped 計數(shù)。記住一個原則日志可以丟業(yè)務(wù)不能卡。5.4 容器或新環(huán)境里連 /dev/log 報 Permission denied現(xiàn)象程序部署進容器后syslog() 打通了但系統(tǒng)日志目錄里收不到strace 顯示打開 /dev/log 時 Permission denied。原因容器里沒有宿主機的 /dev/log或者掛載了但權(quán)限不對。宿主機 /dev/log 通常是 socket 文件屬主 root:root權(quán)限 622普通用戶能寫嗎很多場景是不能的。坑在容器里應(yīng)用跑在非 root 用戶又沒有把宿主機的 /dev/log 掛進來。解決臨時方案是容器啟動時掛載宿主路徑docker run -v /dev/log:/dev/log ...但這會把宿主日志 socket 直接暴露給容器干凈的做法是容器內(nèi)應(yīng)用把日志寫 stdout交給 docker logging driver要進系統(tǒng)日志就起一個輕量 syslog 代理進程把容器內(nèi) /dev/log 轉(zhuǎn)發(fā)到宿主機 rsyslog 的 5514 端口。注意容器內(nèi)起 syslogd 前先確認 socket 路徑不同基礎(chǔ)鏡像的 /dev/log 位置不一致。5.5 代碼在 Linux 編譯通過換個 Unix 就鏈接失敗現(xiàn)象同一個工程在 Linux 上gcc -o sender sender.c順利產(chǎn)出放到 Solaris 或老 AIX 上編譯鏈接階段報 undefined reference tosocket、bind。原因Linux 把網(wǎng)絡(luò)函數(shù)全塞在 libc 里傳統(tǒng) Unix 不是它們把 socket 相關(guān)函數(shù)放在 libsocket網(wǎng)絡(luò)解析相關(guān)放在 libnslMakefile 里沒鏈接這兩個庫。解決Makefile 里加一行。LIBS -lsocket -lnsl對應(yīng)代碼里syslog()本身在 libc真正需要這兩個庫的是 3.3 那種直接建 socket 的收發(fā)端。順帶提一個同源坑壓縮包里的源碼如果從 Windows 解壓出來CRLF 行尾會讓 syslog 消息的 tag 后面多一個\r接收端正則匹配 tag 全失敗日志看起來是亂的。批量sed -i s/\r$//之后再對工程做一次 diff確認沒有內(nèi)容被誤傷。6. 用端到端時間戳驗證 syslog 鏈路一套能堅持到第二年的檢查習(xí)慣驗證 syslog 工程不能只看“消息出來了”還要看消息花了多久、有沒有靜默丟失。我的做法是給發(fā)送端打上本地納秒時間戳和自增序號接收端記錄差值。發(fā)送端關(guān)鍵行struct timespec ts; clock_gettime(CLOCK_REALTIME, ts); syslog(LOG_INFO, benchmark ts%ld.%09ld seq%d, ts.tv_sec, ts.tv_nsec, seq);接收端不要靠肉眼直接統(tǒng)計# 接收端命令過濾 bench 行算當(dāng)前時間與 ts 的差值 tail -f /var/log/syslog | grep benchmark | \ awk {print $NF} | head -1000真正的鏈路延遲應(yīng)該是發(fā)送端調(diào) syslog 到接收端磁盤落盤的間隔只有同一臺機器上同時跑收發(fā)端才能近似測出跨機器測的是網(wǎng)絡(luò)延遲加接收端處理延遲中間有時間校準(zhǔn)誤差別把數(shù)字報得太精確。我現(xiàn)在的習(xí)慣是每改完一版日志代碼先跑十分鐘壓測留下 CSV 記錄再看 99 分位延遲而不是平均延遲。平均延遲會把定時批量發(fā)送攤得很平掩蓋偶發(fā)的緩沖打滿。壓測腳本就是第 4 章那個 bash 循環(huán)稍微收一下速率改成每秒 5000 條跑到穩(wěn)定再算 missing。另一個堅持到現(xiàn)在的習(xí)慣升級 rsyslog、換內(nèi)核版本、調(diào)整過 socket 緩沖參數(shù)之后必然用logger -n做一次冒煙回歸否則這類黑匣子會在半年后某次大促時突然發(fā)作。日志鏈路不值得炫技但它能決定你排障時是十分鐘定位還是熬一個通宵。希望幫到你。本文還有配套的精品資源點擊獲取