mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2024-12-15 07:54:33 +00:00
MEDIUM: log/sink: re-work and merge of build message API.
This patch merges build message code between sink and log and introduce a new API based on struct ist array to prepare message header with zero copy, targeting the log forwarding feature. Log format 'iso' and 'timed' are now avalaible on logs line. A new log format 'priority' is also added.
This commit is contained in:
parent
3835c0dcb5
commit
546488559a
@ -1051,12 +1051,26 @@ log <address> [len <length>] [format <format>] [sample <ranges>:<smp_size>]
|
||||
rfc5424 The RFC5424 syslog message format.
|
||||
(https://tools.ietf.org/html/rfc5424)
|
||||
|
||||
priority A message containing only a level plus syslog facility between
|
||||
angle brackets such as '<63>', followed by the text. The PID,
|
||||
date, time, process name and system name are omitted. This is
|
||||
designed to be used with a local log server.
|
||||
|
||||
short A message containing only a level between angle brackets such as
|
||||
'<3>', followed by the text. The PID, date, time, process name
|
||||
and system name are omitted. This is designed to be used with a
|
||||
local log server. This format is compatible with what the systemd
|
||||
logger consumes.
|
||||
|
||||
timed A message containing only a level between angle brackets such as
|
||||
'<3>', followed by ISO date and by the text. The PID, process
|
||||
name and system name are omitted. This is designed to be
|
||||
used with a local log server.
|
||||
|
||||
iso A message containing only the ISO date, followed by the text.
|
||||
The PID, process name and system name are omitted. This is
|
||||
designed to be used with a local log server.
|
||||
|
||||
raw A message containing only the text. The level, PID, date, time,
|
||||
process name and system name are omitted. This is designed to be
|
||||
used in containers or during development, where the severity only
|
||||
@ -2663,6 +2677,11 @@ format <format>
|
||||
local log server. This format is compatible with what the systemd
|
||||
logger consumes.
|
||||
|
||||
priority A message containing only a level plus syslog facility between angle
|
||||
brackets such as '<63>', followed by the text. The PID, date, time,
|
||||
process name and system name are omitted. This is designed to be used
|
||||
with a local log server.
|
||||
|
||||
timed A message containing only a level between angle brackets such as
|
||||
'<3>', followed by ISO date and by the text. The PID, process
|
||||
name and system name are omitted. This is designed to be
|
||||
@ -6778,12 +6797,26 @@ no log
|
||||
rfc5424 The RFC5424 syslog message format.
|
||||
(https://tools.ietf.org/html/rfc5424)
|
||||
|
||||
priority A message containing only a level plus syslog facility between
|
||||
angle brackets such as '<63>', followed by the text. The PID,
|
||||
date, time, process name and system name are omitted. This is
|
||||
designed to be used with a local log server.
|
||||
|
||||
short A message containing only a level between angle brackets such as
|
||||
'<3>', followed by the text. The PID, date, time, process name
|
||||
and system name are omitted. This is designed to be used with a
|
||||
local log server. This format is compatible with what the
|
||||
systemd logger consumes.
|
||||
|
||||
timed A message containing only a level between angle brackets such as
|
||||
'<3>', followed by ISO date and by the text. The PID, process
|
||||
name and system name are omitted. This is designed to be
|
||||
used with a local log server.
|
||||
|
||||
iso A message containing only the ISO date, followed by the text.
|
||||
The PID, process name and system name are omitted. This is
|
||||
designed to be used with a local log server.
|
||||
|
||||
raw A message containing only the text. The level, PID, date, time,
|
||||
process name and system name are omitted. This is designed to
|
||||
be used in containers or during development, where the severity
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
#define NB_LOG_FACILITIES 24
|
||||
#define NB_LOG_LEVELS 8
|
||||
#define NB_MSG_IOVEC_ELEMENTS 8
|
||||
#define NB_LOG_HDR_MAX_ELEMENTS 15
|
||||
#define SYSLOG_PORT 514
|
||||
#define UNIQUEID_LEN 128
|
||||
|
||||
@ -67,14 +67,33 @@
|
||||
#define LW_FRTIP 8192 /* frontend IP */
|
||||
#define LW_XPRT 16384 /* transport layer information (eg: SSL) */
|
||||
|
||||
#define LOG_LEGACYTIME_LEN 15
|
||||
#define LOG_ISOTIME_MINLEN 20
|
||||
#define LOG_ISOTIME_MAXLEN 32
|
||||
|
||||
/* enum for log format */
|
||||
enum {
|
||||
LOG_FORMAT_RFC3164 = 0,
|
||||
enum log_fmt {
|
||||
LOG_FORMAT_UNSPEC = 0,
|
||||
LOG_FORMAT_RFC3164,
|
||||
LOG_FORMAT_RFC5424,
|
||||
LOG_FORMAT_PRIO,
|
||||
LOG_FORMAT_SHORT,
|
||||
LOG_FORMAT_TIMED,
|
||||
LOG_FORMAT_ISO,
|
||||
LOG_FORMAT_RAW,
|
||||
LOG_FORMATS, /* number of supported log formats, must always be last */
|
||||
LOG_FORMATS /* number of supported log formats, must always be last */
|
||||
};
|
||||
|
||||
/* enum log header meta data */
|
||||
enum log_meta {
|
||||
LOG_META_PRIO,
|
||||
LOG_META_TIME,
|
||||
LOG_META_HOST,
|
||||
LOG_META_TAG,
|
||||
LOG_META_PID,
|
||||
LOG_META_MSGID,
|
||||
LOG_META_STDATA,
|
||||
LOG_META_FIELDS /* must always be the last */
|
||||
};
|
||||
|
||||
/* log target types */
|
||||
@ -210,7 +229,7 @@ struct logsrv {
|
||||
struct sink *sink;
|
||||
char *ring_name;
|
||||
enum log_tgt type;
|
||||
int format;
|
||||
enum log_fmt format;
|
||||
int facility;
|
||||
int level;
|
||||
int minlvl;
|
||||
|
@ -43,8 +43,6 @@ extern char default_rfc5424_sd_log_format[];
|
||||
|
||||
extern unsigned int dropped_logs;
|
||||
|
||||
extern THREAD_LOCAL char *logheader;
|
||||
extern THREAD_LOCAL char *logheader_rfc5424;
|
||||
extern THREAD_LOCAL char *logline;
|
||||
extern THREAD_LOCAL char *logline_rfc5424;
|
||||
|
||||
@ -99,9 +97,9 @@ void send_log(struct proxy *p, int level, const char *format, ...)
|
||||
void __send_log(struct list *logsrvs, struct buffer *tag, int level, char *message, size_t size, char *sd, size_t sd_size);
|
||||
|
||||
/*
|
||||
* returns log format for <fmt> or -1 if not found.
|
||||
* returns log format for <fmt> or LOG_FORMAT_UNSPEC if not found.
|
||||
*/
|
||||
int get_log_format(const char *fmt);
|
||||
enum log_fmt get_log_format(const char *fmt);
|
||||
|
||||
/*
|
||||
* returns log level for <lev> or -1 if not found.
|
||||
@ -161,6 +159,7 @@ static inline int build_logline(struct stream *s, char *dst, size_t maxsize, str
|
||||
return sess_build_logline(strm_sess(s), s, dst, maxsize, list_format);
|
||||
}
|
||||
|
||||
struct ist *build_log_header(enum log_fmt format, int level, int facility, struct ist *metadata, size_t *nbelem);
|
||||
#endif /* _HAPROXY_LOG_H */
|
||||
|
||||
/*
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <import/ist.h>
|
||||
#include <haproxy/api-t.h>
|
||||
#include <haproxy/log-t.h>
|
||||
|
||||
/* A sink may be of 4 distinct types :
|
||||
* - file descriptor (such as stdout)
|
||||
@ -35,18 +36,6 @@ enum sink_type {
|
||||
SINK_TYPE_BUFFER, // events sent to a ring buffer
|
||||
};
|
||||
|
||||
/* This indicates the default event format, which is the destination's
|
||||
* preferred format, but may be overridden by the source.
|
||||
*/
|
||||
enum sink_fmt {
|
||||
SINK_FMT_RAW, // raw text sent as-is
|
||||
SINK_FMT_SHORT, // raw text prefixed with a syslog level
|
||||
SINK_FMT_ISO, // raw text prefixed with ISO time
|
||||
SINK_FMT_TIMED, // syslog level then ISO
|
||||
SINK_FMT_RFC3164, // regular syslog
|
||||
SINK_FMT_RFC5424, // extended syslog
|
||||
};
|
||||
|
||||
struct sink_forward_target {
|
||||
struct server *srv; // used server
|
||||
struct appctx *appctx; // appctx of current session
|
||||
@ -60,7 +49,7 @@ struct sink {
|
||||
struct list sink_list; // position in the sink list
|
||||
char *name; // sink name
|
||||
char *desc; // sink description
|
||||
enum sink_fmt fmt; // format expected by the sink
|
||||
enum log_fmt fmt; // format expected by the sink
|
||||
enum sink_type type; // type of storage
|
||||
uint32_t maxlen; // max message length (truncated above)
|
||||
struct proxy* forward_px; // proxy used to forward
|
||||
|
@ -29,11 +29,10 @@
|
||||
extern struct list sink_list;
|
||||
|
||||
struct sink *sink_find(const char *name);
|
||||
struct sink *sink_new_fd(const char *name, const char *desc, enum sink_fmt fmt, int fd);
|
||||
struct sink *sink_new_fd(const char *name, const char *desc, enum log_fmt, int fd);
|
||||
ssize_t __sink_write(struct sink *sink, const struct ist msg[], size_t nmsg,
|
||||
int level, int facility, struct ist * tag,
|
||||
struct ist *pid, struct ist *sd);
|
||||
int sink_announce_dropped(struct sink *sink, int facility, struct ist *pid);
|
||||
int level, int facility, struct ist * metadata);
|
||||
int sink_announce_dropped(struct sink *sink, int facility);
|
||||
|
||||
|
||||
/* tries to send <nmsg> message parts (up to 8, ignored above) from message
|
||||
@ -44,8 +43,7 @@ int sink_announce_dropped(struct sink *sink, int facility, struct ist *pid);
|
||||
* or <= 0 in other cases.
|
||||
*/
|
||||
static inline ssize_t sink_write(struct sink *sink, const struct ist msg[], size_t nmsg,
|
||||
int level, int facility, struct ist * tag,
|
||||
struct ist *pid, struct ist *sd)
|
||||
int level, int facility, struct ist *metadata)
|
||||
{
|
||||
ssize_t sent;
|
||||
|
||||
@ -57,7 +55,7 @@ static inline ssize_t sink_write(struct sink *sink, const struct ist msg[], size
|
||||
* position.
|
||||
*/
|
||||
HA_RWLOCK_WRLOCK(LOGSRV_LOCK, &sink->ctx.lock);
|
||||
sent = sink_announce_dropped(sink, facility, pid);
|
||||
sent = sink_announce_dropped(sink, facility);
|
||||
HA_RWLOCK_WRUNLOCK(LOGSRV_LOCK, &sink->ctx.lock);
|
||||
|
||||
if (!sent) {
|
||||
@ -69,7 +67,7 @@ static inline ssize_t sink_write(struct sink *sink, const struct ist msg[], size
|
||||
}
|
||||
|
||||
HA_RWLOCK_RDLOCK(LOGSRV_LOCK, &sink->ctx.lock);
|
||||
sent = __sink_write(sink, msg, nmsg, level, facility, tag, pid, sd);
|
||||
sent = __sink_write(sink, msg, nmsg, level, facility, metadata);
|
||||
HA_RWLOCK_RDUNLOCK(LOGSRV_LOCK, &sink->ctx.lock);
|
||||
|
||||
fail:
|
||||
|
760
src/log.c
760
src/log.c
@ -43,57 +43,34 @@
|
||||
#include <haproxy/version.h>
|
||||
|
||||
|
||||
struct log_fmt {
|
||||
struct log_fmt_st {
|
||||
char *name;
|
||||
struct {
|
||||
struct buffer sep1; /* first pid separator */
|
||||
struct buffer sep2; /* second pid separator */
|
||||
} pid;
|
||||
};
|
||||
|
||||
static const struct log_fmt log_formats[LOG_FORMATS] = {
|
||||
static const struct log_fmt_st log_formats[LOG_FORMATS] = {
|
||||
[LOG_FORMAT_RFC3164] = {
|
||||
.name = "rfc3164",
|
||||
.pid = {
|
||||
.sep1 = { .area = "[", .data = 1 },
|
||||
.sep2 = { .area = "]: ", .data = 3 }
|
||||
}
|
||||
},
|
||||
[LOG_FORMAT_RFC5424] = {
|
||||
.name = "rfc5424",
|
||||
.pid = {
|
||||
.sep1 = { .area = " ", .data = 1 },
|
||||
.sep2 = { .area = " - ", .data = 3 }
|
||||
}
|
||||
},
|
||||
[LOG_FORMAT_PRIO] = {
|
||||
.name = "priority",
|
||||
},
|
||||
[LOG_FORMAT_SHORT] = {
|
||||
.name = "short",
|
||||
.pid = {
|
||||
.sep1 = { .area = "", .data = 0 },
|
||||
.sep2 = { .area = " ", .data = 1 },
|
||||
}
|
||||
},
|
||||
[LOG_FORMAT_TIMED] = {
|
||||
.name = "timed",
|
||||
},
|
||||
[LOG_FORMAT_ISO] = {
|
||||
.name = "iso",
|
||||
},
|
||||
[LOG_FORMAT_RAW] = {
|
||||
.name = "raw",
|
||||
.pid = {
|
||||
.sep1 = { .area = "", .data = 0 },
|
||||
.sep2 = { .area = "", .data = 0 },
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
char *get_format_pid_sep1(int format, size_t *len)
|
||||
{
|
||||
*len = log_formats[format].pid.sep1.data;
|
||||
return log_formats[format].pid.sep1.area;
|
||||
}
|
||||
|
||||
char *get_format_pid_sep2(int format, size_t *len)
|
||||
{
|
||||
*len = log_formats[format].pid.sep2.data;
|
||||
return log_formats[format].pid.sep2.area;
|
||||
}
|
||||
|
||||
/*
|
||||
* This map is used with all the FD_* macros to check whether a particular bit
|
||||
* is set or not. Each bit represents an ACSII code. ha_bit_set() sets those
|
||||
@ -233,19 +210,6 @@ char default_rfc5424_sd_log_format[] = "- ";
|
||||
/* total number of dropped logs */
|
||||
unsigned int dropped_logs = 0;
|
||||
|
||||
/* This is a global syslog header, common to all outgoing messages in
|
||||
* RFC3164 format. It begins with time-based part and is updated by
|
||||
* update_log_hdr().
|
||||
*/
|
||||
THREAD_LOCAL char *logheader = NULL;
|
||||
THREAD_LOCAL char *logheader_end = NULL;
|
||||
|
||||
/* This is a global syslog header for messages in RFC5424 format. It is
|
||||
* updated by update_log_hdr_rfc5424().
|
||||
*/
|
||||
THREAD_LOCAL char *logheader_rfc5424 = NULL;
|
||||
THREAD_LOCAL char *logheader_rfc5424_end = NULL;
|
||||
|
||||
/* This is a global syslog message buffer, common to all outgoing
|
||||
* messages. It contains only the data part.
|
||||
*/
|
||||
@ -922,7 +886,7 @@ int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err)
|
||||
/* after the length, a format may be specified */
|
||||
if (strcmp(args[cur_arg], "format") == 0) {
|
||||
logsrv->format = get_log_format(args[cur_arg+1]);
|
||||
if (logsrv->format < 0) {
|
||||
if (logsrv->format == LOG_FORMAT_UNSPEC) {
|
||||
memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
|
||||
goto error;
|
||||
}
|
||||
@ -1179,16 +1143,18 @@ void qfprintf(FILE *out, const char *fmt, ...)
|
||||
}
|
||||
|
||||
/*
|
||||
* returns log format for <fmt> or -1 if not found.
|
||||
* returns log format, LOG_FORMAT_UNSPEC is return if not found.
|
||||
*/
|
||||
int get_log_format(const char *fmt)
|
||||
enum log_fmt get_log_format(const char *fmt)
|
||||
{
|
||||
int format;
|
||||
enum log_fmt format;
|
||||
|
||||
format = LOG_FORMATS - 1;
|
||||
while (format >= 0 && strcmp(log_formats[format].name, fmt))
|
||||
while (format > 0 && log_formats[format].name
|
||||
&& strcmp(log_formats[format].name, fmt))
|
||||
format--;
|
||||
|
||||
/* Note: 0 is LOG_FORMAT_UNSPEC */
|
||||
return format;
|
||||
}
|
||||
|
||||
@ -1431,94 +1397,6 @@ char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const str
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Re-generate time-based part of the syslog header in RFC3164 format at
|
||||
* the beginning of logheader once a second and return the pointer to the
|
||||
* first character after it.
|
||||
*/
|
||||
char *update_log_hdr(const time_t time)
|
||||
{
|
||||
static THREAD_LOCAL long tvsec;
|
||||
static THREAD_LOCAL struct buffer host = { };
|
||||
static THREAD_LOCAL int sep = 0;
|
||||
|
||||
if (unlikely(time != tvsec || logheader_end == NULL)) {
|
||||
/* this string is rebuild only once a second */
|
||||
struct tm tm;
|
||||
int hdr_len;
|
||||
|
||||
tvsec = time;
|
||||
get_localtime(tvsec, &tm);
|
||||
|
||||
if (unlikely(global.log_send_hostname != host.area)) {
|
||||
host.area = global.log_send_hostname;
|
||||
host.data = host.area ? strlen(host.area) : 0;
|
||||
sep = host.data ? 1 : 0;
|
||||
}
|
||||
|
||||
hdr_len = snprintf(logheader, global.max_syslog_len,
|
||||
"<<<<>%s %2d %02d:%02d:%02d %.*s%*s",
|
||||
monthname[tm.tm_mon],
|
||||
tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
|
||||
(int)host.data, host.area, sep, "");
|
||||
/* WARNING: depending upon implementations, snprintf may return
|
||||
* either -1 or the number of bytes that would be needed to store
|
||||
* the total message. In both cases, we must adjust it.
|
||||
*/
|
||||
if (hdr_len < 0 || hdr_len > global.max_syslog_len)
|
||||
hdr_len = global.max_syslog_len;
|
||||
|
||||
logheader_end = logheader + hdr_len;
|
||||
}
|
||||
|
||||
logheader_end[0] = 0; // ensure we get rid of any previous attempt
|
||||
|
||||
return logheader_end;
|
||||
}
|
||||
|
||||
/* Re-generate time-based part of the syslog header in RFC5424 format at
|
||||
* the beginning of logheader_rfc5424 once a second and return the pointer
|
||||
* to the first character after it.
|
||||
*/
|
||||
char *update_log_hdr_rfc5424(const time_t time, const suseconds_t frac)
|
||||
{
|
||||
static THREAD_LOCAL long tvsec;
|
||||
const char *gmt_offset;
|
||||
char c;
|
||||
|
||||
if (unlikely(time != tvsec || logheader_rfc5424_end == NULL)) {
|
||||
/* this string is rebuild only once a second */
|
||||
struct tm tm;
|
||||
int hdr_len;
|
||||
|
||||
tvsec = time;
|
||||
get_localtime(tvsec, &tm);
|
||||
gmt_offset = get_gmt_offset(time, &tm);
|
||||
|
||||
hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
|
||||
"<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d.000000%.3s:%.2s %s ",
|
||||
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
|
||||
tm.tm_hour, tm.tm_min, tm.tm_sec,
|
||||
gmt_offset, gmt_offset+3,
|
||||
global.log_send_hostname ? global.log_send_hostname : hostname);
|
||||
/* WARNING: depending upon implementations, snprintf may return
|
||||
* either -1 or the number of bytes that would be needed to store
|
||||
* the total message. In both cases, we must adjust it.
|
||||
*/
|
||||
if (hdr_len < 0 || hdr_len > global.max_syslog_len)
|
||||
hdr_len = global.max_syslog_len;
|
||||
|
||||
logheader_rfc5424_end = logheader_rfc5424 + hdr_len;
|
||||
}
|
||||
|
||||
/* utoa_pad add a trailing '\0' so we save the char to restore */
|
||||
c = logheader_rfc5424[33];
|
||||
utoa_pad(frac, logheader_rfc5424 + 27, 7);
|
||||
logheader_rfc5424[33] = c;
|
||||
|
||||
logheader_rfc5424_end[0] = 0; // ensure we get rid of any previous attempt
|
||||
|
||||
return logheader_rfc5424_end;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function sends the syslog message using a printf format string. It
|
||||
@ -1541,52 +1419,325 @@ void send_log(struct proxy *p, int level, const char *format, ...)
|
||||
__send_log((p ? &p->logsrvs : NULL), (p ? &p->log_tag : NULL), level,
|
||||
logline, data_len, default_rfc5424_sd_log_format, 2);
|
||||
}
|
||||
/*
|
||||
* This function builds a log header of given format using given
|
||||
* metadata, if format is set to LOF_FORMAT_UNSPEC, it tries
|
||||
* to determine format based on given metadas. It is useful
|
||||
* for log-forwarding to be able to forward any format without
|
||||
* settings.
|
||||
* This function returns a struct ist array of elements of the header
|
||||
* nbelem is set to the number of available elements.
|
||||
* Thos function returns currently a maximum of NB_LOG_HDR_IST_ELEMENTS
|
||||
* elements.
|
||||
*/
|
||||
struct ist *build_log_header(enum log_fmt format, int level, int facility,
|
||||
struct ist *metadata, size_t *nbelem)
|
||||
{
|
||||
static THREAD_LOCAL struct {
|
||||
struct ist ist_vector[NB_LOG_HDR_MAX_ELEMENTS];
|
||||
char timestamp_buffer[LOG_LEGACYTIME_LEN+1+1];
|
||||
time_t cur_legacy_time;
|
||||
char priority_buffer[6];
|
||||
} hdr_ctx = { .priority_buffer = "<<<<>" };
|
||||
|
||||
struct tm logtime;
|
||||
int len;
|
||||
int fac_level = 0;
|
||||
time_t time = date.tv_sec;
|
||||
|
||||
*nbelem = 0;
|
||||
|
||||
|
||||
if (format == LOG_FORMAT_UNSPEC) {
|
||||
format = LOG_FORMAT_RAW;
|
||||
if (metadata) {
|
||||
/* If a hostname is set, it appears we want to perform syslog
|
||||
* because only rfc5427 or rfc3164 support an hostname.
|
||||
*/
|
||||
if (metadata[LOG_META_HOST].len) {
|
||||
/* If a rfc5424 compliant timestamp is used we consider
|
||||
* that output format is rfc5424, else legacy format
|
||||
* is used as specified default for local logs
|
||||
* in documentation.
|
||||
*/
|
||||
if ((metadata[LOG_META_TIME].len == 1 && metadata[LOG_META_TIME].ptr[0] == '-')
|
||||
|| (metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN))
|
||||
format = LOG_FORMAT_RFC5424;
|
||||
else
|
||||
format = LOG_FORMAT_RFC3164;
|
||||
}
|
||||
else if (metadata[LOG_META_PRIO].len) {
|
||||
/* the source seems a parsed message
|
||||
* offering a valid level/prio prefix
|
||||
* so we consider this format.
|
||||
*/
|
||||
format = LOG_FORMAT_PRIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* prepare priority, stored into 1 single elem */
|
||||
switch (format) {
|
||||
case LOG_FORMAT_RFC3164:
|
||||
case LOG_FORMAT_RFC5424:
|
||||
case LOG_FORMAT_PRIO:
|
||||
fac_level = facility << 3;
|
||||
/* further format ignore the facility */
|
||||
/* fall through */
|
||||
case LOG_FORMAT_TIMED:
|
||||
case LOG_FORMAT_SHORT:
|
||||
fac_level += level;
|
||||
hdr_ctx.ist_vector[*nbelem].ptr = &hdr_ctx.priority_buffer[3]; /* last digit of the log level */
|
||||
do {
|
||||
*hdr_ctx.ist_vector[*nbelem].ptr = '0' + fac_level % 10;
|
||||
fac_level /= 10;
|
||||
hdr_ctx.ist_vector[*nbelem].ptr--;
|
||||
} while (fac_level && hdr_ctx.ist_vector[*nbelem].ptr > &hdr_ctx.priority_buffer[0]);
|
||||
*hdr_ctx.ist_vector[*nbelem].ptr = '<';
|
||||
hdr_ctx.ist_vector[(*nbelem)++].len = &hdr_ctx.priority_buffer[5] - hdr_ctx.ist_vector[0].ptr;
|
||||
break;
|
||||
case LOG_FORMAT_ISO:
|
||||
case LOG_FORMAT_RAW:
|
||||
break;
|
||||
case LOG_FORMAT_UNSPEC:
|
||||
case LOG_FORMATS:
|
||||
ABORT_NOW();
|
||||
}
|
||||
|
||||
|
||||
/* prepare timestamp, stored into a max of 4 elems */
|
||||
switch (format) {
|
||||
case LOG_FORMAT_RFC3164:
|
||||
/* rfc3164 ex: 'Jan 1 00:00:00 ' */
|
||||
if (metadata && metadata[LOG_META_TIME].len == LOG_LEGACYTIME_LEN) {
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
|
||||
/* time is set, break immediatly */
|
||||
break;
|
||||
}
|
||||
else if (metadata && metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN) {
|
||||
int month;
|
||||
char *timestamp = metadata[LOG_META_TIME].ptr;
|
||||
|
||||
/* iso time always begins like this: '1970-01-01T00:00:00' */
|
||||
|
||||
/* compute month */
|
||||
month = 10*(timestamp[5] - '0') + (timestamp[6] - '0');
|
||||
if (month)
|
||||
month--;
|
||||
if (month <= 11) {
|
||||
/* builds log prefix ex: 'Jan 1 ' */
|
||||
len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
|
||||
"%s %c%c ", monthname[month],
|
||||
timestamp[8] != '0' ? timestamp[8] : ' ',
|
||||
timestamp[9]);
|
||||
/* we reused the timestamp_buffer, signal that it does not
|
||||
* contain local time anymore
|
||||
*/
|
||||
hdr_ctx.cur_legacy_time = 0;
|
||||
if (len == 7) {
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], len);
|
||||
/* adds 'HH:MM:SS' from iso time */
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(×tamp[11], 8);
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
|
||||
/* we successfully reuse iso time, we can break */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Failed to reuse isotime time, fallback to local legacy time */
|
||||
}
|
||||
|
||||
if (unlikely(time != hdr_ctx.cur_legacy_time)) {
|
||||
/* re-builds timestamp from the current local time */
|
||||
get_localtime(time, &logtime);
|
||||
|
||||
len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
|
||||
"%s %2d %02d:%02d:%02d ",
|
||||
monthname[logtime.tm_mon],
|
||||
logtime.tm_mday, logtime.tm_hour, logtime.tm_min, logtime.tm_sec);
|
||||
if (len != LOG_LEGACYTIME_LEN+1)
|
||||
hdr_ctx.cur_legacy_time = 0;
|
||||
else
|
||||
hdr_ctx.cur_legacy_time = time;
|
||||
}
|
||||
if (likely(hdr_ctx.cur_legacy_time))
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], LOG_LEGACYTIME_LEN+1);
|
||||
else
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2("Jan 1 00:00:00 ", LOG_LEGACYTIME_LEN+1);
|
||||
break;
|
||||
case LOG_FORMAT_RFC5424:
|
||||
/* adds rfc5425 version prefix */
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2("1 ", 2);
|
||||
if (metadata && metadata[LOG_META_TIME].len == 1 && metadata[LOG_META_TIME].ptr[0] == '-') {
|
||||
/* submited len is NILVALUE, it is a valid timestamp for rfc5425 */
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
|
||||
break;
|
||||
}
|
||||
/* let continue as 'timed' and 'iso' format for usual timestamp */
|
||||
/* fall through */
|
||||
case LOG_FORMAT_TIMED:
|
||||
case LOG_FORMAT_ISO:
|
||||
/* ISO format ex: '1900:01:01T12:00:00.123456Z'
|
||||
* '1900:01:01T14:00:00+02:00'
|
||||
* '1900:01:01T10:00:00.123456-02:00'
|
||||
*/
|
||||
if (metadata && metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN) {
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
|
||||
/* time is set, break immediatly */
|
||||
break;
|
||||
}
|
||||
else if (metadata && metadata[LOG_META_TIME].len == LOG_LEGACYTIME_LEN) {
|
||||
int month;
|
||||
char *timestamp = metadata[LOG_META_TIME].ptr;
|
||||
|
||||
for (month = 0; month < 12; month++)
|
||||
if (!memcmp(monthname[month], timestamp, 3))
|
||||
break;
|
||||
|
||||
if (month < 12) {
|
||||
|
||||
/* get local time to retrieve year */
|
||||
get_localtime(time, &logtime);
|
||||
|
||||
/* year seems changed since log */
|
||||
if (logtime.tm_mon < month)
|
||||
logtime.tm_year--;
|
||||
|
||||
/* builds rfc5424 prefix ex: '1900-01-01T' */
|
||||
len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
|
||||
"%4d-%02d-%c%cT",
|
||||
logtime.tm_year+1900, month+1,
|
||||
timestamp[4] != ' ' ? timestamp[4] : '0',
|
||||
timestamp[5]);
|
||||
|
||||
/* we reused the timestamp_buffer, signal that it does not
|
||||
* contain local time anymore
|
||||
*/
|
||||
hdr_ctx.cur_legacy_time = 0;
|
||||
if (len == 11) {
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], len);
|
||||
/* adds HH:MM:SS from legacy timestamp */
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(×tamp[7], 8);
|
||||
/* skip secfraq because optionnal */
|
||||
/* according to rfc: -00:00 means we don't know the timezone */
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2("-00:00 ", 7);
|
||||
/* we successfully reuse legacy time, we can break */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Failed to reuse legacy time, fallback to local iso time */
|
||||
}
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(timeofday_as_iso_us(1), LOG_ISOTIME_MAXLEN + 1);
|
||||
break;
|
||||
case LOG_FORMAT_PRIO:
|
||||
case LOG_FORMAT_SHORT:
|
||||
case LOG_FORMAT_RAW:
|
||||
break;
|
||||
case LOG_FORMAT_UNSPEC:
|
||||
case LOG_FORMATS:
|
||||
ABORT_NOW();
|
||||
}
|
||||
|
||||
/* prepare other meta data, stored into a max of 10 elems */
|
||||
switch (format) {
|
||||
case LOG_FORMAT_RFC3164:
|
||||
if (metadata && metadata[LOG_META_HOST].len) {
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_HOST];
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
|
||||
}
|
||||
else /* the caller MUST fill the hostname */
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2("localhost ", 10);
|
||||
|
||||
if (!metadata || !metadata[LOG_META_TAG].len)
|
||||
break;
|
||||
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TAG];
|
||||
if (metadata[LOG_META_PID].len) {
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2("[", 1);
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_PID];
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2("]", 1);
|
||||
}
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(": ", 2);
|
||||
break;
|
||||
case LOG_FORMAT_RFC5424:
|
||||
if (metadata && metadata[LOG_META_HOST].len) {
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_HOST];
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
|
||||
}
|
||||
else
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
|
||||
|
||||
if (metadata && metadata[LOG_META_TAG].len) {
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TAG];
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
|
||||
}
|
||||
else
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
|
||||
|
||||
if (metadata && metadata[LOG_META_PID].len) {
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_PID];
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
|
||||
}
|
||||
else
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
|
||||
|
||||
if (metadata && metadata[LOG_META_MSGID].len) {
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_MSGID];
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
|
||||
}
|
||||
else
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
|
||||
|
||||
if (metadata && metadata[LOG_META_STDATA].len) {
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_STDATA];
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
|
||||
}
|
||||
else
|
||||
hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
|
||||
break;
|
||||
case LOG_FORMAT_PRIO:
|
||||
case LOG_FORMAT_SHORT:
|
||||
case LOG_FORMAT_TIMED:
|
||||
case LOG_FORMAT_ISO:
|
||||
case LOG_FORMAT_RAW:
|
||||
break;
|
||||
case LOG_FORMAT_UNSPEC:
|
||||
case LOG_FORMATS:
|
||||
ABORT_NOW();
|
||||
}
|
||||
|
||||
return hdr_ctx.ist_vector;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function sends a syslog message to <logsrv>.
|
||||
* <pid_str> is the string to be used for the PID of the caller, <pid_size> is length.
|
||||
* Same thing for <sd> and <sd_size> which are used for the structured-data part
|
||||
* in RFC5424 formatted syslog messages, and <tag_str> and <tag_size> the syslog tag.
|
||||
* The argument <metadata> MUST be an array of size
|
||||
* LOG_META_FIELDS*sizeof(struct ist) containing data to build the header.
|
||||
* It overrides the last byte of the message vector with an LF character.
|
||||
* Does not return any error,
|
||||
*/
|
||||
static inline void __do_send_log(struct logsrv *logsrv, int nblogger, char *pid_str, size_t pid_size,
|
||||
int level, char *message, size_t size, char *sd, size_t sd_size,
|
||||
char *tag_str, size_t tag_size)
|
||||
static inline void __do_send_log(struct logsrv *logsrv, int nblogger, int level, int facility, struct ist *metadata, char *message, size_t size)
|
||||
{
|
||||
static THREAD_LOCAL struct iovec iovec[NB_MSG_IOVEC_ELEMENTS] = { };
|
||||
static THREAD_LOCAL struct iovec iovec[NB_LOG_HDR_MAX_ELEMENTS+1+1] = { }; /* header elements + message + LF */
|
||||
static THREAD_LOCAL struct msghdr msghdr = {
|
||||
//.msg_iov = iovec,
|
||||
.msg_iovlen = NB_MSG_IOVEC_ELEMENTS
|
||||
.msg_iovlen = NB_LOG_HDR_MAX_ELEMENTS+2
|
||||
};
|
||||
static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
|
||||
static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
|
||||
static THREAD_LOCAL char *dataptr = NULL;
|
||||
time_t time = date.tv_sec;
|
||||
char *hdr, *hdr_ptr = NULL;
|
||||
size_t hdr_size;
|
||||
int fac_level;
|
||||
int *plogfd;
|
||||
char *pid_sep1 = "", *pid_sep2 = "";
|
||||
char logheader_short[3];
|
||||
int sent;
|
||||
int maxlen;
|
||||
int hdr_max = 0;
|
||||
int tag_max = 0;
|
||||
int pid_sep1_max = 0;
|
||||
int pid_max = 0;
|
||||
int pid_sep2_max = 0;
|
||||
int sd_max = 0;
|
||||
int max = 0;
|
||||
size_t nbelem;
|
||||
struct ist *msg_header = NULL;
|
||||
|
||||
msghdr.msg_iov = iovec;
|
||||
|
||||
dataptr = message;
|
||||
|
||||
/* historically some messages used to already contain the trailing LF
|
||||
* or Zero. Let's remove all trailing LF or Zero
|
||||
*/
|
||||
while (size && ((dataptr[size-1] == '\n' || (dataptr[size-1] == 0))))
|
||||
while (size && (message[size-1] == '\n' || (message[size-1] == 0)))
|
||||
size--;
|
||||
|
||||
if (logsrv->type == LOG_TARGET_FD) {
|
||||
@ -1623,161 +1774,47 @@ static inline void __do_send_log(struct logsrv *logsrv, int nblogger, char *pid_
|
||||
}
|
||||
}
|
||||
|
||||
switch (logsrv->format) {
|
||||
case LOG_FORMAT_RFC3164:
|
||||
hdr = logheader;
|
||||
hdr_ptr = update_log_hdr(time);
|
||||
break;
|
||||
|
||||
case LOG_FORMAT_RFC5424:
|
||||
hdr = logheader_rfc5424;
|
||||
hdr_ptr = update_log_hdr_rfc5424(time, date.tv_usec);
|
||||
sd_max = sd_size; /* the SD part allowed only in RFC5424 */
|
||||
break;
|
||||
|
||||
case LOG_FORMAT_SHORT:
|
||||
/* all fields are known, skip the header generation */
|
||||
hdr = logheader_short;
|
||||
hdr[0] = '<';
|
||||
hdr[1] = '0' + MAX(level, logsrv->minlvl);
|
||||
hdr[2] = '>';
|
||||
hdr_ptr = hdr;
|
||||
hdr_max = 3;
|
||||
maxlen = logsrv->maxlen - hdr_max;
|
||||
max = MIN(size, maxlen - 1);
|
||||
goto send;
|
||||
|
||||
case LOG_FORMAT_RAW:
|
||||
/* all fields are known, skip the header generation */
|
||||
hdr_ptr = hdr = "";
|
||||
hdr_max = 0;
|
||||
maxlen = logsrv->maxlen;
|
||||
max = MIN(size, maxlen - 1);
|
||||
goto send;
|
||||
|
||||
default:
|
||||
return; /* must never happen */
|
||||
}
|
||||
|
||||
hdr_size = hdr_ptr - hdr;
|
||||
|
||||
/* For each target, we may have a different facility.
|
||||
* We can also have a different log level for each message.
|
||||
* This induces variations in the message header length.
|
||||
* Since we don't want to recompute it each time, nor copy it every
|
||||
* time, we only change the facility in the pre-computed header,
|
||||
* and we change the pointer to the header accordingly.
|
||||
*/
|
||||
fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
|
||||
hdr_ptr = hdr + 3; /* last digit of the log level */
|
||||
do {
|
||||
*hdr_ptr = '0' + fac_level % 10;
|
||||
fac_level /= 10;
|
||||
hdr_ptr--;
|
||||
} while (fac_level && hdr_ptr > hdr);
|
||||
*hdr_ptr = '<';
|
||||
|
||||
hdr_max = hdr_size - (hdr_ptr - hdr);
|
||||
|
||||
/* time-based header */
|
||||
if (unlikely(hdr_size >= logsrv->maxlen)) {
|
||||
hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
|
||||
sd_max = 0;
|
||||
goto send;
|
||||
}
|
||||
|
||||
maxlen = logsrv->maxlen - hdr_max;
|
||||
|
||||
/* tag */
|
||||
tag_max = tag_size;
|
||||
if (unlikely(tag_max >= maxlen)) {
|
||||
tag_max = maxlen - 1;
|
||||
sd_max = 0;
|
||||
goto send;
|
||||
}
|
||||
|
||||
maxlen -= tag_max;
|
||||
|
||||
/* first pid separator */
|
||||
pid_sep1_max = log_formats[logsrv->format].pid.sep1.data;
|
||||
if (unlikely(pid_sep1_max >= maxlen)) {
|
||||
pid_sep1_max = maxlen - 1;
|
||||
sd_max = 0;
|
||||
goto send;
|
||||
}
|
||||
|
||||
pid_sep1 = log_formats[logsrv->format].pid.sep1.area;
|
||||
maxlen -= pid_sep1_max;
|
||||
|
||||
/* pid */
|
||||
pid_max = pid_size;
|
||||
if (unlikely(pid_size >= maxlen)) {
|
||||
pid_size = maxlen - 1;
|
||||
sd_max = 0;
|
||||
goto send;
|
||||
}
|
||||
|
||||
maxlen -= pid_size;
|
||||
|
||||
/* second pid separator */
|
||||
pid_sep2_max = log_formats[logsrv->format].pid.sep2.data;
|
||||
if (unlikely(pid_sep2_max >= maxlen)) {
|
||||
pid_sep2_max = maxlen - 1;
|
||||
sd_max = 0;
|
||||
goto send;
|
||||
}
|
||||
|
||||
pid_sep2 = log_formats[logsrv->format].pid.sep2.area;
|
||||
maxlen -= pid_sep2_max;
|
||||
|
||||
/* structured-data */
|
||||
if (sd_max >= maxlen) {
|
||||
sd_max = maxlen - 1;
|
||||
goto send;
|
||||
}
|
||||
|
||||
max = MIN(size, maxlen - sd_max - 1);
|
||||
send:
|
||||
msg_header = build_log_header(logsrv->format, level, facility, metadata, &nbelem);
|
||||
send:
|
||||
if (logsrv->addr.ss_family == AF_UNSPEC) {
|
||||
/* the target is a file descriptor or a ring buffer */
|
||||
struct ist msg[7];
|
||||
struct ist msg;
|
||||
|
||||
msg = ist2(message, size);
|
||||
if (msg.len > logsrv->maxlen)
|
||||
msg.len = logsrv->maxlen;
|
||||
|
||||
if (logsrv->type == LOG_TARGET_BUFFER) {
|
||||
msg[0] = ist2(message, MIN(size, logsrv->maxlen));
|
||||
msg[1] = ist2(tag_str, tag_size);
|
||||
msg[2] = ist2(pid_str, pid_size);
|
||||
msg[3] = ist2(sd, sd_size);
|
||||
sent = sink_write(logsrv->sink, msg, 1, level, logsrv->facility, &msg[1], &msg[2], &msg[3]);
|
||||
}
|
||||
else /* LOG_TARGET_FD */ {
|
||||
msg[0] = ist2(hdr_ptr, hdr_max);
|
||||
msg[1] = ist2(tag_str, tag_max);
|
||||
msg[2] = ist2(pid_sep1, pid_sep1_max);
|
||||
msg[3] = ist2(pid_str, pid_max);
|
||||
msg[4] = ist2(pid_sep2, pid_sep2_max);
|
||||
msg[5] = ist2(sd, sd_max);
|
||||
msg[6] = ist2(dataptr, max);
|
||||
sent = fd_write_frag_line(*plogfd, ~0, NULL, 0, msg, 7, 1);
|
||||
sent = sink_write(logsrv->sink, &msg, 1, level, logsrv->facility, metadata);
|
||||
}
|
||||
else /* LOG_TARGET_FD */
|
||||
sent = fd_write_frag_line(*plogfd, logsrv->maxlen, msg_header, nbelem, &msg, 1, 1);
|
||||
}
|
||||
else {
|
||||
iovec[0].iov_base = hdr_ptr;
|
||||
iovec[0].iov_len = hdr_max;
|
||||
iovec[1].iov_base = tag_str;
|
||||
iovec[1].iov_len = tag_max;
|
||||
iovec[2].iov_base = pid_sep1;
|
||||
iovec[2].iov_len = pid_sep1_max;
|
||||
iovec[3].iov_base = pid_str;
|
||||
iovec[3].iov_len = pid_max;
|
||||
iovec[4].iov_base = pid_sep2;
|
||||
iovec[4].iov_len = pid_sep2_max;
|
||||
iovec[5].iov_base = sd;
|
||||
iovec[5].iov_len = sd_max;
|
||||
iovec[6].iov_base = dataptr;
|
||||
iovec[6].iov_len = max;
|
||||
iovec[7].iov_base = "\n"; /* insert a \n at the end of the message */
|
||||
iovec[7].iov_len = 1;
|
||||
int i = 0;
|
||||
int totlen = logsrv->maxlen;
|
||||
|
||||
for (i = 0 ; i < nbelem ; i++ ) {
|
||||
iovec[i].iov_base = msg_header[i].ptr;
|
||||
iovec[i].iov_len = msg_header[i].len;
|
||||
if (totlen <= iovec[i].iov_len) {
|
||||
iovec[i].iov_len = totlen;
|
||||
totlen = 0;
|
||||
break;
|
||||
}
|
||||
totlen -= iovec[i].iov_len;
|
||||
}
|
||||
if (totlen) {
|
||||
iovec[i].iov_base = message;
|
||||
iovec[i].iov_len = size;
|
||||
if (totlen <= iovec[i].iov_len)
|
||||
iovec[i].iov_len = totlen;
|
||||
i++;
|
||||
}
|
||||
iovec[i].iov_base = "\n"; /* insert a \n at the end of the message */
|
||||
iovec[i].iov_len = 1;
|
||||
i++;
|
||||
|
||||
msghdr.msg_iovlen = i;
|
||||
msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
|
||||
msghdr.msg_namelen = get_addr_len(&logsrv->addr);
|
||||
|
||||
@ -1800,34 +1837,15 @@ send:
|
||||
/*
|
||||
* This function sends a syslog message.
|
||||
* It doesn't care about errors nor does it report them.
|
||||
* The arguments <sd> and <sd_size> are used for the structured-data part
|
||||
* in RFC5424 formatted syslog messages.
|
||||
* The argument <metadata> MUST be an array of size
|
||||
* LOG_META_FIELDS*sizeof(struct ist) containing
|
||||
* data to build the header.
|
||||
*/
|
||||
void __send_log(struct list *logsrvs, struct buffer *tag, int level,
|
||||
char *message, size_t size, char *sd, size_t sd_size)
|
||||
void process_send_log(struct list *logsrvs, int level, int facility,
|
||||
struct ist *metadata, char *message, size_t size)
|
||||
{
|
||||
struct logsrv *logsrv;
|
||||
int nblogger;
|
||||
static THREAD_LOCAL int curr_pid;
|
||||
static THREAD_LOCAL char pidstr[100];
|
||||
static THREAD_LOCAL struct buffer pid;
|
||||
|
||||
if (logsrvs == NULL) {
|
||||
if (!LIST_ISEMPTY(&global.logsrvs)) {
|
||||
logsrvs = &global.logsrvs;
|
||||
}
|
||||
}
|
||||
if (!tag || !tag->area)
|
||||
tag = &global.log_tag;
|
||||
|
||||
if (!logsrvs || LIST_ISEMPTY(logsrvs))
|
||||
return;
|
||||
|
||||
if (unlikely(curr_pid != getpid())) {
|
||||
curr_pid = getpid();
|
||||
ltoa_o(curr_pid, pidstr, sizeof(pidstr));
|
||||
chunk_initstr(&pid, pidstr);
|
||||
}
|
||||
|
||||
/* Send log messages to syslog server. */
|
||||
nblogger = 0;
|
||||
@ -1856,11 +1874,63 @@ void __send_log(struct list *logsrvs, struct buffer *tag, int level,
|
||||
HA_SPIN_UNLOCK(LOGSRV_LOCK, &logsrv->lock);
|
||||
}
|
||||
if (in_range)
|
||||
__do_send_log(logsrv, ++nblogger, pid.area, pid.data, level,
|
||||
message, size, sd, sd_size, tag->area, tag->data);
|
||||
__do_send_log(logsrv, ++nblogger, MAX(level, logsrv->minlvl),
|
||||
(facility == -1) ? logsrv->facility : facility,
|
||||
metadata, message, size);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function sends a syslog message.
|
||||
* It doesn't care about errors nor does it report them.
|
||||
* The arguments <sd> and <sd_size> are used for the structured-data part
|
||||
* in RFC5424 formatted syslog messages.
|
||||
*/
|
||||
void __send_log(struct list *logsrvs, struct buffer *tagb, int level,
|
||||
char *message, size_t size, char *sd, size_t sd_size)
|
||||
{
|
||||
static THREAD_LOCAL pid_t curr_pid;
|
||||
static THREAD_LOCAL char pidstr[16];
|
||||
static THREAD_LOCAL struct ist metadata[LOG_META_FIELDS];
|
||||
|
||||
if (logsrvs == NULL) {
|
||||
if (!LIST_ISEMPTY(&global.logsrvs)) {
|
||||
logsrvs = &global.logsrvs;
|
||||
}
|
||||
}
|
||||
if (!logsrvs || LIST_ISEMPTY(logsrvs))
|
||||
return;
|
||||
|
||||
if (!metadata[LOG_META_HOST].len) {
|
||||
if (global.log_send_hostname)
|
||||
metadata[LOG_META_HOST] = ist2(global.log_send_hostname, strlen(global.log_send_hostname));
|
||||
else
|
||||
metadata[LOG_META_HOST] = ist2(hostname, strlen(hostname));
|
||||
}
|
||||
|
||||
if (!tagb || !tagb->area)
|
||||
tagb = &global.log_tag;
|
||||
|
||||
if (tagb)
|
||||
metadata[LOG_META_TAG] = ist2(tagb->area, tagb->data);
|
||||
|
||||
if (unlikely(curr_pid != getpid()))
|
||||
metadata[LOG_META_PID].len = 0;
|
||||
|
||||
if (!metadata[LOG_META_PID].len) {
|
||||
curr_pid = getpid();
|
||||
ltoa_o(curr_pid, pidstr, sizeof(pidstr));
|
||||
metadata[LOG_META_PID] = ist2(pidstr, strlen(pidstr));
|
||||
}
|
||||
|
||||
metadata[LOG_META_STDATA] = ist2(sd, sd_size);
|
||||
|
||||
/* Remove trailing space of structured data */
|
||||
while (metadata[LOG_META_STDATA].len && metadata[LOG_META_STDATA].ptr[metadata[LOG_META_STDATA].len-1] == ' ')
|
||||
metadata[LOG_META_STDATA].len--;
|
||||
|
||||
return process_send_log(logsrvs, level, -1, metadata, message, size);
|
||||
}
|
||||
|
||||
const char sess_cookie[8] = "NIDVEOU7"; /* No cookie, Invalid cookie, cookie for a Down server, Valid cookie, Expired cookie, Old cookie, Unused, unknown */
|
||||
const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
|
||||
@ -1958,13 +2028,9 @@ INITCALL0(STG_PREPARE, init_log);
|
||||
/* Initialize log buffers used for syslog messages */
|
||||
int init_log_buffers()
|
||||
{
|
||||
logheader = my_realloc2(logheader, global.max_syslog_len + 1);
|
||||
logheader_end = NULL;
|
||||
logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1);
|
||||
logheader_rfc5424_end = NULL;
|
||||
logline = my_realloc2(logline, global.max_syslog_len + 1);
|
||||
logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
|
||||
if (!logheader || !logline_rfc5424 || !logline || !logline_rfc5424)
|
||||
if (!logline || !logline_rfc5424)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@ -1972,13 +2038,9 @@ int init_log_buffers()
|
||||
/* Deinitialize log buffers used for syslog messages */
|
||||
void deinit_log_buffers()
|
||||
{
|
||||
free(logheader);
|
||||
free(logheader_rfc5424);
|
||||
free(logline);
|
||||
free(logline_rfc5424);
|
||||
ring_free(_HA_ATOMIC_XCHG(&startup_logs, NULL));
|
||||
logheader = NULL;
|
||||
logheader_rfc5424 = NULL;
|
||||
logline = NULL;
|
||||
logline_rfc5424 = NULL;
|
||||
}
|
||||
|
@ -1483,7 +1483,7 @@ static int sample_conv_debug(const struct arg *arg_p, struct sample *smp, void *
|
||||
|
||||
done:
|
||||
line = ist2(buf->area, buf->data);
|
||||
sink_write(sink, &line, 1, 0, 0, NULL, NULL, NULL);
|
||||
sink_write(sink, &line, 1, 0, 0, NULL);
|
||||
end:
|
||||
free_trash_chunk(buf);
|
||||
return 1;
|
||||
|
150
src/sink.c
150
src/sink.c
@ -50,7 +50,7 @@ struct sink *sink_find(const char *name)
|
||||
* exists with the same name, it will be returned. The caller can detect it as
|
||||
* a newly created one has type SINK_TYPE_NEW.
|
||||
*/
|
||||
static struct sink *__sink_new(const char *name, const char *desc, enum sink_fmt fmt)
|
||||
static struct sink *__sink_new(const char *name, const char *desc, int fmt)
|
||||
{
|
||||
struct sink *sink;
|
||||
|
||||
@ -80,7 +80,7 @@ static struct sink *__sink_new(const char *name, const char *desc, enum sink_fmt
|
||||
* and description <desc>. Returns NULL on allocation failure or conflict.
|
||||
* Perfect duplicates are merged (same type, fd, and name).
|
||||
*/
|
||||
struct sink *sink_new_fd(const char *name, const char *desc, enum sink_fmt fmt, int fd)
|
||||
struct sink *sink_new_fd(const char *name, const char *desc, enum log_fmt fmt, int fd)
|
||||
{
|
||||
struct sink *sink;
|
||||
|
||||
@ -104,7 +104,7 @@ struct sink *sink_new_fd(const char *name, const char *desc, enum sink_fmt fmt,
|
||||
* Perfect duplicates are merged (same type and name). If sizes differ, the
|
||||
* largest one is kept.
|
||||
*/
|
||||
struct sink *sink_new_buf(const char *name, const char *desc, enum sink_fmt fmt, size_t size)
|
||||
struct sink *sink_new_buf(const char *name, const char *desc, enum log_fmt fmt, size_t size)
|
||||
{
|
||||
struct sink *sink;
|
||||
|
||||
@ -147,87 +147,16 @@ struct sink *sink_new_buf(const char *name, const char *desc, enum sink_fmt fmt,
|
||||
* messages when there are any. It returns >0 if it could write anything,
|
||||
* <=0 otherwise.
|
||||
*/
|
||||
ssize_t __sink_write(struct sink *sink, const struct ist msg[], size_t nmsg,
|
||||
int level, int facility, struct ist *tag,
|
||||
struct ist *pid, struct ist *sd)
|
||||
{
|
||||
int log_format;
|
||||
char short_hdr[4];
|
||||
struct ist pfx[6];
|
||||
ssize_t __sink_write(struct sink *sink, const struct ist msg[], size_t nmsg,
|
||||
int level, int facility, struct ist *metadata)
|
||||
{
|
||||
struct ist *pfx = NULL;
|
||||
size_t npfx = 0;
|
||||
char *hdr_ptr;
|
||||
int fac_level;
|
||||
|
||||
if (sink->fmt == SINK_FMT_RAW)
|
||||
if (sink->fmt == LOG_FORMAT_RAW)
|
||||
goto send;
|
||||
|
||||
if (sink->fmt == SINK_FMT_SHORT || sink->fmt == SINK_FMT_TIMED) {
|
||||
short_hdr[0] = '<';
|
||||
short_hdr[1] = '0' + level;
|
||||
short_hdr[2] = '>';
|
||||
|
||||
pfx[npfx].ptr = short_hdr;
|
||||
pfx[npfx].len = 3;
|
||||
npfx++;
|
||||
if (sink->fmt == SINK_FMT_SHORT)
|
||||
goto send;
|
||||
}
|
||||
|
||||
|
||||
if (sink->fmt == SINK_FMT_ISO || sink->fmt == SINK_FMT_TIMED) {
|
||||
pfx[npfx].ptr = timeofday_as_iso_us(1);
|
||||
pfx[npfx].len = 33;
|
||||
npfx++;
|
||||
goto send;
|
||||
}
|
||||
else if (sink->fmt == SINK_FMT_RFC5424) {
|
||||
pfx[npfx].ptr = logheader_rfc5424;
|
||||
pfx[npfx].len = update_log_hdr_rfc5424(date.tv_sec, date.tv_usec) - pfx[npfx].ptr;
|
||||
log_format = LOG_FORMAT_RFC5424;
|
||||
}
|
||||
else {
|
||||
pfx[npfx].ptr = logheader;
|
||||
pfx[npfx].len = update_log_hdr(date.tv_sec) - pfx[npfx].ptr;
|
||||
log_format = LOG_FORMAT_RFC3164;
|
||||
sd = NULL;
|
||||
}
|
||||
|
||||
fac_level = (facility << 3) + level;
|
||||
hdr_ptr = pfx[npfx].ptr + 3; /* last digit of the log level */
|
||||
do {
|
||||
*hdr_ptr = '0' + fac_level % 10;
|
||||
fac_level /= 10;
|
||||
hdr_ptr--;
|
||||
} while (fac_level && hdr_ptr > pfx[npfx].ptr);
|
||||
*hdr_ptr = '<';
|
||||
pfx[npfx].len -= hdr_ptr - pfx[npfx].ptr;
|
||||
pfx[npfx].ptr = hdr_ptr;
|
||||
npfx++;
|
||||
|
||||
if (tag && tag->len) {
|
||||
pfx[npfx].ptr = tag->ptr;
|
||||
pfx[npfx].len = tag->len;
|
||||
npfx++;
|
||||
}
|
||||
pfx[npfx].ptr = get_format_pid_sep1(log_format, &pfx[npfx].len);
|
||||
if (pfx[npfx].len)
|
||||
npfx++;
|
||||
|
||||
if (pid && pid->len) {
|
||||
pfx[npfx].ptr = pid->ptr;
|
||||
pfx[npfx].len = pid->len;
|
||||
npfx++;
|
||||
}
|
||||
|
||||
pfx[npfx].ptr = get_format_pid_sep2(log_format, &pfx[npfx].len);
|
||||
if (pfx[npfx].len)
|
||||
npfx++;
|
||||
|
||||
if (sd && sd->len) {
|
||||
pfx[npfx].ptr = sd->ptr;
|
||||
pfx[npfx].len = sd->len;
|
||||
npfx++;
|
||||
}
|
||||
pfx = build_log_header(sink->fmt, level, facility, metadata, &npfx);
|
||||
|
||||
send:
|
||||
if (sink->type == SINK_TYPE_FD) {
|
||||
@ -244,25 +173,41 @@ send:
|
||||
* called under an exclusive lock on the sink to avoid multiple produces doing
|
||||
* the same. On success, >0 is returned, otherwise <=0 on failure.
|
||||
*/
|
||||
int sink_announce_dropped(struct sink *sink, int facility, struct ist *pid)
|
||||
int sink_announce_dropped(struct sink *sink, int facility)
|
||||
{
|
||||
static THREAD_LOCAL struct ist metadata[LOG_META_FIELDS];
|
||||
static THREAD_LOCAL pid_t curr_pid;
|
||||
static THREAD_LOCAL char pidstr[16];
|
||||
unsigned int dropped;
|
||||
struct buffer msg;
|
||||
struct ist msgvec[1];
|
||||
char logbuf[64];
|
||||
struct ist sd;
|
||||
struct ist tag;
|
||||
|
||||
while (unlikely((dropped = sink->ctx.dropped) > 0)) {
|
||||
chunk_init(&msg, logbuf, sizeof(logbuf));
|
||||
chunk_printf(&msg, "%u event%s dropped", dropped, dropped > 1 ? "s" : "");
|
||||
msgvec[0] = ist2(msg.area, msg.data);
|
||||
|
||||
sd.ptr = default_rfc5424_sd_log_format;
|
||||
sd.len = 2;
|
||||
tag.ptr = global.log_tag.area;
|
||||
tag.len = global.log_tag.data;
|
||||
if (__sink_write(sink, msgvec, 1, LOG_NOTICE, facility, &tag, pid, &sd) <= 0)
|
||||
if (!metadata[LOG_META_HOST].len) {
|
||||
if (global.log_send_hostname)
|
||||
metadata[LOG_META_HOST] = ist2(global.log_send_hostname, strlen(global.log_send_hostname));
|
||||
else
|
||||
metadata[LOG_META_HOST] = ist2(hostname, strlen(hostname));
|
||||
}
|
||||
|
||||
if (!metadata[LOG_META_TAG].len)
|
||||
metadata[LOG_META_TAG] = ist2(global.log_tag.area, global.log_tag.data);
|
||||
|
||||
if (unlikely(curr_pid != getpid()))
|
||||
metadata[LOG_META_PID].len = 0;
|
||||
|
||||
if (!metadata[LOG_META_PID].len) {
|
||||
curr_pid = getpid();
|
||||
ltoa_o(curr_pid, pidstr, sizeof(pidstr));
|
||||
metadata[LOG_META_PID] = ist2(pidstr, strlen(pidstr));
|
||||
}
|
||||
|
||||
if (__sink_write(sink, msgvec, 1, LOG_NOTICE, facility, metadata) <= 0)
|
||||
return 0;
|
||||
/* success! */
|
||||
HA_ATOMIC_SUB(&sink->ctx.dropped, dropped);
|
||||
@ -818,7 +763,7 @@ int cfg_parse_ring(const char *file, int linenum, char **args, int kwm)
|
||||
goto err;
|
||||
}
|
||||
|
||||
cfg_sink = sink_new_buf(args[1], args[1] , SINK_FMT_RAW, size);
|
||||
cfg_sink = sink_new_buf(args[1], args[1], LOG_FORMAT_RAW, size);
|
||||
if (!cfg_sink || cfg_sink->type != SINK_TYPE_BUFFER) {
|
||||
ha_alert("parsing [%s:%d] : unable to create a new sink buffer for ring '%s'.\n", file, linenum, args[1]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
@ -909,25 +854,8 @@ int cfg_parse_ring(const char *file, int linenum, char **args, int kwm)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (strcmp(args[1], "raw") == 0) {
|
||||
cfg_sink->fmt = SINK_FMT_RAW;
|
||||
}
|
||||
else if (strcmp(args[1], "short") == 0) {
|
||||
cfg_sink->fmt = SINK_FMT_SHORT;
|
||||
}
|
||||
else if (strcmp(args[1], "iso") == 0) {
|
||||
cfg_sink->fmt = SINK_FMT_ISO;
|
||||
}
|
||||
else if (strcmp(args[1], "timed") == 0) {
|
||||
cfg_sink->fmt = SINK_FMT_TIMED;
|
||||
}
|
||||
else if (strcmp(args[1], "rfc3164") == 0) {
|
||||
cfg_sink->fmt = SINK_FMT_RFC3164;
|
||||
}
|
||||
else if (strcmp(args[1], "rfc5424") == 0) {
|
||||
cfg_sink->fmt = SINK_FMT_RFC5424;
|
||||
}
|
||||
else {
|
||||
cfg_sink->fmt = get_log_format(args[1]);
|
||||
if (cfg_sink->fmt == LOG_FORMAT_UNSPEC) {
|
||||
ha_alert("parsing [%s:%d] : unknown format '%s'.\n", file, linenum, args[1]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto err;
|
||||
@ -1079,9 +1007,9 @@ int post_sink_resolve()
|
||||
|
||||
static void sink_init()
|
||||
{
|
||||
sink_new_fd("stdout", "standard output (fd#1)", SINK_FMT_RAW, 1);
|
||||
sink_new_fd("stderr", "standard output (fd#2)", SINK_FMT_RAW, 2);
|
||||
sink_new_buf("buf0", "in-memory ring buffer", SINK_FMT_TIMED, 1048576);
|
||||
sink_new_fd("stdout", "standard output (fd#1)", LOG_FORMAT_RAW, 1);
|
||||
sink_new_fd("stderr", "standard output (fd#2)", LOG_FORMAT_RAW, 2);
|
||||
sink_new_buf("buf0", "in-memory ring buffer", LOG_FORMAT_TIMED, 1048576);
|
||||
}
|
||||
|
||||
static void sink_deinit()
|
||||
|
@ -230,7 +230,7 @@ void __trace(enum trace_level level, uint64_t mask, struct trace_source *src,
|
||||
}
|
||||
|
||||
if (src->sink)
|
||||
sink_write(src->sink, line, words, 0, 0, NULL, NULL, NULL);
|
||||
sink_write(src->sink, line, words, 0, 0, NULL);
|
||||
|
||||
end:
|
||||
/* check if we need to stop the trace now */
|
||||
|
Loading…
Reference in New Issue
Block a user