一. syslogd簡介
syslogd不僅僅是記錄kernel log的服務,還能記錄user space中的日志。
syslogd是Linux下的一個記錄日志文件服務。新版本叫做rsyslogd。
syslogd有一系列的子服務,例如mail、auth、cron、kern等等,這些子服務提供日志記錄的功能,。當程序要記錄log時,可以直接調用這些子服務將日志記錄到設定的地方。
syslogd是一個守護進程,配置這整個守護進程以及其子服務的地方就是/etc/syslog.conf這個文件。可以從https://www.rsyslog.com/doc/master/獲取官方文檔。
1.1 日志的output格式
1. 如果配置好並運行了 syslogd 或 klogd,一般所有 log的信息也會追加到 /var/log/messages。並且kernel log信息被記錄在/val/log/kern.log。
可以看到基本日志格式包含以下四列:
(1) 事件產生的時間(Jan 1 08:00:09)
(2) 發生事件的服務器的主機名 (cvitek)
(3) 產生事件的服務名或程序名 (kernel or local5)
(4) 事件的具體信息(…cif a0c2000.cif:..)
2.當開啟rsyslogd后,不能透過/proc/kmsg來查看kernel log.
1.2 配置/etc/syslog.conf服務
- /etc/rsyslog.conf 是rsyslog服務的總配置文件
- /etc/rsyslog.d 該目錄是單獨配置的rsyslog配置文件
1.2.1 總配置/etc/syslog.conf
rsyslog記錄哪些日志,到底記錄了什么樣的日志,是通過這個/etc/rsyslog.conf配置文件來決定的,先分析一下rsyslogd的配置文件:
默認規則會定義在/etc/rsyslog.d/50-default.conf中。
#################
#### MODULES ####
################# module(load="imuxsock") # provides support for local system logging ;加載提供對本地系統日志的支持 module(load="imklog") # provides kernel logging support;加載讀取內核消息模塊
#module(load="immark") # provides --MARK-- message capability
# provides UDP syslog reception (接收使用UDP 協議轉發過來的日志,這里#注釋掉了表示不啟用)
#module(load="imudp")
#input(type="imudp" port="514") (允許514端口接收)
# provides TCP syslog reception (接收使用UDP 協議轉發過來的日志)
#module(load="imtcp")
#input(type="imtcp" port="514")(允許514端口接收)
# Enable non-kernel facility klog messages $KLogPermitNonKernelFacility on ###########################
#### GLOBAL DIRECTIVES ####
###########################
#
# Use traditional timestamp format.
# To enable high precision timestamps, comment out the following line.
# $ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
# Filter duplicated messages $RepeatedMsgReduction on
#
# Set the default permissions for all log files.
# $FileOwner syslog $FileGroup adm $FileCreateMode 0640 $DirCreateMode 0755 $Umask 0022 $PrivDropToUser syslog $PrivDropToGroup syslog #
# Where to place spool and state files
# $WorkDirectory /var/spool/rsyslog (記錄所有日志類型的info級別以及大於info級別的信息到/var/log/messages,但是mail郵件信息,authpriv驗證方面的信息和cron時間任務相關的信息除外) #*.info;mail.none;authpriv.none;cron.none /var/log/messages
# Include all config files in /etc/rsyslog.d/
# $IncludeConfig /etc/rsyslog.d/*.conf (表示加載該目錄中的所有配置)
1.2.2 rsyslog規則(/etc/rsyslog.d/*.conf)
rsyslog規則配置文件一般由以下3部分組成,每一行表示一個項目,格式為:facility.level action,分別表示日志類型,日志等級,日志輸出路徑。一般系統默認的規則定義在/etc/rsyslog.d/50-default.conf,
一般所有日志類型都會被追加在/val/log/messages。如:
*.info;mail.none;authpriv.none;cron.none /var/log/messages
facility日志類型:
- kern: 內核信息
- user: 用戶進程相關信息
- mail: 電子郵件相關信息
- Local0- local7: 為本地使用預留的服務
- daemon: 后台進程相關信息
- syslog: 系統日志信息
level(按嚴重程度由低到高排序):
- none: 沒有重要級
- debug: 調試信息
- info: 打印的信息
- notice: 具有重要信息的普通條件
- warning: 警告信息
- err: 錯誤信息
- crit: 阻止某些工具或子系統功能實現的錯誤條件
- alert: 需要立即被修改的條件
- emerg: 該系統不可用
action(表示log保存的位置)
那下面我也抄過來一份比較全面的規則定義示例供大家參考:
# 記錄mail日志等級為error及以上日志 mail.err /var/log/mail_err.log # 記錄mail所有等級為warn級別的日志(僅記錄warn級別) mail.=warn /var/log/mail_err.log # 記錄kern所有日志 kern.* /var/log/kern.log # 將mail的所有信息,除了info以外,其他的都寫入/var/adm/mail mail.*;mail.!=info /var/adm/mail # 將日志等級為crit或更高的內核消息定向到遠程主機finlandia
# 如果主機崩潰,磁盤出現不可修復的錯誤,可能無法讀取存儲的消息。如果有日志在遠程主機上,可以嘗試找出崩潰的原因。 kern.crit @finlandia # 記錄所有類型的warning等級及以上日志
*.warning /var/log/syslog_warn.log # 記錄mail的warning日志和kern的error日志,其他所有的info日志
*.info;mail.warning;kern.error /var/log/messages # 記錄kernel的info到warning日志 kern.info;kern.!err /var/adm/kernel-info # 將mail和news的info級別日志寫入/var/adminfo mail,news.=info /var/adm/info # 將所有系統中所有類型的info日志和notice日志存入/var/log/massages,mail的所有日志除外。
*.=info;*.=notice;\ mail.none /var/log/messages # 緊急消息(emerg級別)將使用wall顯示給當前所有登錄的用戶,這里用等號表示只對emerg日志級別有效
*.=emerg *
# 該規則將所有alert以及更高級別的消息定向到操作員的終端,即登錄的用戶“root”和“joey”的終端。
*.alert root,joey
1.3 程序如何配置syslog子服務(如何自定義rsyslog規則)
問題:進程如何發送消息給rsyslog守護進程,rsyslog守護進程是如何對各種日志區分開來的?
像/usr/sbin/sshd、/usr/bin/login、/usr/bin/su這些進程,它們是調用一個叫syslog的系統調用,syslog系統調用是一個用於向rsyslog守護進程發送消息的的系統函數。
/usr/sbin/sshd,/usr/bin/login、/usr/bin/su這些進程專門執行登錄驗證時,它們在調用syslog系統函數會一般會調用LOG_AUTH這個常量,
而/usr/bin/crond和/usr/bin/at這些在調用syslog系統調用會傳入LOG_CRON這個常量(具體請看syslog()函數),日志歸類規則如下:
所以如果用LOG_AUTH的syslog()函數調用,那么會歸類到了/val/log/secure,
如果用LOG_CRON的syslog()調用則歸類到了/val/log/cron。而kernel等其他log被記錄在了/val/log/messages中。
那么我們可以自定義規則如下:
syslogfacility-text:表示facility日志類型
syslogseverity-text:表示level日志級別
這里對aisdk.conf用local7日志類型產生的大於warn日志級別的log都將記錄到/val/log/aisdk,
對kern.conf用kern類型產生的所有級別日志都記錄到/val/log/kern,
Local5類型的日志記錄到/val/log/middleware, 並且當日志級別等於或高於4(warn)時也會追加到console.
二. syslog()函數
用戶空間也可以用syslog()函數來記錄自己的進程的日志,所以用戶進程可以自定義日志規則。
調用openlog是可選擇的。如果不調用openlog,則在第一次調用syslog時,會自動調用openlog。
syslog的相關函數和宏定義一般在toolchain中都會有定義:
2.1 openlog函數
第1個參數為ident,該參數常用來表示信息的來源。ident信息會被固定地添加在每行日志的前面。
第2個參數 option控制標志
LOG_CONS |
如果將信息發送給syslogd守護進程時發生錯誤,直接將相關信息輸出到終端 |
LOG_PID |
每條日志信息中都包括進程號 |
第3個參數為facility:
facility參數 |
syslog.conf中對應的facility取值 |
LOG_KERN |
kern |
LOG_USER |
user |
LOG_MAIL |
|
LOG_DAEMON |
daemon |
LOG_AUTH |
auth |
LOG_SYSLOG |
syslog |
LOG_LPR |
lpr |
LOG_NEWS |
news |
LOG_UUCP |
uucp |
LOG_CRON |
cron |
LOG_AUTHPRIV |
authpriv |
LOG_FTP |
ftp |
LOG_LOCAL0~LOG_LOCAL7 |
local0~local7 |
2.2 syslog函數
第一個參數priority表示日志級別
priority參數 |
syslog.conf中對應的level取值 |
LOG_EMERG |
emerg |
LOG_ALERT |
alert |
LOG_CRIT |
crit |
LOG_ERR |
err |
LOG_WARNING |
warning |
LOG_NOTICE |
notice |
LOG_INFO |
info |
LOG_DEBUG |
debug |
下面是具體的例子:
這里Printf("%m")等價於printf("%s",strerror(errno));它表示把errno用string形式打印出來。
由於我這里facility為user時,是記錄在/val/log/syslog中的,
因此打印log如下:
2.3 重定向log
那我們也可以把log定向到自己想要的地方,
方法1:修改rsyslog.conf:
將facility=user時的所有level級別的log重定向到/val/log/user.log, 重啟rsyslog服務,
此時log將被寫入到新配置的位置/val/log/user.log, 當然/val/log/syslog也會保留一份.(因為也符合/val/log/syslog這條規則)
方法2:修改code中的facility:
那這里的facility被設置成了local0, 那也會記錄在/val/log/syslog.
2.4 設置log等級
1. 這里新增一個app.conf,然后自定義log路徑:
當然還可以類似於這樣子寫, syslogfacility-text和syslogseverity-text是rsyslog自帶的系統變量。
2. 重啟rsyslog服務
3. 運行程序,查看log如下:
4. 那現在修改log等級為warn, 表示只有大於等於該等級的log才會記錄。
5. 再次重啟rsyslog服務,運行程序,可以看到"log debug"不再打印.
2.5 重定向log到console
再次重啟rsyslog服務,運行程序,那么可以看到err級別的log打印在了console上,但是低於err級別還是會記錄在/val/log/app。
3. dup函數介紹
用來將標准輸出重定向到文件。
static int dup_fd; static int dup_fd_bak = 1000; static int dup_fd(void) { dup_fd = open( "./printf_dup_log.txt ", O_CREAT | O_RDWR | O_TRUNC); dup2(STDOUT_FILENO, dup_fd_bak);/*backup stdout*/ dup2(dup_fd, STDOUT_FILENO); return 0; } static int rst_fd(void) { dup2(dup_fd_bak, fileno(stdout));/*recover stdout*/ close(dup_fd); return 0; }