關鍵詞:rcS、start-stop-daemon、syslogd、syslog()、klogd、klogctl()、syslog.conf、/dev/log、facility/level等等。html
syslog用來記錄應用程序或者硬件設備的日誌;經過syslogd這個進程記錄系統有關事件記錄,也能夠記錄應用程序運做事件。linux
syslogd日誌記錄器由兩個守護進程(klogd,syslogd)和一個配置文件(syslog.conf)組成。ios
syslogd和klogd是頗有意思的守護進程,syslogd是一個分發器,它將接收到的全部日誌按照/etc/syslog.conf的配置策略發送到這些日誌應該去的地方,固然也包括從klogd接收到的日誌。編程
klogd首先接收內核的日誌,而後將之發送給syslogd。緩存
klogd不使用配置文件,它負責截獲內核消息,它既能夠獨立使用也能夠做爲syslogd的客戶端運行。服務器
syslogd默認使用/etc/syslog.conf做爲配置文件,負責截獲應用程序消息,還能夠截獲klogd向其轉發的內核消息。支持internet/unix domain sockets的特性使得這兩個工具能夠用於記錄本地和遠程的日誌。app
注:引用自《Linux/UNIX系統編程》第37.5.1 概述。dom
在busybox中執行make menuconfig,或者在buildroot中執行make busybox-menuconfig均可以對busybox進行配置。socket
打開menuconfig以後,進入System Logging Utilities:
syslog功能主要包括兩大部分syslogd/klogd和配置文件syslog.conf。前者是daemon程序,後者是針對daemon的配置文件。
syslog.conf包括對syslogd的配置,每一個配置包括兩個域:selector和action。
selector有兩部分組成:facility和level,二者之間用"."分隔;多個facility或者level之間用","分隔;多個selector之間用";"分隔。
同時selector中可使用通配符:
Name | Facility | Level |
err/warn/... | 任何一個facility | 顯示大於等於此級別的log |
* | 任何一個facility | 任何一個級別 |
= | 無效 | 只有該級別log才生效 |
! | 無效 | 除了該級別,其餘log生效 |
None | 無效 | 不存儲該facility任何log |
action主要是當信息知足selector時,該日誌消息的輸出方向。
通常分爲三種:(1)存儲到普通文件中;(2)寫入到管道中;(3)遠程轉發到其它主機上。
常規文件 |
管道文件 |
遠程轉發 |
普通的文件名:/xx/bb |
|文件名 |
@hostname |
提到syslog.con中的facility和level,須要看一下他在代碼中對應關係。
相關facility和level都在syslog.h中定義,兩這個共同組成一個32位數值;低3bit(0-3)表示level,其他部分(3-24)表示facility。
facilitynames[]中的名稱對應syslog.conf中的facility,後面的值對應syslog()中的facility代碼。
/* facility codes */ #define LOG_KERN (0<<3) /* kernel messages */ #define LOG_USER (1<<3) /* random user-level messages */ #define LOG_MAIL (2<<3) /* mail system */ #define LOG_DAEMON (3<<3) /* system daemons */ #define LOG_AUTH (4<<3) /* security/authorization messages */ #define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */ #define LOG_LPR (6<<3) /* line printer subsystem */ #define LOG_NEWS (7<<3) /* network news subsystem */ #define LOG_UUCP (8<<3) /* UUCP subsystem */ #define LOG_CRON (9<<3) /* clock daemon */ #define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */ #define LOG_FTP (11<<3) /* ftp daemon */ /* other codes through 15 reserved for system use */ #define LOG_LOCAL0 (16<<3) /* reserved for local use */ #define LOG_LOCAL1 (17<<3) /* reserved for local use */ #define LOG_LOCAL2 (18<<3) /* reserved for local use */ #define LOG_LOCAL3 (19<<3) /* reserved for local use */ #define LOG_LOCAL4 (20<<3) /* reserved for local use */ #define LOG_LOCAL5 (21<<3) /* reserved for local use */ #define LOG_LOCAL6 (22<<3) /* reserved for local use */ #define LOG_LOCAL7 (23<<3) /* reserved for local use */ #define LOG_NFACILITIES 24 /* current number of facilities */ #define LOG_FACMASK 0x03f8 /* mask to extract facility part */ /* facility of pri */ #define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3) #ifdef SYSLOG_NAMES CODE facilitynames[] = { { "auth", LOG_AUTH }, { "authpriv", LOG_AUTHPRIV }, { "cron", LOG_CRON }, { "daemon", LOG_DAEMON }, { "ftp", LOG_FTP }, { "kern", LOG_KERN }, { "lpr", LOG_LPR }, { "mail", LOG_MAIL }, { "mark", INTERNAL_MARK }, /* INTERNAL */ { "news", LOG_NEWS }, { "security", LOG_AUTH }, /* DEPRECATED */ { "syslog", LOG_SYSLOG }, { "user", LOG_USER }, { "uucp", LOG_UUCP }, { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, { NULL, -1 } }; #endif
prioritynames定義了syslog.conf中等級和syslog()關係:
#define LOG_EMERG 0 /* system is unusable */ #define LOG_ALERT 1 /* action must be taken immediately */ #define LOG_CRIT 2 /* critical conditions */ #define LOG_ERR 3 /* error conditions */ #define LOG_WARNING 4 /* warning conditions */ #define LOG_NOTICE 5 /* normal but significant condition */ #define LOG_INFO 6 /* informational */ #define LOG_DEBUG 7 /* debug-level messages */ CODE prioritynames[] = { { "alert", LOG_ALERT }, { "crit", LOG_CRIT }, { "debug", LOG_DEBUG }, { "emerg", LOG_EMERG }, { "err", LOG_ERR }, { "error", LOG_ERR }, /* DEPRECATED */ { "info", LOG_INFO }, { "none", INTERNAL_NOPRI }, /* INTERNAL */ { "notice", LOG_NOTICE }, { "panic", LOG_EMERG }, /* DEPRECATED */ { "warn", LOG_WARNING }, /* DEPRECATED */ { "warning", LOG_WARNING }, { NULL, -1 } };
#syslog.conf
kern,user.* /var/log/messages #all messages of kern and user facilities
kern.!err /var/log/critical #all messages of kern facility with priorities lower than err (warn, notice ...) *.*;auth,authpriv.none /var/log/noauth #all messages except ones with auth and authpriv facilities
kern,user.*;kern.!=notice;*.err;syslog.none /var/log/OMG #some whicked rule just as an example =) *.* /dev/null #this prevents from logging to default log file (-O FILE or /var/log/messages)
syslogd相關API主要有以下幾個,其中openlog()調用是可選的,它創建一個系統日誌工具的鏈接,並未後續的syslog調用設置默認設置。
ident參數是一個指向字符串的指針,syslog()輸出的每條消息都會包含這個字符串。其餘facility和level都在facilitynames[]和prioritynames[]中有定義。
syslog()用於寫入一條日誌消息,priority是facility和level的OR值。
#include <syslog.h> void openlog(const char *ident, int log_options, int facility); void syslog(int priority, const char *format, ...); int setlogmask(int mask_priority); void closelog(void);
下面重點看看openlog()的log_options選項:
#define LOG_PID 0x01 /* log the pid with each message */------------------------log中包含進程pid。 #define LOG_CONS 0x02 /* log on the console if errors in sending */-----------------若是syslogd沒法正確輸出,輸出到控制檯做爲替代。 #define LOG_ODELAY 0x04 /* delay open until first syslog() (default) */ #define LOG_NDELAY 0x08 /* don't delay open */ #define LOG_NOWAIT 0x10 /* don't wait for console forks: DEPRECATED */ #define LOG_PERROR 0x20 /* log to stderr as well */
下面從rcS中啓動syslog功能、syslog服務、syslogd/klogd、syslog()幾個方面,介紹如何使用syslog功能。
在rcS中配置啓動syslog服務:
#To start syslog
/etc/init.d/S01logging start
S01logging中啓動syslogd和klogd兩個daemon:
#!/bin/sh # # Start logging # SYSLOGD_ARGS=-n---------------參照syslogd option。 KLOGD_ARGS=-n-----------------參照klogd option。 [ -r /etc/default/logging ] && . /etc/default/logging start() { printf "Starting logging: " start-stop-daemon -b -S -q -m -p /var/run/syslogd.pid --exec /sbin/syslogd -- $SYSLOGD_ARGS------------b表示background運行,-S是啓動daemon,-q表示quiet,-m -p表示將當前daemon的pid寫入/var/run/syslog.pid中;--exec表示daemon對應的可執行文件, SYSLOGD_ARGS表示可選參數。 start-stop-daemon -b -S -q -m -p /var/run/klogd.pid --exec /sbin/klogd -- $KLOGD_ARGS echo "OK" } stop() { printf "Stopping logging: " start-stop-daemon -K -q -p /var/run/syslogd.pid---------------------------------------------------------K表示中止daemon。 start-stop-daemon -K -q -p /var/run/klogd.pid echo "OK" } case "$1" in start) start ;; stop) stop ;; restart|reload) stop start ;; *) echo "Usage: $0 {start|stop|restart}" exit 1 esac exit $?
經過syslogd -h和klogd -h能夠了解不一樣選項做用。
BusyBox v1.27.2 (2019-03-24 12:37:18 CST) multi-call binary. Usage: syslogd [OPTIONS] System logging utility -n Run in foreground------------------------------------------------做爲後臺進程運行。 -R HOST[:PORT] Log to HOST:PORT (default PORT:514)------------------------------log輸出到指定遠程服務器。 -L Log locally and via network (default is network only if -R) -C[size_kb] Log to shared mem buffer (use logread to read it)----------------log輸出到共享內存,經過logread讀取。 -K Log to kernel printk buffer (use dmesg to read it)---------------log輸出到內核printk緩存中,經過dmesg讀取。 -O FILE Log to FILE (default: /var/log/messages, stdout if -)------------log輸出到指定文件中。 -s SIZE Max size (KB) before rotation (default 200KB, 0=off)-------------在log達到必定大小後,循環log到messgas.0中,後續依次變更。 -b N N rotated logs to keep (default 1, max 99, 0=purge)--------------保持的messages個數,messages.0最新,數字越大越老。一次覆蓋。 -l N Log only messages more urgent than prio N (1-8)------------------設置記錄的log優先級,注意這裏若是設置-l 7,是不會記錄等級7,即LOG_DEBUG的,只會記錄LOG_INFO及更高優先級。 -S Smaller output---------------------------------------------------是否顯示hostname,以及user.info之類的詳細信息。 -f FILE Use FILE as config (default:/etc/syslog.conf)--------------------指定conf文件。 BusyBox v1.27.2 (2019-03-24 12:37:18 CST) multi-call binary. Usage: klogd [-c N] [-n] Kernel logger -c N Print to console messages more urgent than prio N (1-8)------------------將錯誤高於N優先級的log輸出到console。 -n Run in foreground--------------------------------------------------------轉到後臺運行。
編寫syslog,源碼以下:
#include <syslog.h> #include <stdio.h> #include <stdarg.h> int main(int argc, char** argv) { int log_test; /*打開日誌*/ openlog("log_test ", LOG_PID|LOG_CONS, LOG_USER); while(1) { /*寫日誌*/ syslog(LOG_INFO, "%s: PID information, pid=%d", argv[0], getpid()); syslog(LOG_DEBUG,"%s: debug message",argv[0]); usleep(1000); } /*關閉日誌*/ closelog(); }
啓動syslog以後,能夠看出/var/log/messages在不停增長。
Jan 1 08:10:56 log_test [176]: ./syslog: PID information, pid=176---------------------log_test是openlog()中的ident,[176]對應LOG_PID。 Jan 1 08:10:56 log_test [176]: ./syslog: debug message Jan 1 08:10:56 log_test [176]: ./syslog: PID information, pid=176 Jan 1 08:10:56 log_test [176]: ./syslog: debug message ...
若是現實完整信息:
Jan 1 08:17:15 XXXX user.info log_test [176]: ./syslog: PID information, pid=176 Jan 1 08:17:15 XXXX user.debug log_test [176]: ./syslog: debug message Jan 1 08:17:15 XXXX user.info log_test [176]: ./syslog: PID information, pid=176 ...
在syslog.conf中設置filter,顯示local0的debug及更高優先級log、不顯示local1的log、顯示local2的全部log,顯示local3的err及更高優先級log:
local0.info /var/log/local.log local1.none /var/log/local.log local2.* /var/log/local.log local3.err /var/log/local.log
測試程序以下:
#include <syslog.h> #include <stdio.h> #include <stdarg.h> int main(int argc, char** argv) { int log_test; /*打開日誌*/ openlog("log_test ", LOG_PID|LOG_CONS, LOG_USER); /*寫日誌*/ syslog(LOG_LOCAL0|LOG_INFO, "%s: PID information, pid=%d", argv[0], getpid()); syslog(LOG_LOCAL0|LOG_DEBUG,"%s: debug message",argv[0]); syslog(LOG_LOCAL1|LOG_INFO, "%s: PID information, pid=%d", argv[0], getpid()); syslog(LOG_LOCAL1|LOG_DEBUG,"%s: debug message",argv[0]); syslog(LOG_LOCAL2|LOG_INFO, "%s: PID information, pid=%d", argv[0], getpid()); syslog(LOG_LOCAL2|LOG_DEBUG,"%s: debug message",argv[0]); syslog(LOG_LOCAL3|LOG_INFO, "%s: PID information, pid=%d", argv[0], getpid()); syslog(LOG_LOCAL3|LOG_DEBUG,"%s: debug message",argv[0]); syslog(LOG_LOCAL4|LOG_INFO, "%s: PID information, pid=%d", argv[0], getpid()); syslog(LOG_LOCAL4|LOG_DEBUG,"%s: debug message",argv[0]); /*關閉日誌*/ closelog(); }
最終log輸出/var/log/local.log:
Jan 1 08:11:54 xxxx local0.info log_test [259]: ./syslog: PID information, pid=259 Jan 1 08:11:54 xxxx local2.info log_test [259]: ./syslog: PID information, pid=259 Jan 1 08:11:54 xxxx local2.debug log_test [259]: ./syslog: debug message
syslogd和klogd在busybox的sysklogd目錄中,入口是syslogd_main()和klogd_main()。
經過系統日誌概覽可知:
1.內核log由klogd從內核中取出轉發到/dev/log;klogctl()從kmsg的ring buffer中獲取數據,經過syslog()發送到/dev/log。
2.用戶空間的log直接經過syslog()發送到/dev/log。
3.syslogd從syslog.conf獲取配置信息。
4.syslogd從/dev/log接收數據,或者從UDP端口514接收數據。
5.syslogd輸出到磁盤文件、控制檯、FIFO等。
klogd從經過klogctl()將內核log從ring buffer中取出,而後調用syslog()經過/dev/log發送。
這裏的一個核心函數是klogctl(),經過對內核kmsg ring buffer的設置,從中取出內容。
klogctl()的type,決定了後面內容的意義。
SYSLOG_ACTION_CLOSE (0) Close the log. Currently a NOP. SYSLOG_ACTION_OPEN (1) Open the log. Currently a NOP. SYSLOG_ACTION_READ (2)-----------------------------等待內核log非空時,從中讀取len大小的log到bufp中。返回值是實際讀取內容大小,已經讀取的部分會從內核ring buffer中移除。 SYSLOG_ACTION_READ_ALL (3) Read all messages remaining in the ring buffer, placing them in the buffer pointed to by bufp. The call reads the last len bytes from the log buffer (nondestructively), but will not read more than was written into the buffer since the last "clear ring buffer" command (see command 5 below)). The call returns the number of bytes read. SYSLOG_ACTION_READ_CLEAR (4)------------------------讀取全部的內核log,而後清空。 SYSLOG_ACTION_CLEAR (5) SYSLOG_ACTION_CONSOLE_OFF (6)-----------------------經過保存console_loglevel,而後將其設置到最低,來關閉console的顯示。bufp和len參數忽略。 SYSLOG_ACTION_CONSOLE_ON (7)-------------------------打開console顯示。bufp和len參數忽略。 SYSLOG_ACTION_CONSOLE_LEVEL (8)----------------------設置console的等級,1-8之間。詳細以下:
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */ SYSLOG_ACTION_SIZE_UNREAD (9) (since Linux 2.4.10)---返回內核中可讀buffer大小,bufp和len參數忽略。 SYSLOG_ACTION_SIZE_BUFFER (10) (since Linux 2.6.6)---內核中log ring buffer大小。
klogd用到的相關klogctl()包括:
static void klogd_open(void) { /* "Open the log. Currently a NOP" */ klogctl(1, NULL, 0); } static void klogd_setloglevel(int lvl) { klogctl(8, NULL, lvl); } static int klogd_read(char *bufp, int len) { return klogctl(2, bufp, len); } static void klogd_close(void) { klogctl(7, NULL, 0); /* "7 -- Enable printk's to console" */ klogctl(0, NULL, 0); /* "0 -- Close the log. Currently a NOP" */ }
klogd_main()走讀以下:
int klogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int klogd_main(int argc UNUSED_PARAM, char **argv) { int i = 0; char *opt_c; int opt; int used; setup_common_bufsiz();----------------------------------------------------分配COMMON_BUFSIZE大小的bufer緩存,指針bb_common_bufsiz1。 opt = getopt32(argv, "c:n", &opt_c); if (opt & OPT_LEVEL) {-----------------------------------------------------獲取log等級參數 /* Valid levels are between 1 and 8 */ i = xatou_range(opt_c, 1, 8); } if (!(opt & OPT_FOREGROUND)) {---------------------------------------------klogd是否後臺運行。 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); } logmode = LOGMODE_SYSLOG; /* klogd_open() before openlog(), since it might use fixed fd 3, * and openlog() also may use the same fd 3 if we swap them: */ klogd_open(); openlog("kernel", 0, LOG_KERN);---------------------------------------------全部的內核log,都以kernel做爲ident。 if (i) klogd_setloglevel(i);---------------------------------------------------從傳入log等級參數設置到內核進行過濾。 signal(SIGHUP, SIG_IGN);----------------------------------------------------忽略SIGHUP信號。 /* We want klogd_read to not be restarted, thus _norestart: */ bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo);----------------在record_signo()中記錄接收到的信號bb_got_signal。 syslog(LOG_NOTICE, "klogd started: %s", bb_banner); write_pidfile(CONFIG_PID_FILE_PATH "/klogd.pid"); used = 0; while (!bb_got_signal) {----------------------------------------------------沒有受到信號中斷,則while循環。 int n; int priority; char *start; /* "2 -- Read from the log." */ start = log_buffer + used;-----------------------------------------------log_buffer指向bb_common_bufsiz1。 n = klogd_read(start, KLOGD_LOGBUF_SIZE-1 - used);-----------------------讀取KLOGD_LOGBUF_SIZE-1 - used大小log到start。 if (n < 0) { if (errno == EINTR) continue; bb_perror_msg(READ_ERROR); break; } start[n] = '\0'; /* Process each newline-terminated line in the buffer */ start = log_buffer; while (1) { char *newline = strchrnul(start, '\n');------------------------------根據\n進行分行處理,一行一個syslog(),newline指向下一行頭部。 if (*newline == '\0') { /* This line is incomplete */ /* move it to the front of the buffer */ overlapping_strcpy(log_buffer, start); used = newline - start; if (used < KLOGD_LOGBUF_SIZE-1) { /* buffer isn't full */ break; } /* buffer is full, log it anyway */ used = 0; newline = NULL; } else { *newline++ = '\0'; } /* Extract the priority */ priority = LOG_INFO;--------------------------------------------------默認等級 if (*start == '<') { start++; if (*start) priority = strtoul(start, &start, 10);------------------------等級轉換。 if (*start == '>') start++; } /* Log (only non-empty lines) */ if (*start) syslog(priority, "%s", start);------------------------------------獲取等級和一行內容start,經過syslog打印。 if (!newline) break; start = newline;------------------------------------------------------start指向下一行首部,循環處理。 } } klogd_close();----------------------------------------------------------------關閉klog。 syslog(LOG_NOTICE, "klogd: exiting"); remove_pidfile(CONFIG_PID_FILE_PATH "/klogd.pid"); if (bb_got_signal) kill_myself_with_sig(bb_got_signal); return EXIT_FAILURE; }
klogctl()函數經過調用syslog系統調用,來從內核中獲取msg。
SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len) { return do_syslog(type, buf, len, SYSLOG_FROM_READER); } int do_syslog(int type, char __user *buf, int len, int source) { bool clear = false; static int saved_console_loglevel = LOGLEVEL_DEFAULT; int error; error = check_syslog_permissions(type, source); if (error) goto out; switch (type) { case SYSLOG_ACTION_CLOSE: /* Close log */ break; case SYSLOG_ACTION_OPEN: /* Open log */--------------------------CLOSE和OPEN沒有任何做用。 break; case SYSLOG_ACTION_READ: /* Read from log */ error = -EINVAL; if (!buf || len < 0) goto out; error = 0; if (!len) goto out; if (!access_ok(VERIFY_WRITE, buf, len)) { error = -EFAULT; goto out; } error = wait_event_interruptible(log_wait, syslog_seq != log_next_seq); if (error) goto out; error = syslog_print(buf, len); break; /* Read/clear last kernel messages */ case SYSLOG_ACTION_READ_CLEAR: clear = true;-------------------------------------------------能夠看出這裏並不作實際清空,只是SYSLOG_ACTION_READ_ALL以後纔會清空。 /* FALL THRU */ /* Read last kernel messages */ case SYSLOG_ACTION_READ_ALL: error = -EINVAL; if (!buf || len < 0) goto out; error = 0; if (!len) goto out; if (!access_ok(VERIFY_WRITE, buf, len)) { error = -EFAULT; goto out; } error = syslog_print_all(buf, len, clear);--------------------將全部出書到buf中,可是有長度len限制,只保存最新部分。 break; /* Clear ring buffer */ case SYSLOG_ACTION_CLEAR: syslog_print_all(NULL, 0, true);------------------------------僅僅作清空操做。 break; /* Disable logging to console */ case SYSLOG_ACTION_CONSOLE_OFF: if (saved_console_loglevel == LOGLEVEL_DEFAULT) saved_console_loglevel = console_loglevel; console_loglevel = minimum_console_loglevel; break; /* Enable logging to console */ case SYSLOG_ACTION_CONSOLE_ON: if (saved_console_loglevel != LOGLEVEL_DEFAULT) { console_loglevel = saved_console_loglevel; saved_console_loglevel = LOGLEVEL_DEFAULT; } break; /* Set level of messages printed to console */ case SYSLOG_ACTION_CONSOLE_LEVEL: error = -EINVAL; if (len < 1 || len > 8) goto out; if (len < minimum_console_loglevel) len = minimum_console_loglevel; console_loglevel = len;----------------------------------------設置console_loglevel等級。 /* Implicitly re-enable logging to console */ saved_console_loglevel = LOGLEVEL_DEFAULT; error = 0; break; /* Number of chars in the log buffer */ case SYSLOG_ACTION_SIZE_UNREAD: ... break; /* Size of the log buffer */ case SYSLOG_ACTION_SIZE_BUFFER: error = log_buf_len; break; default: error = -EINVAL; break; } out: return error; } static int syslog_print(char __user *buf, int size) { char *text; struct printk_log *msg; int len = 0; text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL); if (!text) return -ENOMEM; while (size > 0) { size_t n; size_t skip; raw_spin_lock_irq(&logbuf_lock); if (syslog_seq < log_first_seq) { /* messages are gone, move to first one */ syslog_seq = log_first_seq; syslog_idx = log_first_idx; syslog_prev = 0; syslog_partial = 0; } if (syslog_seq == log_next_seq) { raw_spin_unlock_irq(&logbuf_lock); break; } skip = syslog_partial; msg = log_from_idx(syslog_idx); n = msg_print_text(msg, syslog_prev, true, text, LOG_LINE_MAX + PREFIX_MAX); if (n - syslog_partial <= size) { /* message fits into buffer, move forward */ syslog_idx = log_next(syslog_idx); syslog_seq++; syslog_prev = msg->flags; n -= syslog_partial; syslog_partial = 0; } else if (!len){ /* partial read(), remember position */ n = size; syslog_partial += n; } else n = 0; raw_spin_unlock_irq(&logbuf_lock); if (!n) break; if (copy_to_user(buf, text + skip, n)) { if (!len) len = -EFAULT; break; } len += n; size -= n; buf += n; } kfree(text); return len; } static int syslog_print_all(char __user *buf, int size, bool clear) { char *text; int len = 0; text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL); if (!text) return -ENOMEM; raw_spin_lock_irq(&logbuf_lock); if (buf) { u64 next_seq; u64 seq; u32 idx; enum log_flags prev; /* * Find first record that fits, including all following records, * into the user-provided buffer for this dump. */ seq = clear_seq; idx = clear_idx; prev = 0; while (seq < log_next_seq) { struct printk_log *msg = log_from_idx(idx); len += msg_print_text(msg, prev, true, NULL, 0);---------------------------獲取全部log大小。 prev = msg->flags; idx = log_next(idx); seq++; } /* move first record forward until length fits into the buffer */ seq = clear_seq; idx = clear_idx; prev = 0; while (len > size && seq < log_next_seq) { struct printk_log *msg = log_from_idx(idx); len -= msg_print_text(msg, prev, true, NULL, 0);---------------------------找到知足剩餘長度小於size大小的seq,後面將seq以後全部的log發送到text中。保證知足buf尺寸前提下,取最新的log。 prev = msg->flags; idx = log_next(idx); seq++; } /* last message fitting into this dump */ next_seq = log_next_seq; len = 0; while (len >= 0 && seq < next_seq) { struct printk_log *msg = log_from_idx(idx); int textlen; textlen = msg_print_text(msg, prev, true, text, LOG_LINE_MAX + PREFIX_MAX);----------------------------------將msg內容輸出到text中,第3參數true表示要輸出給syslog()使用,因此須要加上facility和level頭。 if (textlen < 0) { len = textlen; break; } idx = log_next(idx); seq++; prev = msg->flags; raw_spin_unlock_irq(&logbuf_lock); if (copy_to_user(buf + len, text, textlen)) len = -EFAULT; else len += textlen; raw_spin_lock_irq(&logbuf_lock); if (seq < log_first_seq) { /* messages are gone, move to next one */ seq = log_first_seq; idx = log_first_idx; prev = 0; } } } if (clear) {-------------------------------------------------------------------------清空全部buffer。 clear_seq = log_next_seq; clear_idx = log_next_idx; } raw_spin_unlock_irq(&logbuf_lock); kfree(text); return len; }
在glibc庫的misc/syslog.c中,定義了openlog()/syslog()/closelog()函數。
這裏重點看一下syslog()都作了哪些內容。
ldbl_hidden_def (__syslog, syslog) void __syslog(int pri, const char *fmt, ...) { va_list ap; va_start(ap, fmt); __vsyslog_chk(pri, -1, fmt, ap); va_end(ap); } void __vsyslog_chk(int pri, int flag, const char *fmt, va_list ap) { struct tm now_tm; time_t now; int fd; FILE *f; char *buf = 0; size_t bufsize = 0; size_t msgoff; #ifndef NO_SIGPIPE struct sigaction action, oldaction; int sigpipe; #endif int saved_errno = errno; char failbuf[3 * sizeof (pid_t) + sizeof "out of memory []"]; ... /* Build the message in a memory-buffer stream. */ f = __open_memstream (&buf, &bufsize); if (f == NULL) { ... } else { __fsetlocking (f, FSETLOCKING_BYCALLER); fprintf (f, "<%d>", pri);---------------------------------------------------輸出pri到f,pri包括facility和level。 (void) time (&now); f->_IO_write_ptr += __strftime_l (f->_IO_write_ptr, f->_IO_write_end - f->_IO_write_ptr, "%h %e %T ", __localtime_r (&now, &now_tm), _nl_C_locobj_ptr);----------------------------------------輸出log產生的時間。 msgoff = ftell (f); if (LogTag == NULL) LogTag = __progname; if (LogTag != NULL) __fputs_unlocked (LogTag, f); if (LogStat & LOG_PID) fprintf (f, "[%d]", (int) __getpid ());-----------------------------------在使能LOG_PID以後,輸出pid到f。 if (LogTag != NULL) { __putc_unlocked (':', f); __putc_unlocked (' ', f); } /* Restore errno for %m format. */ __set_errno (saved_errno); /* We have the header. Print the user's format into the buffer. */ if (flag == -1)--------------------------------------------------------------根據flag是否有效,輸出flag、fmt、ap內容到f。 vfprintf (f, fmt, ap); else __vfprintf_chk (f, flag, fmt, ap); /* Close the memory stream; this will finalize the data into a malloc'd buffer in BUF. */ fclose (f); } ... /* Prepare for multiple users. We have to take care: open and write are cancellation points. */ struct cleanup_arg clarg; clarg.buf = buf; clarg.oldaction = NULL; __libc_cleanup_push (cancel_handler, &clarg); __libc_lock_lock (syslog_lock); ... /* Get connected, output the message to the local logger. */ if (!connected) openlog_internal(LogTag, LogStat | LOG_NDELAY, 0);---------------------------若是尚未和/dev/log鏈接上,調用openlog_internal()鏈接,操做句柄是LogFile。/* If we have a SOCK_STREAM connection, also send ASCII NUL as a record terminator. */ if (LogType == SOCK_STREAM) ++bufsize; if (!connected || __send(LogFile, buf, bufsize, send_flags) < 0)-----------------buf中存放上面組裝好的log,經過send()發送到/dev/log。 { if (connected)---------------------------------------------------------------若是經過openlog()打開過,那麼send異常有多是socket異常。須要從新關閉打開。 { /* Try to reopen the syslog connection. Maybe it went down. */ closelog_internal (); openlog_internal(LogTag, LogStat | LOG_NDELAY, 0); } if (!connected || __send(LogFile, buf, bufsize, send_flags) < 0) { closelog_internal (); /* attempt re-open next time */----------------------關閉LogFile。 /*... } } #ifndef NO_SIGPIPE if (sigpipe == 0) __sigaction (SIGPIPE, &oldaction, (struct sigaction *) NULL); #endif /* End of critical section. */ __libc_cleanup_pop (0); __libc_lock_unlock (syslog_lock); if (buf != failbuf) free (buf); }
openlog()和closelog()主要經過openlog_internal()和closelog_internal()實現。
static void openlog_internal(const char *ident, int logstat, int logfac) { if (ident != NULL) LogTag = ident; LogStat = logstat; if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) LogFacility = logfac; int retry = 0; while (retry < 2) { if (LogFile == -1) { SyslogAddr.sun_family = AF_UNIX; (void)strncpy(SyslogAddr.sun_path, _PATH_LOG, sizeof(SyslogAddr.sun_path)); if (LogStat & LOG_NDELAY) { LogFile = __socket(AF_UNIX, LogType | SOCK_CLOEXEC, 0);---------------------打開一個socket。 if (LogFile == -1) return; } } if (LogFile != -1 && !connected) { int old_errno = errno; if (__connect(LogFile, &SyslogAddr, sizeof(SyslogAddr))-----------------------將LogFile和/dev/log socket鏈接,後面能夠傳輸數據。 == -1) { ... } else connected = 1; } break; } } static void closelog_internal (void) { if (!connected) return; __close (LogFile); LogFile = -1; connected = 0; }
syslogd_main()中主要是解析各類選項,轉換成參數。
static void do_syslogd(void) NORETURN; static void do_syslogd(void) { #if ENABLE_FEATURE_REMOTE_LOG llist_t *item; #endif #if ENABLE_FEATURE_SYSLOGD_DUP int last_sz = -1; char *last_buf; char *recvbuf = G.recvbuf; #else #define recvbuf (G.recvbuf) #endif /* Set up signal handlers (so that they interrupt read()) */ signal_no_SA_RESTART_empty_mask(SIGTERM, record_signo); signal_no_SA_RESTART_empty_mask(SIGINT, record_signo); //signal_no_SA_RESTART_empty_mask(SIGQUIT, record_signo); signal(SIGHUP, SIG_IGN); #ifdef SYSLOGD_MARK signal(SIGALRM, do_mark); alarm(G.markInterval); #endif xmove_fd(create_socket(), STDIN_FILENO);-------------------------------------建立/dev/log socket,而且將句柄dup到STDIN_FILENO。從socket接收數據。 if (option_mask32 & OPT_circularlog) ipcsyslog_init(); if (option_mask32 & OPT_kmsg) kmsg_init(); timestamp_and_log_internal("syslogd started: BusyBox v" BB_VER); while (!bb_got_signal) { ssize_t sz; #if ENABLE_FEATURE_SYSLOGD_DUP last_buf = recvbuf; if (recvbuf == G.recvbuf) recvbuf = G.recvbuf + MAX_READ; else recvbuf = G.recvbuf; #endif read_again: sz = read(STDIN_FILENO, recvbuf, MAX_READ - 1);----------------------------------從STDIN_FILENO讀取syslog()輸入的數據。 if (sz < 0) { if (!bb_got_signal) bb_perror_msg("read from %s", _PATH_LOG); break; } /* Drop trailing '\n' and NULs (typically there is one NUL) */ while (1) { if (sz == 0) goto read_again; /* man 3 syslog says: "A trailing newline is added when needed". * However, neither glibc nor uclibc do this: * syslog(prio, "test") sends "test\0" to /dev/log, * syslog(prio, "test\n") sends "test\n\0". * IOW: newline is passed verbatim! * I take it to mean that it's syslogd's job * to make those look identical in the log files. */ if (recvbuf[sz-1] != '\0' && recvbuf[sz-1] != '\n')---------------------------'\0'做爲不一樣syslog()的分隔符;'\n'做爲換行符。 break; sz--; } ... if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog)) { recvbuf[sz] = '\0'; /* ensure it *is* NUL terminated */ split_escape_and_log(recvbuf, sz);---------------------------------------------這裏的recvbuf已經根據'\0'和'\n'進行分割,裏面分析facility和level,進行轉發。 } } /* while (!bb_got_signal) */ timestamp_and_log_internal("syslogd exiting"); remove_pidfile(CONFIG_PID_FILE_PATH "/syslogd.pid"); ipcsyslog_cleanup(); if (option_mask32 & OPT_kmsg) kmsg_cleanup(); kill_myself_with_sig(bb_got_signal); #undef recvbuf }
split_escape_and_log()將消息分割出prio和msg,進行處理。
static void split_escape_and_log(char *tmpbuf, int len) { char *p = tmpbuf; tmpbuf += len; while (p < tmpbuf) { char c; char *q = G.parsebuf; int pri = (LOG_USER | LOG_NOTICE); if (*p == '<') {-------------------------------------------此處的tmpbuf都是在__vsyslog_chk()中組裝,這裏開頭應該是'<pri>',從中解析出pri。 /* Parse the magic priority number */ pri = bb_strtou(p + 1, &p, 10); if (*p == '>') p++; if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) pri = (LOG_USER | LOG_NOTICE); } while ((c = *p++)) {---------------------------------------遇到'\0'中止。並且必定會有。 if (c == '\n') c = ' '; if (!(c & ~0x1f) && c != '\t') { *q++ = '^'; c += '@'; /* ^@, ^A, ^B... */ } *q++ = c; } *q = '\0'; /* Now log it */ timestamp_and_log(pri, G.parsebuf, q - G.parsebuf); } } static void timestamp_and_log(int pri, char *msg, int len) { char *timestamp; time_t now; /* Jan 18 00:11:22 msg... */ /* 01234567890123456 */ if (len < 16 || msg[3] != ' ' || msg[6] != ' ' || msg[9] != ':' || msg[12] != ':' || msg[15] != ' ' ) {----------------------------------------------------------------------------------沒有時間戳的msg處理,添加timestamp。 time(&now); #if 0 timestamp = ctime(&now) + 4; /* skip day of week */ #else char time_str[16]; struct timespec ts; int ret = 0; timestamp = time_str; ret = clock_gettime(CLOCK_MONOTONIC, &ts); snprintf(timestamp, 16, "%8ld%c%6ld", ts.tv_sec, '.', ts.tv_nsec); #endif } else { now = 0; timestamp = msg; #if 1-------------------------------------------------------------------------------------修改時間戳的來源,使用更高精度的時間戳。 struct timespec ts; int ret = 0; ret = clock_gettime(CLOCK_MONOTONIC, &ts); snprintf(timestamp, 16, "%8ld%c%6ld", ts.tv_sec, '.', ts.tv_nsec); #endif msg += 16; } timestamp[15] = '\0';-----------------------------------------------------------------將msg分割成timestamp和msg兩部分、 if (option_mask32 & OPT_kmsg) { log_to_kmsg(pri, msg); return; } if (option_mask32 & OPT_small)-------------------------------------------------------根據syslogd選項'-S'來決定輸出內容。 sprintf(G.printbuf, "%s %s\n", timestamp, msg); else { char res[20]; parse_fac_prio_20(pri, res); sprintf(G.printbuf, "%s %.64s %s %s\n", timestamp, G.hostname, res, msg);--------增長hostname和facility/level輸出。 } /* Log message locally (to file or shared mem) */ #if ENABLE_FEATURE_SYSLOGD_CFG { bool match = 0; logRule_t *rule; uint8_t facility = LOG_FAC(pri); uint8_t prio_bit = 1 << LOG_PRI(pri); for (rule = G.log_rules; rule; rule = rule->next) { if (rule->enabled_facility_priomap[facility] & prio_bit) {--------------------若是當前msg的prio知足/etc/syslog.conf中某一個條件,則將log內容輸出到對應的rule->file中。若是一個msd知足多個rule,那麼輸出屢次。 log_locally(now, G.printbuf, rule->file); match = 1; } } if (match)-------------------------------------------------------------------------若是msg知足rule中的一個,則退出。不會進入下面的全局性/var/log/messages。 return; } #endif if (LOG_PRI(pri) < G.logLevel) { #if ENABLE_FEATURE_IPC_SYSLOG if ((option_mask32 & OPT_circularlog) && G.shbuf) { log_to_shmem(G.printbuf); return; } #endif log_locally(now, G.printbuf, &G.logFile);-------------------------------------------不在/etc/syslog.conf中的msg,知足設置的loglevel後。 } }
log_locally()將log保存到指定的文件。
/* Print a message to the log file. */ static void log_locally(time_t now, char *msg, logFile_t *log_file) { #ifdef SYSLOGD_WRLOCK struct flock fl; #endif int len = strlen(msg); /* fd can't be 0 (we connect fd 0 to /dev/log socket) */----------------------------log_file->fd句柄0給/dev/log使用,1給stdout使用。 /* fd is 1 if "-O -" is in use */ if (log_file->fd > 1) { if (!now) now = time(NULL); if (log_file->last_log_time != now) {-------------------------------------------每一秒鐘開關一次log_file,讓用戶有刪除log_file的機會。 log_file->last_log_time = now; close(log_file->fd); goto reopen; } } else if (log_file->fd == 1) { /* We are logging to stdout: do nothing */ } else { if (LONE_DASH(log_file->path)) { log_file->fd = 1; /* log_file->isRegular = 0; - already is */ } else { reopen: log_file->fd = open(log_file->path, O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND | O_NONBLOCK, 0666);----------------------------------------------------------------打開log_file文件。 if (log_file->fd < 0) {-------------------------------------------------------打開失敗輸出到/dev/console。 /* cannot open logfile? - print to /dev/console then */ int fd = device_open(DEV_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK); if (fd < 0) fd = 2; /* then stderr, dammit */ full_write(fd, msg, len); if (fd != 2) close(fd); return; } #if ENABLE_FEATURE_ROTATE_LOGFILE { struct stat statf; log_file->isRegular = (fstat(log_file->fd, &statf) == 0 && S_ISREG(statf.st_mode)); /* bug (mostly harmless): can wrap around if file > 4gb */ log_file->size = statf.st_size; } #endif } } #ifdef SYSLOGD_WRLOCK fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 1; fl.l_type = F_WRLCK; fcntl(log_file->fd, F_SETLKW, &fl); #endif #if ENABLE_FEATURE_ROTATE_LOGFILE if (G.logFileSize && log_file->isRegular && log_file->size > G.logFileSize) {---------判斷當前寫入是否會發生尺寸溢出,超出分隔尺寸的話。進行文件分隔。 if (G.logFileRotate) { /* always 0..99 */ int i = strlen(log_file->path) + 3 + 1; char oldFile[i]; char newFile[i]; i = G.logFileRotate - 1; /* rename: f.8 -> f.9; f.7 -> f.8; ... */ while (1) {-------------------------------------------------------------------將已有的文件名末尾數字加1,文件名末尾達到最大數字的文件將被覆蓋丟棄。 sprintf(newFile, "%s.%d", log_file->path, i); if (i == 0) break; sprintf(oldFile, "%s.%d", log_file->path, --i); /* ignore errors - file might be missing */ rename(oldFile, newFile); } /* newFile == "f.0" now */ rename(log_file->path, newFile);----------------------------------------------將messages變成messages.0. } unlink(log_file->path); #ifdef SYSLOGD_WRLOCK fl.l_type = F_UNLCK; fcntl(log_file->fd, F_SETLKW, &fl); #endif close(log_file->fd); goto reopen; } /* TODO: what to do on write errors ("disk full")? */ len = full_write(log_file->fd, msg, len);---------------------------------------------將msg寫入文件。 if (len > 0) log_file->size += len; #else full_write(log_file->fd, msg, len); #endif #ifdef SYSLOGD_WRLOCK fl.l_type = F_UNLCK; fcntl(log_file->fd, F_SETLKW, &fl); #endif }
通過如上分析可知內核log如何被klogd經過klogctl()獲取,調用syslog()轉發;應用如何經過syslog()發送消息到/dev/log;syslod是如何從/dev/log接收信息,並進行分發的。
static void parse_syslogdcfg(const char *file) { char *t; logRule_t **pp_rule; /* tok[0] set of selectors */ /* tok[1] file name */ /* tok[2] has to be NULL */ char *tok[3]; parser_t *parser; parser = config_open2(file ? file : "/etc/syslog.conf", file ? xfopen_for_read : fopen_for_read); if (!parser) /* didn't find default /etc/syslog.conf */ /* proceed as if we built busybox without config support */ return; /* use ptr to ptr to avoid checking whether head was initialized */ pp_rule = &G.log_rules; /* iterate through lines of config, skipping comments */ while (config_read(parser, tok, 3, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) {-------------------------讀取syslog.conf每一行。 char *cur_selector; logRule_t *cur_rule; /* unexpected trailing token? */ if (tok[2]) goto cfgerr; cur_rule = *pp_rule = xzalloc(sizeof(*cur_rule)); cur_selector = tok[0]; /* iterate through selectors: "kern.info;kern.!err;..." */ do {-----------------------------------------------------------------------------------------------編譯一行中每個selector。 const CODE *code; char *next_selector; uint8_t negated_prio; /* "kern.!err" */ uint8_t single_prio; /* "kern.=err" */ uint32_t facmap; /* bitmap of enabled facilities */ uint8_t primap; /* bitmap of enabled priorities */ unsigned i; next_selector = strchr(cur_selector, ';'); if (next_selector) *next_selector++ = '\0'; t = strchr(cur_selector, '.'); if (!t) goto cfgerr; *t++ = '\0'; /* separate facility from priority */ negated_prio = 0; single_prio = 0;-------------------------------------------------------------------------------解析selector中的priority,五種狀況!、=、*、none、info/notice/debu/err。 if (*t == '!') {-------------------------------------------------------------------------------排除特定優先級。 negated_prio = 1; ++t; } if (*t == '=') {-------------------------------------------------------------------------------指定特定優先級。 single_prio = 1; ++t; } /* parse priority */ if (*t == '*')---------------------------------------------------------------------------------全部優先級。 primap = 0xff; /* all 8 log levels enabled */ else { uint8_t priority; code = find_by_name(t, prioritynames); if (!code) goto cfgerr; primap = 0; priority = code->c_val; if (priority == INTERNAL_NOPRI) {-----------------------------------------------------------INTERNAL_NOPRI爲0x10,這裏須要取反,因此全部優先級都不顯示。 /* ensure we take "enabled_facility_priomap[fac] &= 0" branch below */ negated_prio = 1; } else { priority = 1 << priority; do { primap |= priority; if (single_prio)---------------------------------------------------------------------特定優先級狀況,只置一位。 break; priority >>= 1;----------------------------------------------------------------------將當前優先級以及更高優先級位置位。 } while (priority); if (negated_prio)------------------------------------------------------------------------'!'狀況,下面enabled_facility_priomap還會特殊處理。 primap = ~primap; } } /* parse facility */------------------------------------------------------------------------------解析facility狀況。 if (*cur_selector == '*')-------------------------------------------------------------------------全部facility。 facmap = (1<<LOG_NFACILITIES) - 1; else { char *next_facility; facmap = 0; t = cur_selector; /* iterate through facilities: "kern,daemon.<priospec>" */ do {------------------------------------------------------------------------------------------多facility狀況。 next_facility = strchr(t, ','); if (next_facility) *next_facility++ = '\0'; code = find_by_name(t, facilitynames); if (!code) goto cfgerr; /* "mark" is not a real facility, skip it */ if (code->c_val != INTERNAL_MARK) facmap |= 1<<(LOG_FAC(code->c_val)); t = next_facility; } while (t); } /* merge result with previous selectors */ for (i = 0; i < LOG_NFACILITIES; ++i) {------------------------------------------------------------合併多selector數據,合併到cur_rule中。 if (!(facmap & (1<<i))) continue; if (negated_prio) cur_rule->enabled_facility_priomap[i] &= primap; else cur_rule->enabled_facility_priomap[i] |= primap; } cur_selector = next_selector; } while (cur_selector); /* check whether current file name was mentioned in previous rules or * as global logfile (G.logFile). */ if (strcmp(G.logFile.path, tok[1]) == 0) { cur_rule->file = &G.logFile; goto found; } /* temporarily use cur_rule as iterator, but *pp_rule still points * to currently processing rule entry. * NOTE: *pp_rule points to the current (and last in the list) rule. */ for (cur_rule = G.log_rules; cur_rule != *pp_rule; cur_rule = cur_rule->next) { if (strcmp(cur_rule->file->path, tok[1]) == 0) { /* found - reuse the same file structure */ (*pp_rule)->file = cur_rule->file; cur_rule = *pp_rule; goto found; } } cur_rule->file = xzalloc(sizeof(*cur_rule->file)); cur_rule->file->fd = -1; cur_rule->file->path = xstrdup(tok[1]);--------------------------------------------------------一行一個rule,設置輸出文件路徑。 found: pp_rule = &cur_rule->next; } config_close(parser); return; cfgerr: bb_error_msg_and_die("error in '%s' at line %d", file ? file : "/etc/syslog.conf", parser->lineno); }
若是在syslogd選項中加-K,那麼就會將syslog()輸出寫到/dev/kmsg中。經過dmesg能夠讀取。
static void kmsg_init(void) { G.kmsgfd = xopen("/dev/kmsg", O_WRONLY);------------------------------------------打開/dev/kmsg文件。 /* * kernel < 3.5 expects single char printk KERN_* priority prefix, * from 3.5 onwards the full syslog facility/priority format is supported */ if (get_linux_version_code() < KERNEL_VERSION(3,5,0)) G.primask = LOG_PRIMASK; else G.primask = -1; } static void kmsg_cleanup(void) { if (ENABLE_FEATURE_CLEAN_UP) close(G.kmsgfd); } /* Write message to /dev/kmsg */ static void log_to_kmsg(int pri, const char *msg) { /* * kernel < 3.5 expects single char printk KERN_* priority prefix, * from 3.5 onwards the full syslog facility/priority format is supported */ pri &= G.primask; full_write(G.kmsgfd, G.printbuf, sprintf(G.printbuf, "<%d>%s\n", pri, msg));-------加上pri,和msg寫入/dev/kmsg。 }
/* our shared key (syslogd.c and logread.c must be in sync) */ enum { KEY_ID = 0x414e4547 }; /* "GENA" */ static void ipcsyslog_cleanup(void) { if (G.shmid != -1) { shmdt(G.shbuf); } if (G.shmid != -1) { shmctl(G.shmid, IPC_RMID, NULL); } if (G.s_semid != -1) { semctl(G.s_semid, 0, IPC_RMID, 0); } } static void ipcsyslog_init(void) { if (DEBUG) printf("shmget(%x, %d,...)\n", (int)KEY_ID, G.shm_size); G.shmid = shmget(KEY_ID, G.shm_size, IPC_CREAT | 0644); if (G.shmid == -1) { bb_perror_msg_and_die("shmget"); } G.shbuf = shmat(G.shmid, NULL, 0); if (G.shbuf == (void*) -1L) { /* shmat has bizarre error return */ bb_perror_msg_and_die("shmat"); } memset(G.shbuf, 0, G.shm_size); G.shbuf->size = G.shm_size - offsetof(struct shbuf_ds, data) - 1; /*G.shbuf->tail = 0;*/ /* we'll trust the OS to set initial semval to 0 (let's hope) */ G.s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023); if (G.s_semid == -1) { if (errno == EEXIST) { G.s_semid = semget(KEY_ID, 2, 0); if (G.s_semid != -1) return; } bb_perror_msg_and_die("semget"); } } /* Write message to shared mem buffer */ static void log_to_shmem(const char *msg) { int old_tail, new_tail; int len; if (semop(G.s_semid, G.SMwdn, 3) == -1) { bb_perror_msg_and_die("SMwdn"); } /* Circular Buffer Algorithm: * -------------------------- * tail == position where to store next syslog message. * tail's max value is (shbuf->size - 1) * Last byte of buffer is never used and remains NUL. */ len = strlen(msg) + 1; /* length with NUL included */ again: old_tail = G.shbuf->tail; new_tail = old_tail + len; if (new_tail < G.shbuf->size) { /* store message, set new tail */ memcpy(G.shbuf->data + old_tail, msg, len); G.shbuf->tail = new_tail; } else { /* k == available buffer space ahead of old tail */ int k = G.shbuf->size - old_tail; /* copy what fits to the end of buffer, and repeat */ memcpy(G.shbuf->data + old_tail, msg, k); msg += k; len -= k; G.shbuf->tail = 0; goto again; } if (semop(G.s_semid, G.SMwup, 1) == -1) { bb_perror_msg_and_die("SMwup"); } if (DEBUG) printf("tail:%d\n", G.shbuf->tail); }
int logread_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int logread_main(int argc UNUSED_PARAM, char **argv) { unsigned cur; int log_semid; /* ipc semaphore id */ int log_shmid; /* ipc shared memory id */ int follow = getopt32(argv, "fF"); INIT_G(); log_shmid = shmget(KEY_ID, 0, 0); if (log_shmid == -1) bb_perror_msg_and_die("can't %s syslogd buffer", "find"); /* Attach shared memory to our char* */ shbuf = shmat(log_shmid, NULL, SHM_RDONLY); if (shbuf == NULL) bb_perror_msg_and_die("can't %s syslogd buffer", "access"); log_semid = semget(KEY_ID, 0, 0); if (log_semid == -1) error_exit("can't get access to semaphores for syslogd buffer"); bb_signals(BB_FATAL_SIGS, interrupted); /* Suppose atomic memory read */ /* Max possible value for tail is shbuf->size - 1 */ cur = shbuf->tail; /* Loop for -f or -F, one pass otherwise */ do { unsigned shbuf_size; unsigned shbuf_tail; const char *shbuf_data; #if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING int i; int len_first_part; int len_total = len_total; /* for gcc */ char *copy = copy; /* for gcc */ #endif if (semop(log_semid, SMrdn, 2) == -1) error_exit("semop[SMrdn]"); /* Copy the info, helps gcc to realize that it doesn't change */ shbuf_size = shbuf->size; shbuf_tail = shbuf->tail; shbuf_data = shbuf->data; /* pointer! */ if (DEBUG) printf("cur:%u tail:%u size:%u\n", cur, shbuf_tail, shbuf_size); if (!(follow & 1)) { /* not -f */ /* if -F, "convert" it to -f, so that we don't * dump the entire buffer on each iteration */ follow >>= 1; /* advance to oldest complete message */ /* find NUL */ cur += strlen(shbuf_data + cur); if (cur >= shbuf_size) { /* last byte in buffer? */ cur = strnlen(shbuf_data, shbuf_tail); if (cur == shbuf_tail) goto unlock; /* no complete messages */ } /* advance to first byte of the message */ cur++; if (cur >= shbuf_size) /* last byte in buffer? */ cur = 0; } else { /* -f */ if (cur == shbuf_tail) { sem_up(log_semid); fflush_all(); sleep(1); /* TODO: replace me with a sleep_on */ continue; } } /* Read from cur to tail */ #if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING len_first_part = len_total = shbuf_tail - cur; if (len_total < 0) { /* message wraps: */ /* [SECOND PART.........FIRST PART] */ /* ^data ^tail ^cur ^size */ len_total += shbuf_size; } copy = xmalloc(len_total + 1); if (len_first_part < 0) { /* message wraps (see above) */ len_first_part = shbuf_size - cur; memcpy(copy + len_first_part, shbuf_data, shbuf_tail); } memcpy(copy, shbuf_data + cur, len_first_part); copy[len_total] = '\0'; cur = shbuf_tail; #else while (cur != shbuf_tail) { fputs(shbuf_data + cur, stdout); cur += strlen(shbuf_data + cur) + 1; if (cur >= shbuf_size) cur = 0; } #endif unlock: /* release the lock on the log chain */ sem_up(log_semid); #if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING for (i = 0; i < len_total; i += strlen(copy + i) + 1) { fputs(copy + i, stdout); } free(copy); #endif fflush_all(); } while (follow); /* shmdt(shbuf); - on Linux, shmdt is not mandatory on exit */ fflush_stdout_and_exit(EXIT_SUCCESS); }
上述講到了syslog機制的各類相關內容,kernel相關的有syslog系統調用、klogctl()、klogd;syslogd相關的有syslog()、syslogd、syslog.conf等。
在使用中有兩個地方能夠配置syslog行爲:一是啓動syslogd/klogd時設置選項;一是配置syslog.conf文件,rule項是越少越好。
在應用中調用syslog()輸出日誌。
參考文檔:《linux日誌:syslogd和klogd及syslog》、《syslogd以及syslog.conf文件解讀說明》