blob: 385bfc71ec0721e173deeffaed1b494c2e878f6b [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);
Willy Tarreauc8368452012-12-21 00:09:23 +0100686 }
687 break;
688
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100689 case LF_VAR: // text part of a variable name
690 var_len = str - var;
Willy Tarreau0f28f822013-12-16 01:38:33 +0100691 if (!isalnum((unsigned char)*str))
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100692 cformat = LF_INIT; // not variable name anymore
693 break;
694
Willy Tarreauc8368452012-12-21 00:09:23 +0100695 default: // LF_INIT, LF_TEXT, LF_SEPARATOR, LF_END, LF_EDEXPR
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100696 cformat = LF_INIT;
697 }
698
699 if (cformat == LF_INIT) { /* resynchronize state to text/sep/startvar */
700 switch (*str) {
701 case '%': cformat = LF_STARTVAR; break;
702 case ' ': cformat = LF_SEPARATOR; break;
703 case 0 : cformat = LF_END; break;
704 default : cformat = LF_TEXT; break;
William Lallemand723b73a2012-02-08 16:37:49 +0100705 }
706 }
707
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100708 if (cformat != pformat || pformat == LF_SEPARATOR) {
709 switch (pformat) {
710 case LF_VAR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100711 if (!parse_logformat_var(arg, arg_len, var, var_len, curproxy, list_format, &options, err))
Willy Tarreau51013e82019-12-11 12:05:39 +0100712 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100713 break;
Willy Tarreauc8368452012-12-21 00:09:23 +0100714 case LF_STEXPR:
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100715 if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err, &sp))
Willy Tarreau51013e82019-12-11 12:05:39 +0100716 goto fail;
Willy Tarreauc8368452012-12-21 00:09:23 +0100717 break;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100718 case LF_TEXT:
719 case LF_SEPARATOR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100720 if (!add_to_logformat_list(sp, str, pformat, list_format, err))
Willy Tarreau51013e82019-12-11 12:05:39 +0100721 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100722 break;
723 }
724 sp = str; /* new start of text at every state switch and at every separator */
William Lallemand723b73a2012-02-08 16:37:49 +0100725 }
726 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100727
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100728 if (pformat == LF_STARTVAR || pformat == LF_STARG || pformat == LF_STEXPR) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100729 memprintf(err, "truncated line after '%s'", var ? var : arg ? arg : "%");
Willy Tarreau51013e82019-12-11 12:05:39 +0100730 goto fail;
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100731 }
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100732 free(backfmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100733
734 return 1;
Willy Tarreau51013e82019-12-11 12:05:39 +0100735 fail:
736 free(backfmt);
737 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100738}
739
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200740/*
Ilya Shipitsin856aabc2020-04-16 23:51:34 +0500741 * 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 +0200742 * ranges of indexes. Note that an index may be considered as a particular range
743 * with a high limit to the low limit.
744 */
745int get_logsrv_smp_range(unsigned int *low, unsigned int *high, char **arg, char **err)
746{
747 char *end, *p;
748
749 *low = *high = 0;
750
751 p = *arg;
752 end = strchr(p, ',');
753 if (!end)
754 end = p + strlen(p);
755
756 *high = *low = read_uint((const char **)&p, end);
757 if (!*low || (p != end && *p != '-'))
758 goto err;
759
760 if (p == end)
761 goto done;
762
763 p++;
764 *high = read_uint((const char **)&p, end);
765 if (!*high || *high <= *low || p != end)
766 goto err;
767
768 done:
769 if (*end == ',')
770 end++;
771 *arg = end;
772 return 1;
773
774 err:
775 memprintf(err, "wrong sample range '%s'", *arg);
776 return 0;
777}
778
779/*
780 * Returns 1 if the range defined by <low> and <high> overlaps
781 * one of them in <rgs> array of ranges with <sz> the size of this
782 * array, 0 if not.
783 */
784int smp_log_ranges_overlap(struct smp_log_range *rgs, size_t sz,
785 unsigned int low, unsigned int high, char **err)
786{
787 size_t i;
788
789 for (i = 0; i < sz; i++) {
790 if ((low >= rgs[i].low && low <= rgs[i].high) ||
791 (high >= rgs[i].low && high <= rgs[i].high)) {
792 memprintf(err, "ranges are overlapping");
793 return 1;
794 }
795 }
796
797 return 0;
798}
799
800int smp_log_range_cmp(const void *a, const void *b)
801{
802 const struct smp_log_range *rg_a = a;
803 const struct smp_log_range *rg_b = b;
804
805 if (rg_a->high < rg_b->low)
806 return -1;
807 else if (rg_a->low > rg_b->high)
808 return 1;
809
810 return 0;
811}
812
813/*
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200814 * Parse "log" keyword and update <logsrvs> list accordingly.
815 *
816 * When <do_del> is set, it means the "no log" line was parsed, so all log
817 * servers in <logsrvs> are released.
818 *
819 * Otherwise, we try to parse the "log" line. First of all, when the list is not
820 * the global one, we look for the parameter "global". If we find it,
821 * global.logsrvs is copied. Else we parse each arguments.
822 *
823 * The function returns 1 in success case, otherwise, it returns 0 and err is
824 * filled.
825 */
826int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err)
827{
828 struct sockaddr_storage *sk;
829 struct logsrv *logsrv = NULL;
830 int port1, port2;
831 int cur_arg;
832
833 /*
834 * "no log": delete previous herited or defined syslog
835 * servers.
836 */
837 if (do_del) {
838 struct logsrv *back;
839
840 if (*(args[1]) != 0) {
841 memprintf(err, "'no log' does not expect arguments");
842 goto error;
843 }
844
845 list_for_each_entry_safe(logsrv, back, logsrvs, list) {
846 LIST_DEL(&logsrv->list);
847 free(logsrv);
848 }
849 return 1;
850 }
851
852 /*
853 * "log global": copy global.logrsvs linked list to the end of logsrvs
854 * list. But first, we check (logsrvs != global.logsrvs).
855 */
856 if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
857 if (logsrvs == &global.logsrvs) {
858 memprintf(err, "'global' is not supported for a global syslog server");
859 goto error;
860 }
861 list_for_each_entry(logsrv, &global.logsrvs, list) {
Christopher Faulet28ac0992018-03-26 16:09:19 +0200862 struct logsrv *node;
863
864 list_for_each_entry(node, logsrvs, list) {
865 if (node->ref == logsrv)
866 goto skip_logsrv;
867 }
868
869 node = malloc(sizeof(*node));
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200870 memcpy(node, logsrv, sizeof(struct logsrv));
Christopher Faulet28ac0992018-03-26 16:09:19 +0200871 node->ref = logsrv;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200872 LIST_INIT(&node->list);
873 LIST_ADDQ(logsrvs, &node->list);
Christopher Faulet28ac0992018-03-26 16:09:19 +0200874
875 skip_logsrv:
876 continue;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200877 }
878 return 1;
879 }
880
881 /*
882 * "log <address> ...: parse a syslog server line
883 */
884 if (*(args[1]) == 0 || *(args[2]) == 0) {
885 memprintf(err, "expects <address> and <facility> %s as arguments",
886 ((logsrvs == &global.logsrvs) ? "" : "or global"));
887 goto error;
888 }
889
Willy Tarreau5a32ecc2018-11-12 07:34:59 +0100890 /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */
891 if (strcmp(args[1], "stdout") == 0)
892 args[1] = "fd@1";
893 else if (strcmp(args[1], "stderr") == 0)
894 args[1] = "fd@2";
895
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200896 logsrv = calloc(1, sizeof(*logsrv));
897 if (!logsrv) {
898 memprintf(err, "out of memory");
899 goto error;
900 }
901
902 /* skip address for now, it will be parsed at the end */
903 cur_arg = 2;
904
905 /* just after the address, a length may be specified */
906 logsrv->maxlen = MAX_SYSLOG_LEN;
907 if (strcmp(args[cur_arg], "len") == 0) {
908 int len = atoi(args[cur_arg+1]);
909 if (len < 80 || len > 65535) {
910 memprintf(err, "invalid log length '%s', must be between 80 and 65535",
911 args[cur_arg+1]);
912 goto error;
913 }
914 logsrv->maxlen = len;
915 cur_arg += 2;
916 }
917 if (logsrv->maxlen > global.max_syslog_len)
918 global.max_syslog_len = logsrv->maxlen;
919
920 /* after the length, a format may be specified */
921 if (strcmp(args[cur_arg], "format") == 0) {
922 logsrv->format = get_log_format(args[cur_arg+1]);
923 if (logsrv->format < 0) {
924 memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
925 goto error;
926 }
927 cur_arg += 2;
928 }
929
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200930 if (strcmp(args[cur_arg], "sample") == 0) {
931 unsigned low, high;
932 char *p, *beg, *end, *smp_sz_str;
933 struct smp_log_range *smp_rgs = NULL;
934 size_t smp_rgs_sz = 0, smp_sz = 0, new_smp_sz;
935
936 p = args[cur_arg+1];
937 smp_sz_str = strchr(p, ':');
938 if (!smp_sz_str) {
939 memprintf(err, "Missing sample size");
940 goto error;
941 }
942
943 *smp_sz_str++ = '\0';
944
945 end = p + strlen(p);
946
947 while (p != end) {
948 if (!get_logsrv_smp_range(&low, &high, &p, err))
949 goto error;
950
951 if (smp_rgs && smp_log_ranges_overlap(smp_rgs, smp_rgs_sz, low, high, err))
952 goto error;
953
954 smp_rgs = my_realloc2(smp_rgs, (smp_rgs_sz + 1) * sizeof *smp_rgs);
955 if (!smp_rgs) {
956 memprintf(err, "out of memory error");
957 goto error;
958 }
959
960 smp_rgs[smp_rgs_sz].low = low;
961 smp_rgs[smp_rgs_sz].high = high;
962 smp_rgs[smp_rgs_sz].sz = high - low + 1;
963 smp_rgs[smp_rgs_sz].curr_idx = 0;
964 if (smp_rgs[smp_rgs_sz].high > smp_sz)
965 smp_sz = smp_rgs[smp_rgs_sz].high;
966 smp_rgs_sz++;
967 }
968
Tim Duesterhus21648002019-06-23 22:10:10 +0200969 if (smp_rgs == NULL) {
970 memprintf(err, "no sampling ranges given");
971 goto error;
972 }
973
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200974 beg = smp_sz_str;
975 end = beg + strlen(beg);
976 new_smp_sz = read_uint((const char **)&beg, end);
977 if (!new_smp_sz || beg != end) {
978 memprintf(err, "wrong sample size '%s' for sample range '%s'",
979 smp_sz_str, args[cur_arg+1]);
980 goto error;
981 }
982
983 if (new_smp_sz < smp_sz) {
984 memprintf(err, "sample size %zu should be greater or equal to "
985 "%zu the maximum of the high ranges limits",
986 new_smp_sz, smp_sz);
987 goto error;
988 }
989 smp_sz = new_smp_sz;
990
991 /* Let's order <smp_rgs> array. */
992 qsort(smp_rgs, smp_rgs_sz, sizeof(struct smp_log_range), smp_log_range_cmp);
993
994 logsrv->lb.smp_rgs = smp_rgs;
995 logsrv->lb.smp_rgs_sz = smp_rgs_sz;
996 logsrv->lb.smp_sz = smp_sz;
997
998 cur_arg += 2;
999 }
Frédéric Lécailled803e472019-04-25 07:42:09 +02001000 HA_SPIN_INIT(&logsrv->lock);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001001 /* parse the facility */
1002 logsrv->facility = get_log_facility(args[cur_arg]);
1003 if (logsrv->facility < 0) {
1004 memprintf(err, "unknown log facility '%s'", args[cur_arg]);
1005 goto error;
1006 }
1007 cur_arg++;
1008
1009 /* parse the max syslog level (default: debug) */
1010 logsrv->level = 7;
1011 if (*(args[cur_arg])) {
1012 logsrv->level = get_log_level(args[cur_arg]);
1013 if (logsrv->level < 0) {
1014 memprintf(err, "unknown optional log level '%s'", args[cur_arg]);
1015 goto error;
1016 }
1017 cur_arg++;
1018 }
1019
1020 /* parse the limit syslog level (default: emerg) */
1021 logsrv->minlvl = 0;
1022 if (*(args[cur_arg])) {
1023 logsrv->minlvl = get_log_level(args[cur_arg]);
1024 if (logsrv->minlvl < 0) {
1025 memprintf(err, "unknown optional minimum log level '%s'", args[cur_arg]);
1026 goto error;
1027 }
1028 cur_arg++;
1029 }
1030
1031 /* Too many args */
1032 if (*(args[cur_arg])) {
1033 memprintf(err, "cannot handle unexpected argument '%s'", args[cur_arg]);
1034 goto error;
1035 }
1036
1037 /* now, back to the address */
Willy Tarreauf3dc30f2019-08-30 14:18:44 +02001038 logsrv->type = LOG_TARGET_DGRAM;
Willy Tarreauc046d162019-08-30 15:24:59 +02001039 if (strncmp(args[1], "ring@", 5) == 0) {
Willy Tarreauc046d162019-08-30 15:24:59 +02001040 logsrv->addr.ss_family = AF_UNSPEC;
1041 logsrv->type = LOG_TARGET_BUFFER;
Emeric Brun99c453d2020-05-25 15:01:04 +02001042 logsrv->sink = NULL;
1043 logsrv->ring_name = strdup(args[1] + 5);
Willy Tarreauc046d162019-08-30 15:24:59 +02001044 goto done;
1045 }
1046
Willy Tarreauf3dc30f2019-08-30 14:18:44 +02001047 if (strncmp(args[1], "fd@", 3) == 0)
1048 logsrv->type = LOG_TARGET_FD;
1049
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001050 sk = str2sa_range(args[1], NULL, &port1, &port2, err, NULL, NULL, 1);
1051 if (!sk)
1052 goto error;
1053 logsrv->addr = *sk;
1054
1055 if (sk->ss_family == AF_INET || sk->ss_family == AF_INET6) {
1056 if (port1 != port2) {
1057 memprintf(err, "port ranges and offsets are not allowed in '%s'", args[1]);
1058 goto error;
1059 }
1060 logsrv->addr = *sk;
1061 if (!port1)
1062 set_host_port(&logsrv->addr, SYSLOG_PORT);
1063 }
Willy Tarreauc046d162019-08-30 15:24:59 +02001064 done:
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001065 LIST_ADDQ(logsrvs, &logsrv->list);
1066 return 1;
1067
1068 error:
1069 free(logsrv);
1070 return 0;
1071}
1072
1073
Christopher Fauletd4696382017-10-24 11:44:05 +02001074/* Generic function to display messages prefixed by a label */
1075static void print_message(const char *label, const char *fmt, va_list argp)
1076{
1077 struct tm tm;
1078 char *head, *msg;
1079
1080 head = msg = NULL;
1081
1082 get_localtime(date.tv_sec, &tm);
1083 memprintf(&head, "[%s] %03d/%02d%02d%02d (%d) : ",
1084 label, tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
1085 memvprintf(&msg, fmt, argp);
1086
Willy Tarreau869efd52019-11-15 15:16:57 +01001087 if (global.mode & MODE_STARTING) {
1088 if (unlikely(!startup_logs))
1089 startup_logs = ring_new(STARTUP_LOG_SIZE);
1090
1091 if (likely(startup_logs)) {
1092 struct ist m[2];
1093
1094 m[0] = ist(head);
1095 m[1] = ist(msg);
1096 /* trim the trailing '\n' */
1097 if (m[1].len > 0 && m[1].ptr[m[1].len - 1] == '\n')
1098 m[1].len--;
1099 ring_write(startup_logs, ~0, 0, 0, m, 2);
1100 }
1101 }
Christopher Fauletd4696382017-10-24 11:44:05 +02001102
1103 fprintf(stderr, "%s%s", head, msg);
1104 fflush(stderr);
1105
1106 free(head);
1107 free(msg);
1108}
1109
Willy Tarreaubaaee002006-06-26 02:48:02 +02001110/*
1111 * Displays the message on stderr with the date and pid. Overrides the quiet
1112 * mode during startup.
1113 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001114void ha_alert(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001115{
1116 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001117
1118 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
Willy Tarreaubb869862020-04-16 10:52:41 +02001119 if (!(warned & WARN_EXEC_PATH)) {
1120 const char *path = get_exec_path();
1121
1122 warned |= WARN_EXEC_PATH;
1123 ha_notice("haproxy version is %s\n", haproxy_version);
1124 if (path)
1125 ha_notice("path to executable is %s\n", path);
1126 }
Willy Tarreaubaaee002006-06-26 02:48:02 +02001127 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001128 print_message("ALERT", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001129 va_end(argp);
1130 }
1131}
1132
1133
1134/*
1135 * Displays the message on stderr with the date and pid.
1136 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001137void ha_warning(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001138{
1139 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001140
Willy Tarreaubebd2122020-04-15 16:06:11 +02001141 warned |= WARN_ANY;
1142
Willy Tarreaubaaee002006-06-26 02:48:02 +02001143 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1144 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001145 print_message("WARNING", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001146 va_end(argp);
1147 }
1148}
1149
1150/*
William Lallemand9c56a222018-11-21 18:04:52 +01001151 * Displays the message on stderr with the date and pid.
1152 */
1153void ha_notice(const char *fmt, ...)
1154{
1155 va_list argp;
1156
1157 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1158 va_start(argp, fmt);
1159 print_message("NOTICE", fmt, argp);
1160 va_end(argp);
1161 }
1162}
1163
1164/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001165 * Displays the message on <out> only if quiet mode is not set.
1166 */
Willy Tarreaub17916e2006-10-15 15:17:57 +02001167void qfprintf(FILE *out, const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001168{
1169 va_list argp;
1170
1171 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1172 va_start(argp, fmt);
1173 vfprintf(out, fmt, argp);
1174 fflush(out);
1175 va_end(argp);
1176 }
1177}
1178
1179/*
Dragan Dosen1322d092015-09-22 16:05:32 +02001180 * returns log format for <fmt> or -1 if not found.
1181 */
1182int get_log_format(const char *fmt)
1183{
1184 int format;
1185
1186 format = LOG_FORMATS - 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001187 while (format >= 0 && strcmp(log_formats[format].name, fmt))
Dragan Dosen1322d092015-09-22 16:05:32 +02001188 format--;
1189
1190 return format;
1191}
1192
1193/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001194 * returns log level for <lev> or -1 if not found.
1195 */
1196int get_log_level(const char *lev)
1197{
1198 int level;
1199
1200 level = NB_LOG_LEVELS - 1;
1201 while (level >= 0 && strcmp(log_levels[level], lev))
1202 level--;
1203
1204 return level;
1205}
1206
Willy Tarreaubaaee002006-06-26 02:48:02 +02001207/*
1208 * returns log facility for <fac> or -1 if not found.
1209 */
1210int get_log_facility(const char *fac)
1211{
1212 int facility;
1213
1214 facility = NB_LOG_FACILITIES - 1;
1215 while (facility >= 0 && strcmp(log_facilities[facility], fac))
1216 facility--;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001217
Willy Tarreaubaaee002006-06-26 02:48:02 +02001218 return facility;
1219}
1220
William Lallemanda1cc3812012-02-08 16:38:44 +01001221/*
Dragan Dosen835b9212016-02-12 13:23:03 +01001222 * Encode the string.
1223 *
1224 * When using the +E log format option, it will try to escape '"\]'
1225 * characters with '\' as prefix. The same prefix should not be used as
1226 * <escape>.
1227 */
1228static char *lf_encode_string(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001229 const char escape, const long *map,
Dragan Dosen835b9212016-02-12 13:23:03 +01001230 const char *string,
1231 struct logformat_node *node)
1232{
1233 if (node->options & LOG_OPT_ESC) {
1234 if (start < stop) {
1235 stop--; /* reserve one byte for the final '\0' */
1236 while (start < stop && *string != '\0') {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001237 if (!ha_bit_test((unsigned char)(*string), map)) {
1238 if (!ha_bit_test((unsigned char)(*string), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001239 *start++ = *string;
1240 else {
1241 if (start + 2 >= stop)
1242 break;
1243 *start++ = '\\';
1244 *start++ = *string;
1245 }
1246 }
1247 else {
1248 if (start + 3 >= stop)
1249 break;
1250 *start++ = escape;
1251 *start++ = hextab[(*string >> 4) & 15];
1252 *start++ = hextab[*string & 15];
1253 }
1254 string++;
1255 }
1256 *start = '\0';
1257 }
1258 }
1259 else {
1260 return encode_string(start, stop, escape, map, string);
1261 }
1262
1263 return start;
1264}
1265
1266/*
1267 * Encode the chunk.
1268 *
1269 * When using the +E log format option, it will try to escape '"\]'
1270 * characters with '\' as prefix. The same prefix should not be used as
1271 * <escape>.
1272 */
1273static char *lf_encode_chunk(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001274 const char escape, const long *map,
Willy Tarreau83061a82018-07-13 11:56:34 +02001275 const struct buffer *chunk,
Dragan Dosen835b9212016-02-12 13:23:03 +01001276 struct logformat_node *node)
1277{
1278 char *str, *end;
1279
1280 if (node->options & LOG_OPT_ESC) {
1281 if (start < stop) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001282 str = chunk->area;
1283 end = chunk->area + chunk->data;
Dragan Dosen835b9212016-02-12 13:23:03 +01001284
1285 stop--; /* reserve one byte for the final '\0' */
1286 while (start < stop && str < end) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001287 if (!ha_bit_test((unsigned char)(*str), map)) {
1288 if (!ha_bit_test((unsigned char)(*str), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001289 *start++ = *str;
1290 else {
1291 if (start + 2 >= stop)
1292 break;
1293 *start++ = '\\';
1294 *start++ = *str;
1295 }
1296 }
1297 else {
1298 if (start + 3 >= stop)
1299 break;
1300 *start++ = escape;
1301 *start++ = hextab[(*str >> 4) & 15];
1302 *start++ = hextab[*str & 15];
1303 }
1304 str++;
1305 }
1306 *start = '\0';
1307 }
1308 }
1309 else {
1310 return encode_chunk(start, stop, escape, map, chunk);
1311 }
1312
1313 return start;
1314}
1315
1316/*
William Lallemanda1cc3812012-02-08 16:38:44 +01001317 * Write a string in the log string
Dragan Dosen835b9212016-02-12 13:23:03 +01001318 * Take cares of quote and escape options
William Lallemanda1cc3812012-02-08 16:38:44 +01001319 *
Joseph Herlant85b40592018-11-15 12:10:04 -08001320 * Return the address of the \0 character, or NULL on error
William Lallemanda1cc3812012-02-08 16:38:44 +01001321 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001322char *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 +01001323{
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001324 if (size < 2)
1325 return NULL;
William Lallemanda1cc3812012-02-08 16:38:44 +01001326
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001327 if (node->options & LOG_OPT_QUOTE) {
1328 *(dst++) = '"';
1329 size--;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001330 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001331
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001332 if (src && len) {
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001333 if (++len > size)
1334 len = size;
Dragan Dosen835b9212016-02-12 13:23:03 +01001335 if (node->options & LOG_OPT_ESC) {
Dragan Dosen835b9212016-02-12 13:23:03 +01001336 char *ret;
1337
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001338 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
Dragan Dosen835b9212016-02-12 13:23:03 +01001339 if (ret == NULL || *ret != '\0')
1340 return NULL;
1341 len = ret - dst;
1342 }
1343 else {
Dragan Dosen835b9212016-02-12 13:23:03 +01001344 len = strlcpy2(dst, src, len);
1345 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001346
1347 size -= len;
1348 dst += len;
1349 }
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001350 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1351 if (size < 2)
1352 return NULL;
1353 *(dst++) = '-';
1354 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001355
1356 if (node->options & LOG_OPT_QUOTE) {
1357 if (size < 2)
1358 return NULL;
1359 *(dst++) = '"';
1360 }
1361
1362 *dst = '\0';
William Lallemandbddd4fd2012-02-27 11:23:10 +01001363 return dst;
William Lallemanda1cc3812012-02-08 16:38:44 +01001364}
1365
Willy Tarreau26ffa852018-09-05 15:23:10 +02001366static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001367{
1368 return lf_text_len(dst, src, size, size, node);
1369}
1370
William Lallemand5f232402012-04-05 18:02:55 +02001371/*
Joseph Herlant85b40592018-11-15 12:10:04 -08001372 * Write a IP address to the log string
Ilya Shipitsin856aabc2020-04-16 23:51:34 +05001373 * +X option write in hexadecimal notation, most significant byte on the left
William Lallemand5f232402012-04-05 18:02:55 +02001374 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001375char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001376{
1377 char *ret = dst;
1378 int iret;
1379 char pn[INET6_ADDRSTRLEN];
1380
1381 if (node->options & LOG_OPT_HEXA) {
Radek Zajic594c4562019-03-22 10:21:54 +00001382 unsigned char *addr = NULL;
1383 switch (sockaddr->sa_family) {
1384 case AF_INET:
1385 addr = (unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1386 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1387 break;
1388 case AF_INET6:
1389 addr = (unsigned char *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr;
1390 iret = snprintf(dst, size, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1391 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
1392 addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
1393 break;
1394 default:
1395 return NULL;
1396 }
William Lallemand5f232402012-04-05 18:02:55 +02001397 if (iret < 0 || iret > size)
1398 return NULL;
1399 ret += iret;
1400 } else {
1401 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1402 ret = lf_text(dst, pn, size, node);
1403 if (ret == NULL)
1404 return NULL;
1405 }
1406 return ret;
1407}
1408
1409/*
1410 * Write a port to the log
Ilya Shipitsin856aabc2020-04-16 23:51:34 +05001411 * +X option write in hexadecimal notation, most significant byte on the left
William Lallemand5f232402012-04-05 18:02:55 +02001412 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001413char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001414{
1415 char *ret = dst;
1416 int iret;
1417
1418 if (node->options & LOG_OPT_HEXA) {
1419 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1420 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1421 if (iret < 0 || iret > size)
1422 return NULL;
1423 ret += iret;
1424 } else {
1425 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1426 if (ret == NULL)
1427 return NULL;
1428 }
1429 return ret;
1430}
1431
Dragan Dosen1322d092015-09-22 16:05:32 +02001432/* Re-generate time-based part of the syslog header in RFC3164 format at
1433 * the beginning of logheader once a second and return the pointer to the
1434 * first character after it.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001435 */
Emeric Brunbd163812020-05-06 14:33:46 +02001436char *update_log_hdr(const time_t time)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001437{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001438 static THREAD_LOCAL long tvsec;
Willy Tarreau83061a82018-07-13 11:56:34 +02001439 static THREAD_LOCAL struct buffer host = { };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001440 static THREAD_LOCAL int sep = 0;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001441
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001442 if (unlikely(time != tvsec || logheader_end == NULL)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +02001443 /* this string is rebuild only once a second */
Willy Tarreaufe944602007-10-25 10:34:16 +02001444 struct tm tm;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001445 int hdr_len;
Willy Tarreaufe944602007-10-25 10:34:16 +02001446
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001447 tvsec = time;
Willy Tarreaufe944602007-10-25 10:34:16 +02001448 get_localtime(tvsec, &tm);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001449
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001450 if (unlikely(global.log_send_hostname != host.area)) {
1451 host.area = global.log_send_hostname;
1452 host.data = host.area ? strlen(host.area) : 0;
1453 sep = host.data ? 1 : 0;
Dragan Dosen43885c72015-10-01 13:18:13 +02001454 }
1455
Dragan Dosen59cee972015-09-19 22:09:02 +02001456 hdr_len = snprintf(logheader, global.max_syslog_len,
Dragan Dosen43885c72015-10-01 13:18:13 +02001457 "<<<<>%s %2d %02d:%02d:%02d %.*s%*s",
Willy Tarreaufe944602007-10-25 10:34:16 +02001458 monthname[tm.tm_mon],
Dragan Dosen43885c72015-10-01 13:18:13 +02001459 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001460 (int)host.data, host.area, sep, "");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001461 /* WARNING: depending upon implementations, snprintf may return
1462 * either -1 or the number of bytes that would be needed to store
1463 * the total message. In both cases, we must adjust it.
1464 */
Willy Tarreau18324f52014-06-27 18:10:07 +02001465 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1466 hdr_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001467
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001468 logheader_end = logheader + hdr_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001469 }
1470
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001471 logheader_end[0] = 0; // ensure we get rid of any previous attempt
Willy Tarreau094af4e2015-01-07 15:03:42 +01001472
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001473 return logheader_end;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001474}
1475
Dragan Dosen1322d092015-09-22 16:05:32 +02001476/* Re-generate time-based part of the syslog header in RFC5424 format at
1477 * the beginning of logheader_rfc5424 once a second and return the pointer
1478 * to the first character after it.
1479 */
Emeric Brunbd163812020-05-06 14:33:46 +02001480char *update_log_hdr_rfc5424(const time_t time)
Dragan Dosen1322d092015-09-22 16:05:32 +02001481{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001482 static THREAD_LOCAL long tvsec;
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001483 const char *gmt_offset;
Dragan Dosen1322d092015-09-22 16:05:32 +02001484
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001485 if (unlikely(time != tvsec || logheader_rfc5424_end == NULL)) {
Dragan Dosen1322d092015-09-22 16:05:32 +02001486 /* this string is rebuild only once a second */
1487 struct tm tm;
1488 int hdr_len;
1489
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001490 tvsec = time;
Dragan Dosen1322d092015-09-22 16:05:32 +02001491 get_localtime(tvsec, &tm);
Benoit GARNIERe2e5bde2016-03-27 03:04:16 +02001492 gmt_offset = get_gmt_offset(time, &tm);
Dragan Dosen1322d092015-09-22 16:05:32 +02001493
1494 hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
Dragan Dosen17def462015-10-09 21:31:43 +02001495 "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%.3s:%.2s %s ",
Dragan Dosen1322d092015-09-22 16:05:32 +02001496 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
Dragan Dosen17def462015-10-09 21:31:43 +02001497 tm.tm_hour, tm.tm_min, tm.tm_sec,
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001498 gmt_offset, gmt_offset+3,
Dragan Dosen43885c72015-10-01 13:18:13 +02001499 global.log_send_hostname ? global.log_send_hostname : hostname);
Dragan Dosen1322d092015-09-22 16:05:32 +02001500 /* WARNING: depending upon implementations, snprintf may return
1501 * either -1 or the number of bytes that would be needed to store
1502 * the total message. In both cases, we must adjust it.
1503 */
1504 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1505 hdr_len = global.max_syslog_len;
1506
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001507 logheader_rfc5424_end = logheader_rfc5424 + hdr_len;
Dragan Dosen1322d092015-09-22 16:05:32 +02001508 }
1509
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001510 logheader_rfc5424_end[0] = 0; // ensure we get rid of any previous attempt
Dragan Dosen1322d092015-09-22 16:05:32 +02001511
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001512 return logheader_rfc5424_end;
Dragan Dosen1322d092015-09-22 16:05:32 +02001513}
1514
William Lallemand2a4a44f2012-02-06 16:00:33 +01001515/*
Dragan Dosen59cee972015-09-19 22:09:02 +02001516 * This function sends the syslog message using a printf format string. It
1517 * expects an LF-terminated message.
William Lallemand2a4a44f2012-02-06 16:00:33 +01001518 */
1519void send_log(struct proxy *p, int level, const char *format, ...)
1520{
1521 va_list argp;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001522 int data_len;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001523
Willy Tarreau8c97ab52015-01-15 16:29:53 +01001524 if (level < 0 || format == NULL || logline == NULL)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001525 return;
1526
William Lallemand2a4a44f2012-02-06 16:00:33 +01001527 va_start(argp, format);
Dragan Dosen59cee972015-09-19 22:09:02 +02001528 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
Willy Tarreau18324f52014-06-27 18:10:07 +02001529 if (data_len < 0 || data_len > global.max_syslog_len)
1530 data_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001531 va_end(argp);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001532
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001533 __send_log((p ? &p->logsrvs : NULL), (p ? &p->log_tag : NULL), level,
1534 logline, data_len, default_rfc5424_sd_log_format, 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001535}
1536
1537/*
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001538 * This function sends a syslog message to <logsrv>.
1539 * <pid_str> is the string to be used for the PID of the caller, <pid_size> is length.
1540 * Same thing for <sd> and <sd_size> which are used for the structured-data part
1541 * in RFC5424 formatted syslog messages, and <tag_str> and <tag_size> the syslog tag.
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001542 * It overrides the last byte of the message vector with an LF character.
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001543 * Does not return any error,
William Lallemand2a4a44f2012-02-06 16:00:33 +01001544 */
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001545static inline void __do_send_log(struct logsrv *logsrv, int nblogger, char *pid_str, size_t pid_size,
1546 int level, char *message, size_t size, char *sd, size_t sd_size,
1547 char *tag_str, size_t tag_size)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001548{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001549 static THREAD_LOCAL struct iovec iovec[NB_MSG_IOVEC_ELEMENTS] = { };
1550 static THREAD_LOCAL struct msghdr msghdr = {
1551 //.msg_iov = iovec,
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001552 .msg_iovlen = NB_MSG_IOVEC_ELEMENTS
1553 };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001554 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1555 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
1556 static THREAD_LOCAL char *dataptr = NULL;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001557 time_t time = date.tv_sec;
Emeric Brune709e1e2020-05-06 17:23:59 +02001558 char *hdr, *hdr_ptr = NULL;
Dragan Dosen59cee972015-09-19 22:09:02 +02001559 size_t hdr_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001560 int fac_level;
1561 int *plogfd;
1562 char *pid_sep1 = "", *pid_sep2 = "";
1563 char logheader_short[3];
1564 int sent;
1565 int maxlen;
1566 int hdr_max = 0;
1567 int tag_max = 0;
1568 int pid_sep1_max = 0;
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001569 int pid_max = 0;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001570 int pid_sep2_max = 0;
1571 int sd_max = 0;
1572 int max = 0;
Christopher Fauletf8188c62017-06-02 16:20:16 +02001573
1574 msghdr.msg_iov = iovec;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001575
1576 dataptr = message;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001577
Emeric Brunfa9d7802020-05-28 14:21:33 +02001578 /* historically some messages used to already contain the trailing LF
1579 * or Zero. Let's remove all trailing LF or Zero
1580 */
1581 while (size && ((dataptr[size-1] == '\n' || (dataptr[size-1] == 0))))
Emeric Brun9e8ea0a2020-05-12 19:33:15 +02001582 size--;
1583
Willy Tarreauf3dc30f2019-08-30 14:18:44 +02001584 if (logsrv->type == LOG_TARGET_FD) {
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001585 /* the socket's address is a file descriptor */
1586 plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
Robert Tsai81ae1952007-12-05 10:47:29 +01001587 }
Willy Tarreauc046d162019-08-30 15:24:59 +02001588 else if (logsrv->type == LOG_TARGET_BUFFER) {
1589 plogfd = NULL;
Emeric Brune709e1e2020-05-06 17:23:59 +02001590 goto send;
Willy Tarreauc046d162019-08-30 15:24:59 +02001591 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001592 else if (logsrv->addr.ss_family == AF_UNIX)
1593 plogfd = &logfdunix;
1594 else
1595 plogfd = &logfdinet;
Robert Tsai81ae1952007-12-05 10:47:29 +01001596
Willy Tarreauc046d162019-08-30 15:24:59 +02001597 if (plogfd && unlikely(*plogfd < 0)) {
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001598 /* socket not successfully initialized yet */
1599 if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1600 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
1601 static char once;
William Lallemand0f99e342011-10-12 17:50:54 +02001602
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001603 if (!once) {
1604 once = 1; /* note: no need for atomic ops here */
1605 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
1606 nblogger, strerror(errno), errno);
1607 }
1608 return;
1609 } else {
1610 /* we don't want to receive anything on this socket */
1611 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1612 /* does nothing under Linux, maybe needed for others */
1613 shutdown(*plogfd, SHUT_RD);
1614 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1615 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001616 }
1617
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001618 switch (logsrv->format) {
1619 case LOG_FORMAT_RFC3164:
1620 hdr = logheader;
1621 hdr_ptr = update_log_hdr(time);
1622 break;
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001623
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001624 case LOG_FORMAT_RFC5424:
1625 hdr = logheader_rfc5424;
1626 hdr_ptr = update_log_hdr_rfc5424(time);
1627 sd_max = sd_size; /* the SD part allowed only in RFC5424 */
1628 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001629
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001630 case LOG_FORMAT_SHORT:
1631 /* all fields are known, skip the header generation */
1632 hdr = logheader_short;
1633 hdr[0] = '<';
1634 hdr[1] = '0' + MAX(level, logsrv->minlvl);
1635 hdr[2] = '>';
1636 hdr_ptr = hdr;
1637 hdr_max = 3;
1638 maxlen = logsrv->maxlen - hdr_max;
Emeric Brun9e8ea0a2020-05-12 19:33:15 +02001639 max = MIN(size, maxlen - 1);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001640 goto send;
Willy Tarreau204e3f12018-12-15 15:48:48 +01001641
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001642 case LOG_FORMAT_RAW:
1643 /* all fields are known, skip the header generation */
1644 hdr_ptr = hdr = "";
1645 hdr_max = 0;
1646 maxlen = logsrv->maxlen;
Emeric Brun9e8ea0a2020-05-12 19:33:15 +02001647 max = MIN(size, maxlen - 1);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001648 goto send;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001649
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001650 default:
1651 return; /* must never happen */
1652 }
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001653
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001654 hdr_size = hdr_ptr - hdr;
Dragan Dosen1322d092015-09-22 16:05:32 +02001655
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001656 /* For each target, we may have a different facility.
1657 * We can also have a different log level for each message.
1658 * This induces variations in the message header length.
1659 * Since we don't want to recompute it each time, nor copy it every
1660 * time, we only change the facility in the pre-computed header,
1661 * and we change the pointer to the header accordingly.
1662 */
1663 fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
1664 hdr_ptr = hdr + 3; /* last digit of the log level */
1665 do {
1666 *hdr_ptr = '0' + fac_level % 10;
1667 fac_level /= 10;
1668 hdr_ptr--;
1669 } while (fac_level && hdr_ptr > hdr);
1670 *hdr_ptr = '<';
Dragan Dosen1322d092015-09-22 16:05:32 +02001671
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001672 hdr_max = hdr_size - (hdr_ptr - hdr);
Willy Tarreaue8746a02018-11-12 08:45:00 +01001673
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001674 /* time-based header */
1675 if (unlikely(hdr_size >= logsrv->maxlen)) {
1676 hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
1677 sd_max = 0;
1678 goto send;
1679 }
Willy Tarreauc1b06452018-11-12 11:57:56 +01001680
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001681 maxlen = logsrv->maxlen - hdr_max;
Dragan Dosen1322d092015-09-22 16:05:32 +02001682
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001683 /* tag */
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001684 tag_max = tag_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001685 if (unlikely(tag_max >= maxlen)) {
1686 tag_max = maxlen - 1;
1687 sd_max = 0;
1688 goto send;
1689 }
Dragan Dosen1322d092015-09-22 16:05:32 +02001690
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001691 maxlen -= tag_max;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001692
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001693 /* first pid separator */
1694 pid_sep1_max = log_formats[logsrv->format].pid.sep1.data;
1695 if (unlikely(pid_sep1_max >= maxlen)) {
1696 pid_sep1_max = maxlen - 1;
1697 sd_max = 0;
1698 goto send;
1699 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001700
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001701 pid_sep1 = log_formats[logsrv->format].pid.sep1.area;
1702 maxlen -= pid_sep1_max;
Dragan Dosen59cee972015-09-19 22:09:02 +02001703
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001704 /* pid */
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001705 pid_max = pid_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001706 if (unlikely(pid_size >= maxlen)) {
1707 pid_size = maxlen - 1;
1708 sd_max = 0;
1709 goto send;
1710 }
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001711
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001712 maxlen -= pid_size;
Dragan Dosen43885c72015-10-01 13:18:13 +02001713
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001714 /* second pid separator */
1715 pid_sep2_max = log_formats[logsrv->format].pid.sep2.data;
1716 if (unlikely(pid_sep2_max >= maxlen)) {
1717 pid_sep2_max = maxlen - 1;
1718 sd_max = 0;
1719 goto send;
1720 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001721
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001722 pid_sep2 = log_formats[logsrv->format].pid.sep2.area;
1723 maxlen -= pid_sep2_max;
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001724
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001725 /* structured-data */
1726 if (sd_max >= maxlen) {
1727 sd_max = maxlen - 1;
1728 goto send;
1729 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001730
Emeric Brun9e8ea0a2020-05-12 19:33:15 +02001731 max = MIN(size, maxlen - sd_max - 1);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001732send:
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001733 if (logsrv->addr.ss_family == AF_UNSPEC) {
Willy Tarreauc046d162019-08-30 15:24:59 +02001734 /* the target is a file descriptor or a ring buffer */
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001735 struct ist msg[7];
1736
Emeric Brune709e1e2020-05-06 17:23:59 +02001737 if (logsrv->type == LOG_TARGET_BUFFER) {
1738 msg[0] = ist2(message, MIN(size, logsrv->maxlen));
1739 msg[1] = ist2(tag_str, tag_size);
1740 msg[2] = ist2(pid_str, pid_size);
1741 msg[3] = ist2(sd, sd_size);
1742 sent = sink_write(logsrv->sink, msg, 1, level, logsrv->facility, &msg[1], &msg[2], &msg[3]);
1743 }
1744 else /* LOG_TARGET_FD */ {
1745 msg[0] = ist2(hdr_ptr, hdr_max);
1746 msg[1] = ist2(tag_str, tag_max);
1747 msg[2] = ist2(pid_sep1, pid_sep1_max);
1748 msg[3] = ist2(pid_str, pid_max);
1749 msg[4] = ist2(pid_sep2, pid_sep2_max);
1750 msg[5] = ist2(sd, sd_max);
1751 msg[6] = ist2(dataptr, max);
Willy Tarreauc046d162019-08-30 15:24:59 +02001752 sent = fd_write_frag_line(*plogfd, ~0, NULL, 0, msg, 7, 1);
Emeric Brune709e1e2020-05-06 17:23:59 +02001753 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001754 }
1755 else {
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001756 iovec[0].iov_base = hdr_ptr;
1757 iovec[0].iov_len = hdr_max;
1758 iovec[1].iov_base = tag_str;
1759 iovec[1].iov_len = tag_max;
1760 iovec[2].iov_base = pid_sep1;
1761 iovec[2].iov_len = pid_sep1_max;
1762 iovec[3].iov_base = pid_str;
1763 iovec[3].iov_len = pid_max;
1764 iovec[4].iov_base = pid_sep2;
1765 iovec[4].iov_len = pid_sep2_max;
1766 iovec[5].iov_base = sd;
1767 iovec[5].iov_len = sd_max;
1768 iovec[6].iov_base = dataptr;
1769 iovec[6].iov_len = max;
1770 iovec[7].iov_base = "\n"; /* insert a \n at the end of the message */
1771 iovec[7].iov_len = 1;
1772
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001773 msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
1774 msghdr.msg_namelen = get_addr_len(&logsrv->addr);
Dragan Dosen43885c72015-10-01 13:18:13 +02001775
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001776 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1777 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001778
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001779 if (sent < 0) {
1780 static char once;
Dragan Dosen43885c72015-10-01 13:18:13 +02001781
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001782 if (errno == EAGAIN)
1783 _HA_ATOMIC_ADD(&dropped_logs, 1);
1784 else if (!once) {
1785 once = 1; /* note: no need for atomic ops here */
1786 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
1787 nblogger, strerror(errno), errno);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001788 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001789 }
1790}
Dragan Dosen59cee972015-09-19 22:09:02 +02001791
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001792/*
1793 * This function sends a syslog message.
1794 * It doesn't care about errors nor does it report them.
1795 * The arguments <sd> and <sd_size> are used for the structured-data part
1796 * in RFC5424 formatted syslog messages.
1797 */
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001798void __send_log(struct list *logsrvs, struct buffer *tag, int level,
1799 char *message, size_t size, char *sd, size_t sd_size)
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001800{
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001801 struct logsrv *logsrv;
1802 int nblogger;
1803 static THREAD_LOCAL int curr_pid;
1804 static THREAD_LOCAL char pidstr[100];
1805 static THREAD_LOCAL struct buffer pid;
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001806
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001807 if (logsrvs == NULL) {
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001808 if (!LIST_ISEMPTY(&global.logsrvs)) {
1809 logsrvs = &global.logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001810 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001811 }
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001812 if (!tag || !tag->area)
1813 tag = &global.log_tag;
Willy Tarreau18324f52014-06-27 18:10:07 +02001814
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001815 if (!logsrvs || LIST_ISEMPTY(logsrvs))
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001816 return;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001817
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001818 if (unlikely(curr_pid != getpid())) {
1819 curr_pid = getpid();
1820 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1821 chunk_initstr(&pid, pidstr);
1822 }
1823
1824 /* Send log messages to syslog server. */
1825 nblogger = 0;
1826 list_for_each_entry(logsrv, logsrvs, list) {
Frédéric Lécailled803e472019-04-25 07:42:09 +02001827 static THREAD_LOCAL int in_range = 1;
1828
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001829 /* we can filter the level of the messages that are sent to each logger */
1830 if (level > logsrv->level)
1831 continue;
1832
Frédéric Lécailled803e472019-04-25 07:42:09 +02001833 if (logsrv->lb.smp_rgs) {
1834 struct smp_log_range *curr_rg;
1835
1836 HA_SPIN_LOCK(LOGSRV_LOCK, &logsrv->lock);
1837 curr_rg = &logsrv->lb.smp_rgs[logsrv->lb.curr_rg];
1838 in_range = in_smp_log_range(curr_rg, logsrv->lb.curr_idx);
1839 if (in_range) {
1840 /* Let's consume this range. */
1841 curr_rg->curr_idx = (curr_rg->curr_idx + 1) % curr_rg->sz;
1842 if (!curr_rg->curr_idx) {
1843 /* If consumed, let's select the next range. */
1844 logsrv->lb.curr_rg = (logsrv->lb.curr_rg + 1) % logsrv->lb.smp_rgs_sz;
1845 }
1846 }
1847 logsrv->lb.curr_idx = (logsrv->lb.curr_idx + 1) % logsrv->lb.smp_sz;
1848 HA_SPIN_UNLOCK(LOGSRV_LOCK, &logsrv->lock);
1849 }
1850 if (in_range)
1851 __do_send_log(logsrv, ++nblogger, pid.area, pid.data, level,
1852 message, size, sd, sd_size, tag->area, tag->data);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001853 }
1854}
1855
1856
Willy Tarreauc89ccb62012-04-05 21:18:22 +02001857const 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 +01001858const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1859 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1860 Set-cookie Updated, unknown, unknown */
1861
William Lallemand1d705562012-03-12 12:46:41 +01001862/*
1863 * try to write a character if there is enough space, or goto out
1864 */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001865#define LOGCHAR(x) do { \
William Lallemand1d705562012-03-12 12:46:41 +01001866 if (tmplog < dst + maxsize - 1) { \
William Lallemandbddd4fd2012-02-27 11:23:10 +01001867 *(tmplog++) = (x); \
1868 } else { \
1869 goto out; \
1870 } \
1871 } while(0)
1872
Dragan Dosen835b9212016-02-12 13:23:03 +01001873
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001874/* Initializes some log data at boot */
1875static void init_log()
Dragan Dosen835b9212016-02-12 13:23:03 +01001876{
1877 char *tmp;
Willy Tarreaue10cd482018-09-10 18:16:53 +02001878 int i;
Dragan Dosen835b9212016-02-12 13:23:03 +01001879
1880 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1881 * inside PARAM-VALUE should be escaped with '\' as prefix.
1882 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1883 * details.
1884 */
1885 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1886
1887 tmp = "\"\\]";
1888 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001889 ha_bit_set(*tmp, rfc5424_escape_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001890 tmp++;
1891 }
Willy Tarreaue10cd482018-09-10 18:16:53 +02001892
1893 /* initialize the log header encoding map : '{|}"#' should be encoded with
1894 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1895 * URL encoding only requires '"', '#' to be encoded as well as non-
1896 * printable characters above.
1897 */
1898 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1899 memset(url_encode_map, 0, sizeof(url_encode_map));
1900 for (i = 0; i < 32; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001901 ha_bit_set(i, hdr_encode_map);
1902 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001903 }
1904 for (i = 127; i < 256; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001905 ha_bit_set(i, hdr_encode_map);
1906 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001907 }
1908
1909 tmp = "\"#{|}";
1910 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001911 ha_bit_set(*tmp, hdr_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001912 tmp++;
1913 }
1914
1915 tmp = "\"#";
1916 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001917 ha_bit_set(*tmp, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001918 tmp++;
1919 }
1920
1921 /* initialize the http header encoding map. The draft httpbis define the
1922 * header content as:
1923 *
1924 * HTTP-message = start-line
1925 * *( header-field CRLF )
1926 * CRLF
1927 * [ message-body ]
1928 * header-field = field-name ":" OWS field-value OWS
1929 * field-value = *( field-content / obs-fold )
1930 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1931 * obs-fold = CRLF 1*( SP / HTAB )
1932 * field-vchar = VCHAR / obs-text
1933 * VCHAR = %x21-7E
1934 * obs-text = %x80-FF
1935 *
1936 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1937 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
Joseph Herlant85b40592018-11-15 12:10:04 -08001938 * "obs-fold" is voluntarily forgotten because haproxy remove this.
Willy Tarreaue10cd482018-09-10 18:16:53 +02001939 */
1940 memset(http_encode_map, 0, sizeof(http_encode_map));
1941 for (i = 0x00; i <= 0x08; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001942 ha_bit_set(i, http_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001943 for (i = 0x0a; i <= 0x1f; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001944 ha_bit_set(i, http_encode_map);
1945 ha_bit_set(0x7f, http_encode_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001946}
William Lallemand1d705562012-03-12 12:46:41 +01001947
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001948INITCALL0(STG_PREPARE, init_log);
1949
Christopher Faulet0132d062017-07-26 15:33:35 +02001950/* Initialize log buffers used for syslog messages */
1951int init_log_buffers()
1952{
1953 logheader = my_realloc2(logheader, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001954 logheader_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001955 logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001956 logheader_rfc5424_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001957 logline = my_realloc2(logline, global.max_syslog_len + 1);
1958 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
1959 if (!logheader || !logline_rfc5424 || !logline || !logline_rfc5424)
1960 return 0;
1961 return 1;
1962}
1963
1964/* Deinitialize log buffers used for syslog messages */
1965void deinit_log_buffers()
1966{
1967 free(logheader);
1968 free(logheader_rfc5424);
1969 free(logline);
1970 free(logline_rfc5424);
Willy Tarreau869efd52019-11-15 15:16:57 +01001971 ring_free(_HA_ATOMIC_XCHG(&startup_logs, NULL));
Christopher Faulet0132d062017-07-26 15:33:35 +02001972 logheader = NULL;
1973 logheader_rfc5424 = NULL;
1974 logline = NULL;
1975 logline_rfc5424 = NULL;
1976}
1977
Willy Tarreaudf974472012-12-28 02:44:01 +01001978/* Builds a log line in <dst> based on <list_format>, and stops before reaching
1979 * <maxsize> characters. Returns the size of the output string in characters,
1980 * not counting the trailing zero which is always added if the resulting size
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001981 * is not zero. It requires a valid session and optionally a stream. If the
1982 * stream is NULL, default values will be assumed for the stream part.
Willy Tarreaudf974472012-12-28 02:44:01 +01001983 */
Willy Tarreau43c538e2018-09-05 14:58:15 +02001984int 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 +02001985{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02001986 struct proxy *fe = sess->fe;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001987 struct proxy *be;
1988 struct http_txn *txn;
1989 const struct strm_logs *logs;
Willy Tarreau8fa99842019-07-17 11:47:11 +02001990 struct connection *be_conn;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001991 unsigned int s_flags;
1992 unsigned int uniq_id;
Willy Tarreau83061a82018-07-13 11:56:34 +02001993 struct buffer chunk;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001994 char *uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001995 char *spc;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00001996 char *qmark;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001997 char *end;
Willy Tarreaufe944602007-10-25 10:34:16 +02001998 struct tm tm;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001999 int t_request;
2000 int hdr;
2001 int last_isspace = 1;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002002 int nspaces = 0;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01002003 char *tmplog;
William Lallemand1d705562012-03-12 12:46:41 +01002004 char *ret;
2005 int iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002006 struct logformat_node *tmp;
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002007 struct timeval tv;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002008 struct strm_logs tmp_strm_log;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002009
William Lallemandbddd4fd2012-02-27 11:23:10 +01002010 /* FIXME: let's limit ourselves to frontend logging for now. */
Willy Tarreaubaaee002006-06-26 02:48:02 +02002011
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002012 if (likely(s)) {
2013 be = s->be;
2014 txn = s->txn;
2015 be_conn = cs_conn(objt_cs(s->si[1].end));
2016 s_flags = s->flags;
2017 uniq_id = s->uniq_id;
2018 logs = &s->logs;
2019 } else {
2020 /* we have no stream so we first need to initialize a few
2021 * things that are needed later. We do increment the request
2022 * ID so that it's uniquely assigned to this request just as
2023 * if the request had reached the point of being processed.
2024 * A request error is reported as it's the only element we have
2025 * here and which justifies emitting such a log.
2026 */
2027 be = fe;
2028 txn = NULL;
2029 be_conn = NULL;
2030 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002031 uniq_id = _HA_ATOMIC_XADD(&global.req_count, 1);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002032
2033 /* prepare a valid log structure */
2034 tmp_strm_log.tv_accept = sess->tv_accept;
2035 tmp_strm_log.accept_date = sess->accept_date;
2036 tmp_strm_log.t_handshake = sess->t_handshake;
2037 tmp_strm_log.t_idle = tv_ms_elapsed(&sess->tv_accept, &now) - sess->t_handshake;
2038 tv_zero(&tmp_strm_log.tv_request);
2039 tmp_strm_log.t_queue = -1;
2040 tmp_strm_log.t_connect = -1;
2041 tmp_strm_log.t_data = -1;
2042 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
2043 tmp_strm_log.bytes_in = 0;
2044 tmp_strm_log.bytes_out = 0;
2045 tmp_strm_log.prx_queue_pos = 0;
2046 tmp_strm_log.srv_queue_pos = 0;
2047
2048 logs = &tmp_strm_log;
2049 }
2050
William Lallemandbddd4fd2012-02-27 11:23:10 +01002051 t_request = -1;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002052 if (tv_isge(&logs->tv_request, &logs->tv_accept))
2053 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
William Lallemandbddd4fd2012-02-27 11:23:10 +01002054
William Lallemand1d705562012-03-12 12:46:41 +01002055 tmplog = dst;
Willy Tarreauc9bd0cc2009-05-10 11:57:02 +02002056
William Lallemandbddd4fd2012-02-27 11:23:10 +01002057 /* fill logbuffer */
William Lallemand1d705562012-03-12 12:46:41 +01002058 if (LIST_ISEMPTY(list_format))
2059 return 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002060
William Lallemand1d705562012-03-12 12:46:41 +01002061 list_for_each_entry(tmp, list_format, list) {
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002062 struct connection *conn;
Willy Tarreau4f653562012-10-12 19:48:16 +02002063 const char *src = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +01002064 struct sample *key;
Willy Tarreau83061a82018-07-13 11:56:34 +02002065 const struct buffer empty = { };
William Lallemandbddd4fd2012-02-27 11:23:10 +01002066
Willy Tarreauc8368452012-12-21 00:09:23 +01002067 switch (tmp->type) {
William Lallemand1d705562012-03-12 12:46:41 +01002068 case LOG_FMT_SEPARATOR:
William Lallemandbddd4fd2012-02-27 11:23:10 +01002069 if (!last_isspace) {
2070 LOGCHAR(' ');
2071 last_isspace = 1;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002072 }
2073 break;
2074
William Lallemand1d705562012-03-12 12:46:41 +01002075 case LOG_FMT_TEXT: // text
William Lallemandbddd4fd2012-02-27 11:23:10 +01002076 src = tmp->arg;
William Lallemand5f232402012-04-05 18:02:55 +02002077 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002078 if (iret == 0)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002079 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002080 tmplog += iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002081 last_isspace = 0;
2082 break;
2083
Willy Tarreauc8368452012-12-21 00:09:23 +01002084 case LOG_FMT_EXPR: // sample expression, may be request or response
2085 key = NULL;
Christopher Faulet5f940702020-04-06 10:40:02 +02002086 if (tmp->options & LOG_OPT_REQ_CAP)
Adis Nezirovic79beb242015-07-06 15:41:02 +02002087 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 +02002088 if (!key && (tmp->options & LOG_OPT_RES_CAP))
Adis Nezirovic79beb242015-07-06 15:41:02 +02002089 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 +01002090 if (tmp->options & LOG_OPT_HTTP)
Dragan Dosen835b9212016-02-12 13:23:03 +01002091 ret = lf_encode_chunk(tmplog, dst + maxsize,
2092 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01002093 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002094 ret = lf_text_len(tmplog,
2095 key ? key->data.u.str.area : NULL,
2096 key ? key->data.u.str.data : 0,
2097 dst + maxsize - tmplog,
2098 tmp);
Willy Tarreauc8368452012-12-21 00:09:23 +01002099 if (ret == 0)
2100 goto out;
2101 tmplog = ret;
2102 last_isspace = 0;
2103 break;
2104
Willy Tarreau2beef582012-12-20 17:22:52 +01002105 case LOG_FMT_CLIENTIP: // %ci
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002106 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002107 if (conn && conn_get_src(conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002108 ret = lf_ip(tmplog, (struct sockaddr *)conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002109 else
2110 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002111 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002112 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002113 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002114 last_isspace = 0;
2115 break;
2116
Willy Tarreau2beef582012-12-20 17:22:52 +01002117 case LOG_FMT_CLIENTPORT: // %cp
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 if (conn->src->ss_family == AF_UNIX) {
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002121 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002122 } else {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002123 ret = lf_port(tmplog, (struct sockaddr *)conn->src,
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002124 dst + maxsize - tmplog, tmp);
2125 }
William Lallemand5f232402012-04-05 18:02:55 +02002126 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002127 else
2128 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2129
William Lallemand5f232402012-04-05 18:02:55 +02002130 if (ret == NULL)
2131 goto out;
2132 tmplog = ret;
2133 last_isspace = 0;
2134 break;
2135
Willy Tarreau2beef582012-12-20 17:22:52 +01002136 case LOG_FMT_FRONTENDIP: // %fi
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002137 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002138 if (conn && conn_get_dst(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002139 ret = lf_ip(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002140 }
2141 else
2142 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2143
William Lallemand1d705562012-03-12 12:46:41 +01002144 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002145 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002146 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002147 last_isspace = 0;
2148 break;
2149
Willy Tarreau2beef582012-12-20 17:22:52 +01002150 case LOG_FMT_FRONTENDPORT: // %fp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002151 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002152 if (conn && conn_get_dst(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002153 if (conn->dst->ss_family == AF_UNIX)
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002154 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002155 else
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002156 ret = lf_port(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
William Lallemand5f232402012-04-05 18:02:55 +02002157 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002158 else
2159 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2160
William Lallemand5f232402012-04-05 18:02:55 +02002161 if (ret == NULL)
2162 goto out;
2163 tmplog = ret;
2164 last_isspace = 0;
2165 break;
2166
Willy Tarreau2beef582012-12-20 17:22:52 +01002167 case LOG_FMT_BACKENDIP: // %bi
Willy Tarreau8fa99842019-07-17 11:47:11 +02002168 if (be_conn && conn_get_src(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002169 ret = lf_ip(tmplog, (const struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002170 else
2171 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2172
William Lallemand1d705562012-03-12 12:46:41 +01002173 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002174 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002175 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002176 last_isspace = 0;
2177 break;
2178
Willy Tarreau2beef582012-12-20 17:22:52 +01002179 case LOG_FMT_BACKENDPORT: // %bp
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_port(tmplog, (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 Lallemand5f232402012-04-05 18:02:55 +02002185 if (ret == NULL)
2186 goto out;
2187 tmplog = ret;
2188 last_isspace = 0;
2189 break;
2190
Willy Tarreau2beef582012-12-20 17:22:52 +01002191 case LOG_FMT_SERVERIP: // %si
Willy Tarreau8fa99842019-07-17 11:47:11 +02002192 if (be_conn && conn_get_dst(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002193 ret = lf_ip(tmplog, (struct sockaddr *)be_conn->dst, 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_SERVERPORT: // %sp
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_port(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 Lallemand1d705562012-03-12 12:46:41 +01002209 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002210 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002211 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002212 last_isspace = 0;
2213 break;
2214
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002215 case LOG_FMT_DATE: // %t = accept date
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002216 get_localtime(logs->accept_date.tv_sec, &tm);
2217 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002218 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002219 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002220 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002221 last_isspace = 0;
2222 break;
2223
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002224 case LOG_FMT_tr: // %tr = start of request date
2225 /* Note that the timers are valid if we get here */
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002226 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 +02002227 get_localtime(tv.tv_sec, &tm);
2228 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
2229 if (ret == NULL)
2230 goto out;
2231 tmplog = ret;
2232 last_isspace = 0;
2233 break;
2234
2235 case LOG_FMT_DATEGMT: // %T = accept date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002236 get_gmtime(logs->accept_date.tv_sec, &tm);
William Lallemand5f232402012-04-05 18:02:55 +02002237 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002238 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002239 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002240 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002241 last_isspace = 0;
2242 break;
2243
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002244 case LOG_FMT_trg: // %trg = start of request date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002245 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 +02002246 get_gmtime(tv.tv_sec, &tm);
2247 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2248 if (ret == NULL)
2249 goto out;
2250 tmplog = ret;
2251 last_isspace = 0;
2252 break;
2253
2254 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002255 get_localtime(logs->accept_date.tv_sec, &tm);
2256 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
Yuxans Yao4e25b012012-10-19 10:36:09 +08002257 if (ret == NULL)
2258 goto out;
2259 tmplog = ret;
2260 last_isspace = 0;
2261 break;
2262
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002263 case LOG_FMT_trl: // %trl = start of request date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002264 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 +02002265 get_localtime(tv.tv_sec, &tm);
2266 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
2267 if (ret == NULL)
2268 goto out;
2269 tmplog = ret;
2270 last_isspace = 0;
2271 break;
2272
William Lallemand5f232402012-04-05 18:02:55 +02002273 case LOG_FMT_TS: // %Ts
William Lallemand5f232402012-04-05 18:02:55 +02002274 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002275 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
William Lallemand5f232402012-04-05 18:02:55 +02002276 if (iret < 0 || iret > dst + maxsize - tmplog)
2277 goto out;
2278 last_isspace = 0;
2279 tmplog += iret;
2280 } else {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002281 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002282 if (ret == NULL)
2283 goto out;
2284 tmplog = ret;
2285 last_isspace = 0;
2286 }
2287 break;
2288
William Lallemand1d705562012-03-12 12:46:41 +01002289 case LOG_FMT_MS: // %ms
William Lallemand5f232402012-04-05 18:02:55 +02002290 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002291 iret = snprintf(tmplog, dst + maxsize - tmplog, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
William Lallemand5f232402012-04-05 18:02:55 +02002292 if (iret < 0 || iret > dst + maxsize - tmplog)
2293 goto out;
2294 last_isspace = 0;
2295 tmplog += iret;
2296 } else {
2297 if ((dst + maxsize - tmplog) < 4)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002298 goto out;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002299 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002300 tmplog, 4);
2301 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002302 goto out;
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002303 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002304 last_isspace = 0;
William Lallemand5f232402012-04-05 18:02:55 +02002305 }
2306 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002307
William Lallemand1d705562012-03-12 12:46:41 +01002308 case LOG_FMT_FRONTEND: // %f
William Lallemandbddd4fd2012-02-27 11:23:10 +01002309 src = fe->id;
William Lallemand5f232402012-04-05 18:02:55 +02002310 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002311 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002312 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002313 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002314 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002315 break;
2316
Willy Tarreau773d65f2012-10-12 14:56:11 +02002317 case LOG_FMT_FRONTEND_XPRT: // %ft
2318 src = fe->id;
2319 if (tmp->options & LOG_OPT_QUOTE)
2320 LOGCHAR('"');
2321 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2322 if (iret == 0)
2323 goto out;
2324 tmplog += iret;
Willy Tarreaua261e9b2016-12-22 20:44:00 +01002325 if (sess->listener->bind_conf->xprt == xprt_get(XPRT_SSL))
Willy Tarreau773d65f2012-10-12 14:56:11 +02002326 LOGCHAR('~');
Willy Tarreau773d65f2012-10-12 14:56:11 +02002327 if (tmp->options & LOG_OPT_QUOTE)
2328 LOGCHAR('"');
2329 last_isspace = 0;
2330 break;
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002331#ifdef USE_OPENSSL
2332 case LOG_FMT_SSL_CIPHER: // %sslc
2333 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002334 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002335 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002336 src = ssl_sock_get_cipher_name(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002337 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002338 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2339 if (ret == NULL)
2340 goto out;
2341 tmplog = ret;
2342 last_isspace = 0;
2343 break;
Willy Tarreau773d65f2012-10-12 14:56:11 +02002344
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002345 case LOG_FMT_SSL_VERSION: // %sslv
2346 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002347 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002348 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002349 src = ssl_sock_get_proto_version(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002350 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002351 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2352 if (ret == NULL)
2353 goto out;
2354 tmplog = ret;
2355 last_isspace = 0;
2356 break;
2357#endif
William Lallemand1d705562012-03-12 12:46:41 +01002358 case LOG_FMT_BACKEND: // %b
William Lallemandbddd4fd2012-02-27 11:23:10 +01002359 src = be->id;
William Lallemand5f232402012-04-05 18:02:55 +02002360 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002361 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002362 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002363 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002364 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002365 break;
2366
William Lallemand1d705562012-03-12 12:46:41 +01002367 case LOG_FMT_SERVER: // %s
Willy Tarreaue1809df2018-09-05 15:30:16 +02002368 switch (obj_type(s ? s->target : NULL)) {
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002369 case OBJ_TYPE_SERVER:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002370 src = __objt_server(s->target)->id;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002371 break;
2372 case OBJ_TYPE_APPLET:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002373 src = __objt_applet(s->target)->name;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002374 break;
2375 default:
2376 src = "<NOSRV>";
2377 break;
2378 }
William Lallemand5f232402012-04-05 18:02:55 +02002379 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002380 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002381 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002382 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002383 last_isspace = 0;
2384 break;
2385
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002386 case LOG_FMT_Th: // %Th = handshake time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002387 ret = ltoa_o(logs->t_handshake, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002388 if (ret == NULL)
2389 goto out;
2390 tmplog = ret;
2391 last_isspace = 0;
2392 break;
2393
2394 case LOG_FMT_Ti: // %Ti = HTTP idle time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002395 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002396 if (ret == NULL)
2397 goto out;
2398 tmplog = ret;
2399 last_isspace = 0;
2400 break;
2401
2402 case LOG_FMT_TR: // %TR = HTTP request time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002403 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002404 tmplog, dst + maxsize - tmplog);
2405 if (ret == NULL)
2406 goto out;
2407 tmplog = ret;
2408 last_isspace = 0;
2409 break;
2410
2411 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
William Lallemand5f232402012-04-05 18:02:55 +02002412 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002413 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002414 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002415 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002416 last_isspace = 0;
2417 break;
2418
William Lallemand1d705562012-03-12 12:46:41 +01002419 case LOG_FMT_TW: // %Tw
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002420 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002421 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002422 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002423 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002424 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002425 last_isspace = 0;
2426 break;
2427
William Lallemand1d705562012-03-12 12:46:41 +01002428 case LOG_FMT_TC: // %Tc
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002429 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002430 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002431 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002432 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002433 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002434 last_isspace = 0;
2435 break;
2436
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002437 case LOG_FMT_Tr: // %Tr
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002438 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002439 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002440 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002441 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002442 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002443 last_isspace = 0;
2444 break;
2445
Willy Tarreau27b639d2016-05-17 17:55:27 +02002446 case LOG_FMT_TD: // %Td
Willy Tarreaua21c0e62018-09-05 15:07:15 +02002447 if (be->mode == PR_MODE_HTTP)
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002448 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002449 tmplog, dst + maxsize - tmplog);
2450 else
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002451 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002452 tmplog, dst + maxsize - tmplog);
2453 if (ret == NULL)
2454 goto out;
2455 tmplog = ret;
2456 last_isspace = 0;
2457 break;
2458
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002459 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2460 if (!(fe->to_log & LW_BYTES))
2461 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002462 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 +02002463 tmplog, dst + maxsize - tmplog);
2464 if (ret == NULL)
2465 goto out;
2466 tmplog = ret;
2467 last_isspace = 0;
2468 break;
2469
2470 case LOG_FMT_TT: // %Tt = total time
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002471 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002472 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002473 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002474 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002475 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002476 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002477 last_isspace = 0;
2478 break;
2479
Damien Claisse57c8eb92020-04-28 12:09:19 +00002480 case LOG_FMT_TU: // %Tu = total time seen by user = Tt - Ti
2481 if (!(fe->to_log & LW_BYTES))
2482 LOGCHAR('+');
2483 ret = ltoa_o(logs->t_close - (logs->t_idle >= 0 ? logs->t_idle : 0),
2484 tmplog, dst + maxsize - tmplog);
2485 if (ret == NULL)
2486 goto out;
2487 tmplog = ret;
2488 last_isspace = 0;
2489 break;
2490
Willy Tarreau2beef582012-12-20 17:22:52 +01002491 case LOG_FMT_STATUS: // %ST
Willy Tarreau57bc8912016-04-25 17:09:40 +02002492 ret = ltoa_o(txn ? txn->status : 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002493 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002494 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002495 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002496 last_isspace = 0;
2497 break;
2498
William Lallemand1d705562012-03-12 12:46:41 +01002499 case LOG_FMT_BYTES: // %B
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002500 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002501 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002502 ret = lltoa(logs->bytes_out, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002503 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002504 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002505 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002506 last_isspace = 0;
2507 break;
2508
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002509 case LOG_FMT_BYTES_UP: // %U
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002510 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002511 if (ret == NULL)
2512 goto out;
2513 tmplog = ret;
2514 last_isspace = 0;
2515 break;
2516
Willy Tarreau2beef582012-12-20 17:22:52 +01002517 case LOG_FMT_CCLIENT: // %CC
Willy Tarreau57bc8912016-04-25 17:09:40 +02002518 src = txn ? txn->cli_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002519 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002520 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002521 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002522 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002523 last_isspace = 0;
2524 break;
2525
Willy Tarreau2beef582012-12-20 17:22:52 +01002526 case LOG_FMT_CSERVER: // %CS
Willy Tarreau57bc8912016-04-25 17:09:40 +02002527 src = txn ? txn->srv_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002528 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002529 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002530 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002531 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002532 last_isspace = 0;
2533 break;
2534
William Lallemand1d705562012-03-12 12:46:41 +01002535 case LOG_FMT_TERMSTATE: // %ts
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002536 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2537 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau6580c062012-03-12 15:09:42 +01002538 *tmplog = '\0';
2539 last_isspace = 0;
2540 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002541
William Lallemand1d705562012-03-12 12:46:41 +01002542 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002543 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2544 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau57bc8912016-04-25 17:09:40 +02002545 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2546 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 +01002547 last_isspace = 0;
2548 break;
2549
William Lallemand1d705562012-03-12 12:46:41 +01002550 case LOG_FMT_ACTCONN: // %ac
William Lallemand5f232402012-04-05 18:02:55 +02002551 ret = ltoa_o(actconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002552 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002553 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002554 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002555 last_isspace = 0;
2556 break;
2557
William Lallemand1d705562012-03-12 12:46:41 +01002558 case LOG_FMT_FECONN: // %fc
William Lallemand5f232402012-04-05 18:02:55 +02002559 ret = ltoa_o(fe->feconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002560 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002561 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002562 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002563 last_isspace = 0;
2564 break;
2565
William Lallemand1d705562012-03-12 12:46:41 +01002566 case LOG_FMT_BECONN: // %bc
William Lallemand5f232402012-04-05 18:02:55 +02002567 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002568 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002569 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002570 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002571 last_isspace = 0;
2572 break;
2573
William Lallemand1d705562012-03-12 12:46:41 +01002574 case LOG_FMT_SRVCONN: // %sc
Willy Tarreaue1809df2018-09-05 15:30:16 +02002575 ret = ultoa_o(objt_server(s ? s->target : NULL) ?
Willy Tarreau3fdb3662012-11-12 00:42:33 +01002576 objt_server(s->target)->cur_sess :
William Lallemand5f232402012-04-05 18:02:55 +02002577 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002578 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002579 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002580 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002581 last_isspace = 0;
2582 break;
2583
William Lallemand1d705562012-03-12 12:46:41 +01002584 case LOG_FMT_RETRIES: // %rq
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002585 if (s_flags & SF_REDISP)
William Lallemand1d705562012-03-12 12:46:41 +01002586 LOGCHAR('+');
Willy Tarreauabd71a52018-09-04 19:21:44 +02002587 ret = ltoa_o((s && s->si[1].conn_retries > 0) ?
Willy Tarreau350f4872014-11-28 14:42:25 +01002588 (be->conn_retries - s->si[1].conn_retries) :
William Lallemand5f232402012-04-05 18:02:55 +02002589 be->conn_retries, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002590 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002591 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_SRVQUEUE: // %sq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002597 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002598 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002599 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002600 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002601 last_isspace = 0;
2602 break;
2603
William Lallemand1d705562012-03-12 12:46:41 +01002604 case LOG_FMT_BCKQUEUE: // %bq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002605 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002606 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002607 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002608 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002609 last_isspace = 0;
2610 break;
2611
William Lallemand1d705562012-03-12 12:46:41 +01002612 case LOG_FMT_HDRREQUEST: // %hr
William Lallemandbddd4fd2012-02-27 11:23:10 +01002613 /* request header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002614 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002615 if (tmp->options & LOG_OPT_QUOTE)
2616 LOGCHAR('"');
2617 LOGCHAR('{');
2618 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2619 if (hdr)
2620 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002621 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002622 ret = lf_encode_string(tmplog, dst + maxsize,
2623 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002624 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002625 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002626 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002627 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002628 }
2629 LOGCHAR('}');
William Lallemand51b5dca2012-03-26 17:52:55 +02002630 if (tmp->options & LOG_OPT_QUOTE)
2631 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002632 last_isspace = 0;
2633 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002634 break;
2635
William Lallemand1d705562012-03-12 12:46:41 +01002636 case LOG_FMT_HDRREQUESTLIST: // %hrl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002637 /* request header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002638 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002639 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2640 if (hdr > 0)
2641 LOGCHAR(' ');
2642 if (tmp->options & LOG_OPT_QUOTE)
2643 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002644 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002645 ret = lf_encode_string(tmplog, dst + maxsize,
2646 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002647 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002648 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002649 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002650 } else if (!(tmp->options & LOG_OPT_QUOTE))
2651 LOGCHAR('-');
2652 if (tmp->options & LOG_OPT_QUOTE)
2653 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002654 last_isspace = 0;
2655 }
2656 }
2657 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002658
William Lallemand1d705562012-03-12 12:46:41 +01002659
2660 case LOG_FMT_HDRRESPONS: // %hs
William Lallemandbddd4fd2012-02-27 11:23:10 +01002661 /* response header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002662 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002663 if (tmp->options & LOG_OPT_QUOTE)
2664 LOGCHAR('"');
2665 LOGCHAR('{');
2666 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2667 if (hdr)
2668 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002669 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002670 ret = lf_encode_string(tmplog, dst + maxsize,
2671 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002672 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002673 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002674 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002675 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002676 }
2677 LOGCHAR('}');
2678 last_isspace = 0;
2679 if (tmp->options & LOG_OPT_QUOTE)
2680 LOGCHAR('"');
2681 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002682 break;
2683
William Lallemand1d705562012-03-12 12:46:41 +01002684 case LOG_FMT_HDRRESPONSLIST: // %hsl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002685 /* response header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002686 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002687 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2688 if (hdr > 0)
2689 LOGCHAR(' ');
2690 if (tmp->options & LOG_OPT_QUOTE)
2691 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002692 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002693 ret = lf_encode_string(tmplog, dst + maxsize,
2694 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002695 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002696 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002697 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002698 } else if (!(tmp->options & LOG_OPT_QUOTE))
2699 LOGCHAR('-');
2700 if (tmp->options & LOG_OPT_QUOTE)
2701 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002702 last_isspace = 0;
2703 }
2704 }
2705 break;
2706
William Lallemand1d705562012-03-12 12:46:41 +01002707 case LOG_FMT_REQ: // %r
William Lallemandbddd4fd2012-02-27 11:23:10 +01002708 /* Request */
2709 if (tmp->options & LOG_OPT_QUOTE)
2710 LOGCHAR('"');
Willy Tarreau57bc8912016-04-25 17:09:40 +02002711 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Dragan Dosen835b9212016-02-12 13:23:03 +01002712 ret = lf_encode_string(tmplog, dst + maxsize,
2713 '#', url_encode_map, uri, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002714 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002715 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002716 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002717 if (tmp->options & LOG_OPT_QUOTE)
2718 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002719 last_isspace = 0;
2720 break;
William Lallemand5f232402012-04-05 18:02:55 +02002721
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002722 case LOG_FMT_HTTP_PATH: // %HP
Willy Tarreau57bc8912016-04-25 17:09:40 +02002723 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002724
Willy Tarreaub7636d12015-06-17 19:58:02 +02002725 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002726 LOGCHAR('"');
2727
2728 end = uri + strlen(uri);
2729 // look for the first whitespace character
2730 while (uri < end && !HTTP_IS_SPHT(*uri))
2731 uri++;
2732
2733 // keep advancing past multiple spaces
2734 while (uri < end && HTTP_IS_SPHT(*uri)) {
2735 uri++; nspaces++;
2736 }
2737
2738 // look for first space or question mark after url
2739 spc = uri;
2740 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2741 spc++;
2742
Nenad Merdanovic54e439f2016-04-26 01:39:02 +02002743 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002744 chunk.area = "<BADREQ>";
2745 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002746 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002747 chunk.area = uri;
2748 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002749 }
2750
Dragan Dosen835b9212016-02-12 13:23:03 +01002751 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002752 if (ret == NULL || *ret != '\0')
2753 goto out;
2754
2755 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002756 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002757 LOGCHAR('"');
2758
2759 last_isspace = 0;
2760 break;
2761
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002762 case LOG_FMT_HTTP_QUERY: // %HQ
2763 if (tmp->options & LOG_OPT_QUOTE)
2764 LOGCHAR('"');
2765
Willy Tarreau57bc8912016-04-25 17:09:40 +02002766 if (!txn || !txn->uri) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002767 chunk.area = "<BADREQ>";
2768 chunk.data = strlen("<BADREQ>");
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002769 } else {
2770 uri = txn->uri;
2771 end = uri + strlen(uri);
2772 // look for the first question mark
2773 while (uri < end && *uri != '?')
2774 uri++;
2775
2776 qmark = uri;
2777 // look for first space or question mark after url
2778 while (uri < end && !HTTP_IS_SPHT(*uri))
2779 uri++;
2780
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002781 chunk.area = qmark;
2782 chunk.data = uri - qmark;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002783 }
2784
Dragan Dosen835b9212016-02-12 13:23:03 +01002785 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002786 if (ret == NULL || *ret != '\0')
2787 goto out;
2788
2789 tmplog = ret;
2790 if (tmp->options & LOG_OPT_QUOTE)
2791 LOGCHAR('"');
2792
2793 last_isspace = 0;
2794 break;
2795
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002796 case LOG_FMT_HTTP_URI: // %HU
Willy Tarreau57bc8912016-04-25 17:09:40 +02002797 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002798
Willy Tarreaub7636d12015-06-17 19:58:02 +02002799 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002800 LOGCHAR('"');
2801
2802 end = uri + strlen(uri);
2803 // look for the first whitespace character
2804 while (uri < end && !HTTP_IS_SPHT(*uri))
2805 uri++;
2806
2807 // keep advancing past multiple spaces
2808 while (uri < end && HTTP_IS_SPHT(*uri)) {
2809 uri++; nspaces++;
2810 }
2811
2812 // look for first space after url
2813 spc = uri;
2814 while (spc < end && !HTTP_IS_SPHT(*spc))
2815 spc++;
2816
Willy Tarreau57bc8912016-04-25 17:09:40 +02002817 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002818 chunk.area = "<BADREQ>";
2819 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002820 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002821 chunk.area = uri;
2822 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002823 }
2824
Dragan Dosen835b9212016-02-12 13:23:03 +01002825 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002826 if (ret == NULL || *ret != '\0')
2827 goto out;
2828
2829 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002830 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002831 LOGCHAR('"');
2832
2833 last_isspace = 0;
2834 break;
2835
2836 case LOG_FMT_HTTP_METHOD: // %HM
Willy Tarreau57bc8912016-04-25 17:09:40 +02002837 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002838 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002839 LOGCHAR('"');
2840
2841 end = uri + strlen(uri);
2842 // look for the first whitespace character
2843 spc = uri;
2844 while (spc < end && !HTTP_IS_SPHT(*spc))
2845 spc++;
2846
2847 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002848 chunk.area = "<BADREQ>";
2849 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002850 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002851 chunk.area = uri;
2852 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002853 }
2854
Dragan Dosen835b9212016-02-12 13:23:03 +01002855 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002856 if (ret == NULL || *ret != '\0')
2857 goto out;
2858
2859 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002860 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002861 LOGCHAR('"');
2862
2863 last_isspace = 0;
2864 break;
2865
2866 case LOG_FMT_HTTP_VERSION: // %HV
Willy Tarreau57bc8912016-04-25 17:09:40 +02002867 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002868 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002869 LOGCHAR('"');
2870
2871 end = uri + strlen(uri);
2872 // look for the first whitespace character
2873 while (uri < end && !HTTP_IS_SPHT(*uri))
2874 uri++;
2875
2876 // keep advancing past multiple spaces
2877 while (uri < end && HTTP_IS_SPHT(*uri)) {
2878 uri++; nspaces++;
2879 }
2880
2881 // look for the next whitespace character
2882 while (uri < end && !HTTP_IS_SPHT(*uri))
2883 uri++;
2884
2885 // keep advancing past multiple spaces
2886 while (uri < end && HTTP_IS_SPHT(*uri))
2887 uri++;
2888
Willy Tarreau57bc8912016-04-25 17:09:40 +02002889 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002890 chunk.area = "<BADREQ>";
2891 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002892 } else if (uri == end) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002893 chunk.area = "HTTP/0.9";
2894 chunk.data = strlen("HTTP/0.9");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002895 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002896 chunk.area = uri;
2897 chunk.data = end - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002898 }
2899
Dragan Dosen835b9212016-02-12 13:23:03 +01002900 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002901 if (ret == NULL || *ret != '\0')
2902 goto out;
2903
2904 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002905 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002906 LOGCHAR('"');
2907
2908 last_isspace = 0;
2909 break;
2910
William Lallemand5f232402012-04-05 18:02:55 +02002911 case LOG_FMT_COUNTER: // %rt
2912 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002913 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
William Lallemand5f232402012-04-05 18:02:55 +02002914 if (iret < 0 || iret > dst + maxsize - tmplog)
2915 goto out;
2916 last_isspace = 0;
2917 tmplog += iret;
2918 } else {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002919 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002920 if (ret == NULL)
2921 goto out;
2922 tmplog = ret;
2923 last_isspace = 0;
2924 }
2925 break;
2926
Willy Tarreau7346acb2014-08-28 15:03:15 +02002927 case LOG_FMT_LOGCNT: // %lc
2928 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002929 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002930 if (iret < 0 || iret > dst + maxsize - tmplog)
2931 goto out;
2932 last_isspace = 0;
2933 tmplog += iret;
2934 } else {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002935 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002936 if (ret == NULL)
2937 goto out;
2938 tmplog = ret;
2939 last_isspace = 0;
2940 }
2941 break;
2942
William Lallemand5f232402012-04-05 18:02:55 +02002943 case LOG_FMT_HOSTNAME: // %H
2944 src = hostname;
2945 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2946 if (ret == NULL)
2947 goto out;
2948 tmplog = ret;
2949 last_isspace = 0;
2950 break;
2951
2952 case LOG_FMT_PID: // %pid
2953 if (tmp->options & LOG_OPT_HEXA) {
2954 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
2955 if (iret < 0 || iret > dst + maxsize - tmplog)
2956 goto out;
2957 last_isspace = 0;
2958 tmplog += iret;
2959 } else {
2960 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
2961 if (ret == NULL)
2962 goto out;
2963 tmplog = ret;
2964 last_isspace = 0;
2965 }
2966 break;
William Lallemanda73203e2012-03-12 12:48:57 +01002967
2968 case LOG_FMT_UNIQUEID: // %ID
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002969 ret = NULL;
Tim Duesterhusa17e6622020-03-05 20:19:02 +01002970 if (s)
2971 ret = lf_text_len(tmplog, s->unique_id.ptr, s->unique_id.len, maxsize - (tmplog - dst), tmp);
2972 else
2973 ret = lf_text_len(tmplog, NULL, 0, maxsize - (tmplog - dst), tmp);
William Lallemanda73203e2012-03-12 12:48:57 +01002974 if (ret == NULL)
2975 goto out;
2976 tmplog = ret;
2977 last_isspace = 0;
2978 break;
2979
William Lallemandbddd4fd2012-02-27 11:23:10 +01002980 }
2981 }
2982
2983out:
William Lallemand1d705562012-03-12 12:46:41 +01002984 /* *tmplog is a unused character */
2985 *tmplog = '\0';
Willy Tarreaudf974472012-12-28 02:44:01 +01002986 return tmplog - dst;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002987
Willy Tarreaubaaee002006-06-26 02:48:02 +02002988}
2989
William Lallemand1d705562012-03-12 12:46:41 +01002990/*
Willy Tarreau87b09662015-04-03 00:22:06 +02002991 * send a log for the stream when we have enough info about it.
William Lallemand1d705562012-03-12 12:46:41 +01002992 * Will not log if the frontend has no log defined.
2993 */
Willy Tarreau87b09662015-04-03 00:22:06 +02002994void strm_log(struct stream *s)
William Lallemand1d705562012-03-12 12:46:41 +01002995{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002996 struct session *sess = s->sess;
William Lallemand1d705562012-03-12 12:46:41 +01002997 int size, err, level;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002998 int sd_size = 0;
William Lallemand1d705562012-03-12 12:46:41 +01002999
3000 /* if we don't want to log normal traffic, return now */
Willy Tarreaue7dff022015-04-03 01:14:29 +02003001 err = (s->flags & SF_REDISP) ||
3002 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
3003 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
Willy Tarreau350f4872014-11-28 14:42:25 +01003004 (s->si[1].conn_retries != s->be->conn_retries)) ||
Willy Tarreaueee5b512015-04-03 23:46:31 +02003005 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
Willy Tarreaubaaee002006-06-26 02:48:02 +02003006
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003007 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
William Lallemand1d705562012-03-12 12:46:41 +01003008 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003009
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003010 if (LIST_ISEMPTY(&sess->fe->logsrvs))
William Lallemand1d705562012-03-12 12:46:41 +01003011 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003012
Willy Tarreauabcd5142013-06-11 17:18:02 +02003013 if (s->logs.level) { /* loglevel was overridden */
3014 if (s->logs.level == -1) {
3015 s->logs.logwait = 0; /* logs disabled */
3016 return;
3017 }
3018 level = s->logs.level - 1;
3019 }
3020 else {
3021 level = LOG_INFO;
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003022 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
Willy Tarreauabcd5142013-06-11 17:18:02 +02003023 level = LOG_ERR;
3024 }
William Lallemand1d705562012-03-12 12:46:41 +01003025
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003026 /* if unique-id was not generated */
Tim Duesterhusa17e6622020-03-05 20:19:02 +01003027 if (!isttest(s->unique_id) && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
Tim Duesterhus2825b4b2020-02-28 15:13:34 +01003028 stream_generate_unique_id(s, &sess->fe->format_unique_id);
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003029 }
3030
Dragan Dosen0b85ece2015-09-25 19:17:44 +02003031 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3032 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
3033 &sess->fe->logformat_sd);
3034 }
3035
Dragan Dosen59cee972015-09-19 22:09:02 +02003036 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
William Lallemand1d705562012-03-12 12:46:41 +01003037 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01003038 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003039 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3040 logline, size + 1, logline_rfc5424, sd_size);
William Lallemand1d705562012-03-12 12:46:41 +01003041 s->logs.logwait = 0;
3042 }
3043}
William Lallemandbddd4fd2012-02-27 11:23:10 +01003044
Willy Tarreau53839352018-09-05 19:51:10 +02003045/*
3046 * send a minimalist log for the session. Will not log if the frontend has no
3047 * log defined. It is assumed that this is only used to report anomalies that
3048 * cannot lead to the creation of a regular stream. Because of this the log
3049 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
3050 * in the frontend. The caller must simply know that it should not call this
Willy Tarreau9fa267d2018-10-05 10:22:27 +02003051 * function to report unimportant events. It is safe to call this function with
3052 * sess==NULL (will not do anything).
Willy Tarreau53839352018-09-05 19:51:10 +02003053 */
3054void sess_log(struct session *sess)
3055{
3056 int size, level;
3057 int sd_size = 0;
3058
Willy Tarreau9fa267d2018-10-05 10:22:27 +02003059 if (!sess)
3060 return;
3061
Willy Tarreau53839352018-09-05 19:51:10 +02003062 if (LIST_ISEMPTY(&sess->fe->logsrvs))
3063 return;
3064
3065 level = LOG_INFO;
3066 if (sess->fe->options2 & PR_O2_LOGERRORS)
3067 level = LOG_ERR;
3068
3069 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3070 sd_size = sess_build_logline(sess, NULL,
3071 logline_rfc5424, global.max_syslog_len,
3072 &sess->fe->logformat_sd);
3073 }
3074
3075 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
3076 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01003077 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003078 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3079 logline, size + 1, logline_rfc5424, sd_size);
Willy Tarreau53839352018-09-05 19:51:10 +02003080 }
3081}
3082
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003083void app_log(struct list *logsrvs, struct buffer *tag, int level, const char *format, ...)
3084{
3085 va_list argp;
3086 int data_len;
3087
3088 if (level < 0 || format == NULL || logline == NULL)
3089 return;
3090
3091 va_start(argp, format);
3092 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
3093 if (data_len < 0 || data_len > global.max_syslog_len)
3094 data_len = global.max_syslog_len;
3095 va_end(argp);
3096
3097 __send_log(logsrvs, tag, level, logline, data_len, default_rfc5424_sd_log_format, 2);
3098}
3099
Willy Tarreau869efd52019-11-15 15:16:57 +01003100/* parse the "show startup-logs" command, returns 1 if a message is returned, otherwise zero */
3101static int cli_parse_show_startup_logs(char **args, char *payload, struct appctx *appctx, void *private)
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003102{
Willy Tarreau869efd52019-11-15 15:16:57 +01003103 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
3104 return 1;
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003105
Willy Tarreau869efd52019-11-15 15:16:57 +01003106 if (!startup_logs)
3107 return cli_msg(appctx, LOG_INFO, "\n"); // nothing to print
3108
3109 return ring_attach_cli(startup_logs, appctx);
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003110}
3111
3112/* register cli keywords */
3113static struct cli_kw_list cli_kws = {{ },{
3114 { { "show", "startup-logs", NULL },
Willy Tarreau869efd52019-11-15 15:16:57 +01003115 "show startup-logs : report logs emitted during HAProxy startup", cli_parse_show_startup_logs, NULL, NULL },
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003116 {{},}
3117}};
3118
Willy Tarreau0108d902018-11-25 19:14:37 +01003119INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
3120
Willy Tarreau082b6282019-05-22 14:42:12 +02003121REGISTER_PER_THREAD_ALLOC(init_log_buffers);
3122REGISTER_PER_THREAD_FREE(deinit_log_buffers);
Willy Tarreau172f5ce2018-11-26 11:21:50 +01003123
Willy Tarreaubaaee002006-06-26 02:48:02 +02003124/*
3125 * Local variables:
3126 * c-indent-level: 8
3127 * c-basic-offset: 8
3128 * End:
3129 */