blob: e65b9ca9958c095ef731d23077f16a39bca2b023 [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 Tarreau4c7e4b72020-05-27 12:58:42 +020027#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020028#include <haproxy/applet-t.h>
Willy Tarreau83487a82020-06-04 20:19:54 +020029#include <haproxy/cli.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020030#include <haproxy/fd.h>
Willy Tarreau762d7a52020-06-04 11:23:07 +020031#include <haproxy/frontend.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020032#include <haproxy/global.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020033#include <haproxy/http.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020034#include <haproxy/log.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020035#include <haproxy/ring.h>
36#include <haproxy/sample.h>
37#include <haproxy/sink.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020038#include <haproxy/ssl_sock.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020039#include <haproxy/stream.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020040#include <haproxy/stream_interface.h>
Willy Tarreau92b4f132020-06-01 11:05:15 +020041#include <haproxy/time.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020042#include <haproxy/tools.h>
Willy Tarreaud6788052020-05-27 15:59:00 +020043#include <haproxy/version.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020044
Willy Tarreaubaaee002006-06-26 02:48:02 +020045
Dragan Dosen43885c72015-10-01 13:18:13 +020046struct log_fmt {
47 char *name;
48 struct {
Willy Tarreau83061a82018-07-13 11:56:34 +020049 struct buffer sep1; /* first pid separator */
50 struct buffer sep2; /* second pid separator */
Dragan Dosen43885c72015-10-01 13:18:13 +020051 } pid;
52};
53
54static const struct log_fmt log_formats[LOG_FORMATS] = {
55 [LOG_FORMAT_RFC3164] = {
56 .name = "rfc3164",
57 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020058 .sep1 = { .area = "[", .data = 1 },
59 .sep2 = { .area = "]: ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020060 }
61 },
62 [LOG_FORMAT_RFC5424] = {
63 .name = "rfc5424",
64 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020065 .sep1 = { .area = " ", .data = 1 },
66 .sep2 = { .area = " - ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020067 }
Willy Tarreaue8746a02018-11-12 08:45:00 +010068 },
69 [LOG_FORMAT_SHORT] = {
70 .name = "short",
71 .pid = {
72 .sep1 = { .area = "", .data = 0 },
73 .sep2 = { .area = " ", .data = 1 },
74 }
75 },
Willy Tarreauc1b06452018-11-12 11:57:56 +010076 [LOG_FORMAT_RAW] = {
77 .name = "raw",
78 .pid = {
79 .sep1 = { .area = "", .data = 0 },
80 .sep2 = { .area = "", .data = 0 },
81 }
82 },
Dragan Dosen1322d092015-09-22 16:05:32 +020083};
84
Emeric Brunbd163812020-05-06 14:33:46 +020085char *get_format_pid_sep1(int format, size_t *len)
86{
87 *len = log_formats[format].pid.sep1.data;
88 return log_formats[format].pid.sep1.area;
89}
90
91char *get_format_pid_sep2(int format, size_t *len)
92{
93 *len = log_formats[format].pid.sep2.data;
94 return log_formats[format].pid.sep2.area;
95}
96
Dragan Dosen835b9212016-02-12 13:23:03 +010097/*
98 * This map is used with all the FD_* macros to check whether a particular bit
Willy Tarreau1bfd6022019-06-07 11:10:07 +020099 * is set or not. Each bit represents an ACSII code. ha_bit_set() sets those
100 * bytes which should be escaped. When ha_bit_test() returns non-zero, it means
101 * that the byte should be escaped. Be careful to always pass bytes from 0 to
102 * 255 exclusively to the macros.
Dragan Dosen835b9212016-02-12 13:23:03 +0100103 */
Willy Tarreau1bfd6022019-06-07 11:10:07 +0200104long rfc5424_escape_map[(256/8) / sizeof(long)];
105long hdr_encode_map[(256/8) / sizeof(long)];
106long url_encode_map[(256/8) / sizeof(long)];
107long http_encode_map[(256/8) / sizeof(long)];
Dragan Dosen835b9212016-02-12 13:23:03 +0100108
Dragan Dosen835b9212016-02-12 13:23:03 +0100109
Willy Tarreaubaaee002006-06-26 02:48:02 +0200110const char *log_facilities[NB_LOG_FACILITIES] = {
111 "kern", "user", "mail", "daemon",
112 "auth", "syslog", "lpr", "news",
113 "uucp", "cron", "auth2", "ftp",
114 "ntp", "audit", "alert", "cron2",
115 "local0", "local1", "local2", "local3",
116 "local4", "local5", "local6", "local7"
117};
118
Willy Tarreaubaaee002006-06-26 02:48:02 +0200119const char *log_levels[NB_LOG_LEVELS] = {
120 "emerg", "alert", "crit", "err",
121 "warning", "notice", "info", "debug"
122};
123
Willy Tarreau570f2212013-06-10 16:42:09 +0200124const 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 +0200125const char sess_fin_state[8] = "-RCHDLQT"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, Tarpit */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200126
William Lallemand723b73a2012-02-08 16:37:49 +0100127
128/* log_format */
129struct logformat_type {
130 char *name;
131 int type;
William Lallemandbddd4fd2012-02-27 11:23:10 +0100132 int mode;
William Lallemand5e19a282012-04-02 16:22:10 +0200133 int lw; /* logwait bitsfield */
William Lallemandb7ff6a32012-03-02 14:35:21 +0100134 int (*config_callback)(struct logformat_node *node, struct proxy *curproxy);
Willy Tarreau2beef582012-12-20 17:22:52 +0100135 const char *replace_by; /* new option to use instead of old one */
William Lallemand723b73a2012-02-08 16:37:49 +0100136};
137
William Lallemandb7ff6a32012-03-02 14:35:21 +0100138int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy);
139
William Lallemand723b73a2012-02-08 16:37:49 +0100140/* log_format variable names */
141static const struct logformat_type logformat_keywords[] = {
William Lallemand5e19a282012-04-02 16:22:10 +0200142 { "o", LOG_FMT_GLOBAL, PR_MODE_TCP, 0, NULL }, /* global option */
Willy Tarreau2beef582012-12-20 17:22:52 +0100143
144 /* please keep these lines sorted ! */
145 { "B", LOG_FMT_BYTES, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from server to client */
146 { "CC", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL }, /* client cookie */
147 { "CS", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL }, /* server cookie */
148 { "H", LOG_FMT_HOSTNAME, PR_MODE_TCP, LW_INIT, NULL }, /* Hostname */
Tim Duesterhuscf6e0c82020-03-13 12:34:24 +0100149 { "ID", LOG_FMT_UNIQUEID, PR_MODE_TCP, LW_BYTES, NULL }, /* Unique ID */
Willy Tarreau4bf99632014-06-13 12:21:40 +0200150 { "ST", LOG_FMT_STATUS, PR_MODE_TCP, LW_RESP, NULL }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200151 { "T", LOG_FMT_DATEGMT, PR_MODE_TCP, LW_INIT, NULL }, /* date GMT */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200152 { "Ta", LOG_FMT_Ta, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time active (tr to end) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100153 { "Tc", LOG_FMT_TC, PR_MODE_TCP, LW_BYTES, NULL }, /* Tc */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200154 { "Th", LOG_FMT_Th, PR_MODE_TCP, LW_BYTES, NULL }, /* Time handshake */
155 { "Ti", LOG_FMT_Ti, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time idle */
156 { "Tl", LOG_FMT_DATELOCAL, PR_MODE_TCP, LW_INIT, NULL }, /* date local timezone */
157 { "Tq", LOG_FMT_TQ, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tq=Th+Ti+TR */
158 { "Tr", LOG_FMT_Tr, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tr */
159 { "TR", LOG_FMT_TR, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time to receive a valid request */
Willy Tarreau27b639d2016-05-17 17:55:27 +0200160 { "Td", LOG_FMT_TD, PR_MODE_TCP, LW_BYTES, NULL }, /* Td = Tt - (Tq + Tw + Tc + Tr) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100161 { "Ts", LOG_FMT_TS, PR_MODE_TCP, LW_INIT, NULL }, /* timestamp GMT */
William Lallemand5e19a282012-04-02 16:22:10 +0200162 { "Tt", LOG_FMT_TT, PR_MODE_TCP, LW_BYTES, NULL }, /* Tt */
Damien Claisse57c8eb92020-04-28 12:09:19 +0000163 { "Tu", LOG_FMT_TU, PR_MODE_TCP, LW_BYTES, NULL }, /* Tu = Tt -Ti */
Willy Tarreau2beef582012-12-20 17:22:52 +0100164 { "Tw", LOG_FMT_TW, PR_MODE_TCP, LW_BYTES, NULL }, /* Tw */
165 { "U", LOG_FMT_BYTES_UP, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from client to server */
William Lallemand5e19a282012-04-02 16:22:10 +0200166 { "ac", LOG_FMT_ACTCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* actconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100167 { "b", LOG_FMT_BACKEND, PR_MODE_TCP, LW_INIT, NULL }, /* backend */
William Lallemand5e19a282012-04-02 16:22:10 +0200168 { "bc", LOG_FMT_BECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* beconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100169 { "bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source ip */
170 { "bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source port */
William Lallemand5e19a282012-04-02 16:22:10 +0200171 { "bq", LOG_FMT_BCKQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* backend_queue */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200172 { "ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client ip */
173 { "cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100174 { "f", LOG_FMT_FRONTEND, PR_MODE_TCP, LW_INIT, NULL }, /* frontend */
175 { "fc", LOG_FMT_FECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* feconn */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200176 { "fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend ip */
177 { "fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100178 { "ft", LOG_FMT_FRONTEND_XPRT, PR_MODE_TCP, LW_INIT, NULL }, /* frontend with transport mode */
Willy Tarreaud9ed3d22014-06-13 12:23:06 +0200179 { "hr", LOG_FMT_HDRREQUEST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request */
180 { "hrl", LOG_FMT_HDRREQUESTLIST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request list */
181 { "hs", LOG_FMT_HDRRESPONS, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response */
182 { "hsl", LOG_FMT_HDRRESPONSLIST, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response list */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000183 { "HM", LOG_FMT_HTTP_METHOD, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP method */
184 { "HP", LOG_FMT_HTTP_PATH, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP path */
Andrew Hayworthe63ac872015-07-31 16:14:16 +0000185 { "HQ", LOG_FMT_HTTP_QUERY, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP query */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000186 { "HU", LOG_FMT_HTTP_URI, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP full URI */
187 { "HV", LOG_FMT_HTTP_VERSION, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP version */
Willy Tarreau7346acb2014-08-28 15:03:15 +0200188 { "lc", LOG_FMT_LOGCNT, PR_MODE_TCP, LW_INIT, NULL }, /* log counter */
Willy Tarreau2beef582012-12-20 17:22:52 +0100189 { "ms", LOG_FMT_MS, PR_MODE_TCP, LW_INIT, NULL }, /* accept date millisecond */
William Lallemand5e19a282012-04-02 16:22:10 +0200190 { "pid", LOG_FMT_PID, PR_MODE_TCP, LW_INIT, NULL }, /* log pid */
Willy Tarreau2beef582012-12-20 17:22:52 +0100191 { "r", LOG_FMT_REQ, PR_MODE_HTTP, LW_REQ, NULL }, /* request */
192 { "rc", LOG_FMT_RETRIES, PR_MODE_TCP, LW_BYTES, NULL }, /* retries */
Willy Tarreau1f0da242014-01-25 11:01:50 +0100193 { "rt", LOG_FMT_COUNTER, PR_MODE_TCP, LW_REQ, NULL }, /* request counter (HTTP or TCP session) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100194 { "s", LOG_FMT_SERVER, PR_MODE_TCP, LW_SVID, NULL }, /* server */
195 { "sc", LOG_FMT_SRVCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_conn */
196 { "si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination ip */
197 { "sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination port */
198 { "sq", LOG_FMT_SRVQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_queue */
Willy Tarreauffc3fcd2012-10-12 20:17:54 +0200199 { "sslc", LOG_FMT_SSL_CIPHER, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL ciphers */
200 { "sslv", LOG_FMT_SSL_VERSION, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL protocol version */
Willy Tarreau2beef582012-12-20 17:22:52 +0100201 { "t", LOG_FMT_DATE, PR_MODE_TCP, LW_INIT, NULL }, /* date */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200202 { "tr", LOG_FMT_tr, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request */
203 { "trg",LOG_FMT_trg, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, GMT */
204 { "trl",LOG_FMT_trl, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, local */
Willy Tarreau2beef582012-12-20 17:22:52 +0100205 { "ts", LOG_FMT_TERMSTATE, PR_MODE_TCP, LW_BYTES, NULL },/* termination state */
206 { "tsc", LOG_FMT_TERMSTATE_CK, PR_MODE_TCP, LW_INIT, NULL },/* termination state */
207
208 /* The following tags are deprecated and will be removed soon */
209 { "Bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bi" }, /* backend source ip */
210 { "Bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bp" }, /* backend source port */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200211 { "Ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "ci" }, /* client ip */
212 { "Cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "cp" }, /* client port */
213 { "Fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fi" }, /* frontend ip */
214 { "Fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fp" }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100215 { "Si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL, "si" }, /* server destination ip */
216 { "Sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL, "sp" }, /* server destination port */
217 { "cc", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL, "CC" }, /* client cookie */
218 { "cs", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL, "CS" }, /* server cookie */
219 { "st", LOG_FMT_STATUS, PR_MODE_HTTP, LW_RESP, NULL, "ST" }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200220 { 0, 0, 0, 0, NULL }
William Lallemand723b73a2012-02-08 16:37:49 +0100221};
222
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200223char 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
224char 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 +0100225char 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 +0100226char *log_format = NULL;
227
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200228/* Default string used for structured-data part in RFC5424 formatted
229 * syslog messages.
230 */
231char default_rfc5424_sd_log_format[] = "- ";
Dragan Dosen1322d092015-09-22 16:05:32 +0200232
Willy Tarreau13ef7732018-11-12 07:25:28 +0100233/* total number of dropped logs */
234unsigned int dropped_logs = 0;
235
Dragan Dosen1322d092015-09-22 16:05:32 +0200236/* This is a global syslog header, common to all outgoing messages in
237 * RFC3164 format. It begins with time-based part and is updated by
238 * update_log_hdr().
Dragan Dosen59cee972015-09-19 22:09:02 +0200239 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200240THREAD_LOCAL char *logheader = NULL;
Willy Tarreau55e2f5a2019-05-05 10:11:39 +0200241THREAD_LOCAL char *logheader_end = NULL;
Dragan Dosen59cee972015-09-19 22:09:02 +0200242
Dragan Dosen1322d092015-09-22 16:05:32 +0200243/* This is a global syslog header for messages in RFC5424 format. It is
244 * updated by update_log_hdr_rfc5424().
245 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200246THREAD_LOCAL char *logheader_rfc5424 = NULL;
Willy Tarreau55e2f5a2019-05-05 10:11:39 +0200247THREAD_LOCAL char *logheader_rfc5424_end = NULL;
Dragan Dosen1322d092015-09-22 16:05:32 +0200248
Dragan Dosen59cee972015-09-19 22:09:02 +0200249/* This is a global syslog message buffer, common to all outgoing
250 * messages. It contains only the data part.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100251 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200252THREAD_LOCAL char *logline = NULL;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100253
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200254/* A global syslog message buffer, common to all RFC5424 syslog messages.
255 * Currently, it is used for generating the structured-data part.
256 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200257THREAD_LOCAL char *logline_rfc5424 = NULL;
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200258
Christopher Fauletd4696382017-10-24 11:44:05 +0200259/* A global buffer used to store all startup alerts/warnings. It will then be
260 * retrieve on the CLI. */
Willy Tarreau869efd52019-11-15 15:16:57 +0100261static struct ring *startup_logs = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +0200262
William Lallemand723b73a2012-02-08 16:37:49 +0100263struct logformat_var_args {
264 char *name;
265 int mask;
266};
267
268struct logformat_var_args var_args_list[] = {
269// global
270 { "M", LOG_OPT_MANDATORY },
271 { "Q", LOG_OPT_QUOTE },
William Lallemand5f232402012-04-05 18:02:55 +0200272 { "X", LOG_OPT_HEXA },
Dragan Dosen835b9212016-02-12 13:23:03 +0100273 { "E", LOG_OPT_ESC },
William Lallemand723b73a2012-02-08 16:37:49 +0100274 { 0, 0 }
275};
276
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200277/* return the name of the directive used in the current proxy for which we're
278 * currently parsing a header, when it is known.
279 */
280static inline const char *fmt_directive(const struct proxy *curproxy)
281{
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100282 switch (curproxy->conf.args.ctx) {
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200283 case ARGC_ACL:
284 return "acl";
285 case ARGC_STK:
286 return "stick";
287 case ARGC_TRK:
288 return "track-sc";
289 case ARGC_LOG:
290 return "log-format";
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200291 case ARGC_LOGSD:
292 return "log-format-sd";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100293 case ARGC_HRQ:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100294 return "http-request";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100295 case ARGC_HRS:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100296 return "http-response";
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200297 case ARGC_UIF:
298 return "unique-id-format";
Thierry FOURNIERd18cd0f2013-11-29 12:15:45 +0100299 case ARGC_RDR:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200300 return "redirect";
301 case ARGC_CAP:
302 return "capture";
Willy Tarreau28d976d2015-07-09 11:39:33 +0200303 case ARGC_SRV:
304 return "server";
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200305 case ARGC_SPOE:
306 return "spoe-message";
Thierry FOURNIER / OZON.IO4ed1c952016-11-24 23:57:54 +0100307 case ARGC_UBK:
308 return "use_backend";
Christopher Faulet3b967c12020-05-15 15:47:44 +0200309 case ARGC_HERR:
310 return "http-error";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100311 default:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200312 return "undefined(please report this bug)"; /* must never happen */
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100313 }
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200314}
315
William Lallemand723b73a2012-02-08 16:37:49 +0100316/*
William Lallemandb7ff6a32012-03-02 14:35:21 +0100317 * callback used to configure addr source retrieval
318 */
319int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy)
320{
321 curproxy->options2 |= PR_O2_SRC_ADDR;
322
323 return 0;
324}
325
326
327/*
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100328 * Parse args in a logformat_var. Returns 0 in error
329 * case, otherwise, it returns 1.
William Lallemand723b73a2012-02-08 16:37:49 +0100330 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100331int parse_logformat_var_args(char *args, struct logformat_node *node, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100332{
333 int i = 0;
334 int end = 0;
335 int flags = 0; // 1 = + 2 = -
336 char *sp = NULL; // start pointer
337
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100338 if (args == NULL) {
339 memprintf(err, "internal error: parse_logformat_var_args() expects non null 'args'");
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100340 return 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100341 }
William Lallemand723b73a2012-02-08 16:37:49 +0100342
343 while (1) {
344 if (*args == '\0')
345 end = 1;
346
347 if (*args == '+') {
348 // add flag
349 sp = args + 1;
350 flags = 1;
351 }
352 if (*args == '-') {
353 // delete flag
354 sp = args + 1;
355 flags = 2;
356 }
357
358 if (*args == '\0' || *args == ',') {
359 *args = '\0';
Willy Tarreau254d44c2012-12-20 18:19:26 +0100360 for (i = 0; sp && var_args_list[i].name; i++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100361 if (strcmp(sp, var_args_list[i].name) == 0) {
362 if (flags == 1) {
363 node->options |= var_args_list[i].mask;
364 break;
365 } else if (flags == 2) {
366 node->options &= ~var_args_list[i].mask;
367 break;
368 }
369 }
370 }
371 sp = NULL;
372 if (end)
373 break;
374 }
Willy Tarreau254d44c2012-12-20 18:19:26 +0100375 args++;
William Lallemand723b73a2012-02-08 16:37:49 +0100376 }
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100377 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100378}
379
380/*
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100381 * Parse a variable '%varname' or '%{args}varname' in log-format. The caller
382 * must pass the args part in the <arg> pointer with its length in <arg_len>,
383 * and varname with its length in <var> and <var_len> respectively. <arg> is
384 * ignored when arg_len is 0. Neither <var> nor <var_len> may be null.
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100385 * Returns false in error case and err is filled, otherwise returns true.
William Lallemand723b73a2012-02-08 16:37:49 +0100386 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100387int 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 +0100388{
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100389 int j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200390 struct logformat_node *node = NULL;
William Lallemand723b73a2012-02-08 16:37:49 +0100391
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100392 for (j = 0; logformat_keywords[j].name; j++) { // search a log type
393 if (strlen(logformat_keywords[j].name) == var_len &&
394 strncmp(var, logformat_keywords[j].name, var_len) == 0) {
395 if (logformat_keywords[j].mode != PR_MODE_HTTP || curproxy->mode == PR_MODE_HTTP) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200396 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100397 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100398 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200399 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100400 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100401 node->type = logformat_keywords[j].type;
402 node->options = *defoptions;
403 if (arg_len) {
404 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100405 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200406 goto error_free;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100407 }
408 if (node->type == LOG_FMT_GLOBAL) {
409 *defoptions = node->options;
410 free(node->arg);
411 free(node);
412 } else {
413 if (logformat_keywords[j].config_callback &&
414 logformat_keywords[j].config_callback(node, curproxy) != 0) {
Dragan Dosen61302da2019-04-30 00:40:02 +0200415 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100416 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100417 curproxy->to_log |= logformat_keywords[j].lw;
418 LIST_ADDQ(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100419 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100420 if (logformat_keywords[j].replace_by)
Christopher Faulet767a84b2017-11-24 16:50:31 +0100421 ha_warning("parsing [%s:%d] : deprecated variable '%s' in '%s', please replace it with '%s'.\n",
422 curproxy->conf.args.file, curproxy->conf.args.line,
423 logformat_keywords[j].name, fmt_directive(curproxy), logformat_keywords[j].replace_by);
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100424 return 1;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100425 } else {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100426 memprintf(err, "format variable '%s' is reserved for HTTP mode",
427 logformat_keywords[j].name);
Dragan Dosen61302da2019-04-30 00:40:02 +0200428 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100429 }
William Lallemand723b73a2012-02-08 16:37:49 +0100430 }
431 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100432
433 j = var[var_len];
434 var[var_len] = 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100435 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 +0100436 var[var_len] = j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200437
438 error_free:
439 if (node) {
440 free(node->arg);
441 free(node);
442 }
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100443 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100444}
445
446/*
447 * push to the logformat linked list
448 *
449 * start: start pointer
450 * end: end text pointer
451 * type: string type
William Lallemand1d705562012-03-12 12:46:41 +0100452 * list_format: destination list
William Lallemand723b73a2012-02-08 16:37:49 +0100453 *
454 * LOG_TEXT: copy chars from start to end excluding end.
455 *
456*/
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100457int add_to_logformat_list(char *start, char *end, int type, struct list *list_format, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100458{
459 char *str;
460
Willy Tarreaua3571662012-12-20 21:59:12 +0100461 if (type == LF_TEXT) { /* type text */
Vincent Bernat02779b62016-04-03 13:48:43 +0200462 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100463 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100464 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100465 return 0;
466 }
Vincent Bernat02779b62016-04-03 13:48:43 +0200467 str = calloc(1, end - start + 1);
William Lallemand723b73a2012-02-08 16:37:49 +0100468 strncpy(str, start, end - start);
William Lallemand723b73a2012-02-08 16:37:49 +0100469 str[end - start] = '\0';
470 node->arg = str;
William Lallemand1d705562012-03-12 12:46:41 +0100471 node->type = LOG_FMT_TEXT; // type string
472 LIST_ADDQ(list_format, &node->list);
Willy Tarreaua3571662012-12-20 21:59:12 +0100473 } else if (type == LF_SEPARATOR) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200474 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100475 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100476 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100477 return 0;
478 }
William Lallemand1d705562012-03-12 12:46:41 +0100479 node->type = LOG_FMT_SEPARATOR;
480 LIST_ADDQ(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100481 }
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100482 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100483}
484
485/*
Willy Tarreauc8368452012-12-21 00:09:23 +0100486 * Parse the sample fetch expression <text> and add a node to <list_format> upon
487 * success. At the moment, sample converters are not yet supported but fetch arguments
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100488 * should work. The curpx->conf.args.ctx must be set by the caller. If an end pointer
489 * is passed in <endptr>, it will be updated with the pointer to the first character
490 * not part of the sample expression.
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100491 *
492 * In error case, the function returns 0, otherwise it returns 1.
Willy Tarreauc8368452012-12-21 00:09:23 +0100493 */
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100494int 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, char **endptr)
Willy Tarreauc8368452012-12-21 00:09:23 +0100495{
496 char *cmd[2];
Dragan Dosen61302da2019-04-30 00:40:02 +0200497 struct sample_expr *expr = NULL;
498 struct logformat_node *node = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +0100499 int cmd_arg;
500
501 cmd[0] = text;
502 cmd[1] = "";
503 cmd_arg = 0;
504
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100505 expr = sample_parse_expr(cmd, &cmd_arg, curpx->conf.args.file, curpx->conf.args.line, err, &curpx->conf.args, endptr);
Willy Tarreauc8368452012-12-21 00:09:23 +0100506 if (!expr) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100507 memprintf(err, "failed to parse sample expression <%s> : %s", text, *err);
Dragan Dosen61302da2019-04-30 00:40:02 +0200508 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100509 }
510
Vincent Bernat02779b62016-04-03 13:48:43 +0200511 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100512 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100513 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200514 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100515 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100516 node->type = LOG_FMT_EXPR;
517 node->expr = expr;
518 node->options = options;
519
520 if (arg_len) {
521 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100522 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200523 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100524 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100525 if (expr->fetch->val & cap & SMP_VAL_REQUEST)
Willy Tarreauc8368452012-12-21 00:09:23 +0100526 node->options |= LOG_OPT_REQ_CAP; /* fetch method is request-compatible */
527
Willy Tarreau434c57c2013-01-08 01:10:24 +0100528 if (expr->fetch->val & cap & SMP_VAL_RESPONSE)
Willy Tarreauc8368452012-12-21 00:09:23 +0100529 node->options |= LOG_OPT_RES_CAP; /* fetch method is response-compatible */
530
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100531 if (!(expr->fetch->val & cap)) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100532 memprintf(err, "sample fetch <%s> may not be reliably used here because it needs '%s' which is not available here",
533 text, sample_src_names(expr->fetch->use));
Dragan Dosen61302da2019-04-30 00:40:02 +0200534 goto error_free;
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100535 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100536
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200537 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreauc8368452012-12-21 00:09:23 +0100538 /* Note, we may also need to set curpx->to_log with certain fetches */
Willy Tarreau25320b22013-03-24 07:22:08 +0100539 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
Willy Tarreauc8368452012-12-21 00:09:23 +0100540
William Lallemand65ad6e12014-01-31 15:08:02 +0100541 /* FIXME: temporary workaround for missing LW_XPRT and LW_REQ flags
542 * needed with some sample fetches (eg: ssl*). We always set it for
543 * now on, but this will leave with sample capabilities soon.
Willy Tarreau1f31c732013-01-10 16:22:27 +0100544 */
545 curpx->to_log |= LW_XPRT;
Christopher Fauletd2236cd2020-04-06 18:29:14 +0200546 if (curpx->http_needed)
547 curpx->to_log |= LW_REQ;
Willy Tarreauc8368452012-12-21 00:09:23 +0100548 LIST_ADDQ(list_format, &node->list);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100549 return 1;
Dragan Dosen61302da2019-04-30 00:40:02 +0200550
551 error_free:
552 release_sample_expr(expr);
553 if (node) {
554 free(node->arg);
555 free(node);
556 }
557 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100558}
559
560/*
William Lallemand723b73a2012-02-08 16:37:49 +0100561 * Parse the log_format string and fill a linked list.
562 * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200563 * You can set arguments using { } : %{many arguments}varname.
564 * The curproxy->conf.args.ctx must be set by the caller.
William Lallemand1d705562012-03-12 12:46:41 +0100565 *
Ilya Shipitsinae40dbc2020-04-04 12:59:53 +0500566 * fmt: the string to parse
William Lallemand1d705562012-03-12 12:46:41 +0100567 * curproxy: the proxy affected
568 * list_format: the destination list
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +0100569 * options: LOG_OPT_* to force on every node
Willy Tarreau434c57c2013-01-08 01:10:24 +0100570 * cap: all SMP_VAL_* flags supported by the consumer
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100571 *
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100572 * The function returns 1 in success case, otherwise, it returns 0 and err is filled.
William Lallemand723b73a2012-02-08 16:37:49 +0100573 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100574int 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 +0100575{
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100576 char *sp, *str, *backfmt; /* start pointer for text parts */
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100577 char *arg = NULL; /* start pointer for args */
578 char *var = NULL; /* start pointer for vars */
579 int arg_len = 0;
580 int var_len = 0;
581 int cformat; /* current token format */
582 int pformat; /* previous token format */
William Lallemand723b73a2012-02-08 16:37:49 +0100583 struct logformat_node *tmplf, *back;
584
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100585 sp = str = backfmt = strdup(fmt);
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100586 if (!str) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100587 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100588 return 0;
589 }
William Lallemand1dc00ef2012-08-09 16:41:35 +0200590 curproxy->to_log |= LW_INIT;
William Lallemand5e19a282012-04-02 16:22:10 +0200591
William Lallemand723b73a2012-02-08 16:37:49 +0100592 /* flush the list first. */
William Lallemand1d705562012-03-12 12:46:41 +0100593 list_for_each_entry_safe(tmplf, back, list_format, list) {
William Lallemand723b73a2012-02-08 16:37:49 +0100594 LIST_DEL(&tmplf->list);
Dragan Dosen61302da2019-04-30 00:40:02 +0200595 release_sample_expr(tmplf->expr);
596 free(tmplf->arg);
William Lallemand723b73a2012-02-08 16:37:49 +0100597 free(tmplf);
598 }
599
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100600 for (cformat = LF_INIT; cformat != LF_END; str++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100601 pformat = cformat;
602
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100603 if (!*str)
604 cformat = LF_END; // preset it to save all states from doing this
William Lallemand723b73a2012-02-08 16:37:49 +0100605
Joseph Herlant85b40592018-11-15 12:10:04 -0800606 /* The principle of the two-step state machine below is to first detect a change, and
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100607 * second have all common paths processed at one place. The common paths are the ones
608 * encountered in text areas (LF_INIT, LF_TEXT, LF_SEPARATOR) and at the end (LF_END).
609 * We use the common LF_INIT state to dispatch to the different final states.
610 */
611 switch (pformat) {
612 case LF_STARTVAR: // text immediately following a '%'
Willy Tarreauc8368452012-12-21 00:09:23 +0100613 arg = NULL; var = NULL;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100614 arg_len = var_len = 0;
615 if (*str == '{') { // optional argument
616 cformat = LF_STARG;
617 arg = str + 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100618 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100619 else if (*str == '[') {
620 cformat = LF_STEXPR;
621 var = str + 1; // store expr in variable name
622 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100623 else if (isalpha((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100624 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100625 var = str;
William Lallemand723b73a2012-02-08 16:37:49 +0100626 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100627 else if (*str == '%')
Ilya Shipitsin856aabc2020-04-16 23:51:34 +0500628 cformat = LF_TEXT; // convert this character to a literal (useful for '%')
Willy Tarreau0f28f822013-12-16 01:38:33 +0100629 else if (isdigit((unsigned char)*str) || *str == ' ' || *str == '\t') {
Willy Tarreau06d97f92013-12-02 17:45:48 +0100630 /* single '%' followed by blank or digit, send them both */
631 cformat = LF_TEXT;
632 pformat = LF_TEXT; /* finally we include the previous char as well */
633 sp = str - 1; /* send both the '%' and the current char */
Jim Freemana2278c82017-04-15 08:01:59 -0600634 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 +0100635 *str, (int)(str - backfmt), fmt);
Willy Tarreau51013e82019-12-11 12:05:39 +0100636 goto fail;
Willy Tarreau06d97f92013-12-02 17:45:48 +0100637
638 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100639 else
Ilya Shipitsin856aabc2020-04-16 23:51:34 +0500640 cformat = LF_INIT; // handle other cases of literals
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100641 break;
642
643 case LF_STARG: // text immediately following '%{'
644 if (*str == '}') { // end of arg
William Lallemand723b73a2012-02-08 16:37:49 +0100645 cformat = LF_EDARG;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100646 arg_len = str - arg;
647 *str = 0; // used for reporting errors
William Lallemand723b73a2012-02-08 16:37:49 +0100648 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100649 break;
650
651 case LF_EDARG: // text immediately following '%{arg}'
Willy Tarreauc8368452012-12-21 00:09:23 +0100652 if (*str == '[') {
653 cformat = LF_STEXPR;
654 var = str + 1; // store expr in variable name
655 break;
656 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100657 else if (isalnum((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100658 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100659 var = str;
660 break;
661 }
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100662 memprintf(err, "parse argument modifier without variable name near '%%{%s}'", arg);
Willy Tarreau51013e82019-12-11 12:05:39 +0100663 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100664
Willy Tarreauc8368452012-12-21 00:09:23 +0100665 case LF_STEXPR: // text immediately following '%['
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100666 /* the whole sample expression is parsed at once,
667 * returning the pointer to the first character not
668 * part of the expression, which MUST be the trailing
669 * angle bracket.
670 */
671 if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err, &str))
672 goto fail;
673
674 if (*str == ']') {
675 // end of arg, go on with next state
676 cformat = pformat = LF_EDEXPR;
677 sp = str;
678 }
679 else {
680 char c = *str;
681 *str = 0;
Willy Tarreau90807112020-02-25 08:16:33 +0100682 if (isprint((unsigned char)c))
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100683 memprintf(err, "expected ']' after '%s', but found '%c'", var, c);
684 else
685 memprintf(err, "missing ']' after '%s'", var);
Dragan Dosen2866acf2020-06-30 21:16:43 +0200686 goto fail;
Willy Tarreauc8368452012-12-21 00:09:23 +0100687 }
688 break;
689
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100690 case LF_VAR: // text part of a variable name
691 var_len = str - var;
Willy Tarreau0f28f822013-12-16 01:38:33 +0100692 if (!isalnum((unsigned char)*str))
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100693 cformat = LF_INIT; // not variable name anymore
694 break;
695
Willy Tarreauc8368452012-12-21 00:09:23 +0100696 default: // LF_INIT, LF_TEXT, LF_SEPARATOR, LF_END, LF_EDEXPR
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100697 cformat = LF_INIT;
698 }
699
700 if (cformat == LF_INIT) { /* resynchronize state to text/sep/startvar */
701 switch (*str) {
702 case '%': cformat = LF_STARTVAR; break;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100703 case 0 : cformat = LF_END; break;
Dragan Dosen1e3b16f2020-06-23 18:16:44 +0200704 case ' ':
705 if (options & LOG_OPT_MERGE_SPACES) {
706 cformat = LF_SEPARATOR;
707 break;
708 }
709 /* fall through */
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100710 default : cformat = LF_TEXT; break;
William Lallemand723b73a2012-02-08 16:37:49 +0100711 }
712 }
713
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100714 if (cformat != pformat || pformat == LF_SEPARATOR) {
715 switch (pformat) {
716 case LF_VAR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100717 if (!parse_logformat_var(arg, arg_len, var, var_len, curproxy, list_format, &options, err))
Willy Tarreau51013e82019-12-11 12:05:39 +0100718 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100719 break;
Willy Tarreauc8368452012-12-21 00:09:23 +0100720 case LF_STEXPR:
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100721 if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err, &sp))
Willy Tarreau51013e82019-12-11 12:05:39 +0100722 goto fail;
Willy Tarreauc8368452012-12-21 00:09:23 +0100723 break;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100724 case LF_TEXT:
725 case LF_SEPARATOR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100726 if (!add_to_logformat_list(sp, str, pformat, list_format, err))
Willy Tarreau51013e82019-12-11 12:05:39 +0100727 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100728 break;
729 }
730 sp = str; /* new start of text at every state switch and at every separator */
William Lallemand723b73a2012-02-08 16:37:49 +0100731 }
732 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100733
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100734 if (pformat == LF_STARTVAR || pformat == LF_STARG || pformat == LF_STEXPR) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100735 memprintf(err, "truncated line after '%s'", var ? var : arg ? arg : "%");
Willy Tarreau51013e82019-12-11 12:05:39 +0100736 goto fail;
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100737 }
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100738 free(backfmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100739
740 return 1;
Willy Tarreau51013e82019-12-11 12:05:39 +0100741 fail:
742 free(backfmt);
743 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100744}
745
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200746/*
Ilya Shipitsin856aabc2020-04-16 23:51:34 +0500747 * Parse the first range of indexes from a string made of a list of comma separated
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200748 * ranges of indexes. Note that an index may be considered as a particular range
749 * with a high limit to the low limit.
750 */
751int get_logsrv_smp_range(unsigned int *low, unsigned int *high, char **arg, char **err)
752{
753 char *end, *p;
754
755 *low = *high = 0;
756
757 p = *arg;
758 end = strchr(p, ',');
759 if (!end)
760 end = p + strlen(p);
761
762 *high = *low = read_uint((const char **)&p, end);
763 if (!*low || (p != end && *p != '-'))
764 goto err;
765
766 if (p == end)
767 goto done;
768
769 p++;
770 *high = read_uint((const char **)&p, end);
771 if (!*high || *high <= *low || p != end)
772 goto err;
773
774 done:
775 if (*end == ',')
776 end++;
777 *arg = end;
778 return 1;
779
780 err:
781 memprintf(err, "wrong sample range '%s'", *arg);
782 return 0;
783}
784
785/*
786 * Returns 1 if the range defined by <low> and <high> overlaps
787 * one of them in <rgs> array of ranges with <sz> the size of this
788 * array, 0 if not.
789 */
790int smp_log_ranges_overlap(struct smp_log_range *rgs, size_t sz,
791 unsigned int low, unsigned int high, char **err)
792{
793 size_t i;
794
795 for (i = 0; i < sz; i++) {
796 if ((low >= rgs[i].low && low <= rgs[i].high) ||
797 (high >= rgs[i].low && high <= rgs[i].high)) {
798 memprintf(err, "ranges are overlapping");
799 return 1;
800 }
801 }
802
803 return 0;
804}
805
806int smp_log_range_cmp(const void *a, const void *b)
807{
808 const struct smp_log_range *rg_a = a;
809 const struct smp_log_range *rg_b = b;
810
811 if (rg_a->high < rg_b->low)
812 return -1;
813 else if (rg_a->low > rg_b->high)
814 return 1;
815
816 return 0;
817}
818
819/*
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200820 * Parse "log" keyword and update <logsrvs> list accordingly.
821 *
822 * When <do_del> is set, it means the "no log" line was parsed, so all log
823 * servers in <logsrvs> are released.
824 *
825 * Otherwise, we try to parse the "log" line. First of all, when the list is not
826 * the global one, we look for the parameter "global". If we find it,
827 * global.logsrvs is copied. Else we parse each arguments.
828 *
829 * The function returns 1 in success case, otherwise, it returns 0 and err is
830 * filled.
831 */
832int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err)
833{
834 struct sockaddr_storage *sk;
835 struct logsrv *logsrv = NULL;
836 int port1, port2;
837 int cur_arg;
838
839 /*
840 * "no log": delete previous herited or defined syslog
841 * servers.
842 */
843 if (do_del) {
844 struct logsrv *back;
845
846 if (*(args[1]) != 0) {
847 memprintf(err, "'no log' does not expect arguments");
848 goto error;
849 }
850
851 list_for_each_entry_safe(logsrv, back, logsrvs, list) {
852 LIST_DEL(&logsrv->list);
853 free(logsrv);
854 }
855 return 1;
856 }
857
858 /*
859 * "log global": copy global.logrsvs linked list to the end of logsrvs
860 * list. But first, we check (logsrvs != global.logsrvs).
861 */
862 if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
863 if (logsrvs == &global.logsrvs) {
864 memprintf(err, "'global' is not supported for a global syslog server");
865 goto error;
866 }
867 list_for_each_entry(logsrv, &global.logsrvs, list) {
Christopher Faulet28ac0992018-03-26 16:09:19 +0200868 struct logsrv *node;
869
870 list_for_each_entry(node, logsrvs, list) {
871 if (node->ref == logsrv)
872 goto skip_logsrv;
873 }
874
875 node = malloc(sizeof(*node));
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200876 memcpy(node, logsrv, sizeof(struct logsrv));
Christopher Faulet28ac0992018-03-26 16:09:19 +0200877 node->ref = logsrv;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200878 LIST_INIT(&node->list);
879 LIST_ADDQ(logsrvs, &node->list);
Christopher Faulet28ac0992018-03-26 16:09:19 +0200880
881 skip_logsrv:
882 continue;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200883 }
884 return 1;
885 }
886
887 /*
888 * "log <address> ...: parse a syslog server line
889 */
890 if (*(args[1]) == 0 || *(args[2]) == 0) {
891 memprintf(err, "expects <address> and <facility> %s as arguments",
892 ((logsrvs == &global.logsrvs) ? "" : "or global"));
893 goto error;
894 }
895
Willy Tarreau5a32ecc2018-11-12 07:34:59 +0100896 /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */
897 if (strcmp(args[1], "stdout") == 0)
898 args[1] = "fd@1";
899 else if (strcmp(args[1], "stderr") == 0)
900 args[1] = "fd@2";
901
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200902 logsrv = calloc(1, sizeof(*logsrv));
903 if (!logsrv) {
904 memprintf(err, "out of memory");
905 goto error;
906 }
907
908 /* skip address for now, it will be parsed at the end */
909 cur_arg = 2;
910
911 /* just after the address, a length may be specified */
912 logsrv->maxlen = MAX_SYSLOG_LEN;
913 if (strcmp(args[cur_arg], "len") == 0) {
914 int len = atoi(args[cur_arg+1]);
915 if (len < 80 || len > 65535) {
916 memprintf(err, "invalid log length '%s', must be between 80 and 65535",
917 args[cur_arg+1]);
918 goto error;
919 }
920 logsrv->maxlen = len;
921 cur_arg += 2;
922 }
923 if (logsrv->maxlen > global.max_syslog_len)
924 global.max_syslog_len = logsrv->maxlen;
925
926 /* after the length, a format may be specified */
927 if (strcmp(args[cur_arg], "format") == 0) {
928 logsrv->format = get_log_format(args[cur_arg+1]);
929 if (logsrv->format < 0) {
930 memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
931 goto error;
932 }
933 cur_arg += 2;
934 }
935
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200936 if (strcmp(args[cur_arg], "sample") == 0) {
937 unsigned low, high;
938 char *p, *beg, *end, *smp_sz_str;
939 struct smp_log_range *smp_rgs = NULL;
940 size_t smp_rgs_sz = 0, smp_sz = 0, new_smp_sz;
941
942 p = args[cur_arg+1];
943 smp_sz_str = strchr(p, ':');
944 if (!smp_sz_str) {
945 memprintf(err, "Missing sample size");
946 goto error;
947 }
948
949 *smp_sz_str++ = '\0';
950
951 end = p + strlen(p);
952
953 while (p != end) {
954 if (!get_logsrv_smp_range(&low, &high, &p, err))
955 goto error;
956
957 if (smp_rgs && smp_log_ranges_overlap(smp_rgs, smp_rgs_sz, low, high, err))
958 goto error;
959
960 smp_rgs = my_realloc2(smp_rgs, (smp_rgs_sz + 1) * sizeof *smp_rgs);
961 if (!smp_rgs) {
962 memprintf(err, "out of memory error");
963 goto error;
964 }
965
966 smp_rgs[smp_rgs_sz].low = low;
967 smp_rgs[smp_rgs_sz].high = high;
968 smp_rgs[smp_rgs_sz].sz = high - low + 1;
969 smp_rgs[smp_rgs_sz].curr_idx = 0;
970 if (smp_rgs[smp_rgs_sz].high > smp_sz)
971 smp_sz = smp_rgs[smp_rgs_sz].high;
972 smp_rgs_sz++;
973 }
974
Tim Duesterhus21648002019-06-23 22:10:10 +0200975 if (smp_rgs == NULL) {
976 memprintf(err, "no sampling ranges given");
977 goto error;
978 }
979
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200980 beg = smp_sz_str;
981 end = beg + strlen(beg);
982 new_smp_sz = read_uint((const char **)&beg, end);
983 if (!new_smp_sz || beg != end) {
984 memprintf(err, "wrong sample size '%s' for sample range '%s'",
985 smp_sz_str, args[cur_arg+1]);
986 goto error;
987 }
988
989 if (new_smp_sz < smp_sz) {
990 memprintf(err, "sample size %zu should be greater or equal to "
991 "%zu the maximum of the high ranges limits",
992 new_smp_sz, smp_sz);
993 goto error;
994 }
995 smp_sz = new_smp_sz;
996
997 /* Let's order <smp_rgs> array. */
998 qsort(smp_rgs, smp_rgs_sz, sizeof(struct smp_log_range), smp_log_range_cmp);
999
1000 logsrv->lb.smp_rgs = smp_rgs;
1001 logsrv->lb.smp_rgs_sz = smp_rgs_sz;
1002 logsrv->lb.smp_sz = smp_sz;
1003
1004 cur_arg += 2;
1005 }
Frédéric Lécailled803e472019-04-25 07:42:09 +02001006 HA_SPIN_INIT(&logsrv->lock);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001007 /* parse the facility */
1008 logsrv->facility = get_log_facility(args[cur_arg]);
1009 if (logsrv->facility < 0) {
1010 memprintf(err, "unknown log facility '%s'", args[cur_arg]);
1011 goto error;
1012 }
1013 cur_arg++;
1014
1015 /* parse the max syslog level (default: debug) */
1016 logsrv->level = 7;
1017 if (*(args[cur_arg])) {
1018 logsrv->level = get_log_level(args[cur_arg]);
1019 if (logsrv->level < 0) {
1020 memprintf(err, "unknown optional log level '%s'", args[cur_arg]);
1021 goto error;
1022 }
1023 cur_arg++;
1024 }
1025
1026 /* parse the limit syslog level (default: emerg) */
1027 logsrv->minlvl = 0;
1028 if (*(args[cur_arg])) {
1029 logsrv->minlvl = get_log_level(args[cur_arg]);
1030 if (logsrv->minlvl < 0) {
1031 memprintf(err, "unknown optional minimum log level '%s'", args[cur_arg]);
1032 goto error;
1033 }
1034 cur_arg++;
1035 }
1036
1037 /* Too many args */
1038 if (*(args[cur_arg])) {
1039 memprintf(err, "cannot handle unexpected argument '%s'", args[cur_arg]);
1040 goto error;
1041 }
1042
1043 /* now, back to the address */
Willy Tarreauf3dc30f2019-08-30 14:18:44 +02001044 logsrv->type = LOG_TARGET_DGRAM;
Willy Tarreauc046d162019-08-30 15:24:59 +02001045 if (strncmp(args[1], "ring@", 5) == 0) {
Willy Tarreauc046d162019-08-30 15:24:59 +02001046 logsrv->addr.ss_family = AF_UNSPEC;
1047 logsrv->type = LOG_TARGET_BUFFER;
Emeric Brun99c453d2020-05-25 15:01:04 +02001048 logsrv->sink = NULL;
1049 logsrv->ring_name = strdup(args[1] + 5);
Willy Tarreauc046d162019-08-30 15:24:59 +02001050 goto done;
1051 }
1052
Willy Tarreauf3dc30f2019-08-30 14:18:44 +02001053 if (strncmp(args[1], "fd@", 3) == 0)
1054 logsrv->type = LOG_TARGET_FD;
1055
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001056 sk = str2sa_range(args[1], NULL, &port1, &port2, err, NULL, NULL, 1);
1057 if (!sk)
1058 goto error;
1059 logsrv->addr = *sk;
1060
1061 if (sk->ss_family == AF_INET || sk->ss_family == AF_INET6) {
1062 if (port1 != port2) {
1063 memprintf(err, "port ranges and offsets are not allowed in '%s'", args[1]);
1064 goto error;
1065 }
1066 logsrv->addr = *sk;
1067 if (!port1)
1068 set_host_port(&logsrv->addr, SYSLOG_PORT);
1069 }
Willy Tarreauc046d162019-08-30 15:24:59 +02001070 done:
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001071 LIST_ADDQ(logsrvs, &logsrv->list);
1072 return 1;
1073
1074 error:
1075 free(logsrv);
1076 return 0;
1077}
1078
1079
Christopher Fauletd4696382017-10-24 11:44:05 +02001080/* Generic function to display messages prefixed by a label */
1081static void print_message(const char *label, const char *fmt, va_list argp)
1082{
1083 struct tm tm;
1084 char *head, *msg;
1085
1086 head = msg = NULL;
1087
1088 get_localtime(date.tv_sec, &tm);
1089 memprintf(&head, "[%s] %03d/%02d%02d%02d (%d) : ",
1090 label, tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
1091 memvprintf(&msg, fmt, argp);
1092
Willy Tarreau869efd52019-11-15 15:16:57 +01001093 if (global.mode & MODE_STARTING) {
1094 if (unlikely(!startup_logs))
1095 startup_logs = ring_new(STARTUP_LOG_SIZE);
1096
1097 if (likely(startup_logs)) {
1098 struct ist m[2];
1099
1100 m[0] = ist(head);
1101 m[1] = ist(msg);
1102 /* trim the trailing '\n' */
1103 if (m[1].len > 0 && m[1].ptr[m[1].len - 1] == '\n')
1104 m[1].len--;
1105 ring_write(startup_logs, ~0, 0, 0, m, 2);
1106 }
1107 }
Christopher Fauletd4696382017-10-24 11:44:05 +02001108
1109 fprintf(stderr, "%s%s", head, msg);
1110 fflush(stderr);
1111
1112 free(head);
1113 free(msg);
1114}
1115
Willy Tarreaubaaee002006-06-26 02:48:02 +02001116/*
1117 * Displays the message on stderr with the date and pid. Overrides the quiet
1118 * mode during startup.
1119 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001120void ha_alert(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001121{
1122 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001123
1124 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
Willy Tarreaubb869862020-04-16 10:52:41 +02001125 if (!(warned & WARN_EXEC_PATH)) {
1126 const char *path = get_exec_path();
1127
1128 warned |= WARN_EXEC_PATH;
1129 ha_notice("haproxy version is %s\n", haproxy_version);
1130 if (path)
1131 ha_notice("path to executable is %s\n", path);
1132 }
Willy Tarreaubaaee002006-06-26 02:48:02 +02001133 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001134 print_message("ALERT", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001135 va_end(argp);
1136 }
1137}
1138
1139
1140/*
1141 * Displays the message on stderr with the date and pid.
1142 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001143void ha_warning(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001144{
1145 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001146
Willy Tarreaubebd2122020-04-15 16:06:11 +02001147 warned |= WARN_ANY;
1148
Willy Tarreaubaaee002006-06-26 02:48:02 +02001149 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1150 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001151 print_message("WARNING", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001152 va_end(argp);
1153 }
1154}
1155
1156/*
William Lallemand9c56a222018-11-21 18:04:52 +01001157 * Displays the message on stderr with the date and pid.
1158 */
1159void ha_notice(const char *fmt, ...)
1160{
1161 va_list argp;
1162
1163 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1164 va_start(argp, fmt);
1165 print_message("NOTICE", fmt, argp);
1166 va_end(argp);
1167 }
1168}
1169
1170/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001171 * Displays the message on <out> only if quiet mode is not set.
1172 */
Willy Tarreaub17916e2006-10-15 15:17:57 +02001173void qfprintf(FILE *out, const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001174{
1175 va_list argp;
1176
1177 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1178 va_start(argp, fmt);
1179 vfprintf(out, fmt, argp);
1180 fflush(out);
1181 va_end(argp);
1182 }
1183}
1184
1185/*
Dragan Dosen1322d092015-09-22 16:05:32 +02001186 * returns log format for <fmt> or -1 if not found.
1187 */
1188int get_log_format(const char *fmt)
1189{
1190 int format;
1191
1192 format = LOG_FORMATS - 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001193 while (format >= 0 && strcmp(log_formats[format].name, fmt))
Dragan Dosen1322d092015-09-22 16:05:32 +02001194 format--;
1195
1196 return format;
1197}
1198
1199/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001200 * returns log level for <lev> or -1 if not found.
1201 */
1202int get_log_level(const char *lev)
1203{
1204 int level;
1205
1206 level = NB_LOG_LEVELS - 1;
1207 while (level >= 0 && strcmp(log_levels[level], lev))
1208 level--;
1209
1210 return level;
1211}
1212
Willy Tarreaubaaee002006-06-26 02:48:02 +02001213/*
1214 * returns log facility for <fac> or -1 if not found.
1215 */
1216int get_log_facility(const char *fac)
1217{
1218 int facility;
1219
1220 facility = NB_LOG_FACILITIES - 1;
1221 while (facility >= 0 && strcmp(log_facilities[facility], fac))
1222 facility--;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001223
Willy Tarreaubaaee002006-06-26 02:48:02 +02001224 return facility;
1225}
1226
William Lallemanda1cc3812012-02-08 16:38:44 +01001227/*
Dragan Dosen835b9212016-02-12 13:23:03 +01001228 * Encode the string.
1229 *
1230 * When using the +E log format option, it will try to escape '"\]'
1231 * characters with '\' as prefix. The same prefix should not be used as
1232 * <escape>.
1233 */
1234static char *lf_encode_string(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001235 const char escape, const long *map,
Dragan Dosen835b9212016-02-12 13:23:03 +01001236 const char *string,
1237 struct logformat_node *node)
1238{
1239 if (node->options & LOG_OPT_ESC) {
1240 if (start < stop) {
1241 stop--; /* reserve one byte for the final '\0' */
1242 while (start < stop && *string != '\0') {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001243 if (!ha_bit_test((unsigned char)(*string), map)) {
1244 if (!ha_bit_test((unsigned char)(*string), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001245 *start++ = *string;
1246 else {
1247 if (start + 2 >= stop)
1248 break;
1249 *start++ = '\\';
1250 *start++ = *string;
1251 }
1252 }
1253 else {
1254 if (start + 3 >= stop)
1255 break;
1256 *start++ = escape;
1257 *start++ = hextab[(*string >> 4) & 15];
1258 *start++ = hextab[*string & 15];
1259 }
1260 string++;
1261 }
1262 *start = '\0';
1263 }
1264 }
1265 else {
1266 return encode_string(start, stop, escape, map, string);
1267 }
1268
1269 return start;
1270}
1271
1272/*
1273 * Encode the chunk.
1274 *
1275 * When using the +E log format option, it will try to escape '"\]'
1276 * characters with '\' as prefix. The same prefix should not be used as
1277 * <escape>.
1278 */
1279static char *lf_encode_chunk(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001280 const char escape, const long *map,
Willy Tarreau83061a82018-07-13 11:56:34 +02001281 const struct buffer *chunk,
Dragan Dosen835b9212016-02-12 13:23:03 +01001282 struct logformat_node *node)
1283{
1284 char *str, *end;
1285
1286 if (node->options & LOG_OPT_ESC) {
1287 if (start < stop) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001288 str = chunk->area;
1289 end = chunk->area + chunk->data;
Dragan Dosen835b9212016-02-12 13:23:03 +01001290
1291 stop--; /* reserve one byte for the final '\0' */
1292 while (start < stop && str < end) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001293 if (!ha_bit_test((unsigned char)(*str), map)) {
1294 if (!ha_bit_test((unsigned char)(*str), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001295 *start++ = *str;
1296 else {
1297 if (start + 2 >= stop)
1298 break;
1299 *start++ = '\\';
1300 *start++ = *str;
1301 }
1302 }
1303 else {
1304 if (start + 3 >= stop)
1305 break;
1306 *start++ = escape;
1307 *start++ = hextab[(*str >> 4) & 15];
1308 *start++ = hextab[*str & 15];
1309 }
1310 str++;
1311 }
1312 *start = '\0';
1313 }
1314 }
1315 else {
1316 return encode_chunk(start, stop, escape, map, chunk);
1317 }
1318
1319 return start;
1320}
1321
1322/*
William Lallemanda1cc3812012-02-08 16:38:44 +01001323 * Write a string in the log string
Dragan Dosen835b9212016-02-12 13:23:03 +01001324 * Take cares of quote and escape options
William Lallemanda1cc3812012-02-08 16:38:44 +01001325 *
Joseph Herlant85b40592018-11-15 12:10:04 -08001326 * Return the address of the \0 character, or NULL on error
William Lallemanda1cc3812012-02-08 16:38:44 +01001327 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001328char *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 +01001329{
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001330 if (size < 2)
1331 return NULL;
William Lallemanda1cc3812012-02-08 16:38:44 +01001332
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001333 if (node->options & LOG_OPT_QUOTE) {
1334 *(dst++) = '"';
1335 size--;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001336 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001337
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001338 if (src && len) {
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001339 if (++len > size)
1340 len = size;
Dragan Dosen835b9212016-02-12 13:23:03 +01001341 if (node->options & LOG_OPT_ESC) {
Dragan Dosen835b9212016-02-12 13:23:03 +01001342 char *ret;
1343
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001344 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
Dragan Dosen835b9212016-02-12 13:23:03 +01001345 if (ret == NULL || *ret != '\0')
1346 return NULL;
1347 len = ret - dst;
1348 }
1349 else {
Dragan Dosen835b9212016-02-12 13:23:03 +01001350 len = strlcpy2(dst, src, len);
1351 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001352
1353 size -= len;
1354 dst += len;
1355 }
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001356 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1357 if (size < 2)
1358 return NULL;
1359 *(dst++) = '-';
1360 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001361
1362 if (node->options & LOG_OPT_QUOTE) {
1363 if (size < 2)
1364 return NULL;
1365 *(dst++) = '"';
1366 }
1367
1368 *dst = '\0';
William Lallemandbddd4fd2012-02-27 11:23:10 +01001369 return dst;
William Lallemanda1cc3812012-02-08 16:38:44 +01001370}
1371
Willy Tarreau26ffa852018-09-05 15:23:10 +02001372static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001373{
1374 return lf_text_len(dst, src, size, size, node);
1375}
1376
William Lallemand5f232402012-04-05 18:02:55 +02001377/*
Joseph Herlant85b40592018-11-15 12:10:04 -08001378 * Write a IP address to the log string
Ilya Shipitsin856aabc2020-04-16 23:51:34 +05001379 * +X option write in hexadecimal notation, most significant byte on the left
William Lallemand5f232402012-04-05 18:02:55 +02001380 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001381char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001382{
1383 char *ret = dst;
1384 int iret;
1385 char pn[INET6_ADDRSTRLEN];
1386
1387 if (node->options & LOG_OPT_HEXA) {
Radek Zajic594c4562019-03-22 10:21:54 +00001388 unsigned char *addr = NULL;
1389 switch (sockaddr->sa_family) {
1390 case AF_INET:
1391 addr = (unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1392 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1393 break;
1394 case AF_INET6:
1395 addr = (unsigned char *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr;
1396 iret = snprintf(dst, size, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1397 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
1398 addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
1399 break;
1400 default:
1401 return NULL;
1402 }
William Lallemand5f232402012-04-05 18:02:55 +02001403 if (iret < 0 || iret > size)
1404 return NULL;
1405 ret += iret;
1406 } else {
1407 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1408 ret = lf_text(dst, pn, size, node);
1409 if (ret == NULL)
1410 return NULL;
1411 }
1412 return ret;
1413}
1414
1415/*
1416 * Write a port to the log
Ilya Shipitsin856aabc2020-04-16 23:51:34 +05001417 * +X option write in hexadecimal notation, most significant byte on the left
William Lallemand5f232402012-04-05 18:02:55 +02001418 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001419char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001420{
1421 char *ret = dst;
1422 int iret;
1423
1424 if (node->options & LOG_OPT_HEXA) {
1425 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1426 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1427 if (iret < 0 || iret > size)
1428 return NULL;
1429 ret += iret;
1430 } else {
1431 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1432 if (ret == NULL)
1433 return NULL;
1434 }
1435 return ret;
1436}
1437
Dragan Dosen1322d092015-09-22 16:05:32 +02001438/* Re-generate time-based part of the syslog header in RFC3164 format at
1439 * the beginning of logheader once a second and return the pointer to the
1440 * first character after it.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001441 */
Emeric Brunbd163812020-05-06 14:33:46 +02001442char *update_log_hdr(const time_t time)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001443{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001444 static THREAD_LOCAL long tvsec;
Willy Tarreau83061a82018-07-13 11:56:34 +02001445 static THREAD_LOCAL struct buffer host = { };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001446 static THREAD_LOCAL int sep = 0;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001447
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001448 if (unlikely(time != tvsec || logheader_end == NULL)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +02001449 /* this string is rebuild only once a second */
Willy Tarreaufe944602007-10-25 10:34:16 +02001450 struct tm tm;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001451 int hdr_len;
Willy Tarreaufe944602007-10-25 10:34:16 +02001452
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001453 tvsec = time;
Willy Tarreaufe944602007-10-25 10:34:16 +02001454 get_localtime(tvsec, &tm);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001455
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001456 if (unlikely(global.log_send_hostname != host.area)) {
1457 host.area = global.log_send_hostname;
1458 host.data = host.area ? strlen(host.area) : 0;
1459 sep = host.data ? 1 : 0;
Dragan Dosen43885c72015-10-01 13:18:13 +02001460 }
1461
Dragan Dosen59cee972015-09-19 22:09:02 +02001462 hdr_len = snprintf(logheader, global.max_syslog_len,
Dragan Dosen43885c72015-10-01 13:18:13 +02001463 "<<<<>%s %2d %02d:%02d:%02d %.*s%*s",
Willy Tarreaufe944602007-10-25 10:34:16 +02001464 monthname[tm.tm_mon],
Dragan Dosen43885c72015-10-01 13:18:13 +02001465 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001466 (int)host.data, host.area, sep, "");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001467 /* WARNING: depending upon implementations, snprintf may return
1468 * either -1 or the number of bytes that would be needed to store
1469 * the total message. In both cases, we must adjust it.
1470 */
Willy Tarreau18324f52014-06-27 18:10:07 +02001471 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1472 hdr_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001473
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001474 logheader_end = logheader + hdr_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001475 }
1476
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001477 logheader_end[0] = 0; // ensure we get rid of any previous attempt
Willy Tarreau094af4e2015-01-07 15:03:42 +01001478
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001479 return logheader_end;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001480}
1481
Dragan Dosen1322d092015-09-22 16:05:32 +02001482/* Re-generate time-based part of the syslog header in RFC5424 format at
1483 * the beginning of logheader_rfc5424 once a second and return the pointer
1484 * to the first character after it.
1485 */
Emeric Brun9f9b22c2020-07-02 16:16:59 +02001486char *update_log_hdr_rfc5424(const time_t time, const suseconds_t frac)
Dragan Dosen1322d092015-09-22 16:05:32 +02001487{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001488 static THREAD_LOCAL long tvsec;
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001489 const char *gmt_offset;
Emeric Brun9f9b22c2020-07-02 16:16:59 +02001490 char c;
Dragan Dosen1322d092015-09-22 16:05:32 +02001491
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001492 if (unlikely(time != tvsec || logheader_rfc5424_end == NULL)) {
Dragan Dosen1322d092015-09-22 16:05:32 +02001493 /* this string is rebuild only once a second */
1494 struct tm tm;
1495 int hdr_len;
1496
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001497 tvsec = time;
Dragan Dosen1322d092015-09-22 16:05:32 +02001498 get_localtime(tvsec, &tm);
Benoit GARNIERe2e5bde2016-03-27 03:04:16 +02001499 gmt_offset = get_gmt_offset(time, &tm);
Dragan Dosen1322d092015-09-22 16:05:32 +02001500
1501 hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
Emeric Brun9f9b22c2020-07-02 16:16:59 +02001502 "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d.000000%.3s:%.2s %s ",
Dragan Dosen1322d092015-09-22 16:05:32 +02001503 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
Dragan Dosen17def462015-10-09 21:31:43 +02001504 tm.tm_hour, tm.tm_min, tm.tm_sec,
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001505 gmt_offset, gmt_offset+3,
Dragan Dosen43885c72015-10-01 13:18:13 +02001506 global.log_send_hostname ? global.log_send_hostname : hostname);
Dragan Dosen1322d092015-09-22 16:05:32 +02001507 /* WARNING: depending upon implementations, snprintf may return
1508 * either -1 or the number of bytes that would be needed to store
1509 * the total message. In both cases, we must adjust it.
1510 */
1511 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1512 hdr_len = global.max_syslog_len;
1513
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001514 logheader_rfc5424_end = logheader_rfc5424 + hdr_len;
Dragan Dosen1322d092015-09-22 16:05:32 +02001515 }
1516
Emeric Brun9f9b22c2020-07-02 16:16:59 +02001517 /* utoa_pad add a trailing '\0' so we save the char to restore */
1518 c = logheader_rfc5424[33];
1519 utoa_pad(frac, logheader_rfc5424 + 27, 7);
1520 logheader_rfc5424[33] = c;
1521
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001522 logheader_rfc5424_end[0] = 0; // ensure we get rid of any previous attempt
Dragan Dosen1322d092015-09-22 16:05:32 +02001523
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001524 return logheader_rfc5424_end;
Dragan Dosen1322d092015-09-22 16:05:32 +02001525}
1526
William Lallemand2a4a44f2012-02-06 16:00:33 +01001527/*
Dragan Dosen59cee972015-09-19 22:09:02 +02001528 * This function sends the syslog message using a printf format string. It
1529 * expects an LF-terminated message.
William Lallemand2a4a44f2012-02-06 16:00:33 +01001530 */
1531void send_log(struct proxy *p, int level, const char *format, ...)
1532{
1533 va_list argp;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001534 int data_len;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001535
Willy Tarreau8c97ab52015-01-15 16:29:53 +01001536 if (level < 0 || format == NULL || logline == NULL)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001537 return;
1538
William Lallemand2a4a44f2012-02-06 16:00:33 +01001539 va_start(argp, format);
Dragan Dosen59cee972015-09-19 22:09:02 +02001540 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
Willy Tarreau18324f52014-06-27 18:10:07 +02001541 if (data_len < 0 || data_len > global.max_syslog_len)
1542 data_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001543 va_end(argp);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001544
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001545 __send_log((p ? &p->logsrvs : NULL), (p ? &p->log_tag : NULL), level,
1546 logline, data_len, default_rfc5424_sd_log_format, 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001547}
1548
1549/*
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001550 * This function sends a syslog message to <logsrv>.
1551 * <pid_str> is the string to be used for the PID of the caller, <pid_size> is length.
1552 * Same thing for <sd> and <sd_size> which are used for the structured-data part
1553 * in RFC5424 formatted syslog messages, and <tag_str> and <tag_size> the syslog tag.
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001554 * It overrides the last byte of the message vector with an LF character.
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001555 * Does not return any error,
William Lallemand2a4a44f2012-02-06 16:00:33 +01001556 */
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001557static inline void __do_send_log(struct logsrv *logsrv, int nblogger, char *pid_str, size_t pid_size,
1558 int level, char *message, size_t size, char *sd, size_t sd_size,
1559 char *tag_str, size_t tag_size)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001560{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001561 static THREAD_LOCAL struct iovec iovec[NB_MSG_IOVEC_ELEMENTS] = { };
1562 static THREAD_LOCAL struct msghdr msghdr = {
1563 //.msg_iov = iovec,
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001564 .msg_iovlen = NB_MSG_IOVEC_ELEMENTS
1565 };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001566 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1567 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
1568 static THREAD_LOCAL char *dataptr = NULL;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001569 time_t time = date.tv_sec;
Emeric Brune709e1e2020-05-06 17:23:59 +02001570 char *hdr, *hdr_ptr = NULL;
Dragan Dosen59cee972015-09-19 22:09:02 +02001571 size_t hdr_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001572 int fac_level;
1573 int *plogfd;
1574 char *pid_sep1 = "", *pid_sep2 = "";
1575 char logheader_short[3];
1576 int sent;
1577 int maxlen;
1578 int hdr_max = 0;
1579 int tag_max = 0;
1580 int pid_sep1_max = 0;
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001581 int pid_max = 0;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001582 int pid_sep2_max = 0;
1583 int sd_max = 0;
1584 int max = 0;
Christopher Fauletf8188c62017-06-02 16:20:16 +02001585
1586 msghdr.msg_iov = iovec;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001587
1588 dataptr = message;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001589
Emeric Brunfa9d7802020-05-28 14:21:33 +02001590 /* historically some messages used to already contain the trailing LF
1591 * or Zero. Let's remove all trailing LF or Zero
1592 */
1593 while (size && ((dataptr[size-1] == '\n' || (dataptr[size-1] == 0))))
Emeric Brun9e8ea0a2020-05-12 19:33:15 +02001594 size--;
1595
Willy Tarreauf3dc30f2019-08-30 14:18:44 +02001596 if (logsrv->type == LOG_TARGET_FD) {
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001597 /* the socket's address is a file descriptor */
1598 plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
Robert Tsai81ae1952007-12-05 10:47:29 +01001599 }
Willy Tarreauc046d162019-08-30 15:24:59 +02001600 else if (logsrv->type == LOG_TARGET_BUFFER) {
1601 plogfd = NULL;
Emeric Brune709e1e2020-05-06 17:23:59 +02001602 goto send;
Willy Tarreauc046d162019-08-30 15:24:59 +02001603 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001604 else if (logsrv->addr.ss_family == AF_UNIX)
1605 plogfd = &logfdunix;
1606 else
1607 plogfd = &logfdinet;
Robert Tsai81ae1952007-12-05 10:47:29 +01001608
Willy Tarreauc046d162019-08-30 15:24:59 +02001609 if (plogfd && unlikely(*plogfd < 0)) {
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001610 /* socket not successfully initialized yet */
1611 if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1612 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
1613 static char once;
William Lallemand0f99e342011-10-12 17:50:54 +02001614
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001615 if (!once) {
1616 once = 1; /* note: no need for atomic ops here */
1617 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
1618 nblogger, strerror(errno), errno);
1619 }
1620 return;
1621 } else {
1622 /* we don't want to receive anything on this socket */
1623 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1624 /* does nothing under Linux, maybe needed for others */
1625 shutdown(*plogfd, SHUT_RD);
1626 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1627 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001628 }
1629
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001630 switch (logsrv->format) {
1631 case LOG_FORMAT_RFC3164:
1632 hdr = logheader;
1633 hdr_ptr = update_log_hdr(time);
1634 break;
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001635
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001636 case LOG_FORMAT_RFC5424:
1637 hdr = logheader_rfc5424;
Emeric Brun9f9b22c2020-07-02 16:16:59 +02001638 hdr_ptr = update_log_hdr_rfc5424(time, date.tv_usec);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001639 sd_max = sd_size; /* the SD part allowed only in RFC5424 */
1640 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001641
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001642 case LOG_FORMAT_SHORT:
1643 /* all fields are known, skip the header generation */
1644 hdr = logheader_short;
1645 hdr[0] = '<';
1646 hdr[1] = '0' + MAX(level, logsrv->minlvl);
1647 hdr[2] = '>';
1648 hdr_ptr = hdr;
1649 hdr_max = 3;
1650 maxlen = logsrv->maxlen - hdr_max;
Emeric Brun9e8ea0a2020-05-12 19:33:15 +02001651 max = MIN(size, maxlen - 1);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001652 goto send;
Willy Tarreau204e3f12018-12-15 15:48:48 +01001653
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001654 case LOG_FORMAT_RAW:
1655 /* all fields are known, skip the header generation */
1656 hdr_ptr = hdr = "";
1657 hdr_max = 0;
1658 maxlen = logsrv->maxlen;
Emeric Brun9e8ea0a2020-05-12 19:33:15 +02001659 max = MIN(size, maxlen - 1);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001660 goto send;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001661
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001662 default:
1663 return; /* must never happen */
1664 }
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001665
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001666 hdr_size = hdr_ptr - hdr;
Dragan Dosen1322d092015-09-22 16:05:32 +02001667
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001668 /* For each target, we may have a different facility.
1669 * We can also have a different log level for each message.
1670 * This induces variations in the message header length.
1671 * Since we don't want to recompute it each time, nor copy it every
1672 * time, we only change the facility in the pre-computed header,
1673 * and we change the pointer to the header accordingly.
1674 */
1675 fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
1676 hdr_ptr = hdr + 3; /* last digit of the log level */
1677 do {
1678 *hdr_ptr = '0' + fac_level % 10;
1679 fac_level /= 10;
1680 hdr_ptr--;
1681 } while (fac_level && hdr_ptr > hdr);
1682 *hdr_ptr = '<';
Dragan Dosen1322d092015-09-22 16:05:32 +02001683
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001684 hdr_max = hdr_size - (hdr_ptr - hdr);
Willy Tarreaue8746a02018-11-12 08:45:00 +01001685
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001686 /* time-based header */
1687 if (unlikely(hdr_size >= logsrv->maxlen)) {
1688 hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
1689 sd_max = 0;
1690 goto send;
1691 }
Willy Tarreauc1b06452018-11-12 11:57:56 +01001692
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001693 maxlen = logsrv->maxlen - hdr_max;
Dragan Dosen1322d092015-09-22 16:05:32 +02001694
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001695 /* tag */
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001696 tag_max = tag_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001697 if (unlikely(tag_max >= maxlen)) {
1698 tag_max = maxlen - 1;
1699 sd_max = 0;
1700 goto send;
1701 }
Dragan Dosen1322d092015-09-22 16:05:32 +02001702
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001703 maxlen -= tag_max;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001704
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001705 /* first pid separator */
1706 pid_sep1_max = log_formats[logsrv->format].pid.sep1.data;
1707 if (unlikely(pid_sep1_max >= maxlen)) {
1708 pid_sep1_max = maxlen - 1;
1709 sd_max = 0;
1710 goto send;
1711 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001712
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001713 pid_sep1 = log_formats[logsrv->format].pid.sep1.area;
1714 maxlen -= pid_sep1_max;
Dragan Dosen59cee972015-09-19 22:09:02 +02001715
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001716 /* pid */
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001717 pid_max = pid_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001718 if (unlikely(pid_size >= maxlen)) {
1719 pid_size = maxlen - 1;
1720 sd_max = 0;
1721 goto send;
1722 }
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001723
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001724 maxlen -= pid_size;
Dragan Dosen43885c72015-10-01 13:18:13 +02001725
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001726 /* second pid separator */
1727 pid_sep2_max = log_formats[logsrv->format].pid.sep2.data;
1728 if (unlikely(pid_sep2_max >= maxlen)) {
1729 pid_sep2_max = maxlen - 1;
1730 sd_max = 0;
1731 goto send;
1732 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001733
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001734 pid_sep2 = log_formats[logsrv->format].pid.sep2.area;
1735 maxlen -= pid_sep2_max;
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001736
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001737 /* structured-data */
1738 if (sd_max >= maxlen) {
1739 sd_max = maxlen - 1;
1740 goto send;
1741 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001742
Emeric Brun9e8ea0a2020-05-12 19:33:15 +02001743 max = MIN(size, maxlen - sd_max - 1);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001744send:
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001745 if (logsrv->addr.ss_family == AF_UNSPEC) {
Willy Tarreauc046d162019-08-30 15:24:59 +02001746 /* the target is a file descriptor or a ring buffer */
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001747 struct ist msg[7];
1748
Emeric Brune709e1e2020-05-06 17:23:59 +02001749 if (logsrv->type == LOG_TARGET_BUFFER) {
1750 msg[0] = ist2(message, MIN(size, logsrv->maxlen));
1751 msg[1] = ist2(tag_str, tag_size);
1752 msg[2] = ist2(pid_str, pid_size);
1753 msg[3] = ist2(sd, sd_size);
1754 sent = sink_write(logsrv->sink, msg, 1, level, logsrv->facility, &msg[1], &msg[2], &msg[3]);
1755 }
1756 else /* LOG_TARGET_FD */ {
1757 msg[0] = ist2(hdr_ptr, hdr_max);
1758 msg[1] = ist2(tag_str, tag_max);
1759 msg[2] = ist2(pid_sep1, pid_sep1_max);
1760 msg[3] = ist2(pid_str, pid_max);
1761 msg[4] = ist2(pid_sep2, pid_sep2_max);
1762 msg[5] = ist2(sd, sd_max);
1763 msg[6] = ist2(dataptr, max);
Willy Tarreauc046d162019-08-30 15:24:59 +02001764 sent = fd_write_frag_line(*plogfd, ~0, NULL, 0, msg, 7, 1);
Emeric Brune709e1e2020-05-06 17:23:59 +02001765 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001766 }
1767 else {
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001768 iovec[0].iov_base = hdr_ptr;
1769 iovec[0].iov_len = hdr_max;
1770 iovec[1].iov_base = tag_str;
1771 iovec[1].iov_len = tag_max;
1772 iovec[2].iov_base = pid_sep1;
1773 iovec[2].iov_len = pid_sep1_max;
1774 iovec[3].iov_base = pid_str;
1775 iovec[3].iov_len = pid_max;
1776 iovec[4].iov_base = pid_sep2;
1777 iovec[4].iov_len = pid_sep2_max;
1778 iovec[5].iov_base = sd;
1779 iovec[5].iov_len = sd_max;
1780 iovec[6].iov_base = dataptr;
1781 iovec[6].iov_len = max;
1782 iovec[7].iov_base = "\n"; /* insert a \n at the end of the message */
1783 iovec[7].iov_len = 1;
1784
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001785 msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
1786 msghdr.msg_namelen = get_addr_len(&logsrv->addr);
Dragan Dosen43885c72015-10-01 13:18:13 +02001787
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001788 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1789 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001790
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001791 if (sent < 0) {
1792 static char once;
Dragan Dosen43885c72015-10-01 13:18:13 +02001793
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001794 if (errno == EAGAIN)
1795 _HA_ATOMIC_ADD(&dropped_logs, 1);
1796 else if (!once) {
1797 once = 1; /* note: no need for atomic ops here */
1798 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
1799 nblogger, strerror(errno), errno);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001800 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001801 }
1802}
Dragan Dosen59cee972015-09-19 22:09:02 +02001803
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001804/*
1805 * This function sends a syslog message.
1806 * It doesn't care about errors nor does it report them.
1807 * The arguments <sd> and <sd_size> are used for the structured-data part
1808 * in RFC5424 formatted syslog messages.
1809 */
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001810void __send_log(struct list *logsrvs, struct buffer *tag, int level,
1811 char *message, size_t size, char *sd, size_t sd_size)
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001812{
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001813 struct logsrv *logsrv;
1814 int nblogger;
1815 static THREAD_LOCAL int curr_pid;
1816 static THREAD_LOCAL char pidstr[100];
1817 static THREAD_LOCAL struct buffer pid;
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001818
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001819 if (logsrvs == NULL) {
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001820 if (!LIST_ISEMPTY(&global.logsrvs)) {
1821 logsrvs = &global.logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001822 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001823 }
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001824 if (!tag || !tag->area)
1825 tag = &global.log_tag;
Willy Tarreau18324f52014-06-27 18:10:07 +02001826
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001827 if (!logsrvs || LIST_ISEMPTY(logsrvs))
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001828 return;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001829
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001830 if (unlikely(curr_pid != getpid())) {
1831 curr_pid = getpid();
1832 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1833 chunk_initstr(&pid, pidstr);
1834 }
1835
1836 /* Send log messages to syslog server. */
1837 nblogger = 0;
1838 list_for_each_entry(logsrv, logsrvs, list) {
Frédéric Lécailled803e472019-04-25 07:42:09 +02001839 static THREAD_LOCAL int in_range = 1;
1840
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001841 /* we can filter the level of the messages that are sent to each logger */
1842 if (level > logsrv->level)
1843 continue;
1844
Frédéric Lécailled803e472019-04-25 07:42:09 +02001845 if (logsrv->lb.smp_rgs) {
1846 struct smp_log_range *curr_rg;
1847
1848 HA_SPIN_LOCK(LOGSRV_LOCK, &logsrv->lock);
1849 curr_rg = &logsrv->lb.smp_rgs[logsrv->lb.curr_rg];
1850 in_range = in_smp_log_range(curr_rg, logsrv->lb.curr_idx);
1851 if (in_range) {
1852 /* Let's consume this range. */
1853 curr_rg->curr_idx = (curr_rg->curr_idx + 1) % curr_rg->sz;
1854 if (!curr_rg->curr_idx) {
1855 /* If consumed, let's select the next range. */
1856 logsrv->lb.curr_rg = (logsrv->lb.curr_rg + 1) % logsrv->lb.smp_rgs_sz;
1857 }
1858 }
1859 logsrv->lb.curr_idx = (logsrv->lb.curr_idx + 1) % logsrv->lb.smp_sz;
1860 HA_SPIN_UNLOCK(LOGSRV_LOCK, &logsrv->lock);
1861 }
1862 if (in_range)
1863 __do_send_log(logsrv, ++nblogger, pid.area, pid.data, level,
1864 message, size, sd, sd_size, tag->area, tag->data);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001865 }
1866}
1867
1868
Willy Tarreauc89ccb62012-04-05 21:18:22 +02001869const 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 +01001870const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1871 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1872 Set-cookie Updated, unknown, unknown */
1873
William Lallemand1d705562012-03-12 12:46:41 +01001874/*
1875 * try to write a character if there is enough space, or goto out
1876 */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001877#define LOGCHAR(x) do { \
William Lallemand1d705562012-03-12 12:46:41 +01001878 if (tmplog < dst + maxsize - 1) { \
William Lallemandbddd4fd2012-02-27 11:23:10 +01001879 *(tmplog++) = (x); \
1880 } else { \
1881 goto out; \
1882 } \
1883 } while(0)
1884
Dragan Dosen835b9212016-02-12 13:23:03 +01001885
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001886/* Initializes some log data at boot */
1887static void init_log()
Dragan Dosen835b9212016-02-12 13:23:03 +01001888{
1889 char *tmp;
Willy Tarreaue10cd482018-09-10 18:16:53 +02001890 int i;
Dragan Dosen835b9212016-02-12 13:23:03 +01001891
1892 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1893 * inside PARAM-VALUE should be escaped with '\' as prefix.
1894 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1895 * details.
1896 */
1897 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1898
1899 tmp = "\"\\]";
1900 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001901 ha_bit_set(*tmp, rfc5424_escape_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001902 tmp++;
1903 }
Willy Tarreaue10cd482018-09-10 18:16:53 +02001904
1905 /* initialize the log header encoding map : '{|}"#' should be encoded with
1906 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1907 * URL encoding only requires '"', '#' to be encoded as well as non-
1908 * printable characters above.
1909 */
1910 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1911 memset(url_encode_map, 0, sizeof(url_encode_map));
1912 for (i = 0; i < 32; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001913 ha_bit_set(i, hdr_encode_map);
1914 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001915 }
1916 for (i = 127; i < 256; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001917 ha_bit_set(i, hdr_encode_map);
1918 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001919 }
1920
1921 tmp = "\"#{|}";
1922 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001923 ha_bit_set(*tmp, hdr_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001924 tmp++;
1925 }
1926
1927 tmp = "\"#";
1928 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001929 ha_bit_set(*tmp, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001930 tmp++;
1931 }
1932
1933 /* initialize the http header encoding map. The draft httpbis define the
1934 * header content as:
1935 *
1936 * HTTP-message = start-line
1937 * *( header-field CRLF )
1938 * CRLF
1939 * [ message-body ]
1940 * header-field = field-name ":" OWS field-value OWS
1941 * field-value = *( field-content / obs-fold )
1942 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1943 * obs-fold = CRLF 1*( SP / HTAB )
1944 * field-vchar = VCHAR / obs-text
1945 * VCHAR = %x21-7E
1946 * obs-text = %x80-FF
1947 *
1948 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1949 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
Joseph Herlant85b40592018-11-15 12:10:04 -08001950 * "obs-fold" is voluntarily forgotten because haproxy remove this.
Willy Tarreaue10cd482018-09-10 18:16:53 +02001951 */
1952 memset(http_encode_map, 0, sizeof(http_encode_map));
1953 for (i = 0x00; i <= 0x08; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001954 ha_bit_set(i, http_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001955 for (i = 0x0a; i <= 0x1f; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001956 ha_bit_set(i, http_encode_map);
1957 ha_bit_set(0x7f, http_encode_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001958}
William Lallemand1d705562012-03-12 12:46:41 +01001959
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001960INITCALL0(STG_PREPARE, init_log);
1961
Christopher Faulet0132d062017-07-26 15:33:35 +02001962/* Initialize log buffers used for syslog messages */
1963int init_log_buffers()
1964{
1965 logheader = my_realloc2(logheader, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001966 logheader_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001967 logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001968 logheader_rfc5424_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001969 logline = my_realloc2(logline, global.max_syslog_len + 1);
1970 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
1971 if (!logheader || !logline_rfc5424 || !logline || !logline_rfc5424)
1972 return 0;
1973 return 1;
1974}
1975
1976/* Deinitialize log buffers used for syslog messages */
1977void deinit_log_buffers()
1978{
1979 free(logheader);
1980 free(logheader_rfc5424);
1981 free(logline);
1982 free(logline_rfc5424);
Willy Tarreau869efd52019-11-15 15:16:57 +01001983 ring_free(_HA_ATOMIC_XCHG(&startup_logs, NULL));
Christopher Faulet0132d062017-07-26 15:33:35 +02001984 logheader = NULL;
1985 logheader_rfc5424 = NULL;
1986 logline = NULL;
1987 logline_rfc5424 = NULL;
1988}
1989
Willy Tarreaudf974472012-12-28 02:44:01 +01001990/* Builds a log line in <dst> based on <list_format>, and stops before reaching
1991 * <maxsize> characters. Returns the size of the output string in characters,
1992 * not counting the trailing zero which is always added if the resulting size
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001993 * is not zero. It requires a valid session and optionally a stream. If the
1994 * stream is NULL, default values will be assumed for the stream part.
Willy Tarreaudf974472012-12-28 02:44:01 +01001995 */
Willy Tarreau43c538e2018-09-05 14:58:15 +02001996int 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 +02001997{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02001998 struct proxy *fe = sess->fe;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001999 struct proxy *be;
2000 struct http_txn *txn;
2001 const struct strm_logs *logs;
Willy Tarreau8fa99842019-07-17 11:47:11 +02002002 struct connection *be_conn;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002003 unsigned int s_flags;
2004 unsigned int uniq_id;
Willy Tarreau83061a82018-07-13 11:56:34 +02002005 struct buffer chunk;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002006 char *uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002007 char *spc;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002008 char *qmark;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002009 char *end;
Willy Tarreaufe944602007-10-25 10:34:16 +02002010 struct tm tm;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002011 int t_request;
2012 int hdr;
2013 int last_isspace = 1;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002014 int nspaces = 0;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01002015 char *tmplog;
William Lallemand1d705562012-03-12 12:46:41 +01002016 char *ret;
2017 int iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002018 struct logformat_node *tmp;
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002019 struct timeval tv;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002020 struct strm_logs tmp_strm_log;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002021
William Lallemandbddd4fd2012-02-27 11:23:10 +01002022 /* FIXME: let's limit ourselves to frontend logging for now. */
Willy Tarreaubaaee002006-06-26 02:48:02 +02002023
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002024 if (likely(s)) {
2025 be = s->be;
2026 txn = s->txn;
2027 be_conn = cs_conn(objt_cs(s->si[1].end));
2028 s_flags = s->flags;
2029 uniq_id = s->uniq_id;
2030 logs = &s->logs;
2031 } else {
2032 /* we have no stream so we first need to initialize a few
2033 * things that are needed later. We do increment the request
2034 * ID so that it's uniquely assigned to this request just as
2035 * if the request had reached the point of being processed.
2036 * A request error is reported as it's the only element we have
2037 * here and which justifies emitting such a log.
2038 */
2039 be = fe;
2040 txn = NULL;
2041 be_conn = NULL;
2042 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002043 uniq_id = _HA_ATOMIC_XADD(&global.req_count, 1);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002044
2045 /* prepare a valid log structure */
2046 tmp_strm_log.tv_accept = sess->tv_accept;
2047 tmp_strm_log.accept_date = sess->accept_date;
2048 tmp_strm_log.t_handshake = sess->t_handshake;
2049 tmp_strm_log.t_idle = tv_ms_elapsed(&sess->tv_accept, &now) - sess->t_handshake;
2050 tv_zero(&tmp_strm_log.tv_request);
2051 tmp_strm_log.t_queue = -1;
2052 tmp_strm_log.t_connect = -1;
2053 tmp_strm_log.t_data = -1;
2054 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
2055 tmp_strm_log.bytes_in = 0;
2056 tmp_strm_log.bytes_out = 0;
2057 tmp_strm_log.prx_queue_pos = 0;
2058 tmp_strm_log.srv_queue_pos = 0;
2059
2060 logs = &tmp_strm_log;
2061 }
2062
William Lallemandbddd4fd2012-02-27 11:23:10 +01002063 t_request = -1;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002064 if (tv_isge(&logs->tv_request, &logs->tv_accept))
2065 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
William Lallemandbddd4fd2012-02-27 11:23:10 +01002066
William Lallemand1d705562012-03-12 12:46:41 +01002067 tmplog = dst;
Willy Tarreauc9bd0cc2009-05-10 11:57:02 +02002068
William Lallemandbddd4fd2012-02-27 11:23:10 +01002069 /* fill logbuffer */
William Lallemand1d705562012-03-12 12:46:41 +01002070 if (LIST_ISEMPTY(list_format))
2071 return 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002072
William Lallemand1d705562012-03-12 12:46:41 +01002073 list_for_each_entry(tmp, list_format, list) {
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002074 struct connection *conn;
Willy Tarreau4f653562012-10-12 19:48:16 +02002075 const char *src = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +01002076 struct sample *key;
Willy Tarreau83061a82018-07-13 11:56:34 +02002077 const struct buffer empty = { };
William Lallemandbddd4fd2012-02-27 11:23:10 +01002078
Willy Tarreauc8368452012-12-21 00:09:23 +01002079 switch (tmp->type) {
William Lallemand1d705562012-03-12 12:46:41 +01002080 case LOG_FMT_SEPARATOR:
William Lallemandbddd4fd2012-02-27 11:23:10 +01002081 if (!last_isspace) {
2082 LOGCHAR(' ');
2083 last_isspace = 1;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002084 }
2085 break;
2086
William Lallemand1d705562012-03-12 12:46:41 +01002087 case LOG_FMT_TEXT: // text
William Lallemandbddd4fd2012-02-27 11:23:10 +01002088 src = tmp->arg;
William Lallemand5f232402012-04-05 18:02:55 +02002089 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002090 if (iret == 0)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002091 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002092 tmplog += iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002093 last_isspace = 0;
2094 break;
2095
Willy Tarreauc8368452012-12-21 00:09:23 +01002096 case LOG_FMT_EXPR: // sample expression, may be request or response
2097 key = NULL;
Christopher Faulet5f940702020-04-06 10:40:02 +02002098 if (tmp->options & LOG_OPT_REQ_CAP)
Adis Nezirovic79beb242015-07-06 15:41:02 +02002099 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
Christopher Faulet5f940702020-04-06 10:40:02 +02002100 if (!key && (tmp->options & LOG_OPT_RES_CAP))
Adis Nezirovic79beb242015-07-06 15:41:02 +02002101 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 +01002102 if (tmp->options & LOG_OPT_HTTP)
Dragan Dosen835b9212016-02-12 13:23:03 +01002103 ret = lf_encode_chunk(tmplog, dst + maxsize,
2104 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01002105 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002106 ret = lf_text_len(tmplog,
2107 key ? key->data.u.str.area : NULL,
2108 key ? key->data.u.str.data : 0,
2109 dst + maxsize - tmplog,
2110 tmp);
Willy Tarreauc8368452012-12-21 00:09:23 +01002111 if (ret == 0)
2112 goto out;
2113 tmplog = ret;
2114 last_isspace = 0;
2115 break;
2116
Willy Tarreau2beef582012-12-20 17:22:52 +01002117 case LOG_FMT_CLIENTIP: // %ci
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002118 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002119 if (conn && conn_get_src(conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002120 ret = lf_ip(tmplog, (struct sockaddr *)conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002121 else
2122 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002123 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002124 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002125 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002126 last_isspace = 0;
2127 break;
2128
Willy Tarreau2beef582012-12-20 17:22:52 +01002129 case LOG_FMT_CLIENTPORT: // %cp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002130 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002131 if (conn && conn_get_src(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002132 if (conn->src->ss_family == AF_UNIX) {
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002133 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002134 } else {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002135 ret = lf_port(tmplog, (struct sockaddr *)conn->src,
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002136 dst + maxsize - tmplog, tmp);
2137 }
William Lallemand5f232402012-04-05 18:02:55 +02002138 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002139 else
2140 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2141
William Lallemand5f232402012-04-05 18:02:55 +02002142 if (ret == NULL)
2143 goto out;
2144 tmplog = ret;
2145 last_isspace = 0;
2146 break;
2147
Willy Tarreau2beef582012-12-20 17:22:52 +01002148 case LOG_FMT_FRONTENDIP: // %fi
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002149 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002150 if (conn && conn_get_dst(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002151 ret = lf_ip(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002152 }
2153 else
2154 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2155
William Lallemand1d705562012-03-12 12:46:41 +01002156 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002157 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002158 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002159 last_isspace = 0;
2160 break;
2161
Willy Tarreau2beef582012-12-20 17:22:52 +01002162 case LOG_FMT_FRONTENDPORT: // %fp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002163 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002164 if (conn && conn_get_dst(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002165 if (conn->dst->ss_family == AF_UNIX)
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002166 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002167 else
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002168 ret = lf_port(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
William Lallemand5f232402012-04-05 18:02:55 +02002169 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002170 else
2171 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2172
William Lallemand5f232402012-04-05 18:02:55 +02002173 if (ret == NULL)
2174 goto out;
2175 tmplog = ret;
2176 last_isspace = 0;
2177 break;
2178
Willy Tarreau2beef582012-12-20 17:22:52 +01002179 case LOG_FMT_BACKENDIP: // %bi
Willy Tarreau8fa99842019-07-17 11:47:11 +02002180 if (be_conn && conn_get_src(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002181 ret = lf_ip(tmplog, (const struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002182 else
2183 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2184
William Lallemand1d705562012-03-12 12:46:41 +01002185 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002186 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002187 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002188 last_isspace = 0;
2189 break;
2190
Willy Tarreau2beef582012-12-20 17:22:52 +01002191 case LOG_FMT_BACKENDPORT: // %bp
Willy Tarreau8fa99842019-07-17 11:47:11 +02002192 if (be_conn && conn_get_src(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002193 ret = lf_port(tmplog, (struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002194 else
2195 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2196
William Lallemand5f232402012-04-05 18:02:55 +02002197 if (ret == NULL)
2198 goto out;
2199 tmplog = ret;
2200 last_isspace = 0;
2201 break;
2202
Willy Tarreau2beef582012-12-20 17:22:52 +01002203 case LOG_FMT_SERVERIP: // %si
Willy Tarreau8fa99842019-07-17 11:47:11 +02002204 if (be_conn && conn_get_dst(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002205 ret = lf_ip(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002206 else
2207 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2208
William Lallemand5f232402012-04-05 18:02:55 +02002209 if (ret == NULL)
2210 goto out;
2211 tmplog = ret;
2212 last_isspace = 0;
2213 break;
2214
Willy Tarreau2beef582012-12-20 17:22:52 +01002215 case LOG_FMT_SERVERPORT: // %sp
Willy Tarreau8fa99842019-07-17 11:47:11 +02002216 if (be_conn && conn_get_dst(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002217 ret = lf_port(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002218 else
2219 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2220
William Lallemand1d705562012-03-12 12:46:41 +01002221 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002222 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002223 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002224 last_isspace = 0;
2225 break;
2226
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002227 case LOG_FMT_DATE: // %t = accept date
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002228 get_localtime(logs->accept_date.tv_sec, &tm);
2229 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002230 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002231 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002232 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002233 last_isspace = 0;
2234 break;
2235
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002236 case LOG_FMT_tr: // %tr = start of request date
2237 /* Note that the timers are valid if we get here */
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002238 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 +02002239 get_localtime(tv.tv_sec, &tm);
2240 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
2241 if (ret == NULL)
2242 goto out;
2243 tmplog = ret;
2244 last_isspace = 0;
2245 break;
2246
2247 case LOG_FMT_DATEGMT: // %T = accept date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002248 get_gmtime(logs->accept_date.tv_sec, &tm);
William Lallemand5f232402012-04-05 18:02:55 +02002249 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002250 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002251 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002252 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002253 last_isspace = 0;
2254 break;
2255
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002256 case LOG_FMT_trg: // %trg = start of request date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002257 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 +02002258 get_gmtime(tv.tv_sec, &tm);
2259 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2260 if (ret == NULL)
2261 goto out;
2262 tmplog = ret;
2263 last_isspace = 0;
2264 break;
2265
2266 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002267 get_localtime(logs->accept_date.tv_sec, &tm);
2268 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
Yuxans Yao4e25b012012-10-19 10:36:09 +08002269 if (ret == NULL)
2270 goto out;
2271 tmplog = ret;
2272 last_isspace = 0;
2273 break;
2274
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002275 case LOG_FMT_trl: // %trl = start of request date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002276 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 +02002277 get_localtime(tv.tv_sec, &tm);
2278 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
2279 if (ret == NULL)
2280 goto out;
2281 tmplog = ret;
2282 last_isspace = 0;
2283 break;
2284
William Lallemand5f232402012-04-05 18:02:55 +02002285 case LOG_FMT_TS: // %Ts
William Lallemand5f232402012-04-05 18:02:55 +02002286 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002287 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
William Lallemand5f232402012-04-05 18:02:55 +02002288 if (iret < 0 || iret > dst + maxsize - tmplog)
2289 goto out;
2290 last_isspace = 0;
2291 tmplog += iret;
2292 } else {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002293 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002294 if (ret == NULL)
2295 goto out;
2296 tmplog = ret;
2297 last_isspace = 0;
2298 }
2299 break;
2300
William Lallemand1d705562012-03-12 12:46:41 +01002301 case LOG_FMT_MS: // %ms
William Lallemand5f232402012-04-05 18:02:55 +02002302 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002303 iret = snprintf(tmplog, dst + maxsize - tmplog, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
William Lallemand5f232402012-04-05 18:02:55 +02002304 if (iret < 0 || iret > dst + maxsize - tmplog)
2305 goto out;
2306 last_isspace = 0;
2307 tmplog += iret;
2308 } else {
2309 if ((dst + maxsize - tmplog) < 4)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002310 goto out;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002311 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002312 tmplog, 4);
2313 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002314 goto out;
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002315 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002316 last_isspace = 0;
William Lallemand5f232402012-04-05 18:02:55 +02002317 }
2318 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002319
William Lallemand1d705562012-03-12 12:46:41 +01002320 case LOG_FMT_FRONTEND: // %f
William Lallemandbddd4fd2012-02-27 11:23:10 +01002321 src = fe->id;
William Lallemand5f232402012-04-05 18:02:55 +02002322 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002323 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002324 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002325 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002326 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002327 break;
2328
Willy Tarreau773d65f2012-10-12 14:56:11 +02002329 case LOG_FMT_FRONTEND_XPRT: // %ft
2330 src = fe->id;
2331 if (tmp->options & LOG_OPT_QUOTE)
2332 LOGCHAR('"');
2333 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2334 if (iret == 0)
2335 goto out;
2336 tmplog += iret;
Willy Tarreaua261e9b2016-12-22 20:44:00 +01002337 if (sess->listener->bind_conf->xprt == xprt_get(XPRT_SSL))
Willy Tarreau773d65f2012-10-12 14:56:11 +02002338 LOGCHAR('~');
Willy Tarreau773d65f2012-10-12 14:56:11 +02002339 if (tmp->options & LOG_OPT_QUOTE)
2340 LOGCHAR('"');
2341 last_isspace = 0;
2342 break;
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002343#ifdef USE_OPENSSL
2344 case LOG_FMT_SSL_CIPHER: // %sslc
2345 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002346 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002347 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002348 src = ssl_sock_get_cipher_name(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002349 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002350 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2351 if (ret == NULL)
2352 goto out;
2353 tmplog = ret;
2354 last_isspace = 0;
2355 break;
Willy Tarreau773d65f2012-10-12 14:56:11 +02002356
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002357 case LOG_FMT_SSL_VERSION: // %sslv
2358 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002359 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002360 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002361 src = ssl_sock_get_proto_version(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002362 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002363 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2364 if (ret == NULL)
2365 goto out;
2366 tmplog = ret;
2367 last_isspace = 0;
2368 break;
2369#endif
William Lallemand1d705562012-03-12 12:46:41 +01002370 case LOG_FMT_BACKEND: // %b
William Lallemandbddd4fd2012-02-27 11:23:10 +01002371 src = be->id;
William Lallemand5f232402012-04-05 18:02:55 +02002372 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002373 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002374 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002375 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002376 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002377 break;
2378
William Lallemand1d705562012-03-12 12:46:41 +01002379 case LOG_FMT_SERVER: // %s
Willy Tarreaue1809df2018-09-05 15:30:16 +02002380 switch (obj_type(s ? s->target : NULL)) {
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002381 case OBJ_TYPE_SERVER:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002382 src = __objt_server(s->target)->id;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002383 break;
2384 case OBJ_TYPE_APPLET:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002385 src = __objt_applet(s->target)->name;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002386 break;
2387 default:
2388 src = "<NOSRV>";
2389 break;
2390 }
William Lallemand5f232402012-04-05 18:02:55 +02002391 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002392 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002393 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002394 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002395 last_isspace = 0;
2396 break;
2397
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002398 case LOG_FMT_Th: // %Th = handshake time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002399 ret = ltoa_o(logs->t_handshake, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002400 if (ret == NULL)
2401 goto out;
2402 tmplog = ret;
2403 last_isspace = 0;
2404 break;
2405
2406 case LOG_FMT_Ti: // %Ti = HTTP idle time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002407 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002408 if (ret == NULL)
2409 goto out;
2410 tmplog = ret;
2411 last_isspace = 0;
2412 break;
2413
2414 case LOG_FMT_TR: // %TR = HTTP request time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002415 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002416 tmplog, dst + maxsize - tmplog);
2417 if (ret == NULL)
2418 goto out;
2419 tmplog = ret;
2420 last_isspace = 0;
2421 break;
2422
2423 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
William Lallemand5f232402012-04-05 18:02:55 +02002424 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002425 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002426 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002427 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002428 last_isspace = 0;
2429 break;
2430
William Lallemand1d705562012-03-12 12:46:41 +01002431 case LOG_FMT_TW: // %Tw
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002432 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002433 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002434 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002435 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002436 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002437 last_isspace = 0;
2438 break;
2439
William Lallemand1d705562012-03-12 12:46:41 +01002440 case LOG_FMT_TC: // %Tc
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002441 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002442 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002443 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002444 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002445 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002446 last_isspace = 0;
2447 break;
2448
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002449 case LOG_FMT_Tr: // %Tr
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002450 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002451 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002452 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002453 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002454 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002455 last_isspace = 0;
2456 break;
2457
Willy Tarreau27b639d2016-05-17 17:55:27 +02002458 case LOG_FMT_TD: // %Td
Willy Tarreaua21c0e62018-09-05 15:07:15 +02002459 if (be->mode == PR_MODE_HTTP)
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002460 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002461 tmplog, dst + maxsize - tmplog);
2462 else
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002463 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002464 tmplog, dst + maxsize - tmplog);
2465 if (ret == NULL)
2466 goto out;
2467 tmplog = ret;
2468 last_isspace = 0;
2469 break;
2470
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002471 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2472 if (!(fe->to_log & LW_BYTES))
2473 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002474 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 +02002475 tmplog, dst + maxsize - tmplog);
2476 if (ret == NULL)
2477 goto out;
2478 tmplog = ret;
2479 last_isspace = 0;
2480 break;
2481
2482 case LOG_FMT_TT: // %Tt = total time
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002483 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002484 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002485 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002486 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002487 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002488 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002489 last_isspace = 0;
2490 break;
2491
Damien Claisse57c8eb92020-04-28 12:09:19 +00002492 case LOG_FMT_TU: // %Tu = total time seen by user = Tt - Ti
2493 if (!(fe->to_log & LW_BYTES))
2494 LOGCHAR('+');
2495 ret = ltoa_o(logs->t_close - (logs->t_idle >= 0 ? logs->t_idle : 0),
2496 tmplog, dst + maxsize - tmplog);
2497 if (ret == NULL)
2498 goto out;
2499 tmplog = ret;
2500 last_isspace = 0;
2501 break;
2502
Willy Tarreau2beef582012-12-20 17:22:52 +01002503 case LOG_FMT_STATUS: // %ST
Willy Tarreau57bc8912016-04-25 17:09:40 +02002504 ret = ltoa_o(txn ? txn->status : 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002505 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002506 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002507 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002508 last_isspace = 0;
2509 break;
2510
William Lallemand1d705562012-03-12 12:46:41 +01002511 case LOG_FMT_BYTES: // %B
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002512 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002513 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002514 ret = lltoa(logs->bytes_out, 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
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002521 case LOG_FMT_BYTES_UP: // %U
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002522 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002523 if (ret == NULL)
2524 goto out;
2525 tmplog = ret;
2526 last_isspace = 0;
2527 break;
2528
Willy Tarreau2beef582012-12-20 17:22:52 +01002529 case LOG_FMT_CCLIENT: // %CC
Willy Tarreau57bc8912016-04-25 17:09:40 +02002530 src = txn ? txn->cli_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002531 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002532 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002533 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002534 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002535 last_isspace = 0;
2536 break;
2537
Willy Tarreau2beef582012-12-20 17:22:52 +01002538 case LOG_FMT_CSERVER: // %CS
Willy Tarreau57bc8912016-04-25 17:09:40 +02002539 src = txn ? txn->srv_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002540 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002541 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002542 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002543 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002544 last_isspace = 0;
2545 break;
2546
William Lallemand1d705562012-03-12 12:46:41 +01002547 case LOG_FMT_TERMSTATE: // %ts
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002548 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2549 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau6580c062012-03-12 15:09:42 +01002550 *tmplog = '\0';
2551 last_isspace = 0;
2552 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002553
William Lallemand1d705562012-03-12 12:46:41 +01002554 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002555 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2556 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau57bc8912016-04-25 17:09:40 +02002557 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2558 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 +01002559 last_isspace = 0;
2560 break;
2561
William Lallemand1d705562012-03-12 12:46:41 +01002562 case LOG_FMT_ACTCONN: // %ac
William Lallemand5f232402012-04-05 18:02:55 +02002563 ret = ltoa_o(actconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002564 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002565 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002566 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002567 last_isspace = 0;
2568 break;
2569
William Lallemand1d705562012-03-12 12:46:41 +01002570 case LOG_FMT_FECONN: // %fc
William Lallemand5f232402012-04-05 18:02:55 +02002571 ret = ltoa_o(fe->feconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002572 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002573 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002574 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002575 last_isspace = 0;
2576 break;
2577
William Lallemand1d705562012-03-12 12:46:41 +01002578 case LOG_FMT_BECONN: // %bc
William Lallemand5f232402012-04-05 18:02:55 +02002579 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002580 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002581 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002582 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002583 last_isspace = 0;
2584 break;
2585
William Lallemand1d705562012-03-12 12:46:41 +01002586 case LOG_FMT_SRVCONN: // %sc
Willy Tarreaue1809df2018-09-05 15:30:16 +02002587 ret = ultoa_o(objt_server(s ? s->target : NULL) ?
Willy Tarreau3fdb3662012-11-12 00:42:33 +01002588 objt_server(s->target)->cur_sess :
William Lallemand5f232402012-04-05 18:02:55 +02002589 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002590 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002591 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002592 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002593 last_isspace = 0;
2594 break;
2595
William Lallemand1d705562012-03-12 12:46:41 +01002596 case LOG_FMT_RETRIES: // %rq
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002597 if (s_flags & SF_REDISP)
William Lallemand1d705562012-03-12 12:46:41 +01002598 LOGCHAR('+');
Willy Tarreauabd71a52018-09-04 19:21:44 +02002599 ret = ltoa_o((s && s->si[1].conn_retries > 0) ?
Willy Tarreau350f4872014-11-28 14:42:25 +01002600 (be->conn_retries - s->si[1].conn_retries) :
William Lallemand5f232402012-04-05 18:02:55 +02002601 be->conn_retries, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002602 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002603 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002604 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002605 last_isspace = 0;
2606 break;
2607
William Lallemand1d705562012-03-12 12:46:41 +01002608 case LOG_FMT_SRVQUEUE: // %sq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002609 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002610 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002611 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002612 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002613 last_isspace = 0;
2614 break;
2615
William Lallemand1d705562012-03-12 12:46:41 +01002616 case LOG_FMT_BCKQUEUE: // %bq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002617 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002618 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002619 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002620 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002621 last_isspace = 0;
2622 break;
2623
William Lallemand1d705562012-03-12 12:46:41 +01002624 case LOG_FMT_HDRREQUEST: // %hr
William Lallemandbddd4fd2012-02-27 11:23:10 +01002625 /* request header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002626 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002627 if (tmp->options & LOG_OPT_QUOTE)
2628 LOGCHAR('"');
2629 LOGCHAR('{');
2630 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2631 if (hdr)
2632 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002633 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002634 ret = lf_encode_string(tmplog, dst + maxsize,
2635 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002636 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002637 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002638 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002639 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002640 }
2641 LOGCHAR('}');
William Lallemand51b5dca2012-03-26 17:52:55 +02002642 if (tmp->options & LOG_OPT_QUOTE)
2643 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002644 last_isspace = 0;
2645 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002646 break;
2647
William Lallemand1d705562012-03-12 12:46:41 +01002648 case LOG_FMT_HDRREQUESTLIST: // %hrl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002649 /* request header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002650 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002651 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2652 if (hdr > 0)
2653 LOGCHAR(' ');
2654 if (tmp->options & LOG_OPT_QUOTE)
2655 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002656 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002657 ret = lf_encode_string(tmplog, dst + maxsize,
2658 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002659 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002660 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002661 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002662 } else if (!(tmp->options & LOG_OPT_QUOTE))
2663 LOGCHAR('-');
2664 if (tmp->options & LOG_OPT_QUOTE)
2665 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002666 last_isspace = 0;
2667 }
2668 }
2669 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002670
William Lallemand1d705562012-03-12 12:46:41 +01002671
2672 case LOG_FMT_HDRRESPONS: // %hs
William Lallemandbddd4fd2012-02-27 11:23:10 +01002673 /* response header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002674 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002675 if (tmp->options & LOG_OPT_QUOTE)
2676 LOGCHAR('"');
2677 LOGCHAR('{');
2678 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2679 if (hdr)
2680 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002681 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002682 ret = lf_encode_string(tmplog, dst + maxsize,
2683 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002684 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002685 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002686 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002687 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002688 }
2689 LOGCHAR('}');
2690 last_isspace = 0;
2691 if (tmp->options & LOG_OPT_QUOTE)
2692 LOGCHAR('"');
2693 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002694 break;
2695
William Lallemand1d705562012-03-12 12:46:41 +01002696 case LOG_FMT_HDRRESPONSLIST: // %hsl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002697 /* response header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002698 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002699 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2700 if (hdr > 0)
2701 LOGCHAR(' ');
2702 if (tmp->options & LOG_OPT_QUOTE)
2703 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002704 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002705 ret = lf_encode_string(tmplog, dst + maxsize,
2706 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002707 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002708 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002709 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002710 } else if (!(tmp->options & LOG_OPT_QUOTE))
2711 LOGCHAR('-');
2712 if (tmp->options & LOG_OPT_QUOTE)
2713 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002714 last_isspace = 0;
2715 }
2716 }
2717 break;
2718
William Lallemand1d705562012-03-12 12:46:41 +01002719 case LOG_FMT_REQ: // %r
William Lallemandbddd4fd2012-02-27 11:23:10 +01002720 /* Request */
2721 if (tmp->options & LOG_OPT_QUOTE)
2722 LOGCHAR('"');
Willy Tarreau57bc8912016-04-25 17:09:40 +02002723 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Dragan Dosen835b9212016-02-12 13:23:03 +01002724 ret = lf_encode_string(tmplog, dst + maxsize,
2725 '#', url_encode_map, uri, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002726 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002727 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002728 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002729 if (tmp->options & LOG_OPT_QUOTE)
2730 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002731 last_isspace = 0;
2732 break;
William Lallemand5f232402012-04-05 18:02:55 +02002733
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002734 case LOG_FMT_HTTP_PATH: // %HP
Willy Tarreau57bc8912016-04-25 17:09:40 +02002735 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002736
Willy Tarreaub7636d12015-06-17 19:58:02 +02002737 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002738 LOGCHAR('"');
2739
2740 end = uri + strlen(uri);
2741 // look for the first whitespace character
2742 while (uri < end && !HTTP_IS_SPHT(*uri))
2743 uri++;
2744
2745 // keep advancing past multiple spaces
2746 while (uri < end && HTTP_IS_SPHT(*uri)) {
2747 uri++; nspaces++;
2748 }
2749
2750 // look for first space or question mark after url
2751 spc = uri;
2752 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2753 spc++;
2754
Nenad Merdanovic54e439f2016-04-26 01:39:02 +02002755 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002756 chunk.area = "<BADREQ>";
2757 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002758 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002759 chunk.area = uri;
2760 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002761 }
2762
Dragan Dosen835b9212016-02-12 13:23:03 +01002763 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002764 if (ret == NULL || *ret != '\0')
2765 goto out;
2766
2767 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002768 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002769 LOGCHAR('"');
2770
2771 last_isspace = 0;
2772 break;
2773
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002774 case LOG_FMT_HTTP_QUERY: // %HQ
2775 if (tmp->options & LOG_OPT_QUOTE)
2776 LOGCHAR('"');
2777
Willy Tarreau57bc8912016-04-25 17:09:40 +02002778 if (!txn || !txn->uri) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002779 chunk.area = "<BADREQ>";
2780 chunk.data = strlen("<BADREQ>");
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002781 } else {
2782 uri = txn->uri;
2783 end = uri + strlen(uri);
2784 // look for the first question mark
2785 while (uri < end && *uri != '?')
2786 uri++;
2787
2788 qmark = uri;
2789 // look for first space or question mark after url
2790 while (uri < end && !HTTP_IS_SPHT(*uri))
2791 uri++;
2792
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002793 chunk.area = qmark;
2794 chunk.data = uri - qmark;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002795 }
2796
Dragan Dosen835b9212016-02-12 13:23:03 +01002797 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002798 if (ret == NULL || *ret != '\0')
2799 goto out;
2800
2801 tmplog = ret;
2802 if (tmp->options & LOG_OPT_QUOTE)
2803 LOGCHAR('"');
2804
2805 last_isspace = 0;
2806 break;
2807
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002808 case LOG_FMT_HTTP_URI: // %HU
Willy Tarreau57bc8912016-04-25 17:09:40 +02002809 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002810
Willy Tarreaub7636d12015-06-17 19:58:02 +02002811 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002812 LOGCHAR('"');
2813
2814 end = uri + strlen(uri);
2815 // look for the first whitespace character
2816 while (uri < end && !HTTP_IS_SPHT(*uri))
2817 uri++;
2818
2819 // keep advancing past multiple spaces
2820 while (uri < end && HTTP_IS_SPHT(*uri)) {
2821 uri++; nspaces++;
2822 }
2823
2824 // look for first space after url
2825 spc = uri;
2826 while (spc < end && !HTTP_IS_SPHT(*spc))
2827 spc++;
2828
Willy Tarreau57bc8912016-04-25 17:09:40 +02002829 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002830 chunk.area = "<BADREQ>";
2831 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002832 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002833 chunk.area = uri;
2834 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002835 }
2836
Dragan Dosen835b9212016-02-12 13:23:03 +01002837 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002838 if (ret == NULL || *ret != '\0')
2839 goto out;
2840
2841 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002842 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002843 LOGCHAR('"');
2844
2845 last_isspace = 0;
2846 break;
2847
2848 case LOG_FMT_HTTP_METHOD: // %HM
Willy Tarreau57bc8912016-04-25 17:09:40 +02002849 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002850 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002851 LOGCHAR('"');
2852
2853 end = uri + strlen(uri);
2854 // look for the first whitespace character
2855 spc = uri;
2856 while (spc < end && !HTTP_IS_SPHT(*spc))
2857 spc++;
2858
2859 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002860 chunk.area = "<BADREQ>";
2861 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002862 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002863 chunk.area = uri;
2864 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002865 }
2866
Dragan Dosen835b9212016-02-12 13:23:03 +01002867 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002868 if (ret == NULL || *ret != '\0')
2869 goto out;
2870
2871 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002872 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002873 LOGCHAR('"');
2874
2875 last_isspace = 0;
2876 break;
2877
2878 case LOG_FMT_HTTP_VERSION: // %HV
Willy Tarreau57bc8912016-04-25 17:09:40 +02002879 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002880 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002881 LOGCHAR('"');
2882
2883 end = uri + strlen(uri);
2884 // look for the first whitespace character
2885 while (uri < end && !HTTP_IS_SPHT(*uri))
2886 uri++;
2887
2888 // keep advancing past multiple spaces
2889 while (uri < end && HTTP_IS_SPHT(*uri)) {
2890 uri++; nspaces++;
2891 }
2892
2893 // look for the next whitespace character
2894 while (uri < end && !HTTP_IS_SPHT(*uri))
2895 uri++;
2896
2897 // keep advancing past multiple spaces
2898 while (uri < end && HTTP_IS_SPHT(*uri))
2899 uri++;
2900
Willy Tarreau57bc8912016-04-25 17:09:40 +02002901 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002902 chunk.area = "<BADREQ>";
2903 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002904 } else if (uri == end) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002905 chunk.area = "HTTP/0.9";
2906 chunk.data = strlen("HTTP/0.9");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002907 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002908 chunk.area = uri;
2909 chunk.data = end - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002910 }
2911
Dragan Dosen835b9212016-02-12 13:23:03 +01002912 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002913 if (ret == NULL || *ret != '\0')
2914 goto out;
2915
2916 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002917 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002918 LOGCHAR('"');
2919
2920 last_isspace = 0;
2921 break;
2922
William Lallemand5f232402012-04-05 18:02:55 +02002923 case LOG_FMT_COUNTER: // %rt
2924 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002925 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
William Lallemand5f232402012-04-05 18:02:55 +02002926 if (iret < 0 || iret > dst + maxsize - tmplog)
2927 goto out;
2928 last_isspace = 0;
2929 tmplog += iret;
2930 } else {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002931 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002932 if (ret == NULL)
2933 goto out;
2934 tmplog = ret;
2935 last_isspace = 0;
2936 }
2937 break;
2938
Willy Tarreau7346acb2014-08-28 15:03:15 +02002939 case LOG_FMT_LOGCNT: // %lc
2940 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002941 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002942 if (iret < 0 || iret > dst + maxsize - tmplog)
2943 goto out;
2944 last_isspace = 0;
2945 tmplog += iret;
2946 } else {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002947 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002948 if (ret == NULL)
2949 goto out;
2950 tmplog = ret;
2951 last_isspace = 0;
2952 }
2953 break;
2954
William Lallemand5f232402012-04-05 18:02:55 +02002955 case LOG_FMT_HOSTNAME: // %H
2956 src = hostname;
2957 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2958 if (ret == NULL)
2959 goto out;
2960 tmplog = ret;
2961 last_isspace = 0;
2962 break;
2963
2964 case LOG_FMT_PID: // %pid
2965 if (tmp->options & LOG_OPT_HEXA) {
2966 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
2967 if (iret < 0 || iret > dst + maxsize - tmplog)
2968 goto out;
2969 last_isspace = 0;
2970 tmplog += iret;
2971 } else {
2972 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
2973 if (ret == NULL)
2974 goto out;
2975 tmplog = ret;
2976 last_isspace = 0;
2977 }
2978 break;
William Lallemanda73203e2012-03-12 12:48:57 +01002979
2980 case LOG_FMT_UNIQUEID: // %ID
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002981 ret = NULL;
Tim Duesterhusa17e6622020-03-05 20:19:02 +01002982 if (s)
2983 ret = lf_text_len(tmplog, s->unique_id.ptr, s->unique_id.len, maxsize - (tmplog - dst), tmp);
2984 else
2985 ret = lf_text_len(tmplog, NULL, 0, maxsize - (tmplog - dst), tmp);
William Lallemanda73203e2012-03-12 12:48:57 +01002986 if (ret == NULL)
2987 goto out;
2988 tmplog = ret;
2989 last_isspace = 0;
2990 break;
2991
William Lallemandbddd4fd2012-02-27 11:23:10 +01002992 }
2993 }
2994
2995out:
William Lallemand1d705562012-03-12 12:46:41 +01002996 /* *tmplog is a unused character */
2997 *tmplog = '\0';
Willy Tarreaudf974472012-12-28 02:44:01 +01002998 return tmplog - dst;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002999
Willy Tarreaubaaee002006-06-26 02:48:02 +02003000}
3001
William Lallemand1d705562012-03-12 12:46:41 +01003002/*
Willy Tarreau87b09662015-04-03 00:22:06 +02003003 * send a log for the stream when we have enough info about it.
William Lallemand1d705562012-03-12 12:46:41 +01003004 * Will not log if the frontend has no log defined.
3005 */
Willy Tarreau87b09662015-04-03 00:22:06 +02003006void strm_log(struct stream *s)
William Lallemand1d705562012-03-12 12:46:41 +01003007{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003008 struct session *sess = s->sess;
William Lallemand1d705562012-03-12 12:46:41 +01003009 int size, err, level;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02003010 int sd_size = 0;
William Lallemand1d705562012-03-12 12:46:41 +01003011
3012 /* if we don't want to log normal traffic, return now */
Willy Tarreaue7dff022015-04-03 01:14:29 +02003013 err = (s->flags & SF_REDISP) ||
3014 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
3015 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
Willy Tarreau350f4872014-11-28 14:42:25 +01003016 (s->si[1].conn_retries != s->be->conn_retries)) ||
Willy Tarreaueee5b512015-04-03 23:46:31 +02003017 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
Willy Tarreaubaaee002006-06-26 02:48:02 +02003018
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003019 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
William Lallemand1d705562012-03-12 12:46:41 +01003020 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003021
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003022 if (LIST_ISEMPTY(&sess->fe->logsrvs))
William Lallemand1d705562012-03-12 12:46:41 +01003023 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003024
Willy Tarreauabcd5142013-06-11 17:18:02 +02003025 if (s->logs.level) { /* loglevel was overridden */
3026 if (s->logs.level == -1) {
3027 s->logs.logwait = 0; /* logs disabled */
3028 return;
3029 }
3030 level = s->logs.level - 1;
3031 }
3032 else {
3033 level = LOG_INFO;
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003034 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
Willy Tarreauabcd5142013-06-11 17:18:02 +02003035 level = LOG_ERR;
3036 }
William Lallemand1d705562012-03-12 12:46:41 +01003037
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003038 /* if unique-id was not generated */
Tim Duesterhusa17e6622020-03-05 20:19:02 +01003039 if (!isttest(s->unique_id) && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
Tim Duesterhus2825b4b2020-02-28 15:13:34 +01003040 stream_generate_unique_id(s, &sess->fe->format_unique_id);
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003041 }
3042
Dragan Dosen0b85ece2015-09-25 19:17:44 +02003043 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3044 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
3045 &sess->fe->logformat_sd);
3046 }
3047
Dragan Dosen59cee972015-09-19 22:09:02 +02003048 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
William Lallemand1d705562012-03-12 12:46:41 +01003049 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01003050 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003051 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3052 logline, size + 1, logline_rfc5424, sd_size);
William Lallemand1d705562012-03-12 12:46:41 +01003053 s->logs.logwait = 0;
3054 }
3055}
William Lallemandbddd4fd2012-02-27 11:23:10 +01003056
Willy Tarreau53839352018-09-05 19:51:10 +02003057/*
3058 * send a minimalist log for the session. Will not log if the frontend has no
3059 * log defined. It is assumed that this is only used to report anomalies that
3060 * cannot lead to the creation of a regular stream. Because of this the log
3061 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
3062 * in the frontend. The caller must simply know that it should not call this
Willy Tarreau9fa267d2018-10-05 10:22:27 +02003063 * function to report unimportant events. It is safe to call this function with
3064 * sess==NULL (will not do anything).
Willy Tarreau53839352018-09-05 19:51:10 +02003065 */
3066void sess_log(struct session *sess)
3067{
3068 int size, level;
3069 int sd_size = 0;
3070
Willy Tarreau9fa267d2018-10-05 10:22:27 +02003071 if (!sess)
3072 return;
3073
Willy Tarreau53839352018-09-05 19:51:10 +02003074 if (LIST_ISEMPTY(&sess->fe->logsrvs))
3075 return;
3076
3077 level = LOG_INFO;
3078 if (sess->fe->options2 & PR_O2_LOGERRORS)
3079 level = LOG_ERR;
3080
3081 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3082 sd_size = sess_build_logline(sess, NULL,
3083 logline_rfc5424, global.max_syslog_len,
3084 &sess->fe->logformat_sd);
3085 }
3086
3087 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
3088 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01003089 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003090 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3091 logline, size + 1, logline_rfc5424, sd_size);
Willy Tarreau53839352018-09-05 19:51:10 +02003092 }
3093}
3094
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003095void app_log(struct list *logsrvs, struct buffer *tag, int level, const char *format, ...)
3096{
3097 va_list argp;
3098 int data_len;
3099
3100 if (level < 0 || format == NULL || logline == NULL)
3101 return;
3102
3103 va_start(argp, format);
3104 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
3105 if (data_len < 0 || data_len > global.max_syslog_len)
3106 data_len = global.max_syslog_len;
3107 va_end(argp);
3108
3109 __send_log(logsrvs, tag, level, logline, data_len, default_rfc5424_sd_log_format, 2);
3110}
3111
Willy Tarreau869efd52019-11-15 15:16:57 +01003112/* parse the "show startup-logs" command, returns 1 if a message is returned, otherwise zero */
3113static int cli_parse_show_startup_logs(char **args, char *payload, struct appctx *appctx, void *private)
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003114{
Willy Tarreau869efd52019-11-15 15:16:57 +01003115 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
3116 return 1;
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003117
Willy Tarreau869efd52019-11-15 15:16:57 +01003118 if (!startup_logs)
3119 return cli_msg(appctx, LOG_INFO, "\n"); // nothing to print
3120
3121 return ring_attach_cli(startup_logs, appctx);
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003122}
3123
3124/* register cli keywords */
3125static struct cli_kw_list cli_kws = {{ },{
3126 { { "show", "startup-logs", NULL },
Willy Tarreau869efd52019-11-15 15:16:57 +01003127 "show startup-logs : report logs emitted during HAProxy startup", cli_parse_show_startup_logs, NULL, NULL },
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003128 {{},}
3129}};
3130
Willy Tarreau0108d902018-11-25 19:14:37 +01003131INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
3132
Willy Tarreau082b6282019-05-22 14:42:12 +02003133REGISTER_PER_THREAD_ALLOC(init_log_buffers);
3134REGISTER_PER_THREAD_FREE(deinit_log_buffers);
Willy Tarreau172f5ce2018-11-26 11:21:50 +01003135
Willy Tarreaubaaee002006-06-26 02:48:02 +02003136/*
3137 * Local variables:
3138 * c-indent-level: 8
3139 * c-basic-offset: 8
3140 * End:
3141 */