enum {
LOG_FMT_TEXT = 0, /* raw text */
-
+ LOG_FMT_EXPR, /* sample expression */
LOG_FMT_SEPARATOR, /* separator replaced by one space */
LOG_FMT_VARIABLE,
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 */
#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>
}
}
+/*
+ * 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
*/
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;
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;
}
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);
}
}
- 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 : "%");
}
/*
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(' ');
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);