MEDIUM: log: Unique ID
The Unique ID, is an ID generated with several informations. You can use
a log-format string to customize it, with the "unique-id-format" keyword,
and insert it in the request header, with the "unique-id-header" keyword.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 66b61ff..8f50213 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1117,6 +1117,8 @@
timeout srvtimeout (deprecated) X - X X
timeout tarpit X X X X
transparent (deprecated) X - X X
+unique-id-format X X X -
+unique-id-header X X X -
use_backend - X X -
use-server - - X X
------------------------------------+----------+----------+---------+---------
@@ -6564,6 +6566,60 @@
See also: "option transparent"
+unique-id-format <string>
+ Generate a unique ID for each request.
+ May be used in sections : defaults | frontend | listen | backend
+ yes | yes | yes | no
+ Arguments :
+ <string> is a log-format string.
+
+ This keyword creates a ID for each request using the custom log format. A
+ unique ID is useful to trace a request passing through many components of
+ a complex infrastructure. The newly created ID may also be logged using the
+ %ID tag the log-format string.
+
+ The format should be composed from elements that are guaranteed to be
+ unique when combined together. For instance, if multiple haproxy instances
+ are involved, it might be important to include the node name. It is often
+ needed to log the incoming connection's source and destination addresses
+ and ports. Note that since multiple requests may be performed over the same
+ connection, including a request counter may help differentiate them.
+ Similarly, a timestamp may protect against a rollover of the counter.
+ Logging the process ID will avoid collisions after a service restart.
+
+ It is recommended to use hexadecimal notation for many fields since it
+ makes them more compact and saves space in logs.
+
+ Example:
+
+ unique-id-format %{+X}o\ %Ci:%Cp_%Fi:%Fp_%Ts_%rt:%pid
+
+ will generate:
+
+ 7F000001:8296_7F00001E:1F90_4F7B0A69_0003:790A
+
+ See also: "unique-id-header"
+
+unique-id-header <name>
+ Add a unique ID header in the HTTP request.
+ May be used in sections : defaults | frontend | listen | backend
+ yes | yes | yes | no
+ Arguments :
+ <name> is the name of the header.
+
+ Add a unique-id header in the HTTP request sent to the server, using the
+ unique-id-format. It can't work if the unique-id-format doesn't exist.
+
+ Example:
+
+ unique-id-format %{+X}o\ %Ci:%Cp_%Fi:%Fp_%Ts_%rt:%pid
+ unique-id-header X-Unique-ID
+
+ will generate:
+
+ X-Unique-ID: 7F000001:8296_7F00001E:1F90_4F7B0A69_0003:790A
+
+ See also: "unique-id-format"
use_backend <backend> if <condition>
use_backend <backend> unless <condition>
@@ -8919,6 +8975,7 @@
| | %Fi | frontend_ip | IP |
| | %Fp | frontend_port | numeric |
| | %H | hostname | string |
+ | | %ID | unique-id | string |
| | %Si | server_IP | IP |
| | %Sp | server_port | numeric |
| | %T | gmt_date_time | date |
diff --git a/include/proto/log.h b/include/proto/log.h
index 0e41eca..5adf674 100644
--- a/include/proto/log.h
+++ b/include/proto/log.h
@@ -33,6 +33,7 @@
#include <types/session.h>
extern struct pool_head *pool2_requri;
+extern struct pool_head *pool2_uniqueid;
extern char *log_format;
extern char default_tcp_log_format[];
diff --git a/include/types/log.h b/include/types/log.h
index ad96e9a..269fb46 100644
--- a/include/types/log.h
+++ b/include/types/log.h
@@ -31,6 +31,8 @@
#define NB_LOG_FACILITIES 24
#define NB_LOG_LEVELS 8
#define SYSLOG_PORT 514
+#define UNIQUEID_LEN 128
+
/* lists of fields that can be logged */
enum {
@@ -86,6 +88,7 @@
LOG_FMT_HDRRESPONSLIST,
LOG_FMT_REQ,
LOG_FMT_HOSTNAME,
+ LOG_FMT_UNIQUEID,
};
/* enum for parse_logformat */
diff --git a/include/types/proxy.h b/include/types/proxy.h
index d69a914..aa6cec8 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -290,6 +290,8 @@
struct proxy *next;
struct list logsrvs;
struct list logformat; /* log_format linked list */
+ char *header_unique_id; /* unique-id header */
+ struct list format_unique_id; /* unique-id format */
int to_log; /* things to be logged (LW_*) */
int stop_time; /* date to stop listening, when stopping != 0 (int ticks) */
struct hdr_exp *req_exp; /* regular expressions for request headers */
diff --git a/include/types/session.h b/include/types/session.h
index 5678e5c..7539c0c 100644
--- a/include/types/session.h
+++ b/include/types/session.h
@@ -207,6 +207,7 @@
void (*srv_error)(struct session *s, /* the function to call upon unrecoverable server errors (or NULL) */
struct stream_interface *si);
unsigned int uniq_id; /* unique ID used for the traces */
+ char *unique_id; /* custom unique ID */
};
/* parameters to configure tracked counters */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index aa57b9b..1267cd7 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1550,6 +1550,18 @@
LIST_ADDQ(&curproxy->logformat, &node->list);
}
+ /* copy default unique_id to curproxy */
+ list_for_each_entry(tmplf, &defproxy.format_unique_id, list) {
+ struct logformat_node *node = malloc(sizeof(struct logformat_node));
+ memcpy(node, tmplf, sizeof(struct logformat_node));
+ LIST_INIT(&node->list);
+ LIST_ADDQ(&curproxy->format_unique_id, &node->list);
+ }
+
+ /* copy default header unique id */
+ if (defproxy.header_unique_id)
+ curproxy->header_unique_id = strdup(defproxy.header_unique_id);
+
curproxy->grace = defproxy.grace;
curproxy->conf.used_listener_id = EB_ROOT;
curproxy->conf.used_server_id = EB_ROOT;
@@ -4591,7 +4603,27 @@
newsrv->prev_state = newsrv->state;
}
+ }
+
+ else if (strcmp(args[0], "unique-id-format") == 0) {
+ if (!*(args[1])) {
+ Alert("parsing [%s:%d] : %s expects an argument.\n", file, linenum, args[0]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ parse_logformat_string(args[1], curproxy, &curproxy->format_unique_id, PR_MODE_HTTP);
}
+
+ else if (strcmp(args[0], "unique-id-header") == 0) {
+ if (!*(args[1])) {
+ Alert("parsing [%s:%d] : %s expects an argument.\n", file, linenum, args[0]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ free(curproxy->header_unique_id);
+ curproxy->header_unique_id = strdup(args[1]);
+ }
+
else if (strcmp(args[0], "log-format") == 0) {
if (!*(args[1])) {
Alert("parsing [%s:%d] : %s expects an argument.\n", file, linenum, args[0]);
diff --git a/src/log.c b/src/log.c
index af2aee9..130d627 100644
--- a/src/log.c
+++ b/src/log.c
@@ -107,6 +107,7 @@
{ "pid", LOG_FMT_PID, PR_MODE_TCP, NULL }, /* log pid */
{ "rt", LOG_FMT_COUNTER, PR_MODE_TCP, NULL }, /* log counter */
{ "H", LOG_FMT_HOSTNAME, PR_MODE_TCP, NULL }, /* Hostname */
+ { "ID", LOG_FMT_UNIQUEID, PR_MODE_HTTP, NULL }, /* Unique ID */
{ 0, 0, 0, NULL }
};
@@ -1305,6 +1306,16 @@
last_isspace = 0;
}
break;
+
+ case LOG_FMT_UNIQUEID: // %ID
+ src = s->unique_id;
+ ret = lf_text(tmplog, src, maxsize - (tmplog - dst), tmp);
+ if (ret == NULL)
+ goto out;
+ tmplog = ret;
+ last_isspace = 0;
+ break;
+
}
}
diff --git a/src/proto_http.c b/src/proto_http.c
index 2f9d3e8..dd2f0d0 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -268,6 +268,7 @@
/* memory allocations */
pool2_requri = create_pool("requri", REQURI_LEN, MEM_F_SHARED);
pool2_capture = create_pool("capture", CAPTURE_LEN, MEM_F_SHARED);
+ pool2_uniqueid = create_pool("uniqueid", UNIQUEID_LEN, MEM_F_SHARED);
}
/*
@@ -858,6 +859,7 @@
extern const char *monthname[12];
struct pool_head *pool2_requri;
struct pool_head *pool2_capture;
+struct pool_head *pool2_uniqueid;
/*
* Capture headers from message starting at <som> according to header list
@@ -2397,6 +2399,10 @@
}
}
+ if (!LIST_ISEMPTY(&s->fe->format_unique_id)) {
+ s->unique_id = pool_alloc2(pool2_uniqueid);
+ }
+
/* 4. We may have to convert HTTP/0.9 requests to HTTP/1.0 */
if (unlikely(msg->sl.rq.v_l == 0) && !http_upgrade_v09_to_v10(req, msg, txn))
goto return_bad_req;
@@ -3274,6 +3280,19 @@
get_srv_from_appsession(s, msg->sol + msg->sl.rq.u, msg->sl.rq.u_l);
}
+ /* add unique-id if "header-unique-id" is specified */
+
+ if (!LIST_ISEMPTY(&s->fe->format_unique_id))
+ build_logline(s, s->unique_id, UNIQUEID_LEN, &s->fe->format_unique_id);
+
+ if (s->fe->header_unique_id && s->unique_id) {
+ int ret = snprintf(trash, global.tune.bufsize, "%s: %s", s->fe->header_unique_id, s->unique_id);
+ if (ret < 0 || ret > global.tune.bufsize)
+ goto return_bad_req;
+ if(unlikely(http_header_add_tail(req, &txn->req, &txn->hdr_idx, trash) < 0))
+ goto return_bad_req;
+ }
+
/*
* 9: add X-Forwarded-For if either the frontend or the backend
* asks for it.
@@ -7381,7 +7400,9 @@
pool_free2(pool2_capture, txn->cli_cookie);
pool_free2(pool2_capture, txn->srv_cookie);
pool_free2(apools.sessid, txn->sessid);
+ pool_free2(pool2_uniqueid, s->unique_id);
+ s->unique_id = NULL;
txn->sessid = NULL;
txn->uri = NULL;
txn->srv_cookie = NULL;
diff --git a/src/proxy.c b/src/proxy.c
index 7f15bb2..bb9fe57 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -439,6 +439,7 @@
LIST_INIT(&p->listener_queue);
LIST_INIT(&p->logsrvs);
LIST_INIT(&p->logformat);
+ LIST_INIT(&p->format_unique_id);
/* Timeouts are defined as -1 */
proxy_reset_timeouts(p);