mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-19 20:20:45 +00:00
MAJOR: log: introduce log backends
Using "mode log" in a backend section turns the proxy in a log backend which can be used to log-balance logs between multiple log targets (udp or tcp servers) log backends can be used as regular log targets using the log directive with "backend@be_name" prefix, like so: | log backend@mybackend local0 A log backend will distribute log messages to servers according to the log load-balancing algorithm that can be set using the "log-balance" option from the log backend section. For now, only the roundrobin algorithm is supported and set by default.
This commit is contained in:
parent
e58a9b4baf
commit
9a74a6cb17
@ -4426,6 +4426,7 @@ id - X X X
|
||||
ignore-persist - - X X
|
||||
load-server-state-from-file X - X X
|
||||
log (*) X X X X
|
||||
log-balance X - X X
|
||||
log-format X X X -
|
||||
log-format-sd X X X -
|
||||
log-tag X X X X
|
||||
@ -8692,6 +8693,12 @@ no log
|
||||
when used as a complement this can help troubleshooting by
|
||||
having the logs instantly available.
|
||||
|
||||
- A log backend in the form "backend@<name>", which will send
|
||||
log messages to the corresponding log backend responsible for
|
||||
sending the message to the proper server according to the
|
||||
backend's lb settings. A log backend is a backend section with
|
||||
"mode log" set (see "mode" for more information).
|
||||
|
||||
- An explicit stream address prefix such as "tcp@","tcp6@",
|
||||
"tcp4@" or "uxst@" will allocate an implicit ring buffer with
|
||||
a stream forward server targeting the given address.
|
||||
@ -8809,6 +8816,43 @@ no log
|
||||
# level and send in tcp
|
||||
log "${LOCAL_SYSLOG}:514" local0 notice # send to local server
|
||||
|
||||
log-balance <algorithm> [ <arguments> ]
|
||||
|
||||
Define the load balancing algorithm to be used in a log backend.
|
||||
("mode log" enabled)
|
||||
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | no | yes | yes
|
||||
Arguments :
|
||||
<algorithm> is the algorithm used to select a server when doing load
|
||||
balancing. This only applies when no persistence information
|
||||
is available, or when a connection is redispatched to another
|
||||
server. <algorithm> may be one of the following :
|
||||
|
||||
roundrobin Each server is used in turns. This is the smoothest and
|
||||
fairest algorithm when the server's processing time remains
|
||||
equally distributed.
|
||||
|
||||
<arguments> is an optional list of arguments which may be needed by some
|
||||
algorithms.
|
||||
|
||||
The load balancing algorithm of a log backend is set to roundrobin when
|
||||
no other algorithm has been set. The algorithm may only be set once for each
|
||||
log backend. The above algorithms support the "backup" server option and the
|
||||
"allbackups" proxy option. However server "weight" is not supported and will
|
||||
be ignored.
|
||||
|
||||
Examples :
|
||||
|
||||
global
|
||||
log backend@mylog-rrb local0 # send all logs to mylog-rrb backend
|
||||
|
||||
backend mylog-rrb
|
||||
mode log
|
||||
log-balance roundrobin
|
||||
|
||||
server s1 udp@127.0.0.1:514 # will receive 50% of log messages
|
||||
server s2 udp@127.0.0.1:514
|
||||
|
||||
log-format <string>
|
||||
Specifies the log format string to use for traffic logs
|
||||
@ -8923,7 +8967,7 @@ maxconn <conns>
|
||||
See also : "server", global section's "maxconn", "fullconn"
|
||||
|
||||
|
||||
mode { tcp|http }
|
||||
mode { tcp|http|log }
|
||||
Set the running mode or protocol of the instance
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
yes | yes | yes | yes
|
||||
@ -8939,6 +8983,16 @@ mode { tcp|http }
|
||||
processing and switching will be possible. This is the mode which
|
||||
brings HAProxy most of its value.
|
||||
|
||||
log When used in a backend section, it will turn the backend into a
|
||||
log backend. Such backend can be used as a log destination for
|
||||
any "log" directive by using the "backend@<name>" syntax. Log
|
||||
messages will be distributed to the servers from the backend
|
||||
according to the lb settings which can be configured using the
|
||||
"log-balance" keyword (in place of the "balance" keyword for TCP
|
||||
and HTTP backends). Log backends support UDP servers by prefixing
|
||||
the server's address with the "udp@" prefix. Common backend and
|
||||
server features are supported, but not TCP or HTTP related ones.
|
||||
|
||||
When doing content switching, it is mandatory that the frontend and the
|
||||
backend are in the same mode (generally HTTP), otherwise the configuration
|
||||
will be refused.
|
||||
@ -16082,7 +16136,7 @@ downinter <delay>
|
||||
|
||||
log-proto <logproto>
|
||||
The "log-proto" specifies the protocol used to forward event messages to
|
||||
a server configured in a ring section. Possible values are "legacy"
|
||||
a server configured in a log or ring section. Possible values are "legacy"
|
||||
and "octet-count" corresponding respectively to "Non-transparent-framing"
|
||||
and "Octet counting" in rfc6587. "legacy" is the default.
|
||||
|
||||
|
@ -145,6 +145,11 @@ struct lbprm {
|
||||
struct lb_fwlc fwlc;
|
||||
struct lb_chash chash;
|
||||
struct lb_fas fas;
|
||||
struct {
|
||||
struct server **srv; /* array containing in-use log servers */
|
||||
struct list avail; /* servers available for lb are registered in this list */
|
||||
uint32_t lastid; /* last relative id used */
|
||||
} log; /* used in log-balancing context (PR_MODE_SYSLOG backend) */
|
||||
};
|
||||
int algo; /* load balancing algorithm and variants: BE_LB_* */
|
||||
int tot_wact, tot_wbck; /* total effective weights of active and backup servers */
|
||||
|
@ -45,6 +45,7 @@ void back_handle_st_cer(struct stream *s);
|
||||
|
||||
const char *backend_lb_algo_str(int algo);
|
||||
int backend_parse_balance(const char **args, char **err, struct proxy *curproxy);
|
||||
int backend_parse_log_balance(const char **args, char **err, struct proxy *curproxy);
|
||||
int tcp_persist_rdp_cookie(struct stream *s, struct channel *req, int an_bit);
|
||||
|
||||
int be_downtime(struct proxy *px);
|
||||
|
@ -116,6 +116,7 @@ enum log_tgt {
|
||||
LOG_TARGET_DGRAM = 0, // datagram address (udp, unix socket)
|
||||
LOG_TARGET_FD, // file descriptor
|
||||
LOG_TARGET_BUFFER, // ring buffer
|
||||
LOG_TARGET_BACKEND, // backend with SYSLOG mode
|
||||
};
|
||||
|
||||
/* lists of fields that can be logged, for logformat_node->type */
|
||||
@ -240,8 +241,11 @@ enum log_target_flags {
|
||||
struct log_target {
|
||||
struct sockaddr_storage *addr;
|
||||
union {
|
||||
char *ring_name; /* type = BUFFER - preparsing */
|
||||
struct sink *sink; /* type = BUFFER - postparsing */
|
||||
char *ring_name; /* type = BUFFER - preparsing */
|
||||
struct sink *sink; /* type = BUFFER - postparsing */
|
||||
char *be_name; /* type = BACKEND - preparsing */
|
||||
struct proxy *be; /* type = BACKEND - postparsing */
|
||||
char *resolv_name; /* generic - preparsing */
|
||||
};
|
||||
enum log_tgt type;
|
||||
uint16_t flags;
|
||||
|
@ -274,7 +274,8 @@ struct server {
|
||||
char *rdr_pfx; /* the redirection prefix */
|
||||
|
||||
struct proxy *proxy; /* the proxy this server belongs to */
|
||||
const struct mux_proto_list *mux_proto; /* the mux to use for all outgoing connections (specified by the "proto" keyword) */
|
||||
const struct mux_proto_list *mux_proto; /* the mux to use for all outgoing connections (specified by the "proto" keyword) */
|
||||
struct log_target *log_target; /* when 'mode log' is enabled, target facility used to transport log messages */
|
||||
unsigned maxconn, minconn; /* max # of active sessions (0 = unlimited), min# for dynamic limit. */
|
||||
struct srv_per_thread *per_thr; /* array of per-thread stuff such as connections lists */
|
||||
struct srv_per_tgroup *per_tgrp; /* array of per-tgroup stuff such as idle conns */
|
||||
@ -331,7 +332,10 @@ struct server {
|
||||
THREAD_PAD(63);
|
||||
__decl_thread(HA_SPINLOCK_T lock); /* may enclose the proxy's lock, must not be taken under */
|
||||
unsigned npos, lpos; /* next and last positions in the LB tree, protected by LB lock */
|
||||
struct eb32_node lb_node; /* node used for tree-based load balancing */
|
||||
union {
|
||||
struct eb32_node lb_node; /* node used for tree-based load balancing */
|
||||
struct list lb_list; /* elem used for list-based load balancing */
|
||||
};
|
||||
struct server *next_full; /* next server in the temporary full list */
|
||||
|
||||
/* usually atomically updated by any thread during parsing or on end of request */
|
||||
@ -374,7 +378,7 @@ struct server {
|
||||
char *hostname; /* server hostname */
|
||||
struct sockaddr_storage init_addr; /* plain IP address specified on the init-addr line */
|
||||
unsigned int init_addr_methods; /* initial address setting, 3-bit per method, ends at 0, enough to store 10 entries */
|
||||
enum srv_log_proto log_proto; /* used proto to emit messages on server lines from ring section */
|
||||
enum srv_log_proto log_proto; /* used proto to emit messages on server lines from log or ring section */
|
||||
|
||||
char *sni_expr; /* Temporary variable to store a sample expression for SNI */
|
||||
struct {
|
||||
@ -621,6 +625,7 @@ struct srv_kw_list {
|
||||
#define SRV_PARSE_PARSE_ADDR 0x08 /* required to parse the server address in the second argument */
|
||||
#define SRV_PARSE_DYNAMIC 0x10 /* dynamic server created at runtime with cli */
|
||||
#define SRV_PARSE_INITIAL_RESOLVE 0x20 /* resolve immediately the fqdn to an ip address */
|
||||
#define SRV_PARSE_IN_LOG_BE 0x40 /* keyword in log backend */
|
||||
|
||||
#endif /* _HAPROXY_SERVER_T_H */
|
||||
|
||||
|
@ -2824,6 +2824,33 @@ int backend_parse_balance(const char **args, char **err, struct proxy *curproxy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function parses a "balance" statement in a log backend section
|
||||
* describing <curproxy>. It returns -1 if there is any error, otherwise zero.
|
||||
* If it returns -1, it will write an error message into the <err> buffer which
|
||||
* will automatically be allocated and must be passed as NULL. The trailing '\n'
|
||||
* will not be written. The function must be called with <args> pointing to the
|
||||
* first word after "balance".
|
||||
*/
|
||||
int backend_parse_log_balance(const char **args, char **err, struct proxy *curproxy)
|
||||
{
|
||||
if (!*(args[0])) {
|
||||
/* if no option is set, use round-robin by default */
|
||||
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
||||
curproxy->lbprm.algo |= BE_LB_ALGO_RR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(args[0], "roundrobin") == 0) {
|
||||
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
||||
curproxy->lbprm.algo |= BE_LB_ALGO_RR;
|
||||
}
|
||||
else {
|
||||
memprintf(err, "only supports 'roundrobin' option");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* All supported sample and ACL keywords must be declared here. */
|
||||
|
@ -50,7 +50,7 @@ static const char *common_kw_list[] = {
|
||||
"use-server", "force-persist", "ignore-persist", "force-persist",
|
||||
"stick-table", "stick", "stats", "option", "default_backend",
|
||||
"http-reuse", "monitor", "transparent", "maxconn", "backlog",
|
||||
"fullconn", "dispatch", "balance", "hash-type",
|
||||
"fullconn", "dispatch", "balance", "log-balance", "hash-type",
|
||||
"hash-balance-factor", "unique-id-format", "unique-id-header",
|
||||
"log-format", "log-format-sd", "log-tag", "log", "source", "usesrc",
|
||||
"error-log-format",
|
||||
@ -438,6 +438,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
if ((strcmp(args[0], "server") == 0)) {
|
||||
err_code |= parse_server(file, linenum, args,
|
||||
curproxy, curr_defproxy,
|
||||
(curproxy->mode == PR_MODE_SYSLOG ? SRV_PARSE_IN_LOG_BE : 0) |
|
||||
SRV_PARSE_PARSE_ADDR);
|
||||
|
||||
if (err_code & ERR_FATAL)
|
||||
@ -446,6 +447,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
else if (strcmp(args[0], "default-server") == 0) {
|
||||
err_code |= parse_server(file, linenum, args,
|
||||
curproxy, curr_defproxy,
|
||||
(curproxy->mode == PR_MODE_SYSLOG ? SRV_PARSE_IN_LOG_BE : 0) |
|
||||
SRV_PARSE_DEFAULT_SERVER);
|
||||
|
||||
if (err_code & ERR_FATAL)
|
||||
@ -454,6 +456,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
else if (strcmp(args[0], "server-template") == 0) {
|
||||
err_code |= parse_server(file, linenum, args,
|
||||
curproxy, curr_defproxy,
|
||||
(curproxy->mode == PR_MODE_SYSLOG ? SRV_PARSE_IN_LOG_BE : 0) |
|
||||
SRV_PARSE_TEMPLATE|SRV_PARSE_PARSE_ADDR);
|
||||
|
||||
if (err_code & ERR_FATAL)
|
||||
@ -544,6 +547,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
|
||||
if (strcmp(args[1], "http") == 0) curproxy->mode = PR_MODE_HTTP;
|
||||
else if (strcmp(args[1], "tcp") == 0) curproxy->mode = PR_MODE_TCP;
|
||||
else if (strcmp(args[1], "log") == 0 && (curproxy->cap & PR_CAP_BE)) curproxy->mode = PR_MODE_SYSLOG;
|
||||
else if (strcmp(args[1], "health") == 0) {
|
||||
ha_alert("parsing [%s:%d] : 'mode health' doesn't exist anymore. Please use 'http-request return status 200' instead.\n", file, linenum);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
@ -554,6 +558,15 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
/* mode log shares lbprm struct with other modes, but makes a different use of it,
|
||||
* thus, we must ensure that defproxy settings cannot persist between incompatibles
|
||||
* modes at this point.
|
||||
*/
|
||||
if ((curr_defproxy->mode == PR_MODE_SYSLOG && curproxy->mode != PR_MODE_SYSLOG) ||
|
||||
(curr_defproxy->mode != PR_MODE_SYSLOG && curproxy->mode == PR_MODE_SYSLOG)) {
|
||||
/* lbprm settings from incompatible defproxy, back to defaults */
|
||||
memset(&curproxy->lbprm, 0, sizeof(curproxy->lbprm));
|
||||
}
|
||||
}
|
||||
else if (strcmp(args[0], "id") == 0) {
|
||||
struct eb32_node *node;
|
||||
@ -2539,6 +2552,21 @@ stats_error_parsing:
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if (strcmp(args[0], "log-balance") == 0) { /* set log-balancing with optional algorithm */
|
||||
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
|
||||
err_code |= ERR_WARN;
|
||||
if (curproxy->mode != PR_MODE_SYSLOG) {
|
||||
ha_alert("parsing [%s:%d] : %s %s\n", file, linenum, args[0], "only available for log backends");
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (backend_parse_log_balance((const char **)args + 1, &errmsg, curproxy) < 0) {
|
||||
ha_alert("parsing [%s:%d] : %s %s\n", file, linenum, args[0], errmsg);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if (strcmp(args[0], "hash-type") == 0) { /* set hashing method */
|
||||
/**
|
||||
* The syntax for hash-type config element is
|
||||
|
@ -3744,6 +3744,12 @@ out_uri_auth_compat:
|
||||
* on what LB algorithm was chosen.
|
||||
*/
|
||||
|
||||
if (curproxy->mode == PR_MODE_SYSLOG) {
|
||||
/* log load-balancing requires special init that is performed
|
||||
* during log-postparsing step
|
||||
*/
|
||||
goto skip_server_lb_init;
|
||||
}
|
||||
curproxy->lbprm.algo &= ~(BE_LB_LKUP | BE_LB_PROP_DYN);
|
||||
switch (curproxy->lbprm.algo & BE_LB_KIND) {
|
||||
case BE_LB_KIND_RR:
|
||||
@ -3783,6 +3789,7 @@ out_uri_auth_compat:
|
||||
}
|
||||
break;
|
||||
}
|
||||
skip_server_lb_init:
|
||||
HA_RWLOCK_INIT(&curproxy->lbprm.lock);
|
||||
|
||||
if (curproxy->options & PR_O_LOGASAP)
|
||||
|
274
src/log.c
274
src/log.c
@ -740,14 +740,14 @@ static inline void init_log_target(struct log_target *target)
|
||||
target->type = 0;
|
||||
target->flags = LOG_TARGET_FL_NONE;
|
||||
target->addr = NULL;
|
||||
target->ring_name = NULL;
|
||||
target->resolv_name = NULL;
|
||||
}
|
||||
|
||||
static void deinit_log_target(struct log_target *target)
|
||||
{
|
||||
ha_free(&target->addr);
|
||||
if (!(target->flags & LOG_TARGET_FL_RESOLVED))
|
||||
ha_free(&target->ring_name);
|
||||
ha_free(&target->resolv_name);
|
||||
}
|
||||
|
||||
/* returns 0 on failure and positive value on success */
|
||||
@ -761,9 +761,9 @@ static int dup_log_target(struct log_target *def, struct log_target *cpy)
|
||||
goto error;
|
||||
*cpy->addr = *def->addr;
|
||||
}
|
||||
if (def->ring_name) {
|
||||
cpy->ring_name = strdup(def->ring_name);
|
||||
if (!cpy->ring_name)
|
||||
if (def->resolv_name) {
|
||||
cpy->resolv_name = strdup(def->resolv_name);
|
||||
if (!cpy->resolv_name)
|
||||
goto error;
|
||||
}
|
||||
cpy->type = def->type;
|
||||
@ -773,6 +773,172 @@ static int dup_log_target(struct log_target *def, struct log_target *cpy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* must be called under the lbprm lock */
|
||||
static void _log_backend_srv_queue(struct server *srv)
|
||||
{
|
||||
struct proxy *p = srv->proxy;
|
||||
|
||||
/* queue the server in the proxy lb array to make it easily searcheable by
|
||||
* log-balance algorithms. Here we use the srv array as a general server
|
||||
* pool of in-use servers, lookup is done using a relative positional id
|
||||
* (array is contiguous)
|
||||
*
|
||||
* We use the avail server list to get a quick hand on available servers
|
||||
* (those that are UP)
|
||||
*/
|
||||
if (srv->flags & SRV_F_BACKUP) {
|
||||
if (!p->srv_act)
|
||||
p->lbprm.log.srv[p->srv_bck] = srv;
|
||||
p->srv_bck++;
|
||||
}
|
||||
else {
|
||||
if (!p->srv_act) {
|
||||
/* we will be switching to act tree in LB logic, thus we need to
|
||||
* reset the lastid
|
||||
*/
|
||||
HA_ATOMIC_STORE(&p->lbprm.log.lastid, 0);
|
||||
}
|
||||
p->lbprm.log.srv[p->srv_act] = srv;
|
||||
p->srv_act++;
|
||||
}
|
||||
/* append the server to the list of available servers */
|
||||
LIST_APPEND(&p->lbprm.log.avail, &srv->lb_list);
|
||||
}
|
||||
|
||||
static void log_backend_srv_up(struct server *srv)
|
||||
{
|
||||
struct proxy *p = srv->proxy;
|
||||
|
||||
if (!srv_lb_status_changed(srv))
|
||||
return; /* nothing to do */
|
||||
if (srv_currently_usable(srv) || !srv_willbe_usable(srv))
|
||||
return; /* false alarm */
|
||||
|
||||
HA_RWLOCK_WRLOCK(LBPRM_LOCK, &p->lbprm.lock);
|
||||
_log_backend_srv_queue(srv);
|
||||
HA_RWLOCK_WRUNLOCK(LBPRM_LOCK, &p->lbprm.lock);
|
||||
}
|
||||
|
||||
/* must be called under lbprm lock */
|
||||
static void _log_backend_srv_recalc(struct proxy *p)
|
||||
{
|
||||
unsigned int it = 0;
|
||||
struct server *cur_srv;
|
||||
|
||||
list_for_each_entry(cur_srv, &p->lbprm.log.avail, lb_list) {
|
||||
uint8_t backup = cur_srv->flags & SRV_F_BACKUP;
|
||||
|
||||
if ((!p->srv_act && backup) ||
|
||||
(p->srv_act && !backup))
|
||||
p->lbprm.log.srv[it++] = cur_srv;
|
||||
}
|
||||
}
|
||||
|
||||
/* must be called under the lbprm lock */
|
||||
static void _log_backend_srv_dequeue(struct server *srv)
|
||||
{
|
||||
struct proxy *p = srv->proxy;
|
||||
|
||||
if (srv->flags & SRV_F_BACKUP) {
|
||||
p->srv_bck--;
|
||||
}
|
||||
else {
|
||||
p->srv_act--;
|
||||
if (!p->srv_act) {
|
||||
/* we will be switching to bck tree in LB logic, thus we need to
|
||||
* reset the lastid
|
||||
*/
|
||||
HA_ATOMIC_STORE(&p->lbprm.log.lastid, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* remove the srv from the list of available (UP) servers */
|
||||
LIST_DELETE(&srv->lb_list);
|
||||
|
||||
/* reconstruct the array of usable servers */
|
||||
_log_backend_srv_recalc(p);
|
||||
}
|
||||
|
||||
static void log_backend_srv_down(struct server *srv)
|
||||
{
|
||||
struct proxy *p = srv->proxy;
|
||||
|
||||
if (!srv_lb_status_changed(srv))
|
||||
return; /* nothing to do */
|
||||
if (!srv_currently_usable(srv) || srv_willbe_usable(srv))
|
||||
return; /* false alarm */
|
||||
|
||||
HA_RWLOCK_WRLOCK(LBPRM_LOCK, &p->lbprm.lock);
|
||||
_log_backend_srv_dequeue(srv);
|
||||
HA_RWLOCK_WRUNLOCK(LBPRM_LOCK, &p->lbprm.lock);
|
||||
}
|
||||
|
||||
static int postcheck_log_backend(struct proxy *be)
|
||||
{
|
||||
char *msg = NULL;
|
||||
struct server *srv;
|
||||
int err_code = ERR_NONE;
|
||||
int target_type = -1; // -1 is unused in log_tgt enum
|
||||
|
||||
if (be->mode != PR_MODE_SYSLOG ||
|
||||
(be->flags & (PR_FL_DISABLED|PR_FL_STOPPED)))
|
||||
return ERR_NONE; /* nothing to do */
|
||||
|
||||
/* First time encoutering this log backend, perform some init
|
||||
*/
|
||||
be->lbprm.set_server_status_up = log_backend_srv_up;
|
||||
be->lbprm.set_server_status_down = log_backend_srv_down;
|
||||
be->lbprm.log.lastid = 0; /* initial value */
|
||||
LIST_INIT(&be->lbprm.log.avail);
|
||||
|
||||
/* alloc srv array (it will be used for active and backup server lists in turn,
|
||||
* so we ensure that the longest list will fit
|
||||
*/
|
||||
be->lbprm.log.srv = calloc(MAX(be->srv_act, be->srv_bck), sizeof(struct server *));
|
||||
|
||||
if (!be->lbprm.log.srv ) {
|
||||
memprintf(&msg, "memory error when allocating server array (%d entries)",
|
||||
MAX(be->srv_act, be->srv_bck));
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* reinit srv counters, lbprm queueing will recount */
|
||||
be->srv_act = 0;
|
||||
be->srv_bck = 0;
|
||||
|
||||
/* finish the initialization of proxy's servers */
|
||||
srv = be->srv;
|
||||
while (srv) {
|
||||
if (target_type == -1)
|
||||
target_type = srv->log_target->type;
|
||||
if (target_type != srv->log_target->type) {
|
||||
memprintf(&msg, "cannot mix server types within a log backend, '%s' srv's network type differs from previous server", srv->id);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto end;
|
||||
}
|
||||
if (target_type == LOG_TARGET_BUFFER) {
|
||||
srv->log_target->sink = sink_new_from_srv(srv, "log backend");
|
||||
if (!srv->log_target->sink) {
|
||||
memprintf(&msg, "error when creating sink from '%s' log server", srv->id);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
srv->cur_eweight = 1; /* ignore weights, all servers have the same weight */
|
||||
_log_backend_srv_queue(srv);
|
||||
srv = srv->next;
|
||||
}
|
||||
end:
|
||||
if (err_code & ERR_CODE) {
|
||||
ha_free(&be->lbprm.log.srv); /* free log servers array */
|
||||
ha_alert("log backend '%s': failed to initialize: %s.\n", be->id, msg);
|
||||
ha_free(&msg);
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/* resolves a single logger entry (it is expected to be called
|
||||
* at postparsing stage)
|
||||
*
|
||||
@ -791,7 +957,26 @@ int resolve_logger(struct logger *logger, char **msg)
|
||||
|
||||
if (target->type == LOG_TARGET_BUFFER)
|
||||
err_code = sink_resolve_logger_buffer(logger, msg);
|
||||
else if (target->type == LOG_TARGET_BACKEND) {
|
||||
struct proxy *be;
|
||||
|
||||
/* special case */
|
||||
be = proxy_find_by_name(target->be_name, PR_CAP_BE, 0);
|
||||
if (!be) {
|
||||
memprintf(msg, "uses unknown log backend '%s'", target->be_name);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto end;
|
||||
}
|
||||
else if (be->mode != PR_MODE_SYSLOG) {
|
||||
memprintf(msg, "uses incompatible log backend '%s'", target->be_name);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto end;
|
||||
}
|
||||
ha_free(&target->be_name); /* backend is resolved and will replace name hint */
|
||||
target->be = be;
|
||||
}
|
||||
|
||||
end:
|
||||
target->flags |= LOG_TARGET_FL_RESOLVED;
|
||||
|
||||
return err_code;
|
||||
@ -861,6 +1046,11 @@ static int parse_log_target(char *raw, struct log_target *target, char **err)
|
||||
target->ring_name = strdup(raw + 5);
|
||||
goto done;
|
||||
}
|
||||
else if (strncmp(raw, "backend@", 8) == 0) {
|
||||
target->type = LOG_TARGET_BACKEND;
|
||||
target->be_name = strdup(raw + 8);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* try to allocate log target addr */
|
||||
target->addr = malloc(sizeof(*target->addr));
|
||||
@ -1876,6 +2066,69 @@ static inline void __do_send_log(struct log_target *target, struct log_header hd
|
||||
}
|
||||
}
|
||||
|
||||
/* does the same as __do_send_log() does for a single target, but here the log
|
||||
* will be sent according to the log backend's lb settings. The function will
|
||||
* leverage __do_send_log() function to actually send the log messages.
|
||||
*/
|
||||
static inline void __do_send_log_backend(struct proxy *be, struct log_header hdr,
|
||||
int nblogger, size_t maxlen,
|
||||
char *message, size_t size)
|
||||
{
|
||||
struct server *srv;
|
||||
uint32_t targetid = ~0; /* default value to check if it was explicitly assigned */
|
||||
uint32_t nb_srv;
|
||||
|
||||
HA_RWLOCK_RDLOCK(LBPRM_LOCK, &be->lbprm.lock);
|
||||
|
||||
if (be->srv_act) {
|
||||
nb_srv = be->srv_act;
|
||||
}
|
||||
else if (be->srv_bck) {
|
||||
/* no more active servers but backup ones are, switch to backup farm */
|
||||
nb_srv = be->srv_bck;
|
||||
if (!(be->options & PR_O_USE_ALL_BK)) {
|
||||
/* log balancing disabled on backup farm */
|
||||
targetid = 0; /* use first server */
|
||||
goto skip_lb;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* no srv available, can't log */
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* log-balancing logic: */
|
||||
|
||||
if ((be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_RR) {
|
||||
/* Atomically load and update lastid since it's not protected
|
||||
* by any write lock
|
||||
*
|
||||
* Wrapping is expected and could lead to unexpected ID reset in the
|
||||
* middle of a cycle, but given that this only happens once in every
|
||||
* 4 billions it is quite negligible
|
||||
*/
|
||||
targetid = HA_ATOMIC_FETCH_ADD(&be->lbprm.log.lastid, 1) % nb_srv;
|
||||
}
|
||||
|
||||
skip_lb:
|
||||
|
||||
if (targetid == ~0) {
|
||||
/* no target assigned, nothing to do */
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* find server based on targetid */
|
||||
srv = be->lbprm.log.srv[targetid];
|
||||
HA_RWLOCK_RDUNLOCK(LBPRM_LOCK, &be->lbprm.lock);
|
||||
|
||||
__do_send_log(srv->log_target, hdr, nblogger, maxlen, message, size);
|
||||
return;
|
||||
|
||||
drop:
|
||||
HA_RWLOCK_RDUNLOCK(LBPRM_LOCK, &be->lbprm.lock);
|
||||
_HA_ATOMIC_INC(&dropped_logs);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function sends a syslog message.
|
||||
* It doesn't care about errors nor does it report them.
|
||||
@ -1931,7 +2184,15 @@ void process_send_log(struct list *loggers, int level, int facility,
|
||||
hdr.facility = (facility == -1) ? logger->facility : facility;
|
||||
hdr.format = logger->format;
|
||||
hdr.metadata = metadata;
|
||||
__do_send_log(&logger->target, hdr, ++nblogger, logger->maxlen, message, size);
|
||||
|
||||
nblogger += 1;
|
||||
if (logger->target.type == LOG_TARGET_BACKEND) {
|
||||
__do_send_log_backend(logger->target.be, hdr, nblogger, logger->maxlen, message, size);
|
||||
}
|
||||
else {
|
||||
/* normal target */
|
||||
__do_send_log(&logger->target, hdr, nblogger, logger->maxlen, message, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4181,6 +4442,7 @@ static int postresolve_loggers()
|
||||
/* config parsers for this section */
|
||||
REGISTER_CONFIG_SECTION("log-forward", cfg_parse_log_forward, NULL);
|
||||
REGISTER_POST_CHECK(postresolve_loggers);
|
||||
REGISTER_POST_PROXY_CHECK(postcheck_log_backend);
|
||||
|
||||
REGISTER_PER_THREAD_ALLOC(init_log_buffers);
|
||||
REGISTER_PER_THREAD_FREE(deinit_log_buffers);
|
||||
|
@ -190,6 +190,8 @@ void free_proxy(struct proxy *p)
|
||||
free(p->conf.uif_file);
|
||||
if ((p->lbprm.algo & BE_LB_LKUP) == BE_LB_LKUP_MAP)
|
||||
free(p->lbprm.map.srv);
|
||||
if (p->mode == PR_MODE_SYSLOG)
|
||||
free(p->lbprm.log.srv);
|
||||
|
||||
if (p->conf.logformat_sd_string != default_rfc5424_sd_log_format)
|
||||
free(p->conf.logformat_sd_string);
|
||||
|
43
src/server.c
43
src/server.c
@ -1901,7 +1901,7 @@ static struct srv_kw_list srv_kws = { "ALL", { }, {
|
||||
{ "ws", srv_parse_ws, 1, 1, 1 }, /* websocket protocol */
|
||||
{ "id", srv_parse_id, 1, 0, 1 }, /* set id# of server */
|
||||
{ "init-addr", srv_parse_init_addr, 1, 1, 0 }, /* */
|
||||
{ "log-proto", srv_parse_log_proto, 1, 1, 0 }, /* Set the protocol for event messages, only relevant in a ring section */
|
||||
{ "log-proto", srv_parse_log_proto, 1, 1, 0 }, /* Set the protocol for event messages, only relevant in a log or ring section */
|
||||
{ "maxconn", srv_parse_maxconn, 1, 1, 1 }, /* Set the max number of concurrent connection */
|
||||
{ "maxqueue", srv_parse_maxqueue, 1, 1, 1 }, /* Set the max number of connection to put in queue */
|
||||
{ "max-reuse", srv_parse_max_reuse, 1, 1, 0 }, /* Set the max number of requests on a connection, -1 means unlimited */
|
||||
@ -2495,6 +2495,7 @@ void srv_free_params(struct server *srv)
|
||||
free(srv->resolvers_id);
|
||||
free(srv->addr_node.key);
|
||||
free(srv->lb_nodes);
|
||||
free(srv->log_target);
|
||||
|
||||
if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->destroy_srv)
|
||||
xprt_get(XPRT_SSL)->destroy_srv(srv);
|
||||
@ -2701,6 +2702,7 @@ static int _srv_parse_init(struct server **srv, char **args, int *cur_arg,
|
||||
const char *err = NULL;
|
||||
int err_code = 0;
|
||||
char *fqdn = NULL;
|
||||
struct protocol *proto;
|
||||
int tmpl_range_low = 0, tmpl_range_high = 0;
|
||||
char *errmsg = NULL;
|
||||
|
||||
@ -2806,11 +2808,12 @@ static int _srv_parse_init(struct server **srv, char **args, int *cur_arg,
|
||||
if (!(parse_flags & SRV_PARSE_PARSE_ADDR))
|
||||
goto skip_addr;
|
||||
|
||||
sk = str2sa_range(args[*cur_arg], &port, &port1, &port2, NULL, NULL,
|
||||
sk = str2sa_range(args[*cur_arg], &port, &port1, &port2, NULL, &proto,
|
||||
&errmsg, NULL, &fqdn,
|
||||
(parse_flags & SRV_PARSE_IN_LOG_BE ? PA_O_DGRAM : PA_O_CONNECT) |
|
||||
(parse_flags & SRV_PARSE_INITIAL_RESOLVE ? PA_O_RESOLVE : 0) | PA_O_PORT_OK |
|
||||
(parse_flags & SRV_PARSE_IN_PEER_SECTION ? PA_O_PORT_MAND : PA_O_PORT_OFS) |
|
||||
PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT);
|
||||
PA_O_STREAM | PA_O_XPRT);
|
||||
if (!sk) {
|
||||
ha_alert("%s\n", errmsg);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
@ -2843,6 +2846,35 @@ static int _srv_parse_init(struct server **srv, char **args, int *cur_arg,
|
||||
}
|
||||
}
|
||||
|
||||
if ((parse_flags & SRV_PARSE_IN_LOG_BE) && proto) {
|
||||
/* mode log enabled, and found proto:
|
||||
* pre-resolve related log target from known infos
|
||||
*/
|
||||
newsrv->log_target = malloc(sizeof(*newsrv->log_target));
|
||||
if (!newsrv->log_target) {
|
||||
ha_alert("memory error when allocating log server\n");
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
newsrv->log_target->addr = &newsrv->addr;
|
||||
switch (proto->xprt_type) {
|
||||
case PROTO_TYPE_DGRAM:
|
||||
newsrv->log_target->type = LOG_TARGET_DGRAM;
|
||||
break;
|
||||
case PROTO_TYPE_STREAM:
|
||||
/* for now BUFFER type only supports TCP server to it's almost
|
||||
* explicit. This will require ring buffer creation during log
|
||||
* postresolving step.
|
||||
*/
|
||||
newsrv->log_target->type = LOG_TARGET_BUFFER;
|
||||
break;
|
||||
default:
|
||||
ha_alert("log server type not supported for log backend server.\n");
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
newsrv->addr = *sk;
|
||||
newsrv->svc_port = port;
|
||||
/*
|
||||
@ -4863,6 +4895,11 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (be->mode == PR_MODE_SYSLOG) {
|
||||
cli_err(appctx," Dynamic servers cannot be used with log backends.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* At this point, some operations might not be thread-safe anymore. This
|
||||
* might be the case for parsing handlers which were designed to run
|
||||
* only at the starting stage on single-thread mode.
|
||||
|
Loading…
Reference in New Issue
Block a user