blob: 9011724df2a08dc3e38042426468463aed931bff [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * General logging functions.
3 *
Willy Tarreaub7f694f2008-06-22 17:18:02 +02004 * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
Willy Tarreaubaaee002006-06-26 02:48:02 +02005 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
Willy Tarreau8a3f52f2012-12-20 21:23:42 +010013#include <ctype.h>
Willy Tarreauc8f24f82007-11-30 18:38:35 +010014#include <fcntl.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020015#include <stdarg.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <syslog.h>
20#include <time.h>
21#include <unistd.h>
Robert Tsai81ae1952007-12-05 10:47:29 +010022#include <errno.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020023
24#include <sys/time.h>
Willy Tarreau077edcb2016-08-10 18:30:56 +020025#include <sys/uio.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026
Willy Tarreaue3ba5f02006-06-29 18:54:54 +020027#include <common/config.h>
Willy Tarreaud6d06902009-08-19 11:22:33 +020028#include <common/compat.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010029#include <common/initcall.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020030#include <common/standard.h>
Willy Tarreaufb278672006-10-15 15:38:50 +020031#include <common/time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020032
Christopher Fauletc1b730a2017-10-24 12:00:51 +020033#include <types/cli.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020034#include <types/global.h>
William Lallemand723b73a2012-02-08 16:37:49 +010035#include <types/log.h>
Willy Tarreauec6c5df2008-07-15 00:22:45 +020036
Christopher Fauletc1b730a2017-10-24 12:00:51 +020037#include <proto/applet.h>
38#include <proto/cli.h>
Willy Tarreaud52a7f82019-08-30 14:05:35 +020039#include <proto/fd.h>
William Lallemand5f232402012-04-05 18:02:55 +020040#include <proto/frontend.h>
Willy Tarreauec6c5df2008-07-15 00:22:45 +020041#include <proto/log.h>
Willy Tarreauc8368452012-12-21 00:09:23 +010042#include <proto/sample.h>
Willy Tarreauc125cef2019-05-10 09:58:43 +020043#include <proto/ssl_sock.h>
Willy Tarreaufb0afa72015-04-03 14:46:27 +020044#include <proto/stream.h>
Willy Tarreau827aee92011-03-10 16:55:02 +010045#include <proto/stream_interface.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020046
Dragan Dosen43885c72015-10-01 13:18:13 +020047struct log_fmt {
48 char *name;
49 struct {
Willy Tarreau83061a82018-07-13 11:56:34 +020050 struct buffer sep1; /* first pid separator */
51 struct buffer sep2; /* second pid separator */
Dragan Dosen43885c72015-10-01 13:18:13 +020052 } pid;
53};
54
55static const struct log_fmt log_formats[LOG_FORMATS] = {
56 [LOG_FORMAT_RFC3164] = {
57 .name = "rfc3164",
58 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020059 .sep1 = { .area = "[", .data = 1 },
60 .sep2 = { .area = "]: ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020061 }
62 },
63 [LOG_FORMAT_RFC5424] = {
64 .name = "rfc5424",
65 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020066 .sep1 = { .area = " ", .data = 1 },
67 .sep2 = { .area = " - ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020068 }
Willy Tarreaue8746a02018-11-12 08:45:00 +010069 },
70 [LOG_FORMAT_SHORT] = {
71 .name = "short",
72 .pid = {
73 .sep1 = { .area = "", .data = 0 },
74 .sep2 = { .area = " ", .data = 1 },
75 }
76 },
Willy Tarreauc1b06452018-11-12 11:57:56 +010077 [LOG_FORMAT_RAW] = {
78 .name = "raw",
79 .pid = {
80 .sep1 = { .area = "", .data = 0 },
81 .sep2 = { .area = "", .data = 0 },
82 }
83 },
Dragan Dosen1322d092015-09-22 16:05:32 +020084};
85
Dragan Dosen835b9212016-02-12 13:23:03 +010086/*
87 * This map is used with all the FD_* macros to check whether a particular bit
Willy Tarreau1bfd6022019-06-07 11:10:07 +020088 * is set or not. Each bit represents an ACSII code. ha_bit_set() sets those
89 * bytes which should be escaped. When ha_bit_test() returns non-zero, it means
90 * that the byte should be escaped. Be careful to always pass bytes from 0 to
91 * 255 exclusively to the macros.
Dragan Dosen835b9212016-02-12 13:23:03 +010092 */
Willy Tarreau1bfd6022019-06-07 11:10:07 +020093long rfc5424_escape_map[(256/8) / sizeof(long)];
94long hdr_encode_map[(256/8) / sizeof(long)];
95long url_encode_map[(256/8) / sizeof(long)];
96long http_encode_map[(256/8) / sizeof(long)];
Dragan Dosen835b9212016-02-12 13:23:03 +010097
Dragan Dosen835b9212016-02-12 13:23:03 +010098
Willy Tarreaubaaee002006-06-26 02:48:02 +020099const char *log_facilities[NB_LOG_FACILITIES] = {
100 "kern", "user", "mail", "daemon",
101 "auth", "syslog", "lpr", "news",
102 "uucp", "cron", "auth2", "ftp",
103 "ntp", "audit", "alert", "cron2",
104 "local0", "local1", "local2", "local3",
105 "local4", "local5", "local6", "local7"
106};
107
Willy Tarreaubaaee002006-06-26 02:48:02 +0200108const char *log_levels[NB_LOG_LEVELS] = {
109 "emerg", "alert", "crit", "err",
110 "warning", "notice", "info", "debug"
111};
112
Willy Tarreau570f2212013-06-10 16:42:09 +0200113const char sess_term_cond[16] = "-LcCsSPRIDKUIIII"; /* normal, Local, CliTo, CliErr, SrvTo, SrvErr, PxErr, Resource, Internal, Down, Killed, Up, -- */
Willy Tarreaub8750a82006-09-03 09:56:00 +0200114const char sess_fin_state[8] = "-RCHDLQT"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, Tarpit */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200115
William Lallemand723b73a2012-02-08 16:37:49 +0100116
117/* log_format */
118struct logformat_type {
119 char *name;
120 int type;
William Lallemandbddd4fd2012-02-27 11:23:10 +0100121 int mode;
William Lallemand5e19a282012-04-02 16:22:10 +0200122 int lw; /* logwait bitsfield */
William Lallemandb7ff6a32012-03-02 14:35:21 +0100123 int (*config_callback)(struct logformat_node *node, struct proxy *curproxy);
Willy Tarreau2beef582012-12-20 17:22:52 +0100124 const char *replace_by; /* new option to use instead of old one */
William Lallemand723b73a2012-02-08 16:37:49 +0100125};
126
William Lallemandb7ff6a32012-03-02 14:35:21 +0100127int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy);
128
William Lallemand723b73a2012-02-08 16:37:49 +0100129/* log_format variable names */
130static const struct logformat_type logformat_keywords[] = {
William Lallemand5e19a282012-04-02 16:22:10 +0200131 { "o", LOG_FMT_GLOBAL, PR_MODE_TCP, 0, NULL }, /* global option */
Willy Tarreau2beef582012-12-20 17:22:52 +0100132
133 /* please keep these lines sorted ! */
134 { "B", LOG_FMT_BYTES, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from server to client */
135 { "CC", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL }, /* client cookie */
136 { "CS", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL }, /* server cookie */
137 { "H", LOG_FMT_HOSTNAME, PR_MODE_TCP, LW_INIT, NULL }, /* Hostname */
138 { "ID", LOG_FMT_UNIQUEID, PR_MODE_HTTP, LW_BYTES, NULL }, /* Unique ID */
Willy Tarreau4bf99632014-06-13 12:21:40 +0200139 { "ST", LOG_FMT_STATUS, PR_MODE_TCP, LW_RESP, NULL }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200140 { "T", LOG_FMT_DATEGMT, PR_MODE_TCP, LW_INIT, NULL }, /* date GMT */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200141 { "Ta", LOG_FMT_Ta, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time active (tr to end) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100142 { "Tc", LOG_FMT_TC, PR_MODE_TCP, LW_BYTES, NULL }, /* Tc */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200143 { "Th", LOG_FMT_Th, PR_MODE_TCP, LW_BYTES, NULL }, /* Time handshake */
144 { "Ti", LOG_FMT_Ti, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time idle */
145 { "Tl", LOG_FMT_DATELOCAL, PR_MODE_TCP, LW_INIT, NULL }, /* date local timezone */
146 { "Tq", LOG_FMT_TQ, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tq=Th+Ti+TR */
147 { "Tr", LOG_FMT_Tr, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tr */
148 { "TR", LOG_FMT_TR, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time to receive a valid request */
Willy Tarreau27b639d2016-05-17 17:55:27 +0200149 { "Td", LOG_FMT_TD, PR_MODE_TCP, LW_BYTES, NULL }, /* Td = Tt - (Tq + Tw + Tc + Tr) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100150 { "Ts", LOG_FMT_TS, PR_MODE_TCP, LW_INIT, NULL }, /* timestamp GMT */
William Lallemand5e19a282012-04-02 16:22:10 +0200151 { "Tt", LOG_FMT_TT, PR_MODE_TCP, LW_BYTES, NULL }, /* Tt */
Willy Tarreau2beef582012-12-20 17:22:52 +0100152 { "Tw", LOG_FMT_TW, PR_MODE_TCP, LW_BYTES, NULL }, /* Tw */
153 { "U", LOG_FMT_BYTES_UP, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from client to server */
William Lallemand5e19a282012-04-02 16:22:10 +0200154 { "ac", LOG_FMT_ACTCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* actconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100155 { "b", LOG_FMT_BACKEND, PR_MODE_TCP, LW_INIT, NULL }, /* backend */
William Lallemand5e19a282012-04-02 16:22:10 +0200156 { "bc", LOG_FMT_BECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* beconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100157 { "bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source ip */
158 { "bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source port */
William Lallemand5e19a282012-04-02 16:22:10 +0200159 { "bq", LOG_FMT_BCKQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* backend_queue */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200160 { "ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client ip */
161 { "cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100162 { "f", LOG_FMT_FRONTEND, PR_MODE_TCP, LW_INIT, NULL }, /* frontend */
163 { "fc", LOG_FMT_FECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* feconn */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200164 { "fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend ip */
165 { "fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100166 { "ft", LOG_FMT_FRONTEND_XPRT, PR_MODE_TCP, LW_INIT, NULL }, /* frontend with transport mode */
Willy Tarreaud9ed3d22014-06-13 12:23:06 +0200167 { "hr", LOG_FMT_HDRREQUEST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request */
168 { "hrl", LOG_FMT_HDRREQUESTLIST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request list */
169 { "hs", LOG_FMT_HDRRESPONS, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response */
170 { "hsl", LOG_FMT_HDRRESPONSLIST, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response list */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000171 { "HM", LOG_FMT_HTTP_METHOD, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP method */
172 { "HP", LOG_FMT_HTTP_PATH, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP path */
Andrew Hayworthe63ac872015-07-31 16:14:16 +0000173 { "HQ", LOG_FMT_HTTP_QUERY, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP query */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000174 { "HU", LOG_FMT_HTTP_URI, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP full URI */
175 { "HV", LOG_FMT_HTTP_VERSION, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP version */
Willy Tarreau7346acb2014-08-28 15:03:15 +0200176 { "lc", LOG_FMT_LOGCNT, PR_MODE_TCP, LW_INIT, NULL }, /* log counter */
Willy Tarreau2beef582012-12-20 17:22:52 +0100177 { "ms", LOG_FMT_MS, PR_MODE_TCP, LW_INIT, NULL }, /* accept date millisecond */
William Lallemand5e19a282012-04-02 16:22:10 +0200178 { "pid", LOG_FMT_PID, PR_MODE_TCP, LW_INIT, NULL }, /* log pid */
Willy Tarreau2beef582012-12-20 17:22:52 +0100179 { "r", LOG_FMT_REQ, PR_MODE_HTTP, LW_REQ, NULL }, /* request */
180 { "rc", LOG_FMT_RETRIES, PR_MODE_TCP, LW_BYTES, NULL }, /* retries */
Willy Tarreau1f0da242014-01-25 11:01:50 +0100181 { "rt", LOG_FMT_COUNTER, PR_MODE_TCP, LW_REQ, NULL }, /* request counter (HTTP or TCP session) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100182 { "s", LOG_FMT_SERVER, PR_MODE_TCP, LW_SVID, NULL }, /* server */
183 { "sc", LOG_FMT_SRVCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_conn */
184 { "si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination ip */
185 { "sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination port */
186 { "sq", LOG_FMT_SRVQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_queue */
Willy Tarreauffc3fcd2012-10-12 20:17:54 +0200187 { "sslc", LOG_FMT_SSL_CIPHER, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL ciphers */
188 { "sslv", LOG_FMT_SSL_VERSION, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL protocol version */
Willy Tarreau2beef582012-12-20 17:22:52 +0100189 { "t", LOG_FMT_DATE, PR_MODE_TCP, LW_INIT, NULL }, /* date */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200190 { "tr", LOG_FMT_tr, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request */
191 { "trg",LOG_FMT_trg, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, GMT */
192 { "trl",LOG_FMT_trl, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, local */
Willy Tarreau2beef582012-12-20 17:22:52 +0100193 { "ts", LOG_FMT_TERMSTATE, PR_MODE_TCP, LW_BYTES, NULL },/* termination state */
194 { "tsc", LOG_FMT_TERMSTATE_CK, PR_MODE_TCP, LW_INIT, NULL },/* termination state */
195
196 /* The following tags are deprecated and will be removed soon */
197 { "Bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bi" }, /* backend source ip */
198 { "Bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bp" }, /* backend source port */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200199 { "Ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "ci" }, /* client ip */
200 { "Cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "cp" }, /* client port */
201 { "Fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fi" }, /* frontend ip */
202 { "Fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fp" }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100203 { "Si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL, "si" }, /* server destination ip */
204 { "Sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL, "sp" }, /* server destination port */
205 { "cc", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL, "CC" }, /* client cookie */
206 { "cs", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL, "CS" }, /* server cookie */
207 { "st", LOG_FMT_STATUS, PR_MODE_HTTP, LW_RESP, NULL, "ST" }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200208 { 0, 0, 0, 0, NULL }
William Lallemand723b73a2012-02-08 16:37:49 +0100209};
210
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200211char default_http_log_format[] = "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"; // default format
212char clf_http_log_format[] = "%{+Q}o %{-Q}ci - - [%trg] %r %ST %B \"\" \"\" %cp %ms %ft %b %s %TR %Tw %Tc %Tr %Ta %tsc %ac %fc %bc %sc %rc %sq %bq %CC %CS %hrl %hsl";
Willy Tarreau2beef582012-12-20 17:22:52 +0100213char default_tcp_log_format[] = "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq";
William Lallemand723b73a2012-02-08 16:37:49 +0100214char *log_format = NULL;
215
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200216/* Default string used for structured-data part in RFC5424 formatted
217 * syslog messages.
218 */
219char default_rfc5424_sd_log_format[] = "- ";
Dragan Dosen1322d092015-09-22 16:05:32 +0200220
Willy Tarreau13ef7732018-11-12 07:25:28 +0100221/* total number of dropped logs */
222unsigned int dropped_logs = 0;
223
Dragan Dosen1322d092015-09-22 16:05:32 +0200224/* This is a global syslog header, common to all outgoing messages in
225 * RFC3164 format. It begins with time-based part and is updated by
226 * update_log_hdr().
Dragan Dosen59cee972015-09-19 22:09:02 +0200227 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200228THREAD_LOCAL char *logheader = NULL;
Willy Tarreau55e2f5a2019-05-05 10:11:39 +0200229THREAD_LOCAL char *logheader_end = NULL;
Dragan Dosen59cee972015-09-19 22:09:02 +0200230
Dragan Dosen1322d092015-09-22 16:05:32 +0200231/* This is a global syslog header for messages in RFC5424 format. It is
232 * updated by update_log_hdr_rfc5424().
233 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200234THREAD_LOCAL char *logheader_rfc5424 = NULL;
Willy Tarreau55e2f5a2019-05-05 10:11:39 +0200235THREAD_LOCAL char *logheader_rfc5424_end = NULL;
Dragan Dosen1322d092015-09-22 16:05:32 +0200236
Dragan Dosen59cee972015-09-19 22:09:02 +0200237/* This is a global syslog message buffer, common to all outgoing
238 * messages. It contains only the data part.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100239 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200240THREAD_LOCAL char *logline = NULL;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100241
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200242/* A global syslog message buffer, common to all RFC5424 syslog messages.
243 * Currently, it is used for generating the structured-data part.
244 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200245THREAD_LOCAL char *logline_rfc5424 = NULL;
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200246
Christopher Fauletd4696382017-10-24 11:44:05 +0200247/* A global buffer used to store all startup alerts/warnings. It will then be
248 * retrieve on the CLI. */
Willy Tarreaua6483992018-12-15 16:55:36 +0100249static char *startup_logs = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +0200250
William Lallemand723b73a2012-02-08 16:37:49 +0100251struct logformat_var_args {
252 char *name;
253 int mask;
254};
255
256struct logformat_var_args var_args_list[] = {
257// global
258 { "M", LOG_OPT_MANDATORY },
259 { "Q", LOG_OPT_QUOTE },
William Lallemand5f232402012-04-05 18:02:55 +0200260 { "X", LOG_OPT_HEXA },
Dragan Dosen835b9212016-02-12 13:23:03 +0100261 { "E", LOG_OPT_ESC },
William Lallemand723b73a2012-02-08 16:37:49 +0100262 { 0, 0 }
263};
264
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200265/* return the name of the directive used in the current proxy for which we're
266 * currently parsing a header, when it is known.
267 */
268static inline const char *fmt_directive(const struct proxy *curproxy)
269{
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100270 switch (curproxy->conf.args.ctx) {
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200271 case ARGC_ACL:
272 return "acl";
273 case ARGC_STK:
274 return "stick";
275 case ARGC_TRK:
276 return "track-sc";
277 case ARGC_LOG:
278 return "log-format";
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200279 case ARGC_LOGSD:
280 return "log-format-sd";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100281 case ARGC_HRQ:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100282 return "http-request";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100283 case ARGC_HRS:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100284 return "http-response";
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200285 case ARGC_UIF:
286 return "unique-id-format";
Thierry FOURNIERd18cd0f2013-11-29 12:15:45 +0100287 case ARGC_RDR:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200288 return "redirect";
289 case ARGC_CAP:
290 return "capture";
Willy Tarreau28d976d2015-07-09 11:39:33 +0200291 case ARGC_SRV:
292 return "server";
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200293 case ARGC_SPOE:
294 return "spoe-message";
Thierry FOURNIER / OZON.IO4ed1c952016-11-24 23:57:54 +0100295 case ARGC_UBK:
296 return "use_backend";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100297 default:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200298 return "undefined(please report this bug)"; /* must never happen */
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100299 }
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200300}
301
William Lallemand723b73a2012-02-08 16:37:49 +0100302/*
William Lallemandb7ff6a32012-03-02 14:35:21 +0100303 * callback used to configure addr source retrieval
304 */
305int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy)
306{
307 curproxy->options2 |= PR_O2_SRC_ADDR;
308
309 return 0;
310}
311
312
313/*
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100314 * Parse args in a logformat_var. Returns 0 in error
315 * case, otherwise, it returns 1.
William Lallemand723b73a2012-02-08 16:37:49 +0100316 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100317int parse_logformat_var_args(char *args, struct logformat_node *node, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100318{
319 int i = 0;
320 int end = 0;
321 int flags = 0; // 1 = + 2 = -
322 char *sp = NULL; // start pointer
323
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100324 if (args == NULL) {
325 memprintf(err, "internal error: parse_logformat_var_args() expects non null 'args'");
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100326 return 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100327 }
William Lallemand723b73a2012-02-08 16:37:49 +0100328
329 while (1) {
330 if (*args == '\0')
331 end = 1;
332
333 if (*args == '+') {
334 // add flag
335 sp = args + 1;
336 flags = 1;
337 }
338 if (*args == '-') {
339 // delete flag
340 sp = args + 1;
341 flags = 2;
342 }
343
344 if (*args == '\0' || *args == ',') {
345 *args = '\0';
Willy Tarreau254d44c2012-12-20 18:19:26 +0100346 for (i = 0; sp && var_args_list[i].name; i++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100347 if (strcmp(sp, var_args_list[i].name) == 0) {
348 if (flags == 1) {
349 node->options |= var_args_list[i].mask;
350 break;
351 } else if (flags == 2) {
352 node->options &= ~var_args_list[i].mask;
353 break;
354 }
355 }
356 }
357 sp = NULL;
358 if (end)
359 break;
360 }
Willy Tarreau254d44c2012-12-20 18:19:26 +0100361 args++;
William Lallemand723b73a2012-02-08 16:37:49 +0100362 }
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100363 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100364}
365
366/*
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100367 * Parse a variable '%varname' or '%{args}varname' in log-format. The caller
368 * must pass the args part in the <arg> pointer with its length in <arg_len>,
369 * and varname with its length in <var> and <var_len> respectively. <arg> is
370 * ignored when arg_len is 0. Neither <var> nor <var_len> may be null.
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100371 * Returns false in error case and err is filled, otherwise returns true.
William Lallemand723b73a2012-02-08 16:37:49 +0100372 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100373int parse_logformat_var(char *arg, int arg_len, char *var, int var_len, struct proxy *curproxy, struct list *list_format, int *defoptions, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100374{
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100375 int j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200376 struct logformat_node *node = NULL;
William Lallemand723b73a2012-02-08 16:37:49 +0100377
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100378 for (j = 0; logformat_keywords[j].name; j++) { // search a log type
379 if (strlen(logformat_keywords[j].name) == var_len &&
380 strncmp(var, logformat_keywords[j].name, var_len) == 0) {
381 if (logformat_keywords[j].mode != PR_MODE_HTTP || curproxy->mode == PR_MODE_HTTP) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200382 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100383 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100384 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200385 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100386 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100387 node->type = logformat_keywords[j].type;
388 node->options = *defoptions;
389 if (arg_len) {
390 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100391 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200392 goto error_free;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100393 }
394 if (node->type == LOG_FMT_GLOBAL) {
395 *defoptions = node->options;
396 free(node->arg);
397 free(node);
398 } else {
399 if (logformat_keywords[j].config_callback &&
400 logformat_keywords[j].config_callback(node, curproxy) != 0) {
Dragan Dosen61302da2019-04-30 00:40:02 +0200401 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100402 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100403 curproxy->to_log |= logformat_keywords[j].lw;
404 LIST_ADDQ(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100405 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100406 if (logformat_keywords[j].replace_by)
Christopher Faulet767a84b2017-11-24 16:50:31 +0100407 ha_warning("parsing [%s:%d] : deprecated variable '%s' in '%s', please replace it with '%s'.\n",
408 curproxy->conf.args.file, curproxy->conf.args.line,
409 logformat_keywords[j].name, fmt_directive(curproxy), logformat_keywords[j].replace_by);
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100410 return 1;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100411 } else {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100412 memprintf(err, "format variable '%s' is reserved for HTTP mode",
413 logformat_keywords[j].name);
Dragan Dosen61302da2019-04-30 00:40:02 +0200414 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100415 }
William Lallemand723b73a2012-02-08 16:37:49 +0100416 }
417 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100418
419 j = var[var_len];
420 var[var_len] = 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100421 memprintf(err, "no such format variable '%s'. If you wanted to emit the '%%' character verbatim, you need to use '%%%%'", var);
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100422 var[var_len] = j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200423
424 error_free:
425 if (node) {
426 free(node->arg);
427 free(node);
428 }
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100429 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100430}
431
432/*
433 * push to the logformat linked list
434 *
435 * start: start pointer
436 * end: end text pointer
437 * type: string type
William Lallemand1d705562012-03-12 12:46:41 +0100438 * list_format: destination list
William Lallemand723b73a2012-02-08 16:37:49 +0100439 *
440 * LOG_TEXT: copy chars from start to end excluding end.
441 *
442*/
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100443int add_to_logformat_list(char *start, char *end, int type, struct list *list_format, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100444{
445 char *str;
446
Willy Tarreaua3571662012-12-20 21:59:12 +0100447 if (type == LF_TEXT) { /* type text */
Vincent Bernat02779b62016-04-03 13:48:43 +0200448 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100449 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100450 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100451 return 0;
452 }
Vincent Bernat02779b62016-04-03 13:48:43 +0200453 str = calloc(1, end - start + 1);
William Lallemand723b73a2012-02-08 16:37:49 +0100454 strncpy(str, start, end - start);
William Lallemand723b73a2012-02-08 16:37:49 +0100455 str[end - start] = '\0';
456 node->arg = str;
William Lallemand1d705562012-03-12 12:46:41 +0100457 node->type = LOG_FMT_TEXT; // type string
458 LIST_ADDQ(list_format, &node->list);
Willy Tarreaua3571662012-12-20 21:59:12 +0100459 } else if (type == LF_SEPARATOR) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200460 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100461 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100462 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100463 return 0;
464 }
William Lallemand1d705562012-03-12 12:46:41 +0100465 node->type = LOG_FMT_SEPARATOR;
466 LIST_ADDQ(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100467 }
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100468 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100469}
470
471/*
Willy Tarreauc8368452012-12-21 00:09:23 +0100472 * Parse the sample fetch expression <text> and add a node to <list_format> upon
473 * success. At the moment, sample converters are not yet supported but fetch arguments
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200474 * should work. The curpx->conf.args.ctx must be set by the caller.
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100475 *
476 * In error case, the function returns 0, otherwise it returns 1.
Willy Tarreauc8368452012-12-21 00:09:23 +0100477 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100478int add_sample_to_logformat_list(char *text, char *arg, int arg_len, struct proxy *curpx, struct list *list_format, int options, int cap, char **err)
Willy Tarreauc8368452012-12-21 00:09:23 +0100479{
480 char *cmd[2];
Dragan Dosen61302da2019-04-30 00:40:02 +0200481 struct sample_expr *expr = NULL;
482 struct logformat_node *node = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +0100483 int cmd_arg;
484
485 cmd[0] = text;
486 cmd[1] = "";
487 cmd_arg = 0;
488
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100489 expr = sample_parse_expr(cmd, &cmd_arg, curpx->conf.args.file, curpx->conf.args.line, err, &curpx->conf.args);
Willy Tarreauc8368452012-12-21 00:09:23 +0100490 if (!expr) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100491 memprintf(err, "failed to parse sample expression <%s> : %s", text, *err);
Dragan Dosen61302da2019-04-30 00:40:02 +0200492 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100493 }
494
Vincent Bernat02779b62016-04-03 13:48:43 +0200495 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100496 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100497 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200498 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100499 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100500 node->type = LOG_FMT_EXPR;
501 node->expr = expr;
502 node->options = options;
503
504 if (arg_len) {
505 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100506 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200507 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100508 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100509 if (expr->fetch->val & cap & SMP_VAL_REQUEST)
Willy Tarreauc8368452012-12-21 00:09:23 +0100510 node->options |= LOG_OPT_REQ_CAP; /* fetch method is request-compatible */
511
Willy Tarreau434c57c2013-01-08 01:10:24 +0100512 if (expr->fetch->val & cap & SMP_VAL_RESPONSE)
Willy Tarreauc8368452012-12-21 00:09:23 +0100513 node->options |= LOG_OPT_RES_CAP; /* fetch method is response-compatible */
514
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100515 if (!(expr->fetch->val & cap)) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100516 memprintf(err, "sample fetch <%s> may not be reliably used here because it needs '%s' which is not available here",
517 text, sample_src_names(expr->fetch->use));
Dragan Dosen61302da2019-04-30 00:40:02 +0200518 goto error_free;
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100519 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100520
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200521 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreauc8368452012-12-21 00:09:23 +0100522 /* Note, we may also need to set curpx->to_log with certain fetches */
Willy Tarreau25320b22013-03-24 07:22:08 +0100523 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
Willy Tarreauc8368452012-12-21 00:09:23 +0100524
William Lallemand65ad6e12014-01-31 15:08:02 +0100525 /* FIXME: temporary workaround for missing LW_XPRT and LW_REQ flags
526 * needed with some sample fetches (eg: ssl*). We always set it for
527 * now on, but this will leave with sample capabilities soon.
Willy Tarreau1f31c732013-01-10 16:22:27 +0100528 */
529 curpx->to_log |= LW_XPRT;
William Lallemand65ad6e12014-01-31 15:08:02 +0100530 curpx->to_log |= LW_REQ;
Willy Tarreauc8368452012-12-21 00:09:23 +0100531 LIST_ADDQ(list_format, &node->list);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100532 return 1;
Dragan Dosen61302da2019-04-30 00:40:02 +0200533
534 error_free:
535 release_sample_expr(expr);
536 if (node) {
537 free(node->arg);
538 free(node);
539 }
540 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100541}
542
543/*
William Lallemand723b73a2012-02-08 16:37:49 +0100544 * Parse the log_format string and fill a linked list.
545 * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200546 * You can set arguments using { } : %{many arguments}varname.
547 * The curproxy->conf.args.ctx must be set by the caller.
William Lallemand1d705562012-03-12 12:46:41 +0100548 *
549 * str: the string to parse
550 * curproxy: the proxy affected
551 * list_format: the destination list
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +0100552 * options: LOG_OPT_* to force on every node
Willy Tarreau434c57c2013-01-08 01:10:24 +0100553 * cap: all SMP_VAL_* flags supported by the consumer
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100554 *
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100555 * The function returns 1 in success case, otherwise, it returns 0 and err is filled.
William Lallemand723b73a2012-02-08 16:37:49 +0100556 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100557int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct list *list_format, int options, int cap, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100558{
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100559 char *sp, *str, *backfmt; /* start pointer for text parts */
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100560 char *arg = NULL; /* start pointer for args */
561 char *var = NULL; /* start pointer for vars */
562 int arg_len = 0;
563 int var_len = 0;
564 int cformat; /* current token format */
565 int pformat; /* previous token format */
William Lallemand723b73a2012-02-08 16:37:49 +0100566 struct logformat_node *tmplf, *back;
567
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100568 sp = str = backfmt = strdup(fmt);
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100569 if (!str) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100570 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100571 return 0;
572 }
William Lallemand1dc00ef2012-08-09 16:41:35 +0200573 curproxy->to_log |= LW_INIT;
William Lallemand5e19a282012-04-02 16:22:10 +0200574
William Lallemand723b73a2012-02-08 16:37:49 +0100575 /* flush the list first. */
William Lallemand1d705562012-03-12 12:46:41 +0100576 list_for_each_entry_safe(tmplf, back, list_format, list) {
William Lallemand723b73a2012-02-08 16:37:49 +0100577 LIST_DEL(&tmplf->list);
Dragan Dosen61302da2019-04-30 00:40:02 +0200578 release_sample_expr(tmplf->expr);
579 free(tmplf->arg);
William Lallemand723b73a2012-02-08 16:37:49 +0100580 free(tmplf);
581 }
582
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100583 for (cformat = LF_INIT; cformat != LF_END; str++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100584 pformat = cformat;
585
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100586 if (!*str)
587 cformat = LF_END; // preset it to save all states from doing this
William Lallemand723b73a2012-02-08 16:37:49 +0100588
Joseph Herlant85b40592018-11-15 12:10:04 -0800589 /* The principle of the two-step state machine below is to first detect a change, and
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100590 * second have all common paths processed at one place. The common paths are the ones
591 * encountered in text areas (LF_INIT, LF_TEXT, LF_SEPARATOR) and at the end (LF_END).
592 * We use the common LF_INIT state to dispatch to the different final states.
593 */
594 switch (pformat) {
595 case LF_STARTVAR: // text immediately following a '%'
Willy Tarreauc8368452012-12-21 00:09:23 +0100596 arg = NULL; var = NULL;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100597 arg_len = var_len = 0;
598 if (*str == '{') { // optional argument
599 cformat = LF_STARG;
600 arg = str + 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100601 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100602 else if (*str == '[') {
603 cformat = LF_STEXPR;
604 var = str + 1; // store expr in variable name
605 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100606 else if (isalpha((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100607 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100608 var = str;
William Lallemand723b73a2012-02-08 16:37:49 +0100609 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100610 else if (*str == '%')
611 cformat = LF_TEXT; // convert this character to a litteral (useful for '%')
Willy Tarreau0f28f822013-12-16 01:38:33 +0100612 else if (isdigit((unsigned char)*str) || *str == ' ' || *str == '\t') {
Willy Tarreau06d97f92013-12-02 17:45:48 +0100613 /* single '%' followed by blank or digit, send them both */
614 cformat = LF_TEXT;
615 pformat = LF_TEXT; /* finally we include the previous char as well */
616 sp = str - 1; /* send both the '%' and the current char */
Jim Freemana2278c82017-04-15 08:01:59 -0600617 memprintf(err, "unexpected variable name near '%c' at position %d line : '%s'. Maybe you want to write a single '%%', use the syntax '%%%%'",
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100618 *str, (int)(str - backfmt), fmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100619 return 0;
Willy Tarreau06d97f92013-12-02 17:45:48 +0100620
621 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100622 else
623 cformat = LF_INIT; // handle other cases of litterals
624 break;
625
626 case LF_STARG: // text immediately following '%{'
627 if (*str == '}') { // end of arg
William Lallemand723b73a2012-02-08 16:37:49 +0100628 cformat = LF_EDARG;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100629 arg_len = str - arg;
630 *str = 0; // used for reporting errors
William Lallemand723b73a2012-02-08 16:37:49 +0100631 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100632 break;
633
634 case LF_EDARG: // text immediately following '%{arg}'
Willy Tarreauc8368452012-12-21 00:09:23 +0100635 if (*str == '[') {
636 cformat = LF_STEXPR;
637 var = str + 1; // store expr in variable name
638 break;
639 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100640 else if (isalnum((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100641 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100642 var = str;
643 break;
644 }
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100645 memprintf(err, "parse argument modifier without variable name near '%%{%s}'", arg);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100646 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100647
Willy Tarreauc8368452012-12-21 00:09:23 +0100648 case LF_STEXPR: // text immediately following '%['
649 if (*str == ']') { // end of arg
650 cformat = LF_EDEXPR;
651 var_len = str - var;
652 *str = 0; // needed for parsing the expression
653 }
654 break;
655
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100656 case LF_VAR: // text part of a variable name
657 var_len = str - var;
Willy Tarreau0f28f822013-12-16 01:38:33 +0100658 if (!isalnum((unsigned char)*str))
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100659 cformat = LF_INIT; // not variable name anymore
660 break;
661
Willy Tarreauc8368452012-12-21 00:09:23 +0100662 default: // LF_INIT, LF_TEXT, LF_SEPARATOR, LF_END, LF_EDEXPR
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100663 cformat = LF_INIT;
664 }
665
666 if (cformat == LF_INIT) { /* resynchronize state to text/sep/startvar */
667 switch (*str) {
668 case '%': cformat = LF_STARTVAR; break;
669 case ' ': cformat = LF_SEPARATOR; break;
670 case 0 : cformat = LF_END; break;
671 default : cformat = LF_TEXT; break;
William Lallemand723b73a2012-02-08 16:37:49 +0100672 }
673 }
674
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100675 if (cformat != pformat || pformat == LF_SEPARATOR) {
676 switch (pformat) {
677 case LF_VAR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100678 if (!parse_logformat_var(arg, arg_len, var, var_len, curproxy, list_format, &options, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100679 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100680 break;
Willy Tarreauc8368452012-12-21 00:09:23 +0100681 case LF_STEXPR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100682 if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100683 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100684 break;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100685 case LF_TEXT:
686 case LF_SEPARATOR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100687 if (!add_to_logformat_list(sp, str, pformat, list_format, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100688 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100689 break;
690 }
691 sp = str; /* new start of text at every state switch and at every separator */
William Lallemand723b73a2012-02-08 16:37:49 +0100692 }
693 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100694
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100695 if (pformat == LF_STARTVAR || pformat == LF_STARG || pformat == LF_STEXPR) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100696 memprintf(err, "truncated line after '%s'", var ? var : arg ? arg : "%");
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100697 return 0;
698 }
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100699 free(backfmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100700
701 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100702}
703
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200704/*
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200705 * Parse the first range of indexes from a string made of a list of comma seperated
706 * ranges of indexes. Note that an index may be considered as a particular range
707 * with a high limit to the low limit.
708 */
709int get_logsrv_smp_range(unsigned int *low, unsigned int *high, char **arg, char **err)
710{
711 char *end, *p;
712
713 *low = *high = 0;
714
715 p = *arg;
716 end = strchr(p, ',');
717 if (!end)
718 end = p + strlen(p);
719
720 *high = *low = read_uint((const char **)&p, end);
721 if (!*low || (p != end && *p != '-'))
722 goto err;
723
724 if (p == end)
725 goto done;
726
727 p++;
728 *high = read_uint((const char **)&p, end);
729 if (!*high || *high <= *low || p != end)
730 goto err;
731
732 done:
733 if (*end == ',')
734 end++;
735 *arg = end;
736 return 1;
737
738 err:
739 memprintf(err, "wrong sample range '%s'", *arg);
740 return 0;
741}
742
743/*
744 * Returns 1 if the range defined by <low> and <high> overlaps
745 * one of them in <rgs> array of ranges with <sz> the size of this
746 * array, 0 if not.
747 */
748int smp_log_ranges_overlap(struct smp_log_range *rgs, size_t sz,
749 unsigned int low, unsigned int high, char **err)
750{
751 size_t i;
752
753 for (i = 0; i < sz; i++) {
754 if ((low >= rgs[i].low && low <= rgs[i].high) ||
755 (high >= rgs[i].low && high <= rgs[i].high)) {
756 memprintf(err, "ranges are overlapping");
757 return 1;
758 }
759 }
760
761 return 0;
762}
763
764int smp_log_range_cmp(const void *a, const void *b)
765{
766 const struct smp_log_range *rg_a = a;
767 const struct smp_log_range *rg_b = b;
768
769 if (rg_a->high < rg_b->low)
770 return -1;
771 else if (rg_a->low > rg_b->high)
772 return 1;
773
774 return 0;
775}
776
777/*
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200778 * Parse "log" keyword and update <logsrvs> list accordingly.
779 *
780 * When <do_del> is set, it means the "no log" line was parsed, so all log
781 * servers in <logsrvs> are released.
782 *
783 * Otherwise, we try to parse the "log" line. First of all, when the list is not
784 * the global one, we look for the parameter "global". If we find it,
785 * global.logsrvs is copied. Else we parse each arguments.
786 *
787 * The function returns 1 in success case, otherwise, it returns 0 and err is
788 * filled.
789 */
790int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err)
791{
792 struct sockaddr_storage *sk;
793 struct logsrv *logsrv = NULL;
794 int port1, port2;
795 int cur_arg;
796
797 /*
798 * "no log": delete previous herited or defined syslog
799 * servers.
800 */
801 if (do_del) {
802 struct logsrv *back;
803
804 if (*(args[1]) != 0) {
805 memprintf(err, "'no log' does not expect arguments");
806 goto error;
807 }
808
809 list_for_each_entry_safe(logsrv, back, logsrvs, list) {
810 LIST_DEL(&logsrv->list);
811 free(logsrv);
812 }
813 return 1;
814 }
815
816 /*
817 * "log global": copy global.logrsvs linked list to the end of logsrvs
818 * list. But first, we check (logsrvs != global.logsrvs).
819 */
820 if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
821 if (logsrvs == &global.logsrvs) {
822 memprintf(err, "'global' is not supported for a global syslog server");
823 goto error;
824 }
825 list_for_each_entry(logsrv, &global.logsrvs, list) {
Christopher Faulet28ac0992018-03-26 16:09:19 +0200826 struct logsrv *node;
827
828 list_for_each_entry(node, logsrvs, list) {
829 if (node->ref == logsrv)
830 goto skip_logsrv;
831 }
832
833 node = malloc(sizeof(*node));
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200834 memcpy(node, logsrv, sizeof(struct logsrv));
Christopher Faulet28ac0992018-03-26 16:09:19 +0200835 node->ref = logsrv;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200836 LIST_INIT(&node->list);
837 LIST_ADDQ(logsrvs, &node->list);
Christopher Faulet28ac0992018-03-26 16:09:19 +0200838
839 skip_logsrv:
840 continue;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200841 }
842 return 1;
843 }
844
845 /*
846 * "log <address> ...: parse a syslog server line
847 */
848 if (*(args[1]) == 0 || *(args[2]) == 0) {
849 memprintf(err, "expects <address> and <facility> %s as arguments",
850 ((logsrvs == &global.logsrvs) ? "" : "or global"));
851 goto error;
852 }
853
Willy Tarreau5a32ecc2018-11-12 07:34:59 +0100854 /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */
855 if (strcmp(args[1], "stdout") == 0)
856 args[1] = "fd@1";
857 else if (strcmp(args[1], "stderr") == 0)
858 args[1] = "fd@2";
859
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200860 logsrv = calloc(1, sizeof(*logsrv));
861 if (!logsrv) {
862 memprintf(err, "out of memory");
863 goto error;
864 }
865
866 /* skip address for now, it will be parsed at the end */
867 cur_arg = 2;
868
869 /* just after the address, a length may be specified */
870 logsrv->maxlen = MAX_SYSLOG_LEN;
871 if (strcmp(args[cur_arg], "len") == 0) {
872 int len = atoi(args[cur_arg+1]);
873 if (len < 80 || len > 65535) {
874 memprintf(err, "invalid log length '%s', must be between 80 and 65535",
875 args[cur_arg+1]);
876 goto error;
877 }
878 logsrv->maxlen = len;
879 cur_arg += 2;
880 }
881 if (logsrv->maxlen > global.max_syslog_len)
882 global.max_syslog_len = logsrv->maxlen;
883
884 /* after the length, a format may be specified */
885 if (strcmp(args[cur_arg], "format") == 0) {
886 logsrv->format = get_log_format(args[cur_arg+1]);
887 if (logsrv->format < 0) {
888 memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
889 goto error;
890 }
891 cur_arg += 2;
892 }
893
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200894 if (strcmp(args[cur_arg], "sample") == 0) {
895 unsigned low, high;
896 char *p, *beg, *end, *smp_sz_str;
897 struct smp_log_range *smp_rgs = NULL;
898 size_t smp_rgs_sz = 0, smp_sz = 0, new_smp_sz;
899
900 p = args[cur_arg+1];
901 smp_sz_str = strchr(p, ':');
902 if (!smp_sz_str) {
903 memprintf(err, "Missing sample size");
904 goto error;
905 }
906
907 *smp_sz_str++ = '\0';
908
909 end = p + strlen(p);
910
911 while (p != end) {
912 if (!get_logsrv_smp_range(&low, &high, &p, err))
913 goto error;
914
915 if (smp_rgs && smp_log_ranges_overlap(smp_rgs, smp_rgs_sz, low, high, err))
916 goto error;
917
918 smp_rgs = my_realloc2(smp_rgs, (smp_rgs_sz + 1) * sizeof *smp_rgs);
919 if (!smp_rgs) {
920 memprintf(err, "out of memory error");
921 goto error;
922 }
923
924 smp_rgs[smp_rgs_sz].low = low;
925 smp_rgs[smp_rgs_sz].high = high;
926 smp_rgs[smp_rgs_sz].sz = high - low + 1;
927 smp_rgs[smp_rgs_sz].curr_idx = 0;
928 if (smp_rgs[smp_rgs_sz].high > smp_sz)
929 smp_sz = smp_rgs[smp_rgs_sz].high;
930 smp_rgs_sz++;
931 }
932
Tim Duesterhus21648002019-06-23 22:10:10 +0200933 if (smp_rgs == NULL) {
934 memprintf(err, "no sampling ranges given");
935 goto error;
936 }
937
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200938 beg = smp_sz_str;
939 end = beg + strlen(beg);
940 new_smp_sz = read_uint((const char **)&beg, end);
941 if (!new_smp_sz || beg != end) {
942 memprintf(err, "wrong sample size '%s' for sample range '%s'",
943 smp_sz_str, args[cur_arg+1]);
944 goto error;
945 }
946
947 if (new_smp_sz < smp_sz) {
948 memprintf(err, "sample size %zu should be greater or equal to "
949 "%zu the maximum of the high ranges limits",
950 new_smp_sz, smp_sz);
951 goto error;
952 }
953 smp_sz = new_smp_sz;
954
955 /* Let's order <smp_rgs> array. */
956 qsort(smp_rgs, smp_rgs_sz, sizeof(struct smp_log_range), smp_log_range_cmp);
957
958 logsrv->lb.smp_rgs = smp_rgs;
959 logsrv->lb.smp_rgs_sz = smp_rgs_sz;
960 logsrv->lb.smp_sz = smp_sz;
961
962 cur_arg += 2;
963 }
Frédéric Lécailled803e472019-04-25 07:42:09 +0200964 HA_SPIN_INIT(&logsrv->lock);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200965 /* parse the facility */
966 logsrv->facility = get_log_facility(args[cur_arg]);
967 if (logsrv->facility < 0) {
968 memprintf(err, "unknown log facility '%s'", args[cur_arg]);
969 goto error;
970 }
971 cur_arg++;
972
973 /* parse the max syslog level (default: debug) */
974 logsrv->level = 7;
975 if (*(args[cur_arg])) {
976 logsrv->level = get_log_level(args[cur_arg]);
977 if (logsrv->level < 0) {
978 memprintf(err, "unknown optional log level '%s'", args[cur_arg]);
979 goto error;
980 }
981 cur_arg++;
982 }
983
984 /* parse the limit syslog level (default: emerg) */
985 logsrv->minlvl = 0;
986 if (*(args[cur_arg])) {
987 logsrv->minlvl = get_log_level(args[cur_arg]);
988 if (logsrv->minlvl < 0) {
989 memprintf(err, "unknown optional minimum log level '%s'", args[cur_arg]);
990 goto error;
991 }
992 cur_arg++;
993 }
994
995 /* Too many args */
996 if (*(args[cur_arg])) {
997 memprintf(err, "cannot handle unexpected argument '%s'", args[cur_arg]);
998 goto error;
999 }
1000
1001 /* now, back to the address */
1002 sk = str2sa_range(args[1], NULL, &port1, &port2, err, NULL, NULL, 1);
1003 if (!sk)
1004 goto error;
1005 logsrv->addr = *sk;
1006
1007 if (sk->ss_family == AF_INET || sk->ss_family == AF_INET6) {
1008 if (port1 != port2) {
1009 memprintf(err, "port ranges and offsets are not allowed in '%s'", args[1]);
1010 goto error;
1011 }
1012 logsrv->addr = *sk;
1013 if (!port1)
1014 set_host_port(&logsrv->addr, SYSLOG_PORT);
1015 }
1016 LIST_ADDQ(logsrvs, &logsrv->list);
1017 return 1;
1018
1019 error:
1020 free(logsrv);
1021 return 0;
1022}
1023
1024
Christopher Fauletd4696382017-10-24 11:44:05 +02001025/* Generic function to display messages prefixed by a label */
1026static void print_message(const char *label, const char *fmt, va_list argp)
1027{
1028 struct tm tm;
1029 char *head, *msg;
1030
1031 head = msg = NULL;
1032
1033 get_localtime(date.tv_sec, &tm);
1034 memprintf(&head, "[%s] %03d/%02d%02d%02d (%d) : ",
1035 label, tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
1036 memvprintf(&msg, fmt, argp);
1037
1038 if (global.mode & MODE_STARTING)
1039 memprintf(&startup_logs, "%s%s%s", (startup_logs ? startup_logs : ""), head, msg);
1040
1041 fprintf(stderr, "%s%s", head, msg);
1042 fflush(stderr);
1043
1044 free(head);
1045 free(msg);
1046}
1047
Willy Tarreaubaaee002006-06-26 02:48:02 +02001048/*
1049 * Displays the message on stderr with the date and pid. Overrides the quiet
1050 * mode during startup.
1051 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001052void ha_alert(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001053{
1054 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001055
1056 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
1057 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001058 print_message("ALERT", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001059 va_end(argp);
1060 }
1061}
1062
1063
1064/*
1065 * Displays the message on stderr with the date and pid.
1066 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001067void ha_warning(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001068{
1069 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001070
1071 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1072 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001073 print_message("WARNING", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001074 va_end(argp);
1075 }
1076}
1077
1078/*
William Lallemand9c56a222018-11-21 18:04:52 +01001079 * Displays the message on stderr with the date and pid.
1080 */
1081void ha_notice(const char *fmt, ...)
1082{
1083 va_list argp;
1084
1085 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1086 va_start(argp, fmt);
1087 print_message("NOTICE", fmt, argp);
1088 va_end(argp);
1089 }
1090}
1091
1092/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001093 * Displays the message on <out> only if quiet mode is not set.
1094 */
Willy Tarreaub17916e2006-10-15 15:17:57 +02001095void qfprintf(FILE *out, const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001096{
1097 va_list argp;
1098
1099 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1100 va_start(argp, fmt);
1101 vfprintf(out, fmt, argp);
1102 fflush(out);
1103 va_end(argp);
1104 }
1105}
1106
1107/*
Dragan Dosen1322d092015-09-22 16:05:32 +02001108 * returns log format for <fmt> or -1 if not found.
1109 */
1110int get_log_format(const char *fmt)
1111{
1112 int format;
1113
1114 format = LOG_FORMATS - 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001115 while (format >= 0 && strcmp(log_formats[format].name, fmt))
Dragan Dosen1322d092015-09-22 16:05:32 +02001116 format--;
1117
1118 return format;
1119}
1120
1121/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001122 * returns log level for <lev> or -1 if not found.
1123 */
1124int get_log_level(const char *lev)
1125{
1126 int level;
1127
1128 level = NB_LOG_LEVELS - 1;
1129 while (level >= 0 && strcmp(log_levels[level], lev))
1130 level--;
1131
1132 return level;
1133}
1134
Willy Tarreaubaaee002006-06-26 02:48:02 +02001135/*
1136 * returns log facility for <fac> or -1 if not found.
1137 */
1138int get_log_facility(const char *fac)
1139{
1140 int facility;
1141
1142 facility = NB_LOG_FACILITIES - 1;
1143 while (facility >= 0 && strcmp(log_facilities[facility], fac))
1144 facility--;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001145
Willy Tarreaubaaee002006-06-26 02:48:02 +02001146 return facility;
1147}
1148
William Lallemanda1cc3812012-02-08 16:38:44 +01001149/*
Dragan Dosen835b9212016-02-12 13:23:03 +01001150 * Encode the string.
1151 *
1152 * When using the +E log format option, it will try to escape '"\]'
1153 * characters with '\' as prefix. The same prefix should not be used as
1154 * <escape>.
1155 */
1156static char *lf_encode_string(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001157 const char escape, const long *map,
Dragan Dosen835b9212016-02-12 13:23:03 +01001158 const char *string,
1159 struct logformat_node *node)
1160{
1161 if (node->options & LOG_OPT_ESC) {
1162 if (start < stop) {
1163 stop--; /* reserve one byte for the final '\0' */
1164 while (start < stop && *string != '\0') {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001165 if (!ha_bit_test((unsigned char)(*string), map)) {
1166 if (!ha_bit_test((unsigned char)(*string), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001167 *start++ = *string;
1168 else {
1169 if (start + 2 >= stop)
1170 break;
1171 *start++ = '\\';
1172 *start++ = *string;
1173 }
1174 }
1175 else {
1176 if (start + 3 >= stop)
1177 break;
1178 *start++ = escape;
1179 *start++ = hextab[(*string >> 4) & 15];
1180 *start++ = hextab[*string & 15];
1181 }
1182 string++;
1183 }
1184 *start = '\0';
1185 }
1186 }
1187 else {
1188 return encode_string(start, stop, escape, map, string);
1189 }
1190
1191 return start;
1192}
1193
1194/*
1195 * Encode the chunk.
1196 *
1197 * When using the +E log format option, it will try to escape '"\]'
1198 * characters with '\' as prefix. The same prefix should not be used as
1199 * <escape>.
1200 */
1201static char *lf_encode_chunk(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001202 const char escape, const long *map,
Willy Tarreau83061a82018-07-13 11:56:34 +02001203 const struct buffer *chunk,
Dragan Dosen835b9212016-02-12 13:23:03 +01001204 struct logformat_node *node)
1205{
1206 char *str, *end;
1207
1208 if (node->options & LOG_OPT_ESC) {
1209 if (start < stop) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001210 str = chunk->area;
1211 end = chunk->area + chunk->data;
Dragan Dosen835b9212016-02-12 13:23:03 +01001212
1213 stop--; /* reserve one byte for the final '\0' */
1214 while (start < stop && str < end) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001215 if (!ha_bit_test((unsigned char)(*str), map)) {
1216 if (!ha_bit_test((unsigned char)(*str), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001217 *start++ = *str;
1218 else {
1219 if (start + 2 >= stop)
1220 break;
1221 *start++ = '\\';
1222 *start++ = *str;
1223 }
1224 }
1225 else {
1226 if (start + 3 >= stop)
1227 break;
1228 *start++ = escape;
1229 *start++ = hextab[(*str >> 4) & 15];
1230 *start++ = hextab[*str & 15];
1231 }
1232 str++;
1233 }
1234 *start = '\0';
1235 }
1236 }
1237 else {
1238 return encode_chunk(start, stop, escape, map, chunk);
1239 }
1240
1241 return start;
1242}
1243
1244/*
William Lallemanda1cc3812012-02-08 16:38:44 +01001245 * Write a string in the log string
Dragan Dosen835b9212016-02-12 13:23:03 +01001246 * Take cares of quote and escape options
William Lallemanda1cc3812012-02-08 16:38:44 +01001247 *
Joseph Herlant85b40592018-11-15 12:10:04 -08001248 * Return the address of the \0 character, or NULL on error
William Lallemanda1cc3812012-02-08 16:38:44 +01001249 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001250char *lf_text_len(char *dst, const char *src, size_t len, size_t size, const struct logformat_node *node)
William Lallemanda1cc3812012-02-08 16:38:44 +01001251{
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001252 if (size < 2)
1253 return NULL;
William Lallemanda1cc3812012-02-08 16:38:44 +01001254
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001255 if (node->options & LOG_OPT_QUOTE) {
1256 *(dst++) = '"';
1257 size--;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001258 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001259
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001260 if (src && len) {
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001261 if (++len > size)
1262 len = size;
Dragan Dosen835b9212016-02-12 13:23:03 +01001263 if (node->options & LOG_OPT_ESC) {
Dragan Dosen835b9212016-02-12 13:23:03 +01001264 char *ret;
1265
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001266 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
Dragan Dosen835b9212016-02-12 13:23:03 +01001267 if (ret == NULL || *ret != '\0')
1268 return NULL;
1269 len = ret - dst;
1270 }
1271 else {
Dragan Dosen835b9212016-02-12 13:23:03 +01001272 len = strlcpy2(dst, src, len);
1273 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001274
1275 size -= len;
1276 dst += len;
1277 }
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001278 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1279 if (size < 2)
1280 return NULL;
1281 *(dst++) = '-';
1282 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001283
1284 if (node->options & LOG_OPT_QUOTE) {
1285 if (size < 2)
1286 return NULL;
1287 *(dst++) = '"';
1288 }
1289
1290 *dst = '\0';
William Lallemandbddd4fd2012-02-27 11:23:10 +01001291 return dst;
William Lallemanda1cc3812012-02-08 16:38:44 +01001292}
1293
Willy Tarreau26ffa852018-09-05 15:23:10 +02001294static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001295{
1296 return lf_text_len(dst, src, size, size, node);
1297}
1298
William Lallemand5f232402012-04-05 18:02:55 +02001299/*
Joseph Herlant85b40592018-11-15 12:10:04 -08001300 * Write a IP address to the log string
William Lallemand5f232402012-04-05 18:02:55 +02001301 * +X option write in hexadecimal notation, most signifant byte on the left
1302 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001303char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001304{
1305 char *ret = dst;
1306 int iret;
1307 char pn[INET6_ADDRSTRLEN];
1308
1309 if (node->options & LOG_OPT_HEXA) {
Radek Zajic594c4562019-03-22 10:21:54 +00001310 unsigned char *addr = NULL;
1311 switch (sockaddr->sa_family) {
1312 case AF_INET:
1313 addr = (unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1314 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1315 break;
1316 case AF_INET6:
1317 addr = (unsigned char *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr;
1318 iret = snprintf(dst, size, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1319 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
1320 addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
1321 break;
1322 default:
1323 return NULL;
1324 }
William Lallemand5f232402012-04-05 18:02:55 +02001325 if (iret < 0 || iret > size)
1326 return NULL;
1327 ret += iret;
1328 } else {
1329 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1330 ret = lf_text(dst, pn, size, node);
1331 if (ret == NULL)
1332 return NULL;
1333 }
1334 return ret;
1335}
1336
1337/*
1338 * Write a port to the log
1339 * +X option write in hexadecimal notation, most signifant byte on the left
1340 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001341char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001342{
1343 char *ret = dst;
1344 int iret;
1345
1346 if (node->options & LOG_OPT_HEXA) {
1347 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1348 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1349 if (iret < 0 || iret > size)
1350 return NULL;
1351 ret += iret;
1352 } else {
1353 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1354 if (ret == NULL)
1355 return NULL;
1356 }
1357 return ret;
1358}
1359
Dragan Dosen1322d092015-09-22 16:05:32 +02001360/* Re-generate time-based part of the syslog header in RFC3164 format at
1361 * the beginning of logheader once a second and return the pointer to the
1362 * first character after it.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001363 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001364static char *update_log_hdr(const time_t time)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001365{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001366 static THREAD_LOCAL long tvsec;
Willy Tarreau83061a82018-07-13 11:56:34 +02001367 static THREAD_LOCAL struct buffer host = { };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001368 static THREAD_LOCAL int sep = 0;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001369
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001370 if (unlikely(time != tvsec || logheader_end == NULL)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +02001371 /* this string is rebuild only once a second */
Willy Tarreaufe944602007-10-25 10:34:16 +02001372 struct tm tm;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001373 int hdr_len;
Willy Tarreaufe944602007-10-25 10:34:16 +02001374
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001375 tvsec = time;
Willy Tarreaufe944602007-10-25 10:34:16 +02001376 get_localtime(tvsec, &tm);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001377
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001378 if (unlikely(global.log_send_hostname != host.area)) {
1379 host.area = global.log_send_hostname;
1380 host.data = host.area ? strlen(host.area) : 0;
1381 sep = host.data ? 1 : 0;
Dragan Dosen43885c72015-10-01 13:18:13 +02001382 }
1383
Dragan Dosen59cee972015-09-19 22:09:02 +02001384 hdr_len = snprintf(logheader, global.max_syslog_len,
Dragan Dosen43885c72015-10-01 13:18:13 +02001385 "<<<<>%s %2d %02d:%02d:%02d %.*s%*s",
Willy Tarreaufe944602007-10-25 10:34:16 +02001386 monthname[tm.tm_mon],
Dragan Dosen43885c72015-10-01 13:18:13 +02001387 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001388 (int)host.data, host.area, sep, "");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001389 /* WARNING: depending upon implementations, snprintf may return
1390 * either -1 or the number of bytes that would be needed to store
1391 * the total message. In both cases, we must adjust it.
1392 */
Willy Tarreau18324f52014-06-27 18:10:07 +02001393 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1394 hdr_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001395
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001396 logheader_end = logheader + hdr_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001397 }
1398
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001399 logheader_end[0] = 0; // ensure we get rid of any previous attempt
Willy Tarreau094af4e2015-01-07 15:03:42 +01001400
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001401 return logheader_end;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001402}
1403
Dragan Dosen1322d092015-09-22 16:05:32 +02001404/* Re-generate time-based part of the syslog header in RFC5424 format at
1405 * the beginning of logheader_rfc5424 once a second and return the pointer
1406 * to the first character after it.
1407 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001408static char *update_log_hdr_rfc5424(const time_t time)
Dragan Dosen1322d092015-09-22 16:05:32 +02001409{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001410 static THREAD_LOCAL long tvsec;
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001411 const char *gmt_offset;
Dragan Dosen1322d092015-09-22 16:05:32 +02001412
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001413 if (unlikely(time != tvsec || logheader_rfc5424_end == NULL)) {
Dragan Dosen1322d092015-09-22 16:05:32 +02001414 /* this string is rebuild only once a second */
1415 struct tm tm;
1416 int hdr_len;
1417
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001418 tvsec = time;
Dragan Dosen1322d092015-09-22 16:05:32 +02001419 get_localtime(tvsec, &tm);
Benoit GARNIERe2e5bde2016-03-27 03:04:16 +02001420 gmt_offset = get_gmt_offset(time, &tm);
Dragan Dosen1322d092015-09-22 16:05:32 +02001421
1422 hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
Dragan Dosen17def462015-10-09 21:31:43 +02001423 "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%.3s:%.2s %s ",
Dragan Dosen1322d092015-09-22 16:05:32 +02001424 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
Dragan Dosen17def462015-10-09 21:31:43 +02001425 tm.tm_hour, tm.tm_min, tm.tm_sec,
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001426 gmt_offset, gmt_offset+3,
Dragan Dosen43885c72015-10-01 13:18:13 +02001427 global.log_send_hostname ? global.log_send_hostname : hostname);
Dragan Dosen1322d092015-09-22 16:05:32 +02001428 /* WARNING: depending upon implementations, snprintf may return
1429 * either -1 or the number of bytes that would be needed to store
1430 * the total message. In both cases, we must adjust it.
1431 */
1432 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1433 hdr_len = global.max_syslog_len;
1434
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001435 logheader_rfc5424_end = logheader_rfc5424 + hdr_len;
Dragan Dosen1322d092015-09-22 16:05:32 +02001436 }
1437
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001438 logheader_rfc5424_end[0] = 0; // ensure we get rid of any previous attempt
Dragan Dosen1322d092015-09-22 16:05:32 +02001439
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001440 return logheader_rfc5424_end;
Dragan Dosen1322d092015-09-22 16:05:32 +02001441}
1442
William Lallemand2a4a44f2012-02-06 16:00:33 +01001443/*
Dragan Dosen59cee972015-09-19 22:09:02 +02001444 * This function sends the syslog message using a printf format string. It
1445 * expects an LF-terminated message.
William Lallemand2a4a44f2012-02-06 16:00:33 +01001446 */
1447void send_log(struct proxy *p, int level, const char *format, ...)
1448{
1449 va_list argp;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001450 int data_len;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001451
Willy Tarreau8c97ab52015-01-15 16:29:53 +01001452 if (level < 0 || format == NULL || logline == NULL)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001453 return;
1454
William Lallemand2a4a44f2012-02-06 16:00:33 +01001455 va_start(argp, format);
Dragan Dosen59cee972015-09-19 22:09:02 +02001456 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
Willy Tarreau18324f52014-06-27 18:10:07 +02001457 if (data_len < 0 || data_len > global.max_syslog_len)
1458 data_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001459 va_end(argp);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001460
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001461 __send_log(p, level, logline, data_len, default_rfc5424_sd_log_format, 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001462}
1463
1464/*
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001465 * This function sends a syslog message to <logsrv>.
1466 * <pid_str> is the string to be used for the PID of the caller, <pid_size> is length.
1467 * Same thing for <sd> and <sd_size> which are used for the structured-data part
1468 * in RFC5424 formatted syslog messages, and <tag_str> and <tag_size> the syslog tag.
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001469 * It overrides the last byte of the message vector with an LF character.
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001470 * Does not return any error,
William Lallemand2a4a44f2012-02-06 16:00:33 +01001471 */
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001472static inline void __do_send_log(struct logsrv *logsrv, int nblogger, char *pid_str, size_t pid_size,
1473 int level, char *message, size_t size, char *sd, size_t sd_size,
1474 char *tag_str, size_t tag_size)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001475{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001476 static THREAD_LOCAL struct iovec iovec[NB_MSG_IOVEC_ELEMENTS] = { };
1477 static THREAD_LOCAL struct msghdr msghdr = {
1478 //.msg_iov = iovec,
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001479 .msg_iovlen = NB_MSG_IOVEC_ELEMENTS
1480 };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001481 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1482 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
1483 static THREAD_LOCAL char *dataptr = NULL;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001484 time_t time = date.tv_sec;
Dragan Dosen1322d092015-09-22 16:05:32 +02001485 char *hdr, *hdr_ptr;
Dragan Dosen59cee972015-09-19 22:09:02 +02001486 size_t hdr_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001487 int fac_level;
1488 int *plogfd;
1489 char *pid_sep1 = "", *pid_sep2 = "";
1490 char logheader_short[3];
1491 int sent;
1492 int maxlen;
1493 int hdr_max = 0;
1494 int tag_max = 0;
1495 int pid_sep1_max = 0;
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001496 int pid_max = 0;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001497 int pid_sep2_max = 0;
1498 int sd_max = 0;
1499 int max = 0;
Christopher Fauletf8188c62017-06-02 16:20:16 +02001500
1501 msghdr.msg_iov = iovec;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001502
1503 dataptr = message;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001504
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001505 if (logsrv->addr.ss_family == AF_UNSPEC) {
1506 /* the socket's address is a file descriptor */
1507 plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
Robert Tsai81ae1952007-12-05 10:47:29 +01001508 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001509 else if (logsrv->addr.ss_family == AF_UNIX)
1510 plogfd = &logfdunix;
1511 else
1512 plogfd = &logfdinet;
Robert Tsai81ae1952007-12-05 10:47:29 +01001513
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001514 if (unlikely(*plogfd < 0)) {
1515 /* socket not successfully initialized yet */
1516 if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1517 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
1518 static char once;
William Lallemand0f99e342011-10-12 17:50:54 +02001519
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001520 if (!once) {
1521 once = 1; /* note: no need for atomic ops here */
1522 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
1523 nblogger, strerror(errno), errno);
1524 }
1525 return;
1526 } else {
1527 /* we don't want to receive anything on this socket */
1528 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1529 /* does nothing under Linux, maybe needed for others */
1530 shutdown(*plogfd, SHUT_RD);
1531 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1532 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001533 }
1534
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001535 switch (logsrv->format) {
1536 case LOG_FORMAT_RFC3164:
1537 hdr = logheader;
1538 hdr_ptr = update_log_hdr(time);
1539 break;
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001540
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001541 case LOG_FORMAT_RFC5424:
1542 hdr = logheader_rfc5424;
1543 hdr_ptr = update_log_hdr_rfc5424(time);
1544 sd_max = sd_size; /* the SD part allowed only in RFC5424 */
1545 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001546
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001547 case LOG_FORMAT_SHORT:
1548 /* all fields are known, skip the header generation */
1549 hdr = logheader_short;
1550 hdr[0] = '<';
1551 hdr[1] = '0' + MAX(level, logsrv->minlvl);
1552 hdr[2] = '>';
1553 hdr_ptr = hdr;
1554 hdr_max = 3;
1555 maxlen = logsrv->maxlen - hdr_max;
1556 max = MIN(size, maxlen) - 1;
1557 goto send;
Willy Tarreau204e3f12018-12-15 15:48:48 +01001558
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001559 case LOG_FORMAT_RAW:
1560 /* all fields are known, skip the header generation */
1561 hdr_ptr = hdr = "";
1562 hdr_max = 0;
1563 maxlen = logsrv->maxlen;
1564 max = MIN(size, maxlen) - 1;
1565 goto send;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001566
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001567 default:
1568 return; /* must never happen */
1569 }
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001570
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001571 hdr_size = hdr_ptr - hdr;
Dragan Dosen1322d092015-09-22 16:05:32 +02001572
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001573 /* For each target, we may have a different facility.
1574 * We can also have a different log level for each message.
1575 * This induces variations in the message header length.
1576 * Since we don't want to recompute it each time, nor copy it every
1577 * time, we only change the facility in the pre-computed header,
1578 * and we change the pointer to the header accordingly.
1579 */
1580 fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
1581 hdr_ptr = hdr + 3; /* last digit of the log level */
1582 do {
1583 *hdr_ptr = '0' + fac_level % 10;
1584 fac_level /= 10;
1585 hdr_ptr--;
1586 } while (fac_level && hdr_ptr > hdr);
1587 *hdr_ptr = '<';
Dragan Dosen1322d092015-09-22 16:05:32 +02001588
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001589 hdr_max = hdr_size - (hdr_ptr - hdr);
Willy Tarreaue8746a02018-11-12 08:45:00 +01001590
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001591 /* time-based header */
1592 if (unlikely(hdr_size >= logsrv->maxlen)) {
1593 hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
1594 sd_max = 0;
1595 goto send;
1596 }
Willy Tarreauc1b06452018-11-12 11:57:56 +01001597
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001598 maxlen = logsrv->maxlen - hdr_max;
Dragan Dosen1322d092015-09-22 16:05:32 +02001599
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001600 /* tag */
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001601 tag_max = tag_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001602 if (unlikely(tag_max >= maxlen)) {
1603 tag_max = maxlen - 1;
1604 sd_max = 0;
1605 goto send;
1606 }
Dragan Dosen1322d092015-09-22 16:05:32 +02001607
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001608 maxlen -= tag_max;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001609
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001610 /* first pid separator */
1611 pid_sep1_max = log_formats[logsrv->format].pid.sep1.data;
1612 if (unlikely(pid_sep1_max >= maxlen)) {
1613 pid_sep1_max = maxlen - 1;
1614 sd_max = 0;
1615 goto send;
1616 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001617
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001618 pid_sep1 = log_formats[logsrv->format].pid.sep1.area;
1619 maxlen -= pid_sep1_max;
Dragan Dosen59cee972015-09-19 22:09:02 +02001620
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001621 /* pid */
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001622 pid_max = pid_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001623 if (unlikely(pid_size >= maxlen)) {
1624 pid_size = maxlen - 1;
1625 sd_max = 0;
1626 goto send;
1627 }
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001628
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001629 maxlen -= pid_size;
Dragan Dosen43885c72015-10-01 13:18:13 +02001630
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001631 /* second pid separator */
1632 pid_sep2_max = log_formats[logsrv->format].pid.sep2.data;
1633 if (unlikely(pid_sep2_max >= maxlen)) {
1634 pid_sep2_max = maxlen - 1;
1635 sd_max = 0;
1636 goto send;
1637 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001638
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001639 pid_sep2 = log_formats[logsrv->format].pid.sep2.area;
1640 maxlen -= pid_sep2_max;
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001641
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001642 /* structured-data */
1643 if (sd_max >= maxlen) {
1644 sd_max = maxlen - 1;
1645 goto send;
1646 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001647
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001648 max = MIN(size, maxlen - sd_max) - 1;
1649send:
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001650 if (logsrv->addr.ss_family == AF_UNSPEC) {
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001651 /* the target is a direct file descriptor */
1652 struct ist msg[7];
1653
1654 msg[0].ptr = hdr_ptr; msg[0].len = hdr_max;
1655 msg[1].ptr = tag_str; msg[1].len = tag_max;
1656 msg[2].ptr = pid_sep1; msg[2].len = pid_sep1_max;
1657 msg[3].ptr = pid_str; msg[3].len = pid_max;
1658 msg[4].ptr = pid_sep2; msg[4].len = pid_sep2_max;
1659 msg[5].ptr = sd; msg[5].len = sd_max;
1660 msg[6].ptr = dataptr; msg[6].len = max;
1661
1662 sent = fd_write_frag_line(*plogfd, ~0, NULL, 0, msg, 7, 1);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001663 }
1664 else {
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001665 iovec[0].iov_base = hdr_ptr;
1666 iovec[0].iov_len = hdr_max;
1667 iovec[1].iov_base = tag_str;
1668 iovec[1].iov_len = tag_max;
1669 iovec[2].iov_base = pid_sep1;
1670 iovec[2].iov_len = pid_sep1_max;
1671 iovec[3].iov_base = pid_str;
1672 iovec[3].iov_len = pid_max;
1673 iovec[4].iov_base = pid_sep2;
1674 iovec[4].iov_len = pid_sep2_max;
1675 iovec[5].iov_base = sd;
1676 iovec[5].iov_len = sd_max;
1677 iovec[6].iov_base = dataptr;
1678 iovec[6].iov_len = max;
1679 iovec[7].iov_base = "\n"; /* insert a \n at the end of the message */
1680 iovec[7].iov_len = 1;
1681
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001682 msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
1683 msghdr.msg_namelen = get_addr_len(&logsrv->addr);
Dragan Dosen43885c72015-10-01 13:18:13 +02001684
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001685 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1686 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001687
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001688 if (sent < 0) {
1689 static char once;
Dragan Dosen43885c72015-10-01 13:18:13 +02001690
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001691 if (errno == EAGAIN)
1692 _HA_ATOMIC_ADD(&dropped_logs, 1);
1693 else if (!once) {
1694 once = 1; /* note: no need for atomic ops here */
1695 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
1696 nblogger, strerror(errno), errno);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001697 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001698 }
1699}
Dragan Dosen59cee972015-09-19 22:09:02 +02001700
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001701/*
1702 * This function sends a syslog message.
1703 * It doesn't care about errors nor does it report them.
1704 * The arguments <sd> and <sd_size> are used for the structured-data part
1705 * in RFC5424 formatted syslog messages.
1706 */
1707void __send_log(struct proxy *p, int level, char *message, size_t size, char *sd, size_t sd_size)
1708{
1709 struct list *logsrvs = NULL;
1710 struct logsrv *logsrv;
1711 int nblogger;
1712 static THREAD_LOCAL int curr_pid;
1713 static THREAD_LOCAL char pidstr[100];
1714 static THREAD_LOCAL struct buffer pid;
1715 struct buffer *tag = &global.log_tag;
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001716
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001717 if (p == NULL) {
1718 if (!LIST_ISEMPTY(&global.logsrvs)) {
1719 logsrvs = &global.logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001720 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001721 } else {
1722 if (!LIST_ISEMPTY(&p->logsrvs)) {
1723 logsrvs = &p->logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001724 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001725 if (p->log_tag.area) {
1726 tag = &p->log_tag;
1727 }
1728 }
Willy Tarreau18324f52014-06-27 18:10:07 +02001729
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001730 if (!logsrvs)
1731 return;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001732
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001733 if (unlikely(curr_pid != getpid())) {
1734 curr_pid = getpid();
1735 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1736 chunk_initstr(&pid, pidstr);
1737 }
1738
1739 /* Send log messages to syslog server. */
1740 nblogger = 0;
1741 list_for_each_entry(logsrv, logsrvs, list) {
Frédéric Lécailled803e472019-04-25 07:42:09 +02001742 static THREAD_LOCAL int in_range = 1;
1743
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001744 /* we can filter the level of the messages that are sent to each logger */
1745 if (level > logsrv->level)
1746 continue;
1747
Frédéric Lécailled803e472019-04-25 07:42:09 +02001748 if (logsrv->lb.smp_rgs) {
1749 struct smp_log_range *curr_rg;
1750
1751 HA_SPIN_LOCK(LOGSRV_LOCK, &logsrv->lock);
1752 curr_rg = &logsrv->lb.smp_rgs[logsrv->lb.curr_rg];
1753 in_range = in_smp_log_range(curr_rg, logsrv->lb.curr_idx);
1754 if (in_range) {
1755 /* Let's consume this range. */
1756 curr_rg->curr_idx = (curr_rg->curr_idx + 1) % curr_rg->sz;
1757 if (!curr_rg->curr_idx) {
1758 /* If consumed, let's select the next range. */
1759 logsrv->lb.curr_rg = (logsrv->lb.curr_rg + 1) % logsrv->lb.smp_rgs_sz;
1760 }
1761 }
1762 logsrv->lb.curr_idx = (logsrv->lb.curr_idx + 1) % logsrv->lb.smp_sz;
1763 HA_SPIN_UNLOCK(LOGSRV_LOCK, &logsrv->lock);
1764 }
1765 if (in_range)
1766 __do_send_log(logsrv, ++nblogger, pid.area, pid.data, level,
1767 message, size, sd, sd_size, tag->area, tag->data);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001768 }
1769}
1770
1771
Willy Tarreauc89ccb62012-04-05 21:18:22 +02001772const char sess_cookie[8] = "NIDVEOU7"; /* No cookie, Invalid cookie, cookie for a Down server, Valid cookie, Expired cookie, Old cookie, Unused, unknown */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001773const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1774 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1775 Set-cookie Updated, unknown, unknown */
1776
William Lallemand1d705562012-03-12 12:46:41 +01001777/*
1778 * try to write a character if there is enough space, or goto out
1779 */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001780#define LOGCHAR(x) do { \
William Lallemand1d705562012-03-12 12:46:41 +01001781 if (tmplog < dst + maxsize - 1) { \
William Lallemandbddd4fd2012-02-27 11:23:10 +01001782 *(tmplog++) = (x); \
1783 } else { \
1784 goto out; \
1785 } \
1786 } while(0)
1787
Dragan Dosen835b9212016-02-12 13:23:03 +01001788
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001789/* Initializes some log data at boot */
1790static void init_log()
Dragan Dosen835b9212016-02-12 13:23:03 +01001791{
1792 char *tmp;
Willy Tarreaue10cd482018-09-10 18:16:53 +02001793 int i;
Dragan Dosen835b9212016-02-12 13:23:03 +01001794
1795 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1796 * inside PARAM-VALUE should be escaped with '\' as prefix.
1797 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1798 * details.
1799 */
1800 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1801
1802 tmp = "\"\\]";
1803 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001804 ha_bit_set(*tmp, rfc5424_escape_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001805 tmp++;
1806 }
Willy Tarreaue10cd482018-09-10 18:16:53 +02001807
1808 /* initialize the log header encoding map : '{|}"#' should be encoded with
1809 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1810 * URL encoding only requires '"', '#' to be encoded as well as non-
1811 * printable characters above.
1812 */
1813 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1814 memset(url_encode_map, 0, sizeof(url_encode_map));
1815 for (i = 0; i < 32; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001816 ha_bit_set(i, hdr_encode_map);
1817 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001818 }
1819 for (i = 127; i < 256; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001820 ha_bit_set(i, hdr_encode_map);
1821 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001822 }
1823
1824 tmp = "\"#{|}";
1825 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001826 ha_bit_set(*tmp, hdr_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001827 tmp++;
1828 }
1829
1830 tmp = "\"#";
1831 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001832 ha_bit_set(*tmp, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001833 tmp++;
1834 }
1835
1836 /* initialize the http header encoding map. The draft httpbis define the
1837 * header content as:
1838 *
1839 * HTTP-message = start-line
1840 * *( header-field CRLF )
1841 * CRLF
1842 * [ message-body ]
1843 * header-field = field-name ":" OWS field-value OWS
1844 * field-value = *( field-content / obs-fold )
1845 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1846 * obs-fold = CRLF 1*( SP / HTAB )
1847 * field-vchar = VCHAR / obs-text
1848 * VCHAR = %x21-7E
1849 * obs-text = %x80-FF
1850 *
1851 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1852 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
Joseph Herlant85b40592018-11-15 12:10:04 -08001853 * "obs-fold" is voluntarily forgotten because haproxy remove this.
Willy Tarreaue10cd482018-09-10 18:16:53 +02001854 */
1855 memset(http_encode_map, 0, sizeof(http_encode_map));
1856 for (i = 0x00; i <= 0x08; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001857 ha_bit_set(i, http_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001858 for (i = 0x0a; i <= 0x1f; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001859 ha_bit_set(i, http_encode_map);
1860 ha_bit_set(0x7f, http_encode_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001861}
William Lallemand1d705562012-03-12 12:46:41 +01001862
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001863INITCALL0(STG_PREPARE, init_log);
1864
Christopher Faulet0132d062017-07-26 15:33:35 +02001865/* Initialize log buffers used for syslog messages */
1866int init_log_buffers()
1867{
1868 logheader = my_realloc2(logheader, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001869 logheader_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001870 logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001871 logheader_rfc5424_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001872 logline = my_realloc2(logline, global.max_syslog_len + 1);
1873 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
1874 if (!logheader || !logline_rfc5424 || !logline || !logline_rfc5424)
1875 return 0;
1876 return 1;
1877}
1878
1879/* Deinitialize log buffers used for syslog messages */
1880void deinit_log_buffers()
1881{
Olivier Houchard7c497112019-03-07 14:19:24 +01001882 void *tmp_startup_logs;
1883
Christopher Faulet0132d062017-07-26 15:33:35 +02001884 free(logheader);
1885 free(logheader_rfc5424);
1886 free(logline);
1887 free(logline_rfc5424);
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01001888 tmp_startup_logs = _HA_ATOMIC_XCHG(&startup_logs, NULL);
Olivier Houchard7c497112019-03-07 14:19:24 +01001889 free(tmp_startup_logs);
1890
Christopher Faulet0132d062017-07-26 15:33:35 +02001891 logheader = NULL;
1892 logheader_rfc5424 = NULL;
1893 logline = NULL;
1894 logline_rfc5424 = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +02001895 startup_logs = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001896}
1897
Willy Tarreaudf974472012-12-28 02:44:01 +01001898/* Builds a log line in <dst> based on <list_format>, and stops before reaching
1899 * <maxsize> characters. Returns the size of the output string in characters,
1900 * not counting the trailing zero which is always added if the resulting size
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001901 * is not zero. It requires a valid session and optionally a stream. If the
1902 * stream is NULL, default values will be assumed for the stream part.
Willy Tarreaudf974472012-12-28 02:44:01 +01001903 */
Willy Tarreau43c538e2018-09-05 14:58:15 +02001904int sess_build_logline(struct session *sess, struct stream *s, char *dst, size_t maxsize, struct list *list_format)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001905{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02001906 struct proxy *fe = sess->fe;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001907 struct proxy *be;
1908 struct http_txn *txn;
1909 const struct strm_logs *logs;
Willy Tarreau8fa99842019-07-17 11:47:11 +02001910 struct connection *be_conn;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001911 unsigned int s_flags;
1912 unsigned int uniq_id;
Willy Tarreau83061a82018-07-13 11:56:34 +02001913 struct buffer chunk;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001914 char *uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001915 char *spc;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00001916 char *qmark;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001917 char *end;
Willy Tarreaufe944602007-10-25 10:34:16 +02001918 struct tm tm;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001919 int t_request;
1920 int hdr;
1921 int last_isspace = 1;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001922 int nspaces = 0;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001923 char *tmplog;
William Lallemand1d705562012-03-12 12:46:41 +01001924 char *ret;
1925 int iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001926 struct logformat_node *tmp;
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001927 struct timeval tv;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001928 struct strm_logs tmp_strm_log;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001929
William Lallemandbddd4fd2012-02-27 11:23:10 +01001930 /* FIXME: let's limit ourselves to frontend logging for now. */
Willy Tarreaubaaee002006-06-26 02:48:02 +02001931
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001932 if (likely(s)) {
1933 be = s->be;
1934 txn = s->txn;
1935 be_conn = cs_conn(objt_cs(s->si[1].end));
1936 s_flags = s->flags;
1937 uniq_id = s->uniq_id;
1938 logs = &s->logs;
1939 } else {
1940 /* we have no stream so we first need to initialize a few
1941 * things that are needed later. We do increment the request
1942 * ID so that it's uniquely assigned to this request just as
1943 * if the request had reached the point of being processed.
1944 * A request error is reported as it's the only element we have
1945 * here and which justifies emitting such a log.
1946 */
1947 be = fe;
1948 txn = NULL;
1949 be_conn = NULL;
1950 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01001951 uniq_id = _HA_ATOMIC_XADD(&global.req_count, 1);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001952
1953 /* prepare a valid log structure */
1954 tmp_strm_log.tv_accept = sess->tv_accept;
1955 tmp_strm_log.accept_date = sess->accept_date;
1956 tmp_strm_log.t_handshake = sess->t_handshake;
1957 tmp_strm_log.t_idle = tv_ms_elapsed(&sess->tv_accept, &now) - sess->t_handshake;
1958 tv_zero(&tmp_strm_log.tv_request);
1959 tmp_strm_log.t_queue = -1;
1960 tmp_strm_log.t_connect = -1;
1961 tmp_strm_log.t_data = -1;
1962 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
1963 tmp_strm_log.bytes_in = 0;
1964 tmp_strm_log.bytes_out = 0;
1965 tmp_strm_log.prx_queue_pos = 0;
1966 tmp_strm_log.srv_queue_pos = 0;
1967
1968 logs = &tmp_strm_log;
1969 }
1970
William Lallemandbddd4fd2012-02-27 11:23:10 +01001971 t_request = -1;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001972 if (tv_isge(&logs->tv_request, &logs->tv_accept))
1973 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
William Lallemandbddd4fd2012-02-27 11:23:10 +01001974
William Lallemand1d705562012-03-12 12:46:41 +01001975 tmplog = dst;
Willy Tarreauc9bd0cc2009-05-10 11:57:02 +02001976
William Lallemandbddd4fd2012-02-27 11:23:10 +01001977 /* fill logbuffer */
William Lallemand1d705562012-03-12 12:46:41 +01001978 if (LIST_ISEMPTY(list_format))
1979 return 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001980
William Lallemand1d705562012-03-12 12:46:41 +01001981 list_for_each_entry(tmp, list_format, list) {
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001982 struct connection *conn;
Willy Tarreau4f653562012-10-12 19:48:16 +02001983 const char *src = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +01001984 struct sample *key;
Willy Tarreau83061a82018-07-13 11:56:34 +02001985 const struct buffer empty = { };
William Lallemandbddd4fd2012-02-27 11:23:10 +01001986
Willy Tarreauc8368452012-12-21 00:09:23 +01001987 switch (tmp->type) {
William Lallemand1d705562012-03-12 12:46:41 +01001988 case LOG_FMT_SEPARATOR:
William Lallemandbddd4fd2012-02-27 11:23:10 +01001989 if (!last_isspace) {
1990 LOGCHAR(' ');
1991 last_isspace = 1;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001992 }
1993 break;
1994
William Lallemand1d705562012-03-12 12:46:41 +01001995 case LOG_FMT_TEXT: // text
William Lallemandbddd4fd2012-02-27 11:23:10 +01001996 src = tmp->arg;
William Lallemand5f232402012-04-05 18:02:55 +02001997 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01001998 if (iret == 0)
William Lallemandbddd4fd2012-02-27 11:23:10 +01001999 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002000 tmplog += iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002001 last_isspace = 0;
2002 break;
2003
Willy Tarreauc8368452012-12-21 00:09:23 +01002004 case LOG_FMT_EXPR: // sample expression, may be request or response
2005 key = NULL;
Olivier Houchardf90db442018-12-15 14:00:06 +01002006 if (tmp->options & LOG_OPT_REQ_CAP && s)
Adis Nezirovic79beb242015-07-06 15:41:02 +02002007 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
Olivier Houchardf90db442018-12-15 14:00:06 +01002008 if (!key && (tmp->options & LOG_OPT_RES_CAP) && s)
Adis Nezirovic79beb242015-07-06 15:41:02 +02002009 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01002010 if (tmp->options & LOG_OPT_HTTP)
Dragan Dosen835b9212016-02-12 13:23:03 +01002011 ret = lf_encode_chunk(tmplog, dst + maxsize,
2012 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01002013 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002014 ret = lf_text_len(tmplog,
2015 key ? key->data.u.str.area : NULL,
2016 key ? key->data.u.str.data : 0,
2017 dst + maxsize - tmplog,
2018 tmp);
Willy Tarreauc8368452012-12-21 00:09:23 +01002019 if (ret == 0)
2020 goto out;
2021 tmplog = ret;
2022 last_isspace = 0;
2023 break;
2024
Willy Tarreau2beef582012-12-20 17:22:52 +01002025 case LOG_FMT_CLIENTIP: // %ci
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002026 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002027 if (conn && conn_get_src(conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002028 ret = lf_ip(tmplog, (struct sockaddr *)conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002029 else
2030 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002031 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002032 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002033 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002034 last_isspace = 0;
2035 break;
2036
Willy Tarreau2beef582012-12-20 17:22:52 +01002037 case LOG_FMT_CLIENTPORT: // %cp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002038 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002039 if (conn && conn_get_src(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002040 if (conn->src->ss_family == AF_UNIX) {
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002041 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002042 } else {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002043 ret = lf_port(tmplog, (struct sockaddr *)conn->src,
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002044 dst + maxsize - tmplog, tmp);
2045 }
William Lallemand5f232402012-04-05 18:02:55 +02002046 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002047 else
2048 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2049
William Lallemand5f232402012-04-05 18:02:55 +02002050 if (ret == NULL)
2051 goto out;
2052 tmplog = ret;
2053 last_isspace = 0;
2054 break;
2055
Willy Tarreau2beef582012-12-20 17:22:52 +01002056 case LOG_FMT_FRONTENDIP: // %fi
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002057 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002058 if (conn && conn_get_dst(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002059 ret = lf_ip(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002060 }
2061 else
2062 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2063
William Lallemand1d705562012-03-12 12:46:41 +01002064 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002065 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002066 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002067 last_isspace = 0;
2068 break;
2069
Willy Tarreau2beef582012-12-20 17:22:52 +01002070 case LOG_FMT_FRONTENDPORT: // %fp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002071 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002072 if (conn && conn_get_dst(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002073 if (conn->dst->ss_family == AF_UNIX)
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002074 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002075 else
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002076 ret = lf_port(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
William Lallemand5f232402012-04-05 18:02:55 +02002077 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002078 else
2079 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2080
William Lallemand5f232402012-04-05 18:02:55 +02002081 if (ret == NULL)
2082 goto out;
2083 tmplog = ret;
2084 last_isspace = 0;
2085 break;
2086
Willy Tarreau2beef582012-12-20 17:22:52 +01002087 case LOG_FMT_BACKENDIP: // %bi
Willy Tarreau8fa99842019-07-17 11:47:11 +02002088 if (be_conn && conn_get_src(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002089 ret = lf_ip(tmplog, (const struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002090 else
2091 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2092
William Lallemand1d705562012-03-12 12:46:41 +01002093 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002094 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002095 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002096 last_isspace = 0;
2097 break;
2098
Willy Tarreau2beef582012-12-20 17:22:52 +01002099 case LOG_FMT_BACKENDPORT: // %bp
Willy Tarreau8fa99842019-07-17 11:47:11 +02002100 if (be_conn && conn_get_src(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002101 ret = lf_port(tmplog, (struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002102 else
2103 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2104
William Lallemand5f232402012-04-05 18:02:55 +02002105 if (ret == NULL)
2106 goto out;
2107 tmplog = ret;
2108 last_isspace = 0;
2109 break;
2110
Willy Tarreau2beef582012-12-20 17:22:52 +01002111 case LOG_FMT_SERVERIP: // %si
Willy Tarreau8fa99842019-07-17 11:47:11 +02002112 if (be_conn && conn_get_dst(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002113 ret = lf_ip(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002114 else
2115 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2116
William Lallemand5f232402012-04-05 18:02:55 +02002117 if (ret == NULL)
2118 goto out;
2119 tmplog = ret;
2120 last_isspace = 0;
2121 break;
2122
Willy Tarreau2beef582012-12-20 17:22:52 +01002123 case LOG_FMT_SERVERPORT: // %sp
Willy Tarreau8fa99842019-07-17 11:47:11 +02002124 if (be_conn && conn_get_dst(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002125 ret = lf_port(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002126 else
2127 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2128
William Lallemand1d705562012-03-12 12:46:41 +01002129 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002130 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002131 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002132 last_isspace = 0;
2133 break;
2134
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002135 case LOG_FMT_DATE: // %t = accept date
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002136 get_localtime(logs->accept_date.tv_sec, &tm);
2137 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002138 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002139 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002140 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002141 last_isspace = 0;
2142 break;
2143
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002144 case LOG_FMT_tr: // %tr = start of request date
2145 /* Note that the timers are valid if we get here */
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002146 tv_ms_add(&tv, &logs->accept_date, logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002147 get_localtime(tv.tv_sec, &tm);
2148 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
2149 if (ret == NULL)
2150 goto out;
2151 tmplog = ret;
2152 last_isspace = 0;
2153 break;
2154
2155 case LOG_FMT_DATEGMT: // %T = accept date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002156 get_gmtime(logs->accept_date.tv_sec, &tm);
William Lallemand5f232402012-04-05 18:02:55 +02002157 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002158 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002159 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002160 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002161 last_isspace = 0;
2162 break;
2163
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002164 case LOG_FMT_trg: // %trg = start of request date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002165 tv_ms_add(&tv, &logs->accept_date, logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002166 get_gmtime(tv.tv_sec, &tm);
2167 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2168 if (ret == NULL)
2169 goto out;
2170 tmplog = ret;
2171 last_isspace = 0;
2172 break;
2173
2174 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002175 get_localtime(logs->accept_date.tv_sec, &tm);
2176 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
Yuxans Yao4e25b012012-10-19 10:36:09 +08002177 if (ret == NULL)
2178 goto out;
2179 tmplog = ret;
2180 last_isspace = 0;
2181 break;
2182
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002183 case LOG_FMT_trl: // %trl = start of request date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002184 tv_ms_add(&tv, &logs->accept_date, logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002185 get_localtime(tv.tv_sec, &tm);
2186 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
2187 if (ret == NULL)
2188 goto out;
2189 tmplog = ret;
2190 last_isspace = 0;
2191 break;
2192
William Lallemand5f232402012-04-05 18:02:55 +02002193 case LOG_FMT_TS: // %Ts
William Lallemand5f232402012-04-05 18:02:55 +02002194 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002195 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
William Lallemand5f232402012-04-05 18:02:55 +02002196 if (iret < 0 || iret > dst + maxsize - tmplog)
2197 goto out;
2198 last_isspace = 0;
2199 tmplog += iret;
2200 } else {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002201 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002202 if (ret == NULL)
2203 goto out;
2204 tmplog = ret;
2205 last_isspace = 0;
2206 }
2207 break;
2208
William Lallemand1d705562012-03-12 12:46:41 +01002209 case LOG_FMT_MS: // %ms
William Lallemand5f232402012-04-05 18:02:55 +02002210 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002211 iret = snprintf(tmplog, dst + maxsize - tmplog, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
William Lallemand5f232402012-04-05 18:02:55 +02002212 if (iret < 0 || iret > dst + maxsize - tmplog)
2213 goto out;
2214 last_isspace = 0;
2215 tmplog += iret;
2216 } else {
2217 if ((dst + maxsize - tmplog) < 4)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002218 goto out;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002219 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002220 tmplog, 4);
2221 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002222 goto out;
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002223 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002224 last_isspace = 0;
William Lallemand5f232402012-04-05 18:02:55 +02002225 }
2226 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002227
William Lallemand1d705562012-03-12 12:46:41 +01002228 case LOG_FMT_FRONTEND: // %f
William Lallemandbddd4fd2012-02-27 11:23:10 +01002229 src = fe->id;
William Lallemand5f232402012-04-05 18:02:55 +02002230 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002231 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002232 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002233 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002234 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002235 break;
2236
Willy Tarreau773d65f2012-10-12 14:56:11 +02002237 case LOG_FMT_FRONTEND_XPRT: // %ft
2238 src = fe->id;
2239 if (tmp->options & LOG_OPT_QUOTE)
2240 LOGCHAR('"');
2241 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2242 if (iret == 0)
2243 goto out;
2244 tmplog += iret;
Willy Tarreaua261e9b2016-12-22 20:44:00 +01002245 if (sess->listener->bind_conf->xprt == xprt_get(XPRT_SSL))
Willy Tarreau773d65f2012-10-12 14:56:11 +02002246 LOGCHAR('~');
Willy Tarreau773d65f2012-10-12 14:56:11 +02002247 if (tmp->options & LOG_OPT_QUOTE)
2248 LOGCHAR('"');
2249 last_isspace = 0;
2250 break;
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002251#ifdef USE_OPENSSL
2252 case LOG_FMT_SSL_CIPHER: // %sslc
2253 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002254 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002255 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002256 src = ssl_sock_get_cipher_name(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002257 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002258 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2259 if (ret == NULL)
2260 goto out;
2261 tmplog = ret;
2262 last_isspace = 0;
2263 break;
Willy Tarreau773d65f2012-10-12 14:56:11 +02002264
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002265 case LOG_FMT_SSL_VERSION: // %sslv
2266 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002267 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002268 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002269 src = ssl_sock_get_proto_version(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002270 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002271 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2272 if (ret == NULL)
2273 goto out;
2274 tmplog = ret;
2275 last_isspace = 0;
2276 break;
2277#endif
William Lallemand1d705562012-03-12 12:46:41 +01002278 case LOG_FMT_BACKEND: // %b
William Lallemandbddd4fd2012-02-27 11:23:10 +01002279 src = be->id;
William Lallemand5f232402012-04-05 18:02:55 +02002280 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002281 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002282 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002283 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002284 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002285 break;
2286
William Lallemand1d705562012-03-12 12:46:41 +01002287 case LOG_FMT_SERVER: // %s
Willy Tarreaue1809df2018-09-05 15:30:16 +02002288 switch (obj_type(s ? s->target : NULL)) {
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002289 case OBJ_TYPE_SERVER:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002290 src = __objt_server(s->target)->id;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002291 break;
2292 case OBJ_TYPE_APPLET:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002293 src = __objt_applet(s->target)->name;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002294 break;
2295 default:
2296 src = "<NOSRV>";
2297 break;
2298 }
William Lallemand5f232402012-04-05 18:02:55 +02002299 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002300 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002301 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002302 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002303 last_isspace = 0;
2304 break;
2305
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002306 case LOG_FMT_Th: // %Th = handshake time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002307 ret = ltoa_o(logs->t_handshake, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002308 if (ret == NULL)
2309 goto out;
2310 tmplog = ret;
2311 last_isspace = 0;
2312 break;
2313
2314 case LOG_FMT_Ti: // %Ti = HTTP idle time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002315 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002316 if (ret == NULL)
2317 goto out;
2318 tmplog = ret;
2319 last_isspace = 0;
2320 break;
2321
2322 case LOG_FMT_TR: // %TR = HTTP request time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002323 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002324 tmplog, dst + maxsize - tmplog);
2325 if (ret == NULL)
2326 goto out;
2327 tmplog = ret;
2328 last_isspace = 0;
2329 break;
2330
2331 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
William Lallemand5f232402012-04-05 18:02:55 +02002332 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002333 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002334 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002335 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002336 last_isspace = 0;
2337 break;
2338
William Lallemand1d705562012-03-12 12:46:41 +01002339 case LOG_FMT_TW: // %Tw
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002340 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002341 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002342 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002343 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002344 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002345 last_isspace = 0;
2346 break;
2347
William Lallemand1d705562012-03-12 12:46:41 +01002348 case LOG_FMT_TC: // %Tc
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002349 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002350 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002351 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002352 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002353 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002354 last_isspace = 0;
2355 break;
2356
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002357 case LOG_FMT_Tr: // %Tr
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002358 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002359 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002360 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002361 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002362 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002363 last_isspace = 0;
2364 break;
2365
Willy Tarreau27b639d2016-05-17 17:55:27 +02002366 case LOG_FMT_TD: // %Td
Willy Tarreaua21c0e62018-09-05 15:07:15 +02002367 if (be->mode == PR_MODE_HTTP)
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002368 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002369 tmplog, dst + maxsize - tmplog);
2370 else
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002371 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002372 tmplog, dst + maxsize - tmplog);
2373 if (ret == NULL)
2374 goto out;
2375 tmplog = ret;
2376 last_isspace = 0;
2377 break;
2378
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002379 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2380 if (!(fe->to_log & LW_BYTES))
2381 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002382 ret = ltoa_o(logs->t_close - (logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0),
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002383 tmplog, dst + maxsize - tmplog);
2384 if (ret == NULL)
2385 goto out;
2386 tmplog = ret;
2387 last_isspace = 0;
2388 break;
2389
2390 case LOG_FMT_TT: // %Tt = total time
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002391 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002392 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002393 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002394 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002395 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002396 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002397 last_isspace = 0;
2398 break;
2399
Willy Tarreau2beef582012-12-20 17:22:52 +01002400 case LOG_FMT_STATUS: // %ST
Willy Tarreau57bc8912016-04-25 17:09:40 +02002401 ret = ltoa_o(txn ? txn->status : 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002402 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002403 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002404 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002405 last_isspace = 0;
2406 break;
2407
William Lallemand1d705562012-03-12 12:46:41 +01002408 case LOG_FMT_BYTES: // %B
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002409 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002410 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002411 ret = lltoa(logs->bytes_out, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002412 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002413 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002414 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002415 last_isspace = 0;
2416 break;
2417
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002418 case LOG_FMT_BYTES_UP: // %U
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002419 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002420 if (ret == NULL)
2421 goto out;
2422 tmplog = ret;
2423 last_isspace = 0;
2424 break;
2425
Willy Tarreau2beef582012-12-20 17:22:52 +01002426 case LOG_FMT_CCLIENT: // %CC
Willy Tarreau57bc8912016-04-25 17:09:40 +02002427 src = txn ? txn->cli_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002428 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002429 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002430 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002431 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002432 last_isspace = 0;
2433 break;
2434
Willy Tarreau2beef582012-12-20 17:22:52 +01002435 case LOG_FMT_CSERVER: // %CS
Willy Tarreau57bc8912016-04-25 17:09:40 +02002436 src = txn ? txn->srv_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002437 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002438 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002439 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002440 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002441 last_isspace = 0;
2442 break;
2443
William Lallemand1d705562012-03-12 12:46:41 +01002444 case LOG_FMT_TERMSTATE: // %ts
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002445 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2446 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau6580c062012-03-12 15:09:42 +01002447 *tmplog = '\0';
2448 last_isspace = 0;
2449 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002450
William Lallemand1d705562012-03-12 12:46:41 +01002451 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002452 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2453 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau57bc8912016-04-25 17:09:40 +02002454 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2455 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_set_cookie[(txn->flags & TX_SCK_MASK) >> TX_SCK_SHIFT] : '-');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002456 last_isspace = 0;
2457 break;
2458
William Lallemand1d705562012-03-12 12:46:41 +01002459 case LOG_FMT_ACTCONN: // %ac
William Lallemand5f232402012-04-05 18:02:55 +02002460 ret = ltoa_o(actconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002461 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002462 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002463 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002464 last_isspace = 0;
2465 break;
2466
William Lallemand1d705562012-03-12 12:46:41 +01002467 case LOG_FMT_FECONN: // %fc
William Lallemand5f232402012-04-05 18:02:55 +02002468 ret = ltoa_o(fe->feconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002469 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002470 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002471 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002472 last_isspace = 0;
2473 break;
2474
William Lallemand1d705562012-03-12 12:46:41 +01002475 case LOG_FMT_BECONN: // %bc
William Lallemand5f232402012-04-05 18:02:55 +02002476 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002477 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002478 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002479 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002480 last_isspace = 0;
2481 break;
2482
William Lallemand1d705562012-03-12 12:46:41 +01002483 case LOG_FMT_SRVCONN: // %sc
Willy Tarreaue1809df2018-09-05 15:30:16 +02002484 ret = ultoa_o(objt_server(s ? s->target : NULL) ?
Willy Tarreau3fdb3662012-11-12 00:42:33 +01002485 objt_server(s->target)->cur_sess :
William Lallemand5f232402012-04-05 18:02:55 +02002486 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002487 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002488 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002489 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002490 last_isspace = 0;
2491 break;
2492
William Lallemand1d705562012-03-12 12:46:41 +01002493 case LOG_FMT_RETRIES: // %rq
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002494 if (s_flags & SF_REDISP)
William Lallemand1d705562012-03-12 12:46:41 +01002495 LOGCHAR('+');
Willy Tarreauabd71a52018-09-04 19:21:44 +02002496 ret = ltoa_o((s && s->si[1].conn_retries > 0) ?
Willy Tarreau350f4872014-11-28 14:42:25 +01002497 (be->conn_retries - s->si[1].conn_retries) :
William Lallemand5f232402012-04-05 18:02:55 +02002498 be->conn_retries, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002499 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002500 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002501 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002502 last_isspace = 0;
2503 break;
2504
William Lallemand1d705562012-03-12 12:46:41 +01002505 case LOG_FMT_SRVQUEUE: // %sq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002506 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002507 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002508 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002509 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002510 last_isspace = 0;
2511 break;
2512
William Lallemand1d705562012-03-12 12:46:41 +01002513 case LOG_FMT_BCKQUEUE: // %bq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002514 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002515 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002516 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002517 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002518 last_isspace = 0;
2519 break;
2520
William Lallemand1d705562012-03-12 12:46:41 +01002521 case LOG_FMT_HDRREQUEST: // %hr
William Lallemandbddd4fd2012-02-27 11:23:10 +01002522 /* request header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002523 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002524 if (tmp->options & LOG_OPT_QUOTE)
2525 LOGCHAR('"');
2526 LOGCHAR('{');
2527 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2528 if (hdr)
2529 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002530 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002531 ret = lf_encode_string(tmplog, dst + maxsize,
2532 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002533 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002534 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002535 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002536 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002537 }
2538 LOGCHAR('}');
William Lallemand51b5dca2012-03-26 17:52:55 +02002539 if (tmp->options & LOG_OPT_QUOTE)
2540 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002541 last_isspace = 0;
2542 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002543 break;
2544
William Lallemand1d705562012-03-12 12:46:41 +01002545 case LOG_FMT_HDRREQUESTLIST: // %hrl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002546 /* request header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002547 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002548 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2549 if (hdr > 0)
2550 LOGCHAR(' ');
2551 if (tmp->options & LOG_OPT_QUOTE)
2552 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002553 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002554 ret = lf_encode_string(tmplog, dst + maxsize,
2555 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002556 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002557 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002558 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002559 } else if (!(tmp->options & LOG_OPT_QUOTE))
2560 LOGCHAR('-');
2561 if (tmp->options & LOG_OPT_QUOTE)
2562 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002563 last_isspace = 0;
2564 }
2565 }
2566 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002567
William Lallemand1d705562012-03-12 12:46:41 +01002568
2569 case LOG_FMT_HDRRESPONS: // %hs
William Lallemandbddd4fd2012-02-27 11:23:10 +01002570 /* response header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002571 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002572 if (tmp->options & LOG_OPT_QUOTE)
2573 LOGCHAR('"');
2574 LOGCHAR('{');
2575 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2576 if (hdr)
2577 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002578 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002579 ret = lf_encode_string(tmplog, dst + maxsize,
2580 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002581 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002582 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002583 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002584 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002585 }
2586 LOGCHAR('}');
2587 last_isspace = 0;
2588 if (tmp->options & LOG_OPT_QUOTE)
2589 LOGCHAR('"');
2590 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002591 break;
2592
William Lallemand1d705562012-03-12 12:46:41 +01002593 case LOG_FMT_HDRRESPONSLIST: // %hsl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002594 /* response header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002595 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002596 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2597 if (hdr > 0)
2598 LOGCHAR(' ');
2599 if (tmp->options & LOG_OPT_QUOTE)
2600 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002601 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002602 ret = lf_encode_string(tmplog, dst + maxsize,
2603 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002604 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002605 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002606 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002607 } else if (!(tmp->options & LOG_OPT_QUOTE))
2608 LOGCHAR('-');
2609 if (tmp->options & LOG_OPT_QUOTE)
2610 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002611 last_isspace = 0;
2612 }
2613 }
2614 break;
2615
William Lallemand1d705562012-03-12 12:46:41 +01002616 case LOG_FMT_REQ: // %r
William Lallemandbddd4fd2012-02-27 11:23:10 +01002617 /* Request */
2618 if (tmp->options & LOG_OPT_QUOTE)
2619 LOGCHAR('"');
Willy Tarreau57bc8912016-04-25 17:09:40 +02002620 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Dragan Dosen835b9212016-02-12 13:23:03 +01002621 ret = lf_encode_string(tmplog, dst + maxsize,
2622 '#', url_encode_map, uri, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002623 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002624 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002625 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002626 if (tmp->options & LOG_OPT_QUOTE)
2627 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002628 last_isspace = 0;
2629 break;
William Lallemand5f232402012-04-05 18:02:55 +02002630
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002631 case LOG_FMT_HTTP_PATH: // %HP
Willy Tarreau57bc8912016-04-25 17:09:40 +02002632 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002633
Willy Tarreaub7636d12015-06-17 19:58:02 +02002634 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002635 LOGCHAR('"');
2636
2637 end = uri + strlen(uri);
2638 // look for the first whitespace character
2639 while (uri < end && !HTTP_IS_SPHT(*uri))
2640 uri++;
2641
2642 // keep advancing past multiple spaces
2643 while (uri < end && HTTP_IS_SPHT(*uri)) {
2644 uri++; nspaces++;
2645 }
2646
2647 // look for first space or question mark after url
2648 spc = uri;
2649 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2650 spc++;
2651
Nenad Merdanovic54e439f2016-04-26 01:39:02 +02002652 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002653 chunk.area = "<BADREQ>";
2654 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002655 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002656 chunk.area = uri;
2657 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002658 }
2659
Dragan Dosen835b9212016-02-12 13:23:03 +01002660 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002661 if (ret == NULL || *ret != '\0')
2662 goto out;
2663
2664 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002665 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002666 LOGCHAR('"');
2667
2668 last_isspace = 0;
2669 break;
2670
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002671 case LOG_FMT_HTTP_QUERY: // %HQ
2672 if (tmp->options & LOG_OPT_QUOTE)
2673 LOGCHAR('"');
2674
Willy Tarreau57bc8912016-04-25 17:09:40 +02002675 if (!txn || !txn->uri) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002676 chunk.area = "<BADREQ>";
2677 chunk.data = strlen("<BADREQ>");
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002678 } else {
2679 uri = txn->uri;
2680 end = uri + strlen(uri);
2681 // look for the first question mark
2682 while (uri < end && *uri != '?')
2683 uri++;
2684
2685 qmark = uri;
2686 // look for first space or question mark after url
2687 while (uri < end && !HTTP_IS_SPHT(*uri))
2688 uri++;
2689
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002690 chunk.area = qmark;
2691 chunk.data = uri - qmark;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002692 }
2693
Dragan Dosen835b9212016-02-12 13:23:03 +01002694 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002695 if (ret == NULL || *ret != '\0')
2696 goto out;
2697
2698 tmplog = ret;
2699 if (tmp->options & LOG_OPT_QUOTE)
2700 LOGCHAR('"');
2701
2702 last_isspace = 0;
2703 break;
2704
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002705 case LOG_FMT_HTTP_URI: // %HU
Willy Tarreau57bc8912016-04-25 17:09:40 +02002706 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002707
Willy Tarreaub7636d12015-06-17 19:58:02 +02002708 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002709 LOGCHAR('"');
2710
2711 end = uri + strlen(uri);
2712 // look for the first whitespace character
2713 while (uri < end && !HTTP_IS_SPHT(*uri))
2714 uri++;
2715
2716 // keep advancing past multiple spaces
2717 while (uri < end && HTTP_IS_SPHT(*uri)) {
2718 uri++; nspaces++;
2719 }
2720
2721 // look for first space after url
2722 spc = uri;
2723 while (spc < end && !HTTP_IS_SPHT(*spc))
2724 spc++;
2725
Willy Tarreau57bc8912016-04-25 17:09:40 +02002726 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002727 chunk.area = "<BADREQ>";
2728 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002729 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002730 chunk.area = uri;
2731 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002732 }
2733
Dragan Dosen835b9212016-02-12 13:23:03 +01002734 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002735 if (ret == NULL || *ret != '\0')
2736 goto out;
2737
2738 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002739 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002740 LOGCHAR('"');
2741
2742 last_isspace = 0;
2743 break;
2744
2745 case LOG_FMT_HTTP_METHOD: // %HM
Willy Tarreau57bc8912016-04-25 17:09:40 +02002746 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002747 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002748 LOGCHAR('"');
2749
2750 end = uri + strlen(uri);
2751 // look for the first whitespace character
2752 spc = uri;
2753 while (spc < end && !HTTP_IS_SPHT(*spc))
2754 spc++;
2755
2756 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002757 chunk.area = "<BADREQ>";
2758 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002759 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002760 chunk.area = uri;
2761 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002762 }
2763
Dragan Dosen835b9212016-02-12 13:23:03 +01002764 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002765 if (ret == NULL || *ret != '\0')
2766 goto out;
2767
2768 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002769 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002770 LOGCHAR('"');
2771
2772 last_isspace = 0;
2773 break;
2774
2775 case LOG_FMT_HTTP_VERSION: // %HV
Willy Tarreau57bc8912016-04-25 17:09:40 +02002776 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002777 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002778 LOGCHAR('"');
2779
2780 end = uri + strlen(uri);
2781 // look for the first whitespace character
2782 while (uri < end && !HTTP_IS_SPHT(*uri))
2783 uri++;
2784
2785 // keep advancing past multiple spaces
2786 while (uri < end && HTTP_IS_SPHT(*uri)) {
2787 uri++; nspaces++;
2788 }
2789
2790 // look for the next whitespace character
2791 while (uri < end && !HTTP_IS_SPHT(*uri))
2792 uri++;
2793
2794 // keep advancing past multiple spaces
2795 while (uri < end && HTTP_IS_SPHT(*uri))
2796 uri++;
2797
Willy Tarreau57bc8912016-04-25 17:09:40 +02002798 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002799 chunk.area = "<BADREQ>";
2800 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002801 } else if (uri == end) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002802 chunk.area = "HTTP/0.9";
2803 chunk.data = strlen("HTTP/0.9");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002804 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002805 chunk.area = uri;
2806 chunk.data = end - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002807 }
2808
Dragan Dosen835b9212016-02-12 13:23:03 +01002809 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002810 if (ret == NULL || *ret != '\0')
2811 goto out;
2812
2813 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002814 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002815 LOGCHAR('"');
2816
2817 last_isspace = 0;
2818 break;
2819
William Lallemand5f232402012-04-05 18:02:55 +02002820 case LOG_FMT_COUNTER: // %rt
2821 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002822 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
William Lallemand5f232402012-04-05 18:02:55 +02002823 if (iret < 0 || iret > dst + maxsize - tmplog)
2824 goto out;
2825 last_isspace = 0;
2826 tmplog += iret;
2827 } else {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002828 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002829 if (ret == NULL)
2830 goto out;
2831 tmplog = ret;
2832 last_isspace = 0;
2833 }
2834 break;
2835
Willy Tarreau7346acb2014-08-28 15:03:15 +02002836 case LOG_FMT_LOGCNT: // %lc
2837 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002838 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002839 if (iret < 0 || iret > dst + maxsize - tmplog)
2840 goto out;
2841 last_isspace = 0;
2842 tmplog += iret;
2843 } else {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002844 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002845 if (ret == NULL)
2846 goto out;
2847 tmplog = ret;
2848 last_isspace = 0;
2849 }
2850 break;
2851
William Lallemand5f232402012-04-05 18:02:55 +02002852 case LOG_FMT_HOSTNAME: // %H
2853 src = hostname;
2854 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2855 if (ret == NULL)
2856 goto out;
2857 tmplog = ret;
2858 last_isspace = 0;
2859 break;
2860
2861 case LOG_FMT_PID: // %pid
2862 if (tmp->options & LOG_OPT_HEXA) {
2863 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
2864 if (iret < 0 || iret > dst + maxsize - tmplog)
2865 goto out;
2866 last_isspace = 0;
2867 tmplog += iret;
2868 } else {
2869 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
2870 if (ret == NULL)
2871 goto out;
2872 tmplog = ret;
2873 last_isspace = 0;
2874 }
2875 break;
William Lallemanda73203e2012-03-12 12:48:57 +01002876
2877 case LOG_FMT_UNIQUEID: // %ID
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002878 ret = NULL;
Willy Tarreau02fdf4f2018-09-05 15:49:01 +02002879 src = s ? s->unique_id : NULL;
Thierry FOURNIER1be69102014-04-15 01:38:48 +02002880 ret = lf_text(tmplog, src, maxsize - (tmplog - dst), tmp);
William Lallemanda73203e2012-03-12 12:48:57 +01002881 if (ret == NULL)
2882 goto out;
2883 tmplog = ret;
2884 last_isspace = 0;
2885 break;
2886
William Lallemandbddd4fd2012-02-27 11:23:10 +01002887 }
2888 }
2889
2890out:
William Lallemand1d705562012-03-12 12:46:41 +01002891 /* *tmplog is a unused character */
2892 *tmplog = '\0';
Willy Tarreaudf974472012-12-28 02:44:01 +01002893 return tmplog - dst;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002894
Willy Tarreaubaaee002006-06-26 02:48:02 +02002895}
2896
William Lallemand1d705562012-03-12 12:46:41 +01002897/*
Willy Tarreau87b09662015-04-03 00:22:06 +02002898 * send a log for the stream when we have enough info about it.
William Lallemand1d705562012-03-12 12:46:41 +01002899 * Will not log if the frontend has no log defined.
2900 */
Willy Tarreau87b09662015-04-03 00:22:06 +02002901void strm_log(struct stream *s)
William Lallemand1d705562012-03-12 12:46:41 +01002902{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002903 struct session *sess = s->sess;
William Lallemand1d705562012-03-12 12:46:41 +01002904 int size, err, level;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002905 int sd_size = 0;
William Lallemand1d705562012-03-12 12:46:41 +01002906
2907 /* if we don't want to log normal traffic, return now */
Willy Tarreaue7dff022015-04-03 01:14:29 +02002908 err = (s->flags & SF_REDISP) ||
2909 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
2910 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
Willy Tarreau350f4872014-11-28 14:42:25 +01002911 (s->si[1].conn_retries != s->be->conn_retries)) ||
Willy Tarreaueee5b512015-04-03 23:46:31 +02002912 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
Willy Tarreaubaaee002006-06-26 02:48:02 +02002913
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002914 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
William Lallemand1d705562012-03-12 12:46:41 +01002915 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002916
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002917 if (LIST_ISEMPTY(&sess->fe->logsrvs))
William Lallemand1d705562012-03-12 12:46:41 +01002918 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002919
Willy Tarreauabcd5142013-06-11 17:18:02 +02002920 if (s->logs.level) { /* loglevel was overridden */
2921 if (s->logs.level == -1) {
2922 s->logs.logwait = 0; /* logs disabled */
2923 return;
2924 }
2925 level = s->logs.level - 1;
2926 }
2927 else {
2928 level = LOG_INFO;
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002929 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
Willy Tarreauabcd5142013-06-11 17:18:02 +02002930 level = LOG_ERR;
2931 }
William Lallemand1d705562012-03-12 12:46:41 +01002932
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002933 /* if unique-id was not generated */
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002934 if (!s->unique_id && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
Willy Tarreaubafbe012017-11-24 17:34:44 +01002935 if ((s->unique_id = pool_alloc(pool_head_uniqueid)) != NULL)
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002936 build_logline(s, s->unique_id, UNIQUEID_LEN, &sess->fe->format_unique_id);
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002937 }
2938
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002939 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2940 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
2941 &sess->fe->logformat_sd);
2942 }
2943
Dragan Dosen59cee972015-09-19 22:09:02 +02002944 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
William Lallemand1d705562012-03-12 12:46:41 +01002945 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002946 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002947 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
William Lallemand1d705562012-03-12 12:46:41 +01002948 s->logs.logwait = 0;
2949 }
2950}
William Lallemandbddd4fd2012-02-27 11:23:10 +01002951
Willy Tarreau53839352018-09-05 19:51:10 +02002952/*
2953 * send a minimalist log for the session. Will not log if the frontend has no
2954 * log defined. It is assumed that this is only used to report anomalies that
2955 * cannot lead to the creation of a regular stream. Because of this the log
2956 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
2957 * in the frontend. The caller must simply know that it should not call this
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002958 * function to report unimportant events. It is safe to call this function with
2959 * sess==NULL (will not do anything).
Willy Tarreau53839352018-09-05 19:51:10 +02002960 */
2961void sess_log(struct session *sess)
2962{
2963 int size, level;
2964 int sd_size = 0;
2965
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002966 if (!sess)
2967 return;
2968
Willy Tarreau53839352018-09-05 19:51:10 +02002969 if (LIST_ISEMPTY(&sess->fe->logsrvs))
2970 return;
2971
2972 level = LOG_INFO;
2973 if (sess->fe->options2 & PR_O2_LOGERRORS)
2974 level = LOG_ERR;
2975
2976 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2977 sd_size = sess_build_logline(sess, NULL,
2978 logline_rfc5424, global.max_syslog_len,
2979 &sess->fe->logformat_sd);
2980 }
2981
2982 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
2983 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002984 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Willy Tarreau53839352018-09-05 19:51:10 +02002985 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
2986 }
2987}
2988
Christopher Fauletc1b730a2017-10-24 12:00:51 +02002989static int cli_io_handler_show_startup_logs(struct appctx *appctx)
2990{
2991 struct stream_interface *si = appctx->owner;
2992 const char *msg = (startup_logs ? startup_logs : "No startup alerts/warnings.\n");
2993
2994 if (ci_putstr(si_ic(si), msg) == -1) {
Willy Tarreaudb398432018-11-15 11:08:52 +01002995 si_rx_room_blk(si);
Christopher Fauletc1b730a2017-10-24 12:00:51 +02002996 return 0;
2997 }
2998 return 1;
2999}
3000
3001/* register cli keywords */
3002static struct cli_kw_list cli_kws = {{ },{
3003 { { "show", "startup-logs", NULL },
3004 "show startup-logs : report logs emitted during HAProxy startup",
3005 NULL, cli_io_handler_show_startup_logs },
3006 {{},}
3007}};
3008
Willy Tarreau0108d902018-11-25 19:14:37 +01003009INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
3010
Willy Tarreau082b6282019-05-22 14:42:12 +02003011REGISTER_PER_THREAD_ALLOC(init_log_buffers);
3012REGISTER_PER_THREAD_FREE(deinit_log_buffers);
Willy Tarreau172f5ce2018-11-26 11:21:50 +01003013
Willy Tarreaubaaee002006-06-26 02:48:02 +02003014/*
3015 * Local variables:
3016 * c-indent-level: 8
3017 * c-basic-offset: 8
3018 * End:
3019 */