mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-10 17:17:06 +02:00
MEDIUM: log: add a new log format flag "E"
The +E mode escapes characters '"', '\' and ']' with '\' as prefix. It mostly makes sense to use it in the RFC5424 structured-data log formats. Example: log-format-sd %{+Q,+E}o\ [exampleSDID@1234\ header=%[capture.req.hdr(0)]]
This commit is contained in:
parent
0edd10925d
commit
835b9212f6
@ -14500,7 +14500,7 @@ prefixing them with a '+' or '-' sign.
|
|||||||
|
|
||||||
Special variable "%o" may be used to propagate its flags to all other
|
Special variable "%o" may be used to propagate its flags to all other
|
||||||
variables on the same format string. This is particularly handy with quoted
|
variables on the same format string. This is particularly handy with quoted
|
||||||
string formats ("Q").
|
("Q") and escaped ("E") string formats.
|
||||||
|
|
||||||
If a variable is named between square brackets ('[' .. ']') then it is used
|
If a variable is named between square brackets ('[' .. ']') then it is used
|
||||||
as a sample expression rule (see section 7.3). This it useful to add some
|
as a sample expression rule (see section 7.3). This it useful to add some
|
||||||
@ -14511,15 +14511,24 @@ Note: spaces must be escaped. A space character is considered as a separator.
|
|||||||
In order to emit a verbatim '%', it must be preceded by another '%' resulting
|
In order to emit a verbatim '%', it must be preceded by another '%' resulting
|
||||||
in '%%'. HAProxy will automatically merge consecutive separators.
|
in '%%'. HAProxy will automatically merge consecutive separators.
|
||||||
|
|
||||||
|
Note: when using the RFC5424 syslog message format, the characters '"',
|
||||||
|
'\' and ']' inside PARAM-VALUE should be escaped with '\' as prefix (see
|
||||||
|
https://tools.ietf.org/html/rfc5424#section-6.3.3 for more details). In
|
||||||
|
such cases, the use of the flag "E" should be considered.
|
||||||
|
|
||||||
Flags are :
|
Flags are :
|
||||||
* Q: quote a string
|
* Q: quote a string
|
||||||
* X: hexadecimal representation (IPs, Ports, %Ts, %rt, %pid)
|
* X: hexadecimal representation (IPs, Ports, %Ts, %rt, %pid)
|
||||||
|
* E: escape characters '"', '\' and ']' in a string with '\' as prefix
|
||||||
|
(intended purpose is for the RFC5424 structured-data log formats)
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
log-format %T\ %t\ Some\ Text
|
log-format %T\ %t\ Some\ Text
|
||||||
log-format %{+Q}o\ %t\ %s\ %{-Q}r
|
log-format %{+Q}o\ %t\ %s\ %{-Q}r
|
||||||
|
|
||||||
|
log-format-sd %{+Q,+E}o\ [exampleSDID@1234\ header=%[capture.req.hdr(0)]]
|
||||||
|
|
||||||
At the moment, the default HTTP format is defined this way :
|
At the moment, the default HTTP format is defined this way :
|
||||||
|
|
||||||
log-format %ci:%cp\ [%t]\ %ft\ %b/%s\ %Tq/%Tw/%Tc/%Tr/%Tt\ %ST\ %B\ %CC\ \
|
log-format %ci:%cp\ [%t]\ %ft\ %b/%s\ %Tq/%Tw/%Tc/%Tr/%Tt\ %ST\ %B\ %CC\ \
|
||||||
|
@ -48,6 +48,14 @@ extern char *logline;
|
|||||||
extern char *logline_rfc5424;
|
extern char *logline_rfc5424;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializes some log data.
|
||||||
|
*/
|
||||||
|
void init_log();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Builds a log line.
|
||||||
|
*/
|
||||||
int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list_format);
|
int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list_format);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -141,6 +141,7 @@ struct logformat_node {
|
|||||||
#define LOG_OPT_REQ_CAP 0x00000008
|
#define LOG_OPT_REQ_CAP 0x00000008
|
||||||
#define LOG_OPT_RES_CAP 0x00000010
|
#define LOG_OPT_RES_CAP 0x00000010
|
||||||
#define LOG_OPT_HTTP 0x00000020
|
#define LOG_OPT_HTTP 0x00000020
|
||||||
|
#define LOG_OPT_ESC 0x00000040
|
||||||
|
|
||||||
|
|
||||||
/* Fields that need to be extracted from the incoming connection or request for
|
/* Fields that need to be extracted from the incoming connection or request for
|
||||||
|
@ -597,6 +597,7 @@ void init(int argc, char **argv)
|
|||||||
get_localtime(start_date.tv_sec, &curtime);
|
get_localtime(start_date.tv_sec, &curtime);
|
||||||
strftime(localtimezone, 6, "%z", &curtime);
|
strftime(localtimezone, 6, "%z", &curtime);
|
||||||
|
|
||||||
|
init_log();
|
||||||
signal_init();
|
signal_init();
|
||||||
if (init_acl() != 0)
|
if (init_acl() != 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
|
179
src/log.c
179
src/log.c
@ -66,6 +66,21 @@ static const struct log_fmt log_formats[LOG_FORMATS] = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define FD_SETS_ARE_BITFIELDS
|
||||||
|
#ifdef FD_SETS_ARE_BITFIELDS
|
||||||
|
/*
|
||||||
|
* 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. FD_SET() sets those bytes
|
||||||
|
* which should be escaped. When FD_ISSET() returns non-zero, it means that the
|
||||||
|
* byte should be escaped. Be careful to always pass bytes from 0 to 255
|
||||||
|
* exclusively to the macros.
|
||||||
|
*/
|
||||||
|
fd_set rfc5424_escape_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Check if your OS uses bitfields for fd_sets"
|
||||||
|
#endif
|
||||||
|
|
||||||
const char *log_facilities[NB_LOG_FACILITIES] = {
|
const char *log_facilities[NB_LOG_FACILITIES] = {
|
||||||
"kern", "user", "mail", "daemon",
|
"kern", "user", "mail", "daemon",
|
||||||
"auth", "syslog", "lpr", "news",
|
"auth", "syslog", "lpr", "news",
|
||||||
@ -211,6 +226,7 @@ struct logformat_var_args var_args_list[] = {
|
|||||||
{ "M", LOG_OPT_MANDATORY },
|
{ "M", LOG_OPT_MANDATORY },
|
||||||
{ "Q", LOG_OPT_QUOTE },
|
{ "Q", LOG_OPT_QUOTE },
|
||||||
{ "X", LOG_OPT_HEXA },
|
{ "X", LOG_OPT_HEXA },
|
||||||
|
{ "E", LOG_OPT_ESC },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -701,9 +717,104 @@ int get_log_facility(const char *fac)
|
|||||||
return facility;
|
return facility;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encode the string.
|
||||||
|
*
|
||||||
|
* When using the +E log format option, it will try to escape '"\]'
|
||||||
|
* characters with '\' as prefix. The same prefix should not be used as
|
||||||
|
* <escape>.
|
||||||
|
*/
|
||||||
|
static char *lf_encode_string(char *start, char *stop,
|
||||||
|
const char escape, const fd_set *map,
|
||||||
|
const char *string,
|
||||||
|
struct logformat_node *node)
|
||||||
|
{
|
||||||
|
if (node->options & LOG_OPT_ESC) {
|
||||||
|
if (start < stop) {
|
||||||
|
stop--; /* reserve one byte for the final '\0' */
|
||||||
|
while (start < stop && *string != '\0') {
|
||||||
|
if (!FD_ISSET((unsigned char)(*string), map)) {
|
||||||
|
if (!FD_ISSET((unsigned char)(*string), rfc5424_escape_map))
|
||||||
|
*start++ = *string;
|
||||||
|
else {
|
||||||
|
if (start + 2 >= stop)
|
||||||
|
break;
|
||||||
|
*start++ = '\\';
|
||||||
|
*start++ = *string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (start + 3 >= stop)
|
||||||
|
break;
|
||||||
|
*start++ = escape;
|
||||||
|
*start++ = hextab[(*string >> 4) & 15];
|
||||||
|
*start++ = hextab[*string & 15];
|
||||||
|
}
|
||||||
|
string++;
|
||||||
|
}
|
||||||
|
*start = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return encode_string(start, stop, escape, map, string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encode the chunk.
|
||||||
|
*
|
||||||
|
* When using the +E log format option, it will try to escape '"\]'
|
||||||
|
* characters with '\' as prefix. The same prefix should not be used as
|
||||||
|
* <escape>.
|
||||||
|
*/
|
||||||
|
static char *lf_encode_chunk(char *start, char *stop,
|
||||||
|
const char escape, const fd_set *map,
|
||||||
|
const struct chunk *chunk,
|
||||||
|
struct logformat_node *node)
|
||||||
|
{
|
||||||
|
char *str, *end;
|
||||||
|
|
||||||
|
if (node->options & LOG_OPT_ESC) {
|
||||||
|
if (start < stop) {
|
||||||
|
str = chunk->str;
|
||||||
|
end = chunk->str + chunk->len;
|
||||||
|
|
||||||
|
stop--; /* reserve one byte for the final '\0' */
|
||||||
|
while (start < stop && str < end) {
|
||||||
|
if (!FD_ISSET((unsigned char)(*str), map)) {
|
||||||
|
if (!FD_ISSET((unsigned char)(*str), rfc5424_escape_map))
|
||||||
|
*start++ = *str;
|
||||||
|
else {
|
||||||
|
if (start + 2 >= stop)
|
||||||
|
break;
|
||||||
|
*start++ = '\\';
|
||||||
|
*start++ = *str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (start + 3 >= stop)
|
||||||
|
break;
|
||||||
|
*start++ = escape;
|
||||||
|
*start++ = hextab[(*str >> 4) & 15];
|
||||||
|
*start++ = hextab[*str & 15];
|
||||||
|
}
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
*start = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return encode_chunk(start, stop, escape, map, chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write a string in the log string
|
* Write a string in the log string
|
||||||
* Take cares of quote options
|
* Take cares of quote and escape options
|
||||||
*
|
*
|
||||||
* Return the adress of the \0 character, or NULL on error
|
* Return the adress of the \0 character, or NULL on error
|
||||||
*/
|
*/
|
||||||
@ -718,9 +829,21 @@ char *lf_text_len(char *dst, const char *src, size_t len, size_t size, struct lo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (src && len) {
|
if (src && len) {
|
||||||
|
if (node->options & LOG_OPT_ESC) {
|
||||||
|
struct chunk chunk;
|
||||||
|
char *ret;
|
||||||
|
|
||||||
|
chunk_initlen(&chunk, (char *)src, 0, len);
|
||||||
|
ret = escape_chunk(dst, dst + size, '\\', rfc5424_escape_map, &chunk);
|
||||||
|
if (ret == NULL || *ret != '\0')
|
||||||
|
return NULL;
|
||||||
|
len = ret - dst;
|
||||||
|
}
|
||||||
|
else {
|
||||||
if (++len > size)
|
if (++len > size)
|
||||||
len = size;
|
len = size;
|
||||||
len = strlcpy2(dst, src, len);
|
len = strlcpy2(dst, src, len);
|
||||||
|
}
|
||||||
|
|
||||||
size -= len;
|
size -= len;
|
||||||
dst += len;
|
dst += len;
|
||||||
@ -1135,6 +1258,26 @@ const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found a
|
|||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
|
||||||
|
/* Initializes some log data.
|
||||||
|
*/
|
||||||
|
void init_log()
|
||||||
|
{
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
|
/* Initialize the escape map for the RFC5424 structured-data : '"\]'
|
||||||
|
* inside PARAM-VALUE should be escaped with '\' as prefix.
|
||||||
|
* See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
|
||||||
|
* details.
|
||||||
|
*/
|
||||||
|
memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
|
||||||
|
|
||||||
|
tmp = "\"\\]";
|
||||||
|
while (*tmp) {
|
||||||
|
FD_SET(*tmp, rfc5424_escape_map);
|
||||||
|
tmp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Builds a log line in <dst> based on <list_format>, and stops before reaching
|
/* Builds a log line in <dst> based on <list_format>, and stops before reaching
|
||||||
* <maxsize> characters. Returns the size of the output string in characters,
|
* <maxsize> characters. Returns the size of the output string in characters,
|
||||||
* not counting the trailing zero which is always added if the resulting size
|
* not counting the trailing zero which is always added if the resulting size
|
||||||
@ -1203,8 +1346,8 @@ int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list
|
|||||||
if (!key && (tmp->options & LOG_OPT_RES_CAP))
|
if (!key && (tmp->options & LOG_OPT_RES_CAP))
|
||||||
key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
|
key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
|
||||||
if (tmp->options & LOG_OPT_HTTP)
|
if (tmp->options & LOG_OPT_HTTP)
|
||||||
ret = encode_chunk(tmplog, dst + maxsize,
|
ret = lf_encode_chunk(tmplog, dst + maxsize,
|
||||||
'%', http_encode_map, key ? &key->data.u.str : &empty);
|
'%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
|
||||||
else
|
else
|
||||||
ret = lf_text_len(tmplog, key ? key->data.u.str.str : NULL, key ? key->data.u.str.len : 0, dst + maxsize - tmplog, tmp);
|
ret = lf_text_len(tmplog, key ? key->data.u.str.str : NULL, key ? key->data.u.str.len : 0, dst + maxsize - tmplog, tmp);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
@ -1651,8 +1794,8 @@ int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list
|
|||||||
if (hdr)
|
if (hdr)
|
||||||
LOGCHAR('|');
|
LOGCHAR('|');
|
||||||
if (s->req_cap[hdr] != NULL) {
|
if (s->req_cap[hdr] != NULL) {
|
||||||
ret = encode_string(tmplog, dst + maxsize,
|
ret = lf_encode_string(tmplog, dst + maxsize,
|
||||||
'#', hdr_encode_map, s->req_cap[hdr]);
|
'#', hdr_encode_map, s->req_cap[hdr], tmp);
|
||||||
if (ret == NULL || *ret != '\0')
|
if (ret == NULL || *ret != '\0')
|
||||||
goto out;
|
goto out;
|
||||||
tmplog = ret;
|
tmplog = ret;
|
||||||
@ -1674,8 +1817,8 @@ int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list
|
|||||||
if (tmp->options & LOG_OPT_QUOTE)
|
if (tmp->options & LOG_OPT_QUOTE)
|
||||||
LOGCHAR('"');
|
LOGCHAR('"');
|
||||||
if (s->req_cap[hdr] != NULL) {
|
if (s->req_cap[hdr] != NULL) {
|
||||||
ret = encode_string(tmplog, dst + maxsize,
|
ret = lf_encode_string(tmplog, dst + maxsize,
|
||||||
'#', hdr_encode_map, s->req_cap[hdr]);
|
'#', hdr_encode_map, s->req_cap[hdr], tmp);
|
||||||
if (ret == NULL || *ret != '\0')
|
if (ret == NULL || *ret != '\0')
|
||||||
goto out;
|
goto out;
|
||||||
tmplog = ret;
|
tmplog = ret;
|
||||||
@ -1699,8 +1842,8 @@ int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list
|
|||||||
if (hdr)
|
if (hdr)
|
||||||
LOGCHAR('|');
|
LOGCHAR('|');
|
||||||
if (s->res_cap[hdr] != NULL) {
|
if (s->res_cap[hdr] != NULL) {
|
||||||
ret = encode_string(tmplog, dst + maxsize,
|
ret = lf_encode_string(tmplog, dst + maxsize,
|
||||||
'#', hdr_encode_map, s->res_cap[hdr]);
|
'#', hdr_encode_map, s->res_cap[hdr], tmp);
|
||||||
if (ret == NULL || *ret != '\0')
|
if (ret == NULL || *ret != '\0')
|
||||||
goto out;
|
goto out;
|
||||||
tmplog = ret;
|
tmplog = ret;
|
||||||
@ -1722,8 +1865,8 @@ int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list
|
|||||||
if (tmp->options & LOG_OPT_QUOTE)
|
if (tmp->options & LOG_OPT_QUOTE)
|
||||||
LOGCHAR('"');
|
LOGCHAR('"');
|
||||||
if (s->res_cap[hdr] != NULL) {
|
if (s->res_cap[hdr] != NULL) {
|
||||||
ret = encode_string(tmplog, dst + maxsize,
|
ret = lf_encode_string(tmplog, dst + maxsize,
|
||||||
'#', hdr_encode_map, s->res_cap[hdr]);
|
'#', hdr_encode_map, s->res_cap[hdr], tmp);
|
||||||
if (ret == NULL || *ret != '\0')
|
if (ret == NULL || *ret != '\0')
|
||||||
goto out;
|
goto out;
|
||||||
tmplog = ret;
|
tmplog = ret;
|
||||||
@ -1741,8 +1884,8 @@ int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list
|
|||||||
if (tmp->options & LOG_OPT_QUOTE)
|
if (tmp->options & LOG_OPT_QUOTE)
|
||||||
LOGCHAR('"');
|
LOGCHAR('"');
|
||||||
uri = txn->uri ? txn->uri : "<BADREQ>";
|
uri = txn->uri ? txn->uri : "<BADREQ>";
|
||||||
ret = encode_string(tmplog, dst + maxsize,
|
ret = lf_encode_string(tmplog, dst + maxsize,
|
||||||
'#', url_encode_map, uri);
|
'#', url_encode_map, uri, tmp);
|
||||||
if (ret == NULL || *ret != '\0')
|
if (ret == NULL || *ret != '\0')
|
||||||
goto out;
|
goto out;
|
||||||
tmplog = ret;
|
tmplog = ret;
|
||||||
@ -1780,7 +1923,7 @@ int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list
|
|||||||
chunk.len = spc - uri;
|
chunk.len = spc - uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk);
|
ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
|
||||||
if (ret == NULL || *ret != '\0')
|
if (ret == NULL || *ret != '\0')
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -1814,7 +1957,7 @@ int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list
|
|||||||
chunk.len = uri - qmark;
|
chunk.len = uri - qmark;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk);
|
ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
|
||||||
if (ret == NULL || *ret != '\0')
|
if (ret == NULL || *ret != '\0')
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -1854,7 +1997,7 @@ int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list
|
|||||||
chunk.len = spc - uri;
|
chunk.len = spc - uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk);
|
ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
|
||||||
if (ret == NULL || *ret != '\0')
|
if (ret == NULL || *ret != '\0')
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -1884,7 +2027,7 @@ int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list
|
|||||||
chunk.len = spc - uri;
|
chunk.len = spc - uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk);
|
ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
|
||||||
if (ret == NULL || *ret != '\0')
|
if (ret == NULL || *ret != '\0')
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -1929,7 +2072,7 @@ int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list
|
|||||||
chunk.len = end - uri;
|
chunk.len = end - uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk);
|
ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
|
||||||
if (ret == NULL || *ret != '\0')
|
if (ret == NULL || *ret != '\0')
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user