diff --git a/doc/configuration.txt b/doc/configuration.txt index 7679998e9..9f4fb5934 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -837,11 +837,30 @@ log
[len ] [format ] [max level [min level] no port is specified, 514 is used by default (the standard syslog port). - - A filesystem path to a UNIX domain socket, keeping in mind + - A filesystem path to a datagram UNIX domain socket, keeping in mind considerations for chroot (be sure the path is accessible inside the chroot) and uid/gid (be sure the path is appropriately writable). + - A file descriptor number in the form "fd@", which may point + to a pipe, terminal, or socket. In this case unbuffered logs are used + and one writev() call per log is performed. This is a bit expensive + but acceptable for most workloads. Messages sent this way will not be + truncated but may be dropped, in which case the DroppedLogs counter + will be incremented. The writev() call is atomic even on pipes for + messages up to PIPE_BUF size, which POSIX recommends to be at least + 512 and which is 4096 bytes on most modern operating systems. Any + larger message may be interleaved with messages from other processes. + Exceptionally for debugging purposes the file descriptor may also be + directed to a file, but doing so will significantly slow haproxy down + as non-blocking calls will be ignored. Also there will be no way to + purge nor rotate this file without restarting the process. Note that + the configured syslog format is preserved, so the output is suitable + for use with a TCP syslog server. + + - "stdout" / "stderr", which are respectively aliases for "fd@1" and + "fd@2", see above. + You may want to reference some environment variables in the address parameter, see section 2.3 about environment variables. @@ -5101,8 +5120,29 @@ no log inside the chroot) and uid/gid (be sure the path is appropriately writable). - You may want to reference some environment variables in the - address parameter, see section 2.3 about environment variables. + - A file descriptor number in the form "fd@", which may + point to a pipe, terminal, or socket. In this case unbuffered + logs are used and one writev() call per log is performed. This + is a bit expensive but acceptable for most workloads. Messages + sent this way will not be truncated but may be dropped, in + which case the DroppedLogs counter will be incremented. The + writev() call is atomic even on pipes for messages up to + PIPE_BUF size, which POSIX recommends to be at least 512 and + which is 4096 bytes on most modern operating systems. Any + larger message may be interleaved with messages from other + processes. Exceptionally for debugging purposes the file + descriptor may also be directed to a file, but doing so will + significantly slow haproxy down as non-blocking calls will be + ignored. Also there will be no way to purge nor rotate this + file without restarting the process. Note that the configured + syslog format is preserved, so the output is suitable for use + with a TCP syslog server. + + - "stdout" / "stderr", which are respectively aliases for "fd@1" + and "fd@2", see above. + + You may want to reference some environment variables in the + address parameter, see section 2.3 about environment variables. is an optional maximum line length. Log lines larger than this value will be truncated before being sent. The reason is that diff --git a/src/log.c b/src/log.c index 3434550a3..c8ee6a943 100644 --- a/src/log.c +++ b/src/log.c @@ -754,6 +754,12 @@ int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err) goto error; } + /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */ + if (strcmp(args[1], "stdout") == 0) + args[1] = "fd@1"; + else if (strcmp(args[1], "stderr") == 0) + args[1] = "fd@2"; + logsrv = calloc(1, sizeof(*logsrv)); if (!logsrv) { memprintf(err, "out of memory"); @@ -1342,9 +1348,13 @@ void __send_log(struct proxy *p, int level, char *message, size_t size, char *sd if (unlikely(*plogfd < 0)) { /* socket not successfully initialized yet */ - int proto = logsrv->addr.ss_family == AF_UNIX ? 0 : IPPROTO_UDP; - - if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM, proto)) < 0) { + if (logsrv->addr.ss_family == AF_UNSPEC) { + /* the socket's address is a file descriptor */ + *plogfd = ((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr; + fcntl(*plogfd, F_SETFL, O_NONBLOCK); + } + else if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM, + (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) { static char once; if (!once) { @@ -1473,10 +1483,16 @@ void __send_log(struct proxy *p, int level, char *message, size_t size, char *sd iovec[7].iov_base = "\n"; /* insert a \n at the end of the message */ iovec[7].iov_len = 1; - msghdr.msg_name = (struct sockaddr *)&logsrv->addr; - msghdr.msg_namelen = get_addr_len(&logsrv->addr); + if (logsrv->addr.ss_family == AF_UNSPEC) { + /* the target is a direct file descriptor */ + sent = writev(*plogfd, iovec, 8); + } + else { + msghdr.msg_name = (struct sockaddr *)&logsrv->addr; + msghdr.msg_namelen = get_addr_len(&logsrv->addr); - sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL); + sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL); + } if (sent < 0) { static char once; @@ -1485,7 +1501,7 @@ void __send_log(struct proxy *p, int level, char *message, size_t size, char *sd HA_ATOMIC_ADD(&dropped_logs, 1); else if (!once) { once = 1; /* note: no need for atomic ops here */ - ha_alert("sendmsg() failed in logger #%d: %s (errno=%d)\n", + ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n", nblogger, strerror(errno), errno); } }