mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-20 04:37:04 +00:00
MEDIUM: log: add the ability to include samples in logs
Using %[expression] it becomes possible to make the log engine fetch some samples from the request or the response and provide them in the logs. Note that this feature is still limited, it does not yet allow to apply converters, to limit the output length, nor to specify the direction which should be fetched when a fetch function works in both directions. However it's quite convenient to log SSL information or to include some information that are used in stick tables. It is worth noting that this has been done in the generic log format handler, which means that the same information may be used to build the unique-id header and to pass the information to a backend server.
This commit is contained in:
parent
2b0108adf6
commit
c83684519b
@ -10235,6 +10235,11 @@ 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
|
||||
string formats ("Q").
|
||||
|
||||
If a variable is named between square brackets ('[' .. ']') then it is used
|
||||
as a pattern extraction rule (see section 7.8). This it useful to add some
|
||||
less common information such as the client's SSL certificate's DN, or to log
|
||||
the key that would be used to store an entry into a stick table.
|
||||
|
||||
Note: spaces must be escaped. A space character is considered as a separator.
|
||||
HAproxy will automatically merge consecutive separators.
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
||||
enum {
|
||||
|
||||
LOG_FMT_TEXT = 0, /* raw text */
|
||||
|
||||
LOG_FMT_EXPR, /* sample expression */
|
||||
LOG_FMT_SEPARATOR, /* separator replaced by one space */
|
||||
LOG_FMT_VARIABLE,
|
||||
|
||||
@ -106,21 +106,25 @@ enum {
|
||||
LF_STARTVAR, // % in text
|
||||
LF_STARG, // after '%{' and berore '}'
|
||||
LF_EDARG, // '}' after '%{'
|
||||
LF_STEXPR, // after '%[' or '%{..}[' and berore ']'
|
||||
LF_EDEXPR, // ']' after '%['
|
||||
LF_END, // \0 found
|
||||
};
|
||||
|
||||
|
||||
struct logformat_node {
|
||||
struct list list;
|
||||
int type;
|
||||
int options;
|
||||
char *arg;
|
||||
int type; // LOG_FMT_*
|
||||
int options; // LOG_OPT_*
|
||||
char *arg; // text for LOG_FMT_TEXT, arg for others
|
||||
void *expr; // for use with LOG_FMT_EXPR
|
||||
};
|
||||
|
||||
#define LOG_OPT_HEXA 0x00000001
|
||||
#define LOG_OPT_MANDATORY 0x00000002
|
||||
#define LOG_OPT_QUOTE 0x00000004
|
||||
|
||||
#define LOG_OPT_REQ_CAP 0x00000008
|
||||
#define LOG_OPT_RES_CAP 0x00000010
|
||||
|
||||
|
||||
/* fields that need to be logged. They appear as flags in session->logs.logwait */
|
||||
|
95
src/log.c
95
src/log.c
@ -33,6 +33,7 @@
|
||||
|
||||
#include <proto/frontend.h>
|
||||
#include <proto/log.h>
|
||||
#include <proto/sample.h>
|
||||
#include <proto/stream_interface.h>
|
||||
#ifdef USE_OPENSSL
|
||||
#include <proto/ssl_sock.h>
|
||||
@ -303,6 +304,51 @@ void add_to_logformat_list(char *start, char *end, int type, struct list *list_f
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the sample fetch expression <text> and add a node to <list_format> upon
|
||||
* success. At the moment, sample converters are not yet supported but fetch arguments
|
||||
* should work.
|
||||
*/
|
||||
void add_sample_to_logformat_list(char *text, char *arg, int arg_len, struct proxy *curpx, struct list *list_format, int options)
|
||||
{
|
||||
char *cmd[2];
|
||||
struct sample_expr *expr;
|
||||
struct logformat_node *node;
|
||||
int cmd_arg;
|
||||
|
||||
cmd[0] = text;
|
||||
cmd[1] = "";
|
||||
cmd_arg = 0;
|
||||
|
||||
expr = sample_parse_expr(cmd, &cmd_arg, trash.str, trash.size);
|
||||
if (!expr) {
|
||||
Warning("log-format: sample fetch <%s> failed with : %s\n", text, trash.str);
|
||||
return;
|
||||
}
|
||||
|
||||
node = calloc(1, sizeof(struct logformat_node));
|
||||
node->type = LOG_FMT_EXPR;
|
||||
node->expr = expr;
|
||||
node->options = options;
|
||||
|
||||
if (arg_len) {
|
||||
node->arg = my_strndup(arg, arg_len);
|
||||
parse_logformat_var_args(node->arg, node);
|
||||
}
|
||||
if (expr->fetch->cap & SMP_CAP_REQ)
|
||||
node->options |= LOG_OPT_REQ_CAP; /* fetch method is request-compatible */
|
||||
|
||||
if (expr->fetch->cap & SMP_CAP_RES)
|
||||
node->options |= LOG_OPT_RES_CAP; /* fetch method is response-compatible */
|
||||
|
||||
/* check if we need to allocate an hdr_idx struct for HTTP parsing */
|
||||
/* Note, we may also need to set curpx->to_log with certain fetches */
|
||||
if (expr->fetch->cap & SMP_CAP_L7)
|
||||
curpx->acl_requires |= ACL_USE_L7_ANY;
|
||||
|
||||
LIST_ADDQ(list_format, &node->list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the log_format string and fill a linked list.
|
||||
* Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
|
||||
@ -346,12 +392,16 @@ void parse_logformat_string(char *str, struct proxy *curproxy, struct list *list
|
||||
*/
|
||||
switch (pformat) {
|
||||
case LF_STARTVAR: // text immediately following a '%'
|
||||
arg = NULL;
|
||||
arg = NULL; var = NULL;
|
||||
arg_len = var_len = 0;
|
||||
if (*str == '{') { // optional argument
|
||||
cformat = LF_STARG;
|
||||
arg = str + 1;
|
||||
}
|
||||
else if (*str == '[') {
|
||||
cformat = LF_STEXPR;
|
||||
var = str + 1; // store expr in variable name
|
||||
}
|
||||
else if (isalnum((int)*str)) { // variable name
|
||||
cformat = LF_VAR;
|
||||
var = str;
|
||||
@ -371,23 +421,35 @@ void parse_logformat_string(char *str, struct proxy *curproxy, struct list *list
|
||||
break;
|
||||
|
||||
case LF_EDARG: // text immediately following '%{arg}'
|
||||
if (isalnum((int)*str)) { // variable name
|
||||
if (*str == '[') {
|
||||
cformat = LF_STEXPR;
|
||||
var = str + 1; // store expr in variable name
|
||||
break;
|
||||
}
|
||||
else if (isalnum((int)*str)) { // variable name
|
||||
cformat = LF_VAR;
|
||||
var = str;
|
||||
break;
|
||||
}
|
||||
|
||||
Warning("Skipping isolated argument in log-format line : '%%{%s}'\n", arg);
|
||||
cformat = LF_INIT;
|
||||
break;
|
||||
|
||||
case LF_STEXPR: // text immediately following '%['
|
||||
if (*str == ']') { // end of arg
|
||||
cformat = LF_EDEXPR;
|
||||
var_len = str - var;
|
||||
*str = 0; // needed for parsing the expression
|
||||
}
|
||||
break;
|
||||
|
||||
case LF_VAR: // text part of a variable name
|
||||
var_len = str - var;
|
||||
if (!isalnum((int)*str))
|
||||
cformat = LF_INIT; // not variable name anymore
|
||||
break;
|
||||
|
||||
default: // LF_INIT, LF_TEXT, LF_SEPARATOR, LF_END
|
||||
default: // LF_INIT, LF_TEXT, LF_SEPARATOR, LF_END, LF_EDEXPR
|
||||
cformat = LF_INIT;
|
||||
}
|
||||
|
||||
@ -405,6 +467,9 @@ void parse_logformat_string(char *str, struct proxy *curproxy, struct list *list
|
||||
case LF_VAR:
|
||||
parse_logformat_var(arg, arg_len, var, var_len, curproxy, list_format, &options);
|
||||
break;
|
||||
case LF_STEXPR:
|
||||
add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options);
|
||||
break;
|
||||
case LF_TEXT:
|
||||
case LF_SEPARATOR:
|
||||
add_to_logformat_list(sp, str, pformat, list_format);
|
||||
@ -414,8 +479,8 @@ void parse_logformat_string(char *str, struct proxy *curproxy, struct list *list
|
||||
}
|
||||
}
|
||||
|
||||
if (pformat == LF_STARTVAR || pformat == LF_STARG)
|
||||
Warning("Ignoring end of truncated log-format line after '%s'\n", arg ? arg : "%");
|
||||
if (pformat == LF_STARTVAR || pformat == LF_STARG || pformat == LF_STEXPR)
|
||||
Warning("Ignoring end of truncated log-format line after '%s'\n", var ? var : arg ? arg : "%");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -823,8 +888,9 @@ int build_logline(struct session *s, char *dst, size_t maxsize, struct list *lis
|
||||
|
||||
list_for_each_entry(tmp, list_format, list) {
|
||||
const char *src = NULL;
|
||||
switch (tmp->type) {
|
||||
struct sample *key;
|
||||
|
||||
switch (tmp->type) {
|
||||
case LOG_FMT_SEPARATOR:
|
||||
if (!last_isspace) {
|
||||
LOGCHAR(' ');
|
||||
@ -841,6 +907,21 @@ int build_logline(struct session *s, char *dst, size_t maxsize, struct list *lis
|
||||
last_isspace = 0;
|
||||
break;
|
||||
|
||||
case LOG_FMT_EXPR: // sample expression, may be request or response
|
||||
key = NULL;
|
||||
if (tmp->options & LOG_OPT_REQ_CAP)
|
||||
key = sample_fetch_string(be, s, txn, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, tmp->expr);
|
||||
if (!key && (tmp->options & LOG_OPT_RES_CAP))
|
||||
key = sample_fetch_string(be, s, txn, SMP_OPT_DIR_RES|SMP_OPT_FINAL, tmp->expr);
|
||||
if (!key)
|
||||
break;
|
||||
ret = lf_text_len(tmplog, key->data.str.str, key->data.str.len, dst + maxsize - tmplog, tmp);
|
||||
if (ret == 0)
|
||||
goto out;
|
||||
tmplog = ret;
|
||||
last_isspace = 0;
|
||||
break;
|
||||
|
||||
case LOG_FMT_CLIENTIP: // %ci
|
||||
ret = lf_ip(tmplog, (struct sockaddr *)&s->req->prod->conn->addr.from,
|
||||
dst + maxsize - tmplog, tmp);
|
||||
|
Loading…
Reference in New Issue
Block a user