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:
Aurelien DARRAGON 2023-09-13 11:52:31 +02:00 committed by Christopher Faulet
parent e58a9b4baf
commit 9a74a6cb17
11 changed files with 449 additions and 17 deletions

View File

@ -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.

View File

@ -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 */

View File

@ -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);

View File

@ -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;

View File

@ -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 */

View File

@ -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. */

View File

@ -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

View File

@ -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
View File

@ -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);

View File

@ -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);

View File

@ -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.