syslogd详解

版权声明:本文为博主原创文章,转载请务必注明作者与原文链接。 https://blog.csdn.net/jingerppp/article/details/89105854

1. syslogd架构

  • App通过syslog的接口进行log的打印,该接口define在syslog.h中;
  • syslog会通过socket发送消息,将log发送给syslogd;
  • syslogd在获取到log后,会进行log的处理;
  • syslogd可以将log保存到本地,也可以发送到共享内存或远程服务器

2. openlog()

在syslog接口使用之前,有必要通过openlog进行一些log信息的初始化工作,openlog定义在系统头文件syslog.h中,函数原型:

void openlog (const char *__ident, int __option, int __facility)
  • 第一个参数为log tag,同Android中LOG_TAG;
  • 第二个参数为log flags,定义在syslog.h中;

如下:

#define	LOG_PID		0x01	/* log the pid with each message */
#define	LOG_CONS	0x02	/* log on the console if errors in sending */
#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 */

例如:

使用LOG_PID,会在每一条log中添加pid信息;

使用LOG_CONS,在出现error信息的时候,会将log输出到congsole上。

  • 第三个参数为syslog 的facility,syslog.h中一共有23种选择;
/* 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)

例如:

使用LOG_KERN,表示log为kernel信息;

使用LOB_USER,表示log为普通用户信息,一般package使用的信息可以设为LOG_USER;

使用LOG_DAEMON,表示log为系统守护进程信息;

下面来看下openlog() 的源码:

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);
			  if (LogFile == -1)
			    return;
			}
		}
		if (LogFile != -1 && !connected)
		{
			int old_errno = errno;
			if (__connect(LogFile, &SyslogAddr, sizeof(SyslogAddr))
			    == -1)
			{
				int saved_errno = errno;
				int fd = LogFile;
				LogFile = -1;
				(void)__close(fd);
				__set_errno (old_errno);
				if (saved_errno == EPROTOTYPE)
				{
					/* retry with the other type: */
					LogType = (LogType == SOCK_DGRAM
						   ? SOCK_STREAM : SOCK_DGRAM);
					++retry;
					continue;
				}
			} else
				connected = 1;
		}
		break;
	}
}

主要是建立socket通信,通信设备是/dev/log,注意,如果openlog() 的第二个参数没有设定LOG_NDELAY,这里不会建立socket,如果等到syslog()接口调用后才能触发。

3. syslog()

对于syslogd来说,log是通过syslog传入的,函数的原型:

void syslog (int __pri, const char *__fmt, ...)

应用可以通过openlog进行初始化,例如log的tag信息或者是pid信息。如果不进行openlog而直接使用syslog也是可以的,在syslog中会进行openlog的操作。当然,不进行openlog操作,log中有些信息就无法显示,例如pid信息。

下面来看下syslog中的参数:

  • 第一个参数为log的优先级,与Android中的LOG_DEBUG或LOG_INFO等类似;
#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 */

这里的priority定义与Android略有不同,例如LOG_CRIT表示critical的信息。

  • 第二个参数为log的具体信息,同printf;

下面来看下源码:

__vsyslog_chk(int pri, int flag, const char *fmt, va_list ap)
{
    ...
    ...

	/* Get connected, output the message to the local logger. */
	if (!connected)
		openlog_internal(LogTag, LogStat | LOG_NDELAY, 0);

	/* 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)
	  {
	    if (connected)
	      {
		/* 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 */
		/*
		 * Output the message to the console; don't worry
		 * about blocking, if console blocks everything will.
		 * Make sure the error reported is the one from the
		 * syslogd failure.
		 */
		if (LogStat & LOG_CONS &&
		    (fd = __open(_PATH_CONSOLE, O_WRONLY|O_NOCTTY, 0)) >= 0)
		  {
		    __dprintf (fd, "%s\r\n", buf + msgoff);
		    (void)__close(fd);
		  }
	      }
	  }
    ...
    ...
}

通过socket将带有log的信息发送出去。如果在之前没有openlog(),这里会进行openlog,但是参数就不一样了。注意的是,如果openlog()中没有设定LOG_NDELAY,openlog是不会建立socket,需要等到这里进行创建。

4. closelog()

在使用syslog结束之后通过closelog进行扫尾工作。函数原型:

void closelog (void)

主要就是将socket 关闭。

5. syslog.conf

syslogd机制中可以通过 syslog.conf 对log进行管理配置。默认存放在etc目录下。当然,也可以通过initrc进行动态设置。

设置syslog.conf必须按照形式:

facility.priority            action

例如:

user.debug             /var/log/log.debug

将openlog第二个参数为LOG_USER,syslog第一个参数为LOG_DEBUG的所有信息存放到/var/log/log.debug文件中。

  • priority有:

alert、crit、debug、emerg、err、error、info、none、notice、panic、warn、warning

  • facility有:

auth、authpriv、cron、daemon、ftp、kern、mail、lpr、mark、news、security、syslog、user、uucp、local0~ local7

举例:

#mail相关的info级别的log记录到mail.log中
mail.info         /var/log/mail.log

#表示将auth相关的,级别为info的log记录到10.1.1.1,前提是10.1.1.1要能接收其发来的日志信息
auth.=info        @10.1.1.1 

#user相关的所有log,但不包括error级别的log
user.!=error      /var/log/user.log

#记录所有info级别的log
*.info           /var/log/log.info

#如果是多个来源,中间可以用分号隔开
cron.info;mail.info   /var/log/mail.log

#相当于cron.info;mail.info
cron,mail.info

#mail相关所有级别的log,但不包括info级别的信息
mail.*;mail.!=info

6. 读取log

syslogd中提供了很多种获取log 的方式,其中一种就是共享内存。当syslogd获取到log信息的时候,会通过log_to_shmem()将buf传入共享内存,用于其他的进程读取:

	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;
	}

在另外的进程中通过shmat进行获取:

	/* 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");

将拿到的shbuf进行处理,例如输出到stdout中。

需要注意的是:syslogd中要求local和ipc是互斥的,所以想要log既能保存到本地log文件,又能通过共享内存方式读取,需要更改代码。

 

另外,需要使用ipc方式,在运行syslogd的时候需要加上参数 -C,这里需要特别注意。

 

7. logread.c

在busybox中提供了通过共享内存方式进行读取log的程序logread.c,可以将syslogd共享内存中的log输出到stdout中。

将CONFIG_LOGREAD打开就可以编译出来,默认放置在sbin目录下,运行命令:

./sbin/logread -f

来看下源码:

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;

		printf("ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING: %d\n",ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING);
#if 0
		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 0
		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 0
		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);
}

8. 重启syslogd

有两种方式,将syslogd kill掉,然后重新运行。

另一种方式,在/etc/init.d/目录下有个相关的可执行程序,例如名称为S01logging。运行:

./etc/init.d/S01logging restart
or
./etc/init.d/S01logging reload

更多详细的,以source code 为准!!

猜你喜欢

转载自blog.csdn.net/jingerppp/article/details/89105854