blob: 937b675a73a4d09831ca9a759e1c427792e2215a [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * General logging functions.
3 *
Willy Tarreaub7f694f2008-06-22 17:18:02 +02004 * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
Willy Tarreaubaaee002006-06-26 02:48:02 +02005 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
Willy Tarreau8a3f52f2012-12-20 21:23:42 +010013#include <ctype.h>
Willy Tarreauc8f24f82007-11-30 18:38:35 +010014#include <fcntl.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020015#include <stdarg.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <syslog.h>
20#include <time.h>
21#include <unistd.h>
Robert Tsai81ae1952007-12-05 10:47:29 +010022#include <errno.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020023
24#include <sys/time.h>
Willy Tarreau077edcb2016-08-10 18:30:56 +020025#include <sys/uio.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020027#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020028#include <haproxy/applet-t.h>
Willy Tarreau83487a82020-06-04 20:19:54 +020029#include <haproxy/cli.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020030#include <haproxy/fd.h>
Willy Tarreau762d7a52020-06-04 11:23:07 +020031#include <haproxy/frontend.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020032#include <haproxy/global.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020033#include <haproxy/http.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020034#include <haproxy/log.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020035#include <haproxy/ring.h>
36#include <haproxy/sample.h>
37#include <haproxy/sink.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020038#include <haproxy/ssl_sock.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020039#include <haproxy/stream.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020040#include <haproxy/stream_interface.h>
Willy Tarreau92b4f132020-06-01 11:05:15 +020041#include <haproxy/time.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020042#include <haproxy/tools.h>
Willy Tarreaud6788052020-05-27 15:59:00 +020043#include <haproxy/version.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020044
Willy Tarreaubaaee002006-06-26 02:48:02 +020045
Dragan Dosen43885c72015-10-01 13:18:13 +020046struct log_fmt {
47 char *name;
48 struct {
Willy Tarreau83061a82018-07-13 11:56:34 +020049 struct buffer sep1; /* first pid separator */
50 struct buffer sep2; /* second pid separator */
Dragan Dosen43885c72015-10-01 13:18:13 +020051 } pid;
52};
53
54static const struct log_fmt log_formats[LOG_FORMATS] = {
55 [LOG_FORMAT_RFC3164] = {
56 .name = "rfc3164",
57 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020058 .sep1 = { .area = "[", .data = 1 },
59 .sep2 = { .area = "]: ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020060 }
61 },
62 [LOG_FORMAT_RFC5424] = {
63 .name = "rfc5424",
64 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020065 .sep1 = { .area = " ", .data = 1 },
66 .sep2 = { .area = " - ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020067 }
Willy Tarreaue8746a02018-11-12 08:45:00 +010068 },
69 [LOG_FORMAT_SHORT] = {
70 .name = "short",
71 .pid = {
72 .sep1 = { .area = "", .data = 0 },
73 .sep2 = { .area = " ", .data = 1 },
74 }
75 },
Willy Tarreauc1b06452018-11-12 11:57:56 +010076 [LOG_FORMAT_RAW] = {
77 .name = "raw",
78 .pid = {
79 .sep1 = { .area = "", .data = 0 },
80 .sep2 = { .area = "", .data = 0 },
81 }
82 },
Dragan Dosen1322d092015-09-22 16:05:32 +020083};
84
Emeric Brunbd163812020-05-06 14:33:46 +020085char *get_format_pid_sep1(int format, size_t *len)
86{
87 *len = log_formats[format].pid.sep1.data;
88 return log_formats[format].pid.sep1.area;
89}
90
91char *get_format_pid_sep2(int format, size_t *len)
92{
93 *len = log_formats[format].pid.sep2.data;
94 return log_formats[format].pid.sep2.area;
95}
96
Dragan Dosen835b9212016-02-12 13:23:03 +010097/*
98 * This map is used with all the FD_* macros to check whether a particular bit
Willy Tarreau1bfd6022019-06-07 11:10:07 +020099 * is set or not. Each bit represents an ACSII code. ha_bit_set() sets those
100 * bytes which should be escaped. When ha_bit_test() returns non-zero, it means
101 * that the byte should be escaped. Be careful to always pass bytes from 0 to
102 * 255 exclusively to the macros.
Dragan Dosen835b9212016-02-12 13:23:03 +0100103 */
Willy Tarreau1bfd6022019-06-07 11:10:07 +0200104long rfc5424_escape_map[(256/8) / sizeof(long)];
105long hdr_encode_map[(256/8) / sizeof(long)];
106long url_encode_map[(256/8) / sizeof(long)];
107long http_encode_map[(256/8) / sizeof(long)];
Dragan Dosen835b9212016-02-12 13:23:03 +0100108
Dragan Dosen835b9212016-02-12 13:23:03 +0100109
Willy Tarreaubaaee002006-06-26 02:48:02 +0200110const char *log_facilities[NB_LOG_FACILITIES] = {
111 "kern", "user", "mail", "daemon",
112 "auth", "syslog", "lpr", "news",
113 "uucp", "cron", "auth2", "ftp",
114 "ntp", "audit", "alert", "cron2",
115 "local0", "local1", "local2", "local3",
116 "local4", "local5", "local6", "local7"
117};
118
Willy Tarreaubaaee002006-06-26 02:48:02 +0200119const char *log_levels[NB_LOG_LEVELS] = {
120 "emerg", "alert", "crit", "err",
121 "warning", "notice", "info", "debug"
122};
123
Willy Tarreau570f2212013-06-10 16:42:09 +0200124const char sess_term_cond[16] = "-LcCsSPRIDKUIIII"; /* normal, Local, CliTo, CliErr, SrvTo, SrvErr, PxErr, Resource, Internal, Down, Killed, Up, -- */
Willy Tarreaub8750a82006-09-03 09:56:00 +0200125const char sess_fin_state[8] = "-RCHDLQT"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, Tarpit */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200126
William Lallemand723b73a2012-02-08 16:37:49 +0100127
128/* log_format */
129struct logformat_type {
130 char *name;
131 int type;
William Lallemandbddd4fd2012-02-27 11:23:10 +0100132 int mode;
William Lallemand5e19a282012-04-02 16:22:10 +0200133 int lw; /* logwait bitsfield */
William Lallemandb7ff6a32012-03-02 14:35:21 +0100134 int (*config_callback)(struct logformat_node *node, struct proxy *curproxy);
Willy Tarreau2beef582012-12-20 17:22:52 +0100135 const char *replace_by; /* new option to use instead of old one */
William Lallemand723b73a2012-02-08 16:37:49 +0100136};
137
William Lallemandb7ff6a32012-03-02 14:35:21 +0100138int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy);
139
William Lallemand723b73a2012-02-08 16:37:49 +0100140/* log_format variable names */
141static const struct logformat_type logformat_keywords[] = {
William Lallemand5e19a282012-04-02 16:22:10 +0200142 { "o", LOG_FMT_GLOBAL, PR_MODE_TCP, 0, NULL }, /* global option */
Willy Tarreau2beef582012-12-20 17:22:52 +0100143
144 /* please keep these lines sorted ! */
145 { "B", LOG_FMT_BYTES, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from server to client */
146 { "CC", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL }, /* client cookie */
147 { "CS", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL }, /* server cookie */
148 { "H", LOG_FMT_HOSTNAME, PR_MODE_TCP, LW_INIT, NULL }, /* Hostname */
Tim Duesterhuscf6e0c82020-03-13 12:34:24 +0100149 { "ID", LOG_FMT_UNIQUEID, PR_MODE_TCP, LW_BYTES, NULL }, /* Unique ID */
Willy Tarreau4bf99632014-06-13 12:21:40 +0200150 { "ST", LOG_FMT_STATUS, PR_MODE_TCP, LW_RESP, NULL }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200151 { "T", LOG_FMT_DATEGMT, PR_MODE_TCP, LW_INIT, NULL }, /* date GMT */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200152 { "Ta", LOG_FMT_Ta, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time active (tr to end) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100153 { "Tc", LOG_FMT_TC, PR_MODE_TCP, LW_BYTES, NULL }, /* Tc */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200154 { "Th", LOG_FMT_Th, PR_MODE_TCP, LW_BYTES, NULL }, /* Time handshake */
155 { "Ti", LOG_FMT_Ti, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time idle */
156 { "Tl", LOG_FMT_DATELOCAL, PR_MODE_TCP, LW_INIT, NULL }, /* date local timezone */
157 { "Tq", LOG_FMT_TQ, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tq=Th+Ti+TR */
158 { "Tr", LOG_FMT_Tr, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tr */
159 { "TR", LOG_FMT_TR, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time to receive a valid request */
Willy Tarreau27b639d2016-05-17 17:55:27 +0200160 { "Td", LOG_FMT_TD, PR_MODE_TCP, LW_BYTES, NULL }, /* Td = Tt - (Tq + Tw + Tc + Tr) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100161 { "Ts", LOG_FMT_TS, PR_MODE_TCP, LW_INIT, NULL }, /* timestamp GMT */
William Lallemand5e19a282012-04-02 16:22:10 +0200162 { "Tt", LOG_FMT_TT, PR_MODE_TCP, LW_BYTES, NULL }, /* Tt */
Damien Claisse57c8eb92020-04-28 12:09:19 +0000163 { "Tu", LOG_FMT_TU, PR_MODE_TCP, LW_BYTES, NULL }, /* Tu = Tt -Ti */
Willy Tarreau2beef582012-12-20 17:22:52 +0100164 { "Tw", LOG_FMT_TW, PR_MODE_TCP, LW_BYTES, NULL }, /* Tw */
165 { "U", LOG_FMT_BYTES_UP, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from client to server */
William Lallemand5e19a282012-04-02 16:22:10 +0200166 { "ac", LOG_FMT_ACTCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* actconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100167 { "b", LOG_FMT_BACKEND, PR_MODE_TCP, LW_INIT, NULL }, /* backend */
William Lallemand5e19a282012-04-02 16:22:10 +0200168 { "bc", LOG_FMT_BECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* beconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100169 { "bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source ip */
170 { "bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source port */
William Lallemand5e19a282012-04-02 16:22:10 +0200171 { "bq", LOG_FMT_BCKQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* backend_queue */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200172 { "ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client ip */
173 { "cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100174 { "f", LOG_FMT_FRONTEND, PR_MODE_TCP, LW_INIT, NULL }, /* frontend */
175 { "fc", LOG_FMT_FECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* feconn */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200176 { "fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend ip */
177 { "fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100178 { "ft", LOG_FMT_FRONTEND_XPRT, PR_MODE_TCP, LW_INIT, NULL }, /* frontend with transport mode */
Willy Tarreaud9ed3d22014-06-13 12:23:06 +0200179 { "hr", LOG_FMT_HDRREQUEST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request */
180 { "hrl", LOG_FMT_HDRREQUESTLIST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request list */
181 { "hs", LOG_FMT_HDRRESPONS, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response */
182 { "hsl", LOG_FMT_HDRRESPONSLIST, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response list */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000183 { "HM", LOG_FMT_HTTP_METHOD, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP method */
184 { "HP", LOG_FMT_HTTP_PATH, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP path */
Andrew Hayworthe63ac872015-07-31 16:14:16 +0000185 { "HQ", LOG_FMT_HTTP_QUERY, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP query */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000186 { "HU", LOG_FMT_HTTP_URI, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP full URI */
187 { "HV", LOG_FMT_HTTP_VERSION, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP version */
Willy Tarreau7346acb2014-08-28 15:03:15 +0200188 { "lc", LOG_FMT_LOGCNT, PR_MODE_TCP, LW_INIT, NULL }, /* log counter */
Willy Tarreau2beef582012-12-20 17:22:52 +0100189 { "ms", LOG_FMT_MS, PR_MODE_TCP, LW_INIT, NULL }, /* accept date millisecond */
William Lallemand5e19a282012-04-02 16:22:10 +0200190 { "pid", LOG_FMT_PID, PR_MODE_TCP, LW_INIT, NULL }, /* log pid */
Willy Tarreau2beef582012-12-20 17:22:52 +0100191 { "r", LOG_FMT_REQ, PR_MODE_HTTP, LW_REQ, NULL }, /* request */
192 { "rc", LOG_FMT_RETRIES, PR_MODE_TCP, LW_BYTES, NULL }, /* retries */
Willy Tarreau1f0da242014-01-25 11:01:50 +0100193 { "rt", LOG_FMT_COUNTER, PR_MODE_TCP, LW_REQ, NULL }, /* request counter (HTTP or TCP session) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100194 { "s", LOG_FMT_SERVER, PR_MODE_TCP, LW_SVID, NULL }, /* server */
195 { "sc", LOG_FMT_SRVCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_conn */
196 { "si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination ip */
197 { "sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination port */
198 { "sq", LOG_FMT_SRVQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_queue */
Willy Tarreauffc3fcd2012-10-12 20:17:54 +0200199 { "sslc", LOG_FMT_SSL_CIPHER, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL ciphers */
200 { "sslv", LOG_FMT_SSL_VERSION, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL protocol version */
Willy Tarreau2beef582012-12-20 17:22:52 +0100201 { "t", LOG_FMT_DATE, PR_MODE_TCP, LW_INIT, NULL }, /* date */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200202 { "tr", LOG_FMT_tr, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request */
203 { "trg",LOG_FMT_trg, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, GMT */
204 { "trl",LOG_FMT_trl, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, local */
Willy Tarreau2beef582012-12-20 17:22:52 +0100205 { "ts", LOG_FMT_TERMSTATE, PR_MODE_TCP, LW_BYTES, NULL },/* termination state */
206 { "tsc", LOG_FMT_TERMSTATE_CK, PR_MODE_TCP, LW_INIT, NULL },/* termination state */
207
208 /* The following tags are deprecated and will be removed soon */
209 { "Bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bi" }, /* backend source ip */
210 { "Bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bp" }, /* backend source port */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200211 { "Ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "ci" }, /* client ip */
212 { "Cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "cp" }, /* client port */
213 { "Fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fi" }, /* frontend ip */
214 { "Fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fp" }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100215 { "Si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL, "si" }, /* server destination ip */
216 { "Sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL, "sp" }, /* server destination port */
217 { "cc", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL, "CC" }, /* client cookie */
218 { "cs", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL, "CS" }, /* server cookie */
219 { "st", LOG_FMT_STATUS, PR_MODE_HTTP, LW_RESP, NULL, "ST" }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200220 { 0, 0, 0, 0, NULL }
William Lallemand723b73a2012-02-08 16:37:49 +0100221};
222
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200223char default_http_log_format[] = "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"; // default format
224char clf_http_log_format[] = "%{+Q}o %{-Q}ci - - [%trg] %r %ST %B \"\" \"\" %cp %ms %ft %b %s %TR %Tw %Tc %Tr %Ta %tsc %ac %fc %bc %sc %rc %sq %bq %CC %CS %hrl %hsl";
Willy Tarreau2beef582012-12-20 17:22:52 +0100225char default_tcp_log_format[] = "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq";
William Lallemand723b73a2012-02-08 16:37:49 +0100226char *log_format = NULL;
227
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200228/* Default string used for structured-data part in RFC5424 formatted
229 * syslog messages.
230 */
231char default_rfc5424_sd_log_format[] = "- ";
Dragan Dosen1322d092015-09-22 16:05:32 +0200232
Willy Tarreau13ef7732018-11-12 07:25:28 +0100233/* total number of dropped logs */
234unsigned int dropped_logs = 0;
235
Dragan Dosen1322d092015-09-22 16:05:32 +0200236/* This is a global syslog header, common to all outgoing messages in
237 * RFC3164 format. It begins with time-based part and is updated by
238 * update_log_hdr().
Dragan Dosen59cee972015-09-19 22:09:02 +0200239 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200240THREAD_LOCAL char *logheader = NULL;
Willy Tarreau55e2f5a2019-05-05 10:11:39 +0200241THREAD_LOCAL char *logheader_end = NULL;
Dragan Dosen59cee972015-09-19 22:09:02 +0200242
Dragan Dosen1322d092015-09-22 16:05:32 +0200243/* This is a global syslog header for messages in RFC5424 format. It is
244 * updated by update_log_hdr_rfc5424().
245 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200246THREAD_LOCAL char *logheader_rfc5424 = NULL;
Willy Tarreau55e2f5a2019-05-05 10:11:39 +0200247THREAD_LOCAL char *logheader_rfc5424_end = NULL;
Dragan Dosen1322d092015-09-22 16:05:32 +0200248
Dragan Dosen59cee972015-09-19 22:09:02 +0200249/* This is a global syslog message buffer, common to all outgoing
250 * messages. It contains only the data part.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100251 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200252THREAD_LOCAL char *logline = NULL;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100253
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200254/* A global syslog message buffer, common to all RFC5424 syslog messages.
255 * Currently, it is used for generating the structured-data part.
256 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200257THREAD_LOCAL char *logline_rfc5424 = NULL;
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200258
Christopher Fauletd4696382017-10-24 11:44:05 +0200259/* A global buffer used to store all startup alerts/warnings. It will then be
260 * retrieve on the CLI. */
Willy Tarreau869efd52019-11-15 15:16:57 +0100261static struct ring *startup_logs = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +0200262
William Lallemand723b73a2012-02-08 16:37:49 +0100263struct logformat_var_args {
264 char *name;
265 int mask;
266};
267
268struct logformat_var_args var_args_list[] = {
269// global
270 { "M", LOG_OPT_MANDATORY },
271 { "Q", LOG_OPT_QUOTE },
William Lallemand5f232402012-04-05 18:02:55 +0200272 { "X", LOG_OPT_HEXA },
Dragan Dosen835b9212016-02-12 13:23:03 +0100273 { "E", LOG_OPT_ESC },
William Lallemand723b73a2012-02-08 16:37:49 +0100274 { 0, 0 }
275};
276
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200277/* return the name of the directive used in the current proxy for which we're
278 * currently parsing a header, when it is known.
279 */
280static inline const char *fmt_directive(const struct proxy *curproxy)
281{
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100282 switch (curproxy->conf.args.ctx) {
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200283 case ARGC_ACL:
284 return "acl";
285 case ARGC_STK:
286 return "stick";
287 case ARGC_TRK:
288 return "track-sc";
289 case ARGC_LOG:
290 return "log-format";
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200291 case ARGC_LOGSD:
292 return "log-format-sd";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100293 case ARGC_HRQ:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100294 return "http-request";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100295 case ARGC_HRS:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100296 return "http-response";
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200297 case ARGC_UIF:
298 return "unique-id-format";
Thierry FOURNIERd18cd0f2013-11-29 12:15:45 +0100299 case ARGC_RDR:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200300 return "redirect";
301 case ARGC_CAP:
302 return "capture";
Willy Tarreau28d976d2015-07-09 11:39:33 +0200303 case ARGC_SRV:
304 return "server";
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200305 case ARGC_SPOE:
306 return "spoe-message";
Thierry FOURNIER / OZON.IO4ed1c952016-11-24 23:57:54 +0100307 case ARGC_UBK:
308 return "use_backend";
Christopher Faulet3b967c12020-05-15 15:47:44 +0200309 case ARGC_HERR:
310 return "http-error";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100311 default:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200312 return "undefined(please report this bug)"; /* must never happen */
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100313 }
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200314}
315
William Lallemand723b73a2012-02-08 16:37:49 +0100316/*
William Lallemandb7ff6a32012-03-02 14:35:21 +0100317 * callback used to configure addr source retrieval
318 */
319int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy)
320{
321 curproxy->options2 |= PR_O2_SRC_ADDR;
322
323 return 0;
324}
325
326
327/*
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100328 * Parse args in a logformat_var. Returns 0 in error
329 * case, otherwise, it returns 1.
William Lallemand723b73a2012-02-08 16:37:49 +0100330 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100331int parse_logformat_var_args(char *args, struct logformat_node *node, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100332{
333 int i = 0;
334 int end = 0;
335 int flags = 0; // 1 = + 2 = -
336 char *sp = NULL; // start pointer
337
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100338 if (args == NULL) {
339 memprintf(err, "internal error: parse_logformat_var_args() expects non null 'args'");
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100340 return 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100341 }
William Lallemand723b73a2012-02-08 16:37:49 +0100342
343 while (1) {
344 if (*args == '\0')
345 end = 1;
346
347 if (*args == '+') {
348 // add flag
349 sp = args + 1;
350 flags = 1;
351 }
352 if (*args == '-') {
353 // delete flag
354 sp = args + 1;
355 flags = 2;
356 }
357
358 if (*args == '\0' || *args == ',') {
359 *args = '\0';
Willy Tarreau254d44c2012-12-20 18:19:26 +0100360 for (i = 0; sp && var_args_list[i].name; i++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100361 if (strcmp(sp, var_args_list[i].name) == 0) {
362 if (flags == 1) {
363 node->options |= var_args_list[i].mask;
364 break;
365 } else if (flags == 2) {
366 node->options &= ~var_args_list[i].mask;
367 break;
368 }
369 }
370 }
371 sp = NULL;
372 if (end)
373 break;
374 }
Willy Tarreau254d44c2012-12-20 18:19:26 +0100375 args++;
William Lallemand723b73a2012-02-08 16:37:49 +0100376 }
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100377 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100378}
379
380/*
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100381 * Parse a variable '%varname' or '%{args}varname' in log-format. The caller
382 * must pass the args part in the <arg> pointer with its length in <arg_len>,
383 * and varname with its length in <var> and <var_len> respectively. <arg> is
384 * ignored when arg_len is 0. Neither <var> nor <var_len> may be null.
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100385 * Returns false in error case and err is filled, otherwise returns true.
William Lallemand723b73a2012-02-08 16:37:49 +0100386 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100387int parse_logformat_var(char *arg, int arg_len, char *var, int var_len, struct proxy *curproxy, struct list *list_format, int *defoptions, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100388{
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100389 int j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200390 struct logformat_node *node = NULL;
William Lallemand723b73a2012-02-08 16:37:49 +0100391
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100392 for (j = 0; logformat_keywords[j].name; j++) { // search a log type
393 if (strlen(logformat_keywords[j].name) == var_len &&
394 strncmp(var, logformat_keywords[j].name, var_len) == 0) {
395 if (logformat_keywords[j].mode != PR_MODE_HTTP || curproxy->mode == PR_MODE_HTTP) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200396 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100397 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100398 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200399 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100400 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100401 node->type = logformat_keywords[j].type;
402 node->options = *defoptions;
403 if (arg_len) {
404 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100405 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200406 goto error_free;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100407 }
408 if (node->type == LOG_FMT_GLOBAL) {
409 *defoptions = node->options;
410 free(node->arg);
411 free(node);
412 } else {
413 if (logformat_keywords[j].config_callback &&
414 logformat_keywords[j].config_callback(node, curproxy) != 0) {
Dragan Dosen61302da2019-04-30 00:40:02 +0200415 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100416 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100417 curproxy->to_log |= logformat_keywords[j].lw;
418 LIST_ADDQ(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100419 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100420 if (logformat_keywords[j].replace_by)
Christopher Faulet767a84b2017-11-24 16:50:31 +0100421 ha_warning("parsing [%s:%d] : deprecated variable '%s' in '%s', please replace it with '%s'.\n",
422 curproxy->conf.args.file, curproxy->conf.args.line,
423 logformat_keywords[j].name, fmt_directive(curproxy), logformat_keywords[j].replace_by);
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100424 return 1;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100425 } else {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100426 memprintf(err, "format variable '%s' is reserved for HTTP mode",
427 logformat_keywords[j].name);
Dragan Dosen61302da2019-04-30 00:40:02 +0200428 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100429 }
William Lallemand723b73a2012-02-08 16:37:49 +0100430 }
431 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100432
433 j = var[var_len];
434 var[var_len] = 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100435 memprintf(err, "no such format variable '%s'. If you wanted to emit the '%%' character verbatim, you need to use '%%%%'", var);
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100436 var[var_len] = j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200437
438 error_free:
439 if (node) {
440 free(node->arg);
441 free(node);
442 }
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100443 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100444}
445
446/*
447 * push to the logformat linked list
448 *
449 * start: start pointer
450 * end: end text pointer
451 * type: string type
William Lallemand1d705562012-03-12 12:46:41 +0100452 * list_format: destination list
William Lallemand723b73a2012-02-08 16:37:49 +0100453 *
454 * LOG_TEXT: copy chars from start to end excluding end.
455 *
456*/
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100457int add_to_logformat_list(char *start, char *end, int type, struct list *list_format, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100458{
459 char *str;
460
Willy Tarreaua3571662012-12-20 21:59:12 +0100461 if (type == LF_TEXT) { /* type text */
Vincent Bernat02779b62016-04-03 13:48:43 +0200462 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100463 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100464 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100465 return 0;
466 }
Vincent Bernat02779b62016-04-03 13:48:43 +0200467 str = calloc(1, end - start + 1);
William Lallemand723b73a2012-02-08 16:37:49 +0100468 strncpy(str, start, end - start);
William Lallemand723b73a2012-02-08 16:37:49 +0100469 str[end - start] = '\0';
470 node->arg = str;
William Lallemand1d705562012-03-12 12:46:41 +0100471 node->type = LOG_FMT_TEXT; // type string
472 LIST_ADDQ(list_format, &node->list);
Willy Tarreaua3571662012-12-20 21:59:12 +0100473 } else if (type == LF_SEPARATOR) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200474 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100475 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100476 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100477 return 0;
478 }
William Lallemand1d705562012-03-12 12:46:41 +0100479 node->type = LOG_FMT_SEPARATOR;
480 LIST_ADDQ(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100481 }
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100482 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100483}
484
485/*
Willy Tarreauc8368452012-12-21 00:09:23 +0100486 * Parse the sample fetch expression <text> and add a node to <list_format> upon
487 * success. At the moment, sample converters are not yet supported but fetch arguments
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100488 * should work. The curpx->conf.args.ctx must be set by the caller. If an end pointer
489 * is passed in <endptr>, it will be updated with the pointer to the first character
490 * not part of the sample expression.
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100491 *
492 * In error case, the function returns 0, otherwise it returns 1.
Willy Tarreauc8368452012-12-21 00:09:23 +0100493 */
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100494int add_sample_to_logformat_list(char *text, char *arg, int arg_len, struct proxy *curpx, struct list *list_format, int options, int cap, char **err, char **endptr)
Willy Tarreauc8368452012-12-21 00:09:23 +0100495{
496 char *cmd[2];
Dragan Dosen61302da2019-04-30 00:40:02 +0200497 struct sample_expr *expr = NULL;
498 struct logformat_node *node = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +0100499 int cmd_arg;
500
501 cmd[0] = text;
502 cmd[1] = "";
503 cmd_arg = 0;
504
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100505 expr = sample_parse_expr(cmd, &cmd_arg, curpx->conf.args.file, curpx->conf.args.line, err, &curpx->conf.args, endptr);
Willy Tarreauc8368452012-12-21 00:09:23 +0100506 if (!expr) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100507 memprintf(err, "failed to parse sample expression <%s> : %s", text, *err);
Dragan Dosen61302da2019-04-30 00:40:02 +0200508 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100509 }
510
Vincent Bernat02779b62016-04-03 13:48:43 +0200511 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100512 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100513 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200514 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100515 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100516 node->type = LOG_FMT_EXPR;
517 node->expr = expr;
518 node->options = options;
519
520 if (arg_len) {
521 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100522 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200523 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100524 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100525 if (expr->fetch->val & cap & SMP_VAL_REQUEST)
Willy Tarreauc8368452012-12-21 00:09:23 +0100526 node->options |= LOG_OPT_REQ_CAP; /* fetch method is request-compatible */
527
Willy Tarreau434c57c2013-01-08 01:10:24 +0100528 if (expr->fetch->val & cap & SMP_VAL_RESPONSE)
Willy Tarreauc8368452012-12-21 00:09:23 +0100529 node->options |= LOG_OPT_RES_CAP; /* fetch method is response-compatible */
530
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100531 if (!(expr->fetch->val & cap)) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100532 memprintf(err, "sample fetch <%s> may not be reliably used here because it needs '%s' which is not available here",
533 text, sample_src_names(expr->fetch->use));
Dragan Dosen61302da2019-04-30 00:40:02 +0200534 goto error_free;
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100535 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100536
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200537 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreauc8368452012-12-21 00:09:23 +0100538 /* Note, we may also need to set curpx->to_log with certain fetches */
Willy Tarreau25320b22013-03-24 07:22:08 +0100539 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
Willy Tarreauc8368452012-12-21 00:09:23 +0100540
William Lallemand65ad6e12014-01-31 15:08:02 +0100541 /* FIXME: temporary workaround for missing LW_XPRT and LW_REQ flags
542 * needed with some sample fetches (eg: ssl*). We always set it for
543 * now on, but this will leave with sample capabilities soon.
Willy Tarreau1f31c732013-01-10 16:22:27 +0100544 */
545 curpx->to_log |= LW_XPRT;
Christopher Fauletd2236cd2020-04-06 18:29:14 +0200546 if (curpx->http_needed)
547 curpx->to_log |= LW_REQ;
Willy Tarreauc8368452012-12-21 00:09:23 +0100548 LIST_ADDQ(list_format, &node->list);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100549 return 1;
Dragan Dosen61302da2019-04-30 00:40:02 +0200550
551 error_free:
552 release_sample_expr(expr);
553 if (node) {
554 free(node->arg);
555 free(node);
556 }
557 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100558}
559
560/*
William Lallemand723b73a2012-02-08 16:37:49 +0100561 * Parse the log_format string and fill a linked list.
562 * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200563 * You can set arguments using { } : %{many arguments}varname.
564 * The curproxy->conf.args.ctx must be set by the caller.
William Lallemand1d705562012-03-12 12:46:41 +0100565 *
Ilya Shipitsinae40dbc2020-04-04 12:59:53 +0500566 * fmt: the string to parse
William Lallemand1d705562012-03-12 12:46:41 +0100567 * curproxy: the proxy affected
568 * list_format: the destination list
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +0100569 * options: LOG_OPT_* to force on every node
Willy Tarreau434c57c2013-01-08 01:10:24 +0100570 * cap: all SMP_VAL_* flags supported by the consumer
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100571 *
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100572 * The function returns 1 in success case, otherwise, it returns 0 and err is filled.
William Lallemand723b73a2012-02-08 16:37:49 +0100573 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100574int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct list *list_format, int options, int cap, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100575{
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100576 char *sp, *str, *backfmt; /* start pointer for text parts */
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100577 char *arg = NULL; /* start pointer for args */
578 char *var = NULL; /* start pointer for vars */
579 int arg_len = 0;
580 int var_len = 0;
581 int cformat; /* current token format */
582 int pformat; /* previous token format */
William Lallemand723b73a2012-02-08 16:37:49 +0100583 struct logformat_node *tmplf, *back;
584
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100585 sp = str = backfmt = strdup(fmt);
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100586 if (!str) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100587 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100588 return 0;
589 }
William Lallemand1dc00ef2012-08-09 16:41:35 +0200590 curproxy->to_log |= LW_INIT;
William Lallemand5e19a282012-04-02 16:22:10 +0200591
William Lallemand723b73a2012-02-08 16:37:49 +0100592 /* flush the list first. */
William Lallemand1d705562012-03-12 12:46:41 +0100593 list_for_each_entry_safe(tmplf, back, list_format, list) {
William Lallemand723b73a2012-02-08 16:37:49 +0100594 LIST_DEL(&tmplf->list);
Dragan Dosen61302da2019-04-30 00:40:02 +0200595 release_sample_expr(tmplf->expr);
596 free(tmplf->arg);
William Lallemand723b73a2012-02-08 16:37:49 +0100597 free(tmplf);
598 }
599
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100600 for (cformat = LF_INIT; cformat != LF_END; str++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100601 pformat = cformat;
602
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100603 if (!*str)
604 cformat = LF_END; // preset it to save all states from doing this
William Lallemand723b73a2012-02-08 16:37:49 +0100605
Joseph Herlant85b40592018-11-15 12:10:04 -0800606 /* The principle of the two-step state machine below is to first detect a change, and
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100607 * second have all common paths processed at one place. The common paths are the ones
608 * encountered in text areas (LF_INIT, LF_TEXT, LF_SEPARATOR) and at the end (LF_END).
609 * We use the common LF_INIT state to dispatch to the different final states.
610 */
611 switch (pformat) {
612 case LF_STARTVAR: // text immediately following a '%'
Willy Tarreauc8368452012-12-21 00:09:23 +0100613 arg = NULL; var = NULL;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100614 arg_len = var_len = 0;
615 if (*str == '{') { // optional argument
616 cformat = LF_STARG;
617 arg = str + 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100618 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100619 else if (*str == '[') {
620 cformat = LF_STEXPR;
621 var = str + 1; // store expr in variable name
622 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100623 else if (isalpha((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100624 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100625 var = str;
William Lallemand723b73a2012-02-08 16:37:49 +0100626 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100627 else if (*str == '%')
Ilya Shipitsin856aabc2020-04-16 23:51:34 +0500628 cformat = LF_TEXT; // convert this character to a literal (useful for '%')
Willy Tarreau0f28f822013-12-16 01:38:33 +0100629 else if (isdigit((unsigned char)*str) || *str == ' ' || *str == '\t') {
Willy Tarreau06d97f92013-12-02 17:45:48 +0100630 /* single '%' followed by blank or digit, send them both */
631 cformat = LF_TEXT;
632 pformat = LF_TEXT; /* finally we include the previous char as well */
633 sp = str - 1; /* send both the '%' and the current char */
Jim Freemana2278c82017-04-15 08:01:59 -0600634 memprintf(err, "unexpected variable name near '%c' at position %d line : '%s'. Maybe you want to write a single '%%', use the syntax '%%%%'",
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100635 *str, (int)(str - backfmt), fmt);
Willy Tarreau51013e82019-12-11 12:05:39 +0100636 goto fail;
Willy Tarreau06d97f92013-12-02 17:45:48 +0100637
638 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100639 else
Ilya Shipitsin856aabc2020-04-16 23:51:34 +0500640 cformat = LF_INIT; // handle other cases of literals
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100641 break;
642
643 case LF_STARG: // text immediately following '%{'
644 if (*str == '}') { // end of arg
William Lallemand723b73a2012-02-08 16:37:49 +0100645 cformat = LF_EDARG;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100646 arg_len = str - arg;
647 *str = 0; // used for reporting errors
William Lallemand723b73a2012-02-08 16:37:49 +0100648 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100649 break;
650
651 case LF_EDARG: // text immediately following '%{arg}'
Willy Tarreauc8368452012-12-21 00:09:23 +0100652 if (*str == '[') {
653 cformat = LF_STEXPR;
654 var = str + 1; // store expr in variable name
655 break;
656 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100657 else if (isalnum((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100658 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100659 var = str;
660 break;
661 }
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100662 memprintf(err, "parse argument modifier without variable name near '%%{%s}'", arg);
Willy Tarreau51013e82019-12-11 12:05:39 +0100663 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100664
Willy Tarreauc8368452012-12-21 00:09:23 +0100665 case LF_STEXPR: // text immediately following '%['
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100666 /* the whole sample expression is parsed at once,
667 * returning the pointer to the first character not
668 * part of the expression, which MUST be the trailing
669 * angle bracket.
670 */
671 if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err, &str))
672 goto fail;
673
674 if (*str == ']') {
675 // end of arg, go on with next state
676 cformat = pformat = LF_EDEXPR;
677 sp = str;
678 }
679 else {
680 char c = *str;
681 *str = 0;
Willy Tarreau90807112020-02-25 08:16:33 +0100682 if (isprint((unsigned char)c))
Willy Tarreaucd0d2ed2020-02-14 17:33:06 +0100683 memprintf(err, "expected ']' after '%s', but found '%c'", var, c);
684 else
685 memprintf(err, "missing ']' after '%s'", var);
Dragan Dosen2866acf2020-06-30 21:16:43 +0200686 goto fail;
Willy Tarreauc8368452012-12-21 00:09:23 +0100687 }
688 break;
689
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100690 case LF_VAR: // text part of a variable name
691 var_len = str - var;
Willy Tarreau0f28f822013-12-16 01:38:33 +0100692 if (!isalnum((unsigned char)*str))
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100693 cformat = LF_INIT; // not variable name anymore
694 break;
695
Willy Tarreauc8368452012-12-21 00:09:23 +0100696 default: // LF_INIT, LF_TEXT, LF_SEPARATOR, LF_END, LF_EDEXPR
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100697 cformat = LF_INIT;
698 }
699
700 if (cformat == LF_INIT) { /* resynchronize state to text/sep/startvar */
701 switch (*str) {
702 case '%': cformat = LF_STARTVAR; break;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100703 case 0 : cformat = LF_END; break;
Dragan Dosen1e3b16f2020-06-23 18:16:44 +0200704 case ' ':
705 if (options & LOG_OPT_MERGE_SPACES) {
706 cformat = LF_SEPARATOR;
707 break;
708 }
709 /* fall through */
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100710 default : cformat = LF_TEXT; break;
William Lallemand723b73a2012-02-08 16:37:49 +0100711 }
712 }
713
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100714 if (cformat != pformat || pformat == LF_SEPARATOR) {
715 switch (pformat) {
716 case LF_VAR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100717 if (!parse_logformat_var(arg, arg_len, var, var_len, curproxy, list_format, &options, err))
Willy Tarreau51013e82019-12-11 12:05:39 +0100718 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100719 break;
720 case LF_TEXT:
721 case LF_SEPARATOR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100722 if (!add_to_logformat_list(sp, str, pformat, list_format, err))
Willy Tarreau51013e82019-12-11 12:05:39 +0100723 goto fail;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100724 break;
725 }
726 sp = str; /* new start of text at every state switch and at every separator */
William Lallemand723b73a2012-02-08 16:37:49 +0100727 }
728 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100729
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100730 if (pformat == LF_STARTVAR || pformat == LF_STARG || pformat == LF_STEXPR) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100731 memprintf(err, "truncated line after '%s'", var ? var : arg ? arg : "%");
Willy Tarreau51013e82019-12-11 12:05:39 +0100732 goto fail;
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100733 }
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100734 free(backfmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100735
736 return 1;
Willy Tarreau51013e82019-12-11 12:05:39 +0100737 fail:
738 free(backfmt);
739 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100740}
741
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200742/*
Ilya Shipitsin856aabc2020-04-16 23:51:34 +0500743 * 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 +0200744 * ranges of indexes. Note that an index may be considered as a particular range
745 * with a high limit to the low limit.
746 */
747int get_logsrv_smp_range(unsigned int *low, unsigned int *high, char **arg, char **err)
748{
749 char *end, *p;
750
751 *low = *high = 0;
752
753 p = *arg;
754 end = strchr(p, ',');
755 if (!end)
756 end = p + strlen(p);
757
758 *high = *low = read_uint((const char **)&p, end);
759 if (!*low || (p != end && *p != '-'))
760 goto err;
761
762 if (p == end)
763 goto done;
764
765 p++;
766 *high = read_uint((const char **)&p, end);
767 if (!*high || *high <= *low || p != end)
768 goto err;
769
770 done:
771 if (*end == ',')
772 end++;
773 *arg = end;
774 return 1;
775
776 err:
777 memprintf(err, "wrong sample range '%s'", *arg);
778 return 0;
779}
780
781/*
782 * Returns 1 if the range defined by <low> and <high> overlaps
783 * one of them in <rgs> array of ranges with <sz> the size of this
784 * array, 0 if not.
785 */
786int smp_log_ranges_overlap(struct smp_log_range *rgs, size_t sz,
787 unsigned int low, unsigned int high, char **err)
788{
789 size_t i;
790
791 for (i = 0; i < sz; i++) {
792 if ((low >= rgs[i].low && low <= rgs[i].high) ||
793 (high >= rgs[i].low && high <= rgs[i].high)) {
794 memprintf(err, "ranges are overlapping");
795 return 1;
796 }
797 }
798
799 return 0;
800}
801
802int smp_log_range_cmp(const void *a, const void *b)
803{
804 const struct smp_log_range *rg_a = a;
805 const struct smp_log_range *rg_b = b;
806
807 if (rg_a->high < rg_b->low)
808 return -1;
809 else if (rg_a->low > rg_b->high)
810 return 1;
811
812 return 0;
813}
814
815/*
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200816 * Parse "log" keyword and update <logsrvs> list accordingly.
817 *
818 * When <do_del> is set, it means the "no log" line was parsed, so all log
819 * servers in <logsrvs> are released.
820 *
821 * Otherwise, we try to parse the "log" line. First of all, when the list is not
822 * the global one, we look for the parameter "global". If we find it,
823 * global.logsrvs is copied. Else we parse each arguments.
824 *
825 * The function returns 1 in success case, otherwise, it returns 0 and err is
826 * filled.
827 */
828int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err)
829{
830 struct sockaddr_storage *sk;
831 struct logsrv *logsrv = NULL;
832 int port1, port2;
833 int cur_arg;
834
835 /*
836 * "no log": delete previous herited or defined syslog
837 * servers.
838 */
839 if (do_del) {
840 struct logsrv *back;
841
842 if (*(args[1]) != 0) {
843 memprintf(err, "'no log' does not expect arguments");
844 goto error;
845 }
846
847 list_for_each_entry_safe(logsrv, back, logsrvs, list) {
848 LIST_DEL(&logsrv->list);
849 free(logsrv);
850 }
851 return 1;
852 }
853
854 /*
855 * "log global": copy global.logrsvs linked list to the end of logsrvs
856 * list. But first, we check (logsrvs != global.logsrvs).
857 */
858 if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
859 if (logsrvs == &global.logsrvs) {
860 memprintf(err, "'global' is not supported for a global syslog server");
861 goto error;
862 }
863 list_for_each_entry(logsrv, &global.logsrvs, list) {
Christopher Faulet28ac0992018-03-26 16:09:19 +0200864 struct logsrv *node;
865
866 list_for_each_entry(node, logsrvs, list) {
867 if (node->ref == logsrv)
868 goto skip_logsrv;
869 }
870
871 node = malloc(sizeof(*node));
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200872 memcpy(node, logsrv, sizeof(struct logsrv));
Christopher Faulet28ac0992018-03-26 16:09:19 +0200873 node->ref = logsrv;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200874 LIST_INIT(&node->list);
875 LIST_ADDQ(logsrvs, &node->list);
Christopher Faulet28ac0992018-03-26 16:09:19 +0200876
877 skip_logsrv:
878 continue;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200879 }
880 return 1;
881 }
882
883 /*
884 * "log <address> ...: parse a syslog server line
885 */
886 if (*(args[1]) == 0 || *(args[2]) == 0) {
887 memprintf(err, "expects <address> and <facility> %s as arguments",
888 ((logsrvs == &global.logsrvs) ? "" : "or global"));
889 goto error;
890 }
891
Willy Tarreau5a32ecc2018-11-12 07:34:59 +0100892 /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */
893 if (strcmp(args[1], "stdout") == 0)
894 args[1] = "fd@1";
895 else if (strcmp(args[1], "stderr") == 0)
896 args[1] = "fd@2";
897
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200898 logsrv = calloc(1, sizeof(*logsrv));
899 if (!logsrv) {
900 memprintf(err, "out of memory");
901 goto error;
902 }
903
904 /* skip address for now, it will be parsed at the end */
905 cur_arg = 2;
906
907 /* just after the address, a length may be specified */
908 logsrv->maxlen = MAX_SYSLOG_LEN;
909 if (strcmp(args[cur_arg], "len") == 0) {
910 int len = atoi(args[cur_arg+1]);
911 if (len < 80 || len > 65535) {
912 memprintf(err, "invalid log length '%s', must be between 80 and 65535",
913 args[cur_arg+1]);
914 goto error;
915 }
916 logsrv->maxlen = len;
917 cur_arg += 2;
918 }
919 if (logsrv->maxlen > global.max_syslog_len)
920 global.max_syslog_len = logsrv->maxlen;
921
922 /* after the length, a format may be specified */
923 if (strcmp(args[cur_arg], "format") == 0) {
924 logsrv->format = get_log_format(args[cur_arg+1]);
925 if (logsrv->format < 0) {
926 memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
927 goto error;
928 }
929 cur_arg += 2;
930 }
931
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200932 if (strcmp(args[cur_arg], "sample") == 0) {
933 unsigned low, high;
934 char *p, *beg, *end, *smp_sz_str;
935 struct smp_log_range *smp_rgs = NULL;
936 size_t smp_rgs_sz = 0, smp_sz = 0, new_smp_sz;
937
938 p = args[cur_arg+1];
939 smp_sz_str = strchr(p, ':');
940 if (!smp_sz_str) {
941 memprintf(err, "Missing sample size");
942 goto error;
943 }
944
945 *smp_sz_str++ = '\0';
946
947 end = p + strlen(p);
948
949 while (p != end) {
950 if (!get_logsrv_smp_range(&low, &high, &p, err))
951 goto error;
952
953 if (smp_rgs && smp_log_ranges_overlap(smp_rgs, smp_rgs_sz, low, high, err))
954 goto error;
955
956 smp_rgs = my_realloc2(smp_rgs, (smp_rgs_sz + 1) * sizeof *smp_rgs);
957 if (!smp_rgs) {
958 memprintf(err, "out of memory error");
959 goto error;
960 }
961
962 smp_rgs[smp_rgs_sz].low = low;
963 smp_rgs[smp_rgs_sz].high = high;
964 smp_rgs[smp_rgs_sz].sz = high - low + 1;
965 smp_rgs[smp_rgs_sz].curr_idx = 0;
966 if (smp_rgs[smp_rgs_sz].high > smp_sz)
967 smp_sz = smp_rgs[smp_rgs_sz].high;
968 smp_rgs_sz++;
969 }
970
Tim Duesterhus21648002019-06-23 22:10:10 +0200971 if (smp_rgs == NULL) {
972 memprintf(err, "no sampling ranges given");
973 goto error;
974 }
975
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200976 beg = smp_sz_str;
977 end = beg + strlen(beg);
978 new_smp_sz = read_uint((const char **)&beg, end);
979 if (!new_smp_sz || beg != end) {
980 memprintf(err, "wrong sample size '%s' for sample range '%s'",
981 smp_sz_str, args[cur_arg+1]);
982 goto error;
983 }
984
985 if (new_smp_sz < smp_sz) {
986 memprintf(err, "sample size %zu should be greater or equal to "
987 "%zu the maximum of the high ranges limits",
988 new_smp_sz, smp_sz);
989 goto error;
990 }
991 smp_sz = new_smp_sz;
992
993 /* Let's order <smp_rgs> array. */
994 qsort(smp_rgs, smp_rgs_sz, sizeof(struct smp_log_range), smp_log_range_cmp);
995
996 logsrv->lb.smp_rgs = smp_rgs;
997 logsrv->lb.smp_rgs_sz = smp_rgs_sz;
998 logsrv->lb.smp_sz = smp_sz;
999
1000 cur_arg += 2;
1001 }
Frédéric Lécailled803e472019-04-25 07:42:09 +02001002 HA_SPIN_INIT(&logsrv->lock);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001003 /* parse the facility */
1004 logsrv->facility = get_log_facility(args[cur_arg]);
1005 if (logsrv->facility < 0) {
1006 memprintf(err, "unknown log facility '%s'", args[cur_arg]);
1007 goto error;
1008 }
1009 cur_arg++;
1010
1011 /* parse the max syslog level (default: debug) */
1012 logsrv->level = 7;
1013 if (*(args[cur_arg])) {
1014 logsrv->level = get_log_level(args[cur_arg]);
1015 if (logsrv->level < 0) {
1016 memprintf(err, "unknown optional log level '%s'", args[cur_arg]);
1017 goto error;
1018 }
1019 cur_arg++;
1020 }
1021
1022 /* parse the limit syslog level (default: emerg) */
1023 logsrv->minlvl = 0;
1024 if (*(args[cur_arg])) {
1025 logsrv->minlvl = get_log_level(args[cur_arg]);
1026 if (logsrv->minlvl < 0) {
1027 memprintf(err, "unknown optional minimum log level '%s'", args[cur_arg]);
1028 goto error;
1029 }
1030 cur_arg++;
1031 }
1032
1033 /* Too many args */
1034 if (*(args[cur_arg])) {
1035 memprintf(err, "cannot handle unexpected argument '%s'", args[cur_arg]);
1036 goto error;
1037 }
1038
1039 /* now, back to the address */
Willy Tarreauf3dc30f2019-08-30 14:18:44 +02001040 logsrv->type = LOG_TARGET_DGRAM;
Willy Tarreauc046d162019-08-30 15:24:59 +02001041 if (strncmp(args[1], "ring@", 5) == 0) {
Willy Tarreauc046d162019-08-30 15:24:59 +02001042 logsrv->addr.ss_family = AF_UNSPEC;
1043 logsrv->type = LOG_TARGET_BUFFER;
Emeric Brun99c453d2020-05-25 15:01:04 +02001044 logsrv->sink = NULL;
1045 logsrv->ring_name = strdup(args[1] + 5);
Willy Tarreauc046d162019-08-30 15:24:59 +02001046 goto done;
1047 }
1048
Willy Tarreauf3dc30f2019-08-30 14:18:44 +02001049 if (strncmp(args[1], "fd@", 3) == 0)
1050 logsrv->type = LOG_TARGET_FD;
1051
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001052 sk = str2sa_range(args[1], NULL, &port1, &port2, err, NULL, NULL, 1);
1053 if (!sk)
1054 goto error;
1055 logsrv->addr = *sk;
1056
1057 if (sk->ss_family == AF_INET || sk->ss_family == AF_INET6) {
1058 if (port1 != port2) {
1059 memprintf(err, "port ranges and offsets are not allowed in '%s'", args[1]);
1060 goto error;
1061 }
1062 logsrv->addr = *sk;
1063 if (!port1)
1064 set_host_port(&logsrv->addr, SYSLOG_PORT);
1065 }
Willy Tarreauc046d162019-08-30 15:24:59 +02001066 done:
Christopher Faulet4b0b79d2018-03-26 15:54:32 +02001067 LIST_ADDQ(logsrvs, &logsrv->list);
1068 return 1;
1069
1070 error:
1071 free(logsrv);
1072 return 0;
1073}
1074
1075
Christopher Fauletd4696382017-10-24 11:44:05 +02001076/* Generic function to display messages prefixed by a label */
1077static void print_message(const char *label, const char *fmt, va_list argp)
1078{
1079 struct tm tm;
1080 char *head, *msg;
1081
1082 head = msg = NULL;
1083
1084 get_localtime(date.tv_sec, &tm);
1085 memprintf(&head, "[%s] %03d/%02d%02d%02d (%d) : ",
1086 label, tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
1087 memvprintf(&msg, fmt, argp);
1088
Willy Tarreau869efd52019-11-15 15:16:57 +01001089 if (global.mode & MODE_STARTING) {
1090 if (unlikely(!startup_logs))
1091 startup_logs = ring_new(STARTUP_LOG_SIZE);
1092
1093 if (likely(startup_logs)) {
1094 struct ist m[2];
1095
1096 m[0] = ist(head);
1097 m[1] = ist(msg);
1098 /* trim the trailing '\n' */
1099 if (m[1].len > 0 && m[1].ptr[m[1].len - 1] == '\n')
1100 m[1].len--;
1101 ring_write(startup_logs, ~0, 0, 0, m, 2);
1102 }
1103 }
Christopher Fauletd4696382017-10-24 11:44:05 +02001104
1105 fprintf(stderr, "%s%s", head, msg);
1106 fflush(stderr);
1107
1108 free(head);
1109 free(msg);
1110}
1111
Willy Tarreaubaaee002006-06-26 02:48:02 +02001112/*
1113 * Displays the message on stderr with the date and pid. Overrides the quiet
1114 * mode during startup.
1115 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001116void ha_alert(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001117{
1118 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001119
1120 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
Willy Tarreaubb869862020-04-16 10:52:41 +02001121 if (!(warned & WARN_EXEC_PATH)) {
1122 const char *path = get_exec_path();
1123
1124 warned |= WARN_EXEC_PATH;
1125 ha_notice("haproxy version is %s\n", haproxy_version);
1126 if (path)
1127 ha_notice("path to executable is %s\n", path);
1128 }
Willy Tarreaubaaee002006-06-26 02:48:02 +02001129 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001130 print_message("ALERT", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001131 va_end(argp);
1132 }
1133}
1134
1135
1136/*
1137 * Displays the message on stderr with the date and pid.
1138 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001139void ha_warning(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001140{
1141 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001142
Willy Tarreaubebd2122020-04-15 16:06:11 +02001143 warned |= WARN_ANY;
1144
Willy Tarreaubaaee002006-06-26 02:48:02 +02001145 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1146 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001147 print_message("WARNING", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001148 va_end(argp);
1149 }
1150}
1151
1152/*
William Lallemand9c56a222018-11-21 18:04:52 +01001153 * Displays the message on stderr with the date and pid.
1154 */
1155void ha_notice(const char *fmt, ...)
1156{
1157 va_list argp;
1158
1159 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1160 va_start(argp, fmt);
1161 print_message("NOTICE", fmt, argp);
1162 va_end(argp);
1163 }
1164}
1165
1166/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001167 * Displays the message on <out> only if quiet mode is not set.
1168 */
Willy Tarreaub17916e2006-10-15 15:17:57 +02001169void qfprintf(FILE *out, const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001170{
1171 va_list argp;
1172
1173 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1174 va_start(argp, fmt);
1175 vfprintf(out, fmt, argp);
1176 fflush(out);
1177 va_end(argp);
1178 }
1179}
1180
1181/*
Dragan Dosen1322d092015-09-22 16:05:32 +02001182 * returns log format for <fmt> or -1 if not found.
1183 */
1184int get_log_format(const char *fmt)
1185{
1186 int format;
1187
1188 format = LOG_FORMATS - 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001189 while (format >= 0 && strcmp(log_formats[format].name, fmt))
Dragan Dosen1322d092015-09-22 16:05:32 +02001190 format--;
1191
1192 return format;
1193}
1194
1195/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001196 * returns log level for <lev> or -1 if not found.
1197 */
1198int get_log_level(const char *lev)
1199{
1200 int level;
1201
1202 level = NB_LOG_LEVELS - 1;
1203 while (level >= 0 && strcmp(log_levels[level], lev))
1204 level--;
1205
1206 return level;
1207}
1208
Willy Tarreaubaaee002006-06-26 02:48:02 +02001209/*
1210 * returns log facility for <fac> or -1 if not found.
1211 */
1212int get_log_facility(const char *fac)
1213{
1214 int facility;
1215
1216 facility = NB_LOG_FACILITIES - 1;
1217 while (facility >= 0 && strcmp(log_facilities[facility], fac))
1218 facility--;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001219
Willy Tarreaubaaee002006-06-26 02:48:02 +02001220 return facility;
1221}
1222
William Lallemanda1cc3812012-02-08 16:38:44 +01001223/*
Dragan Dosen835b9212016-02-12 13:23:03 +01001224 * Encode the string.
1225 *
1226 * When using the +E log format option, it will try to escape '"\]'
1227 * characters with '\' as prefix. The same prefix should not be used as
1228 * <escape>.
1229 */
1230static char *lf_encode_string(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001231 const char escape, const long *map,
Dragan Dosen835b9212016-02-12 13:23:03 +01001232 const char *string,
1233 struct logformat_node *node)
1234{
1235 if (node->options & LOG_OPT_ESC) {
1236 if (start < stop) {
1237 stop--; /* reserve one byte for the final '\0' */
1238 while (start < stop && *string != '\0') {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001239 if (!ha_bit_test((unsigned char)(*string), map)) {
1240 if (!ha_bit_test((unsigned char)(*string), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001241 *start++ = *string;
1242 else {
1243 if (start + 2 >= stop)
1244 break;
1245 *start++ = '\\';
1246 *start++ = *string;
1247 }
1248 }
1249 else {
1250 if (start + 3 >= stop)
1251 break;
1252 *start++ = escape;
1253 *start++ = hextab[(*string >> 4) & 15];
1254 *start++ = hextab[*string & 15];
1255 }
1256 string++;
1257 }
1258 *start = '\0';
1259 }
1260 }
1261 else {
1262 return encode_string(start, stop, escape, map, string);
1263 }
1264
1265 return start;
1266}
1267
1268/*
1269 * Encode the chunk.
1270 *
1271 * When using the +E log format option, it will try to escape '"\]'
1272 * characters with '\' as prefix. The same prefix should not be used as
1273 * <escape>.
1274 */
1275static char *lf_encode_chunk(char *start, char *stop,
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001276 const char escape, const long *map,
Willy Tarreau83061a82018-07-13 11:56:34 +02001277 const struct buffer *chunk,
Dragan Dosen835b9212016-02-12 13:23:03 +01001278 struct logformat_node *node)
1279{
1280 char *str, *end;
1281
1282 if (node->options & LOG_OPT_ESC) {
1283 if (start < stop) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001284 str = chunk->area;
1285 end = chunk->area + chunk->data;
Dragan Dosen835b9212016-02-12 13:23:03 +01001286
1287 stop--; /* reserve one byte for the final '\0' */
1288 while (start < stop && str < end) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001289 if (!ha_bit_test((unsigned char)(*str), map)) {
1290 if (!ha_bit_test((unsigned char)(*str), rfc5424_escape_map))
Dragan Dosen835b9212016-02-12 13:23:03 +01001291 *start++ = *str;
1292 else {
1293 if (start + 2 >= stop)
1294 break;
1295 *start++ = '\\';
1296 *start++ = *str;
1297 }
1298 }
1299 else {
1300 if (start + 3 >= stop)
1301 break;
1302 *start++ = escape;
1303 *start++ = hextab[(*str >> 4) & 15];
1304 *start++ = hextab[*str & 15];
1305 }
1306 str++;
1307 }
1308 *start = '\0';
1309 }
1310 }
1311 else {
1312 return encode_chunk(start, stop, escape, map, chunk);
1313 }
1314
1315 return start;
1316}
1317
1318/*
William Lallemanda1cc3812012-02-08 16:38:44 +01001319 * Write a string in the log string
Dragan Dosen835b9212016-02-12 13:23:03 +01001320 * Take cares of quote and escape options
William Lallemanda1cc3812012-02-08 16:38:44 +01001321 *
Joseph Herlant85b40592018-11-15 12:10:04 -08001322 * Return the address of the \0 character, or NULL on error
William Lallemanda1cc3812012-02-08 16:38:44 +01001323 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001324char *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 +01001325{
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001326 if (size < 2)
1327 return NULL;
William Lallemanda1cc3812012-02-08 16:38:44 +01001328
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001329 if (node->options & LOG_OPT_QUOTE) {
1330 *(dst++) = '"';
1331 size--;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001332 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001333
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001334 if (src && len) {
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001335 if (++len > size)
1336 len = size;
Dragan Dosen835b9212016-02-12 13:23:03 +01001337 if (node->options & LOG_OPT_ESC) {
Dragan Dosen835b9212016-02-12 13:23:03 +01001338 char *ret;
1339
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001340 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
Dragan Dosen835b9212016-02-12 13:23:03 +01001341 if (ret == NULL || *ret != '\0')
1342 return NULL;
1343 len = ret - dst;
1344 }
1345 else {
Dragan Dosen835b9212016-02-12 13:23:03 +01001346 len = strlcpy2(dst, src, len);
1347 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001348
1349 size -= len;
1350 dst += len;
1351 }
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001352 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1353 if (size < 2)
1354 return NULL;
1355 *(dst++) = '-';
1356 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001357
1358 if (node->options & LOG_OPT_QUOTE) {
1359 if (size < 2)
1360 return NULL;
1361 *(dst++) = '"';
1362 }
1363
1364 *dst = '\0';
William Lallemandbddd4fd2012-02-27 11:23:10 +01001365 return dst;
William Lallemanda1cc3812012-02-08 16:38:44 +01001366}
1367
Willy Tarreau26ffa852018-09-05 15:23:10 +02001368static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001369{
1370 return lf_text_len(dst, src, size, size, node);
1371}
1372
William Lallemand5f232402012-04-05 18:02:55 +02001373/*
Joseph Herlant85b40592018-11-15 12:10:04 -08001374 * Write a IP address to the log string
Ilya Shipitsin856aabc2020-04-16 23:51:34 +05001375 * +X option write in hexadecimal notation, most significant byte on the left
William Lallemand5f232402012-04-05 18:02:55 +02001376 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001377char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001378{
1379 char *ret = dst;
1380 int iret;
1381 char pn[INET6_ADDRSTRLEN];
1382
1383 if (node->options & LOG_OPT_HEXA) {
Radek Zajic594c4562019-03-22 10:21:54 +00001384 unsigned char *addr = NULL;
1385 switch (sockaddr->sa_family) {
1386 case AF_INET:
1387 addr = (unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1388 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1389 break;
1390 case AF_INET6:
1391 addr = (unsigned char *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr;
1392 iret = snprintf(dst, size, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1393 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
1394 addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
1395 break;
1396 default:
1397 return NULL;
1398 }
William Lallemand5f232402012-04-05 18:02:55 +02001399 if (iret < 0 || iret > size)
1400 return NULL;
1401 ret += iret;
1402 } else {
1403 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1404 ret = lf_text(dst, pn, size, node);
1405 if (ret == NULL)
1406 return NULL;
1407 }
1408 return ret;
1409}
1410
1411/*
1412 * Write a port to the log
Ilya Shipitsin856aabc2020-04-16 23:51:34 +05001413 * +X option write in hexadecimal notation, most significant byte on the left
William Lallemand5f232402012-04-05 18:02:55 +02001414 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001415char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001416{
1417 char *ret = dst;
1418 int iret;
1419
1420 if (node->options & LOG_OPT_HEXA) {
1421 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1422 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1423 if (iret < 0 || iret > size)
1424 return NULL;
1425 ret += iret;
1426 } else {
1427 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1428 if (ret == NULL)
1429 return NULL;
1430 }
1431 return ret;
1432}
1433
Dragan Dosen1322d092015-09-22 16:05:32 +02001434/* Re-generate time-based part of the syslog header in RFC3164 format at
1435 * the beginning of logheader once a second and return the pointer to the
1436 * first character after it.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001437 */
Emeric Brunbd163812020-05-06 14:33:46 +02001438char *update_log_hdr(const time_t time)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001439{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001440 static THREAD_LOCAL long tvsec;
Willy Tarreau83061a82018-07-13 11:56:34 +02001441 static THREAD_LOCAL struct buffer host = { };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001442 static THREAD_LOCAL int sep = 0;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001443
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001444 if (unlikely(time != tvsec || logheader_end == NULL)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +02001445 /* this string is rebuild only once a second */
Willy Tarreaufe944602007-10-25 10:34:16 +02001446 struct tm tm;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001447 int hdr_len;
Willy Tarreaufe944602007-10-25 10:34:16 +02001448
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001449 tvsec = time;
Willy Tarreaufe944602007-10-25 10:34:16 +02001450 get_localtime(tvsec, &tm);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001451
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001452 if (unlikely(global.log_send_hostname != host.area)) {
1453 host.area = global.log_send_hostname;
1454 host.data = host.area ? strlen(host.area) : 0;
1455 sep = host.data ? 1 : 0;
Dragan Dosen43885c72015-10-01 13:18:13 +02001456 }
1457
Dragan Dosen59cee972015-09-19 22:09:02 +02001458 hdr_len = snprintf(logheader, global.max_syslog_len,
Dragan Dosen43885c72015-10-01 13:18:13 +02001459 "<<<<>%s %2d %02d:%02d:%02d %.*s%*s",
Willy Tarreaufe944602007-10-25 10:34:16 +02001460 monthname[tm.tm_mon],
Dragan Dosen43885c72015-10-01 13:18:13 +02001461 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001462 (int)host.data, host.area, sep, "");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001463 /* WARNING: depending upon implementations, snprintf may return
1464 * either -1 or the number of bytes that would be needed to store
1465 * the total message. In both cases, we must adjust it.
1466 */
Willy Tarreau18324f52014-06-27 18:10:07 +02001467 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1468 hdr_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001469
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001470 logheader_end = logheader + hdr_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001471 }
1472
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001473 logheader_end[0] = 0; // ensure we get rid of any previous attempt
Willy Tarreau094af4e2015-01-07 15:03:42 +01001474
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001475 return logheader_end;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001476}
1477
Dragan Dosen1322d092015-09-22 16:05:32 +02001478/* Re-generate time-based part of the syslog header in RFC5424 format at
1479 * the beginning of logheader_rfc5424 once a second and return the pointer
1480 * to the first character after it.
1481 */
Emeric Brun9f9b22c2020-07-02 16:16:59 +02001482char *update_log_hdr_rfc5424(const time_t time, const suseconds_t frac)
Dragan Dosen1322d092015-09-22 16:05:32 +02001483{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001484 static THREAD_LOCAL long tvsec;
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001485 const char *gmt_offset;
Emeric Brun9f9b22c2020-07-02 16:16:59 +02001486 char c;
Dragan Dosen1322d092015-09-22 16:05:32 +02001487
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001488 if (unlikely(time != tvsec || logheader_rfc5424_end == NULL)) {
Dragan Dosen1322d092015-09-22 16:05:32 +02001489 /* this string is rebuild only once a second */
1490 struct tm tm;
1491 int hdr_len;
1492
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001493 tvsec = time;
Dragan Dosen1322d092015-09-22 16:05:32 +02001494 get_localtime(tvsec, &tm);
Benoit GARNIERe2e5bde2016-03-27 03:04:16 +02001495 gmt_offset = get_gmt_offset(time, &tm);
Dragan Dosen1322d092015-09-22 16:05:32 +02001496
1497 hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
Emeric Brun9f9b22c2020-07-02 16:16:59 +02001498 "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d.000000%.3s:%.2s %s ",
Dragan Dosen1322d092015-09-22 16:05:32 +02001499 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
Dragan Dosen17def462015-10-09 21:31:43 +02001500 tm.tm_hour, tm.tm_min, tm.tm_sec,
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001501 gmt_offset, gmt_offset+3,
Dragan Dosen43885c72015-10-01 13:18:13 +02001502 global.log_send_hostname ? global.log_send_hostname : hostname);
Dragan Dosen1322d092015-09-22 16:05:32 +02001503 /* WARNING: depending upon implementations, snprintf may return
1504 * either -1 or the number of bytes that would be needed to store
1505 * the total message. In both cases, we must adjust it.
1506 */
1507 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1508 hdr_len = global.max_syslog_len;
1509
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001510 logheader_rfc5424_end = logheader_rfc5424 + hdr_len;
Dragan Dosen1322d092015-09-22 16:05:32 +02001511 }
1512
Emeric Brun9f9b22c2020-07-02 16:16:59 +02001513 /* utoa_pad add a trailing '\0' so we save the char to restore */
1514 c = logheader_rfc5424[33];
1515 utoa_pad(frac, logheader_rfc5424 + 27, 7);
1516 logheader_rfc5424[33] = c;
1517
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001518 logheader_rfc5424_end[0] = 0; // ensure we get rid of any previous attempt
Dragan Dosen1322d092015-09-22 16:05:32 +02001519
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001520 return logheader_rfc5424_end;
Dragan Dosen1322d092015-09-22 16:05:32 +02001521}
1522
William Lallemand2a4a44f2012-02-06 16:00:33 +01001523/*
Dragan Dosen59cee972015-09-19 22:09:02 +02001524 * This function sends the syslog message using a printf format string. It
1525 * expects an LF-terminated message.
William Lallemand2a4a44f2012-02-06 16:00:33 +01001526 */
1527void send_log(struct proxy *p, int level, const char *format, ...)
1528{
1529 va_list argp;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001530 int data_len;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001531
Willy Tarreau8c97ab52015-01-15 16:29:53 +01001532 if (level < 0 || format == NULL || logline == NULL)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001533 return;
1534
William Lallemand2a4a44f2012-02-06 16:00:33 +01001535 va_start(argp, format);
Dragan Dosen59cee972015-09-19 22:09:02 +02001536 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
Willy Tarreau18324f52014-06-27 18:10:07 +02001537 if (data_len < 0 || data_len > global.max_syslog_len)
1538 data_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001539 va_end(argp);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001540
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001541 __send_log((p ? &p->logsrvs : NULL), (p ? &p->log_tag : NULL), level,
1542 logline, data_len, default_rfc5424_sd_log_format, 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001543}
1544
1545/*
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001546 * This function sends a syslog message to <logsrv>.
1547 * <pid_str> is the string to be used for the PID of the caller, <pid_size> is length.
1548 * Same thing for <sd> and <sd_size> which are used for the structured-data part
1549 * in RFC5424 formatted syslog messages, and <tag_str> and <tag_size> the syslog tag.
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001550 * It overrides the last byte of the message vector with an LF character.
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001551 * Does not return any error,
William Lallemand2a4a44f2012-02-06 16:00:33 +01001552 */
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001553static inline void __do_send_log(struct logsrv *logsrv, int nblogger, char *pid_str, size_t pid_size,
1554 int level, char *message, size_t size, char *sd, size_t sd_size,
1555 char *tag_str, size_t tag_size)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001556{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001557 static THREAD_LOCAL struct iovec iovec[NB_MSG_IOVEC_ELEMENTS] = { };
1558 static THREAD_LOCAL struct msghdr msghdr = {
1559 //.msg_iov = iovec,
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001560 .msg_iovlen = NB_MSG_IOVEC_ELEMENTS
1561 };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001562 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1563 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
1564 static THREAD_LOCAL char *dataptr = NULL;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001565 time_t time = date.tv_sec;
Emeric Brune709e1e2020-05-06 17:23:59 +02001566 char *hdr, *hdr_ptr = NULL;
Dragan Dosen59cee972015-09-19 22:09:02 +02001567 size_t hdr_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001568 int fac_level;
1569 int *plogfd;
1570 char *pid_sep1 = "", *pid_sep2 = "";
1571 char logheader_short[3];
1572 int sent;
1573 int maxlen;
1574 int hdr_max = 0;
1575 int tag_max = 0;
1576 int pid_sep1_max = 0;
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001577 int pid_max = 0;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001578 int pid_sep2_max = 0;
1579 int sd_max = 0;
1580 int max = 0;
Christopher Fauletf8188c62017-06-02 16:20:16 +02001581
1582 msghdr.msg_iov = iovec;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001583
1584 dataptr = message;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001585
Emeric Brunfa9d7802020-05-28 14:21:33 +02001586 /* historically some messages used to already contain the trailing LF
1587 * or Zero. Let's remove all trailing LF or Zero
1588 */
1589 while (size && ((dataptr[size-1] == '\n' || (dataptr[size-1] == 0))))
Emeric Brun9e8ea0a2020-05-12 19:33:15 +02001590 size--;
1591
Willy Tarreauf3dc30f2019-08-30 14:18:44 +02001592 if (logsrv->type == LOG_TARGET_FD) {
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001593 /* the socket's address is a file descriptor */
1594 plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
Robert Tsai81ae1952007-12-05 10:47:29 +01001595 }
Willy Tarreauc046d162019-08-30 15:24:59 +02001596 else if (logsrv->type == LOG_TARGET_BUFFER) {
1597 plogfd = NULL;
Emeric Brune709e1e2020-05-06 17:23:59 +02001598 goto send;
Willy Tarreauc046d162019-08-30 15:24:59 +02001599 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001600 else if (logsrv->addr.ss_family == AF_UNIX)
1601 plogfd = &logfdunix;
1602 else
1603 plogfd = &logfdinet;
Robert Tsai81ae1952007-12-05 10:47:29 +01001604
Willy Tarreauc046d162019-08-30 15:24:59 +02001605 if (plogfd && unlikely(*plogfd < 0)) {
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001606 /* socket not successfully initialized yet */
1607 if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1608 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
1609 static char once;
William Lallemand0f99e342011-10-12 17:50:54 +02001610
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001611 if (!once) {
1612 once = 1; /* note: no need for atomic ops here */
1613 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
1614 nblogger, strerror(errno), errno);
1615 }
1616 return;
1617 } else {
1618 /* we don't want to receive anything on this socket */
1619 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1620 /* does nothing under Linux, maybe needed for others */
1621 shutdown(*plogfd, SHUT_RD);
1622 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1623 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001624 }
1625
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001626 switch (logsrv->format) {
1627 case LOG_FORMAT_RFC3164:
1628 hdr = logheader;
1629 hdr_ptr = update_log_hdr(time);
1630 break;
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001631
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001632 case LOG_FORMAT_RFC5424:
1633 hdr = logheader_rfc5424;
Emeric Brun9f9b22c2020-07-02 16:16:59 +02001634 hdr_ptr = update_log_hdr_rfc5424(time, date.tv_usec);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001635 sd_max = sd_size; /* the SD part allowed only in RFC5424 */
1636 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001637
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001638 case LOG_FORMAT_SHORT:
1639 /* all fields are known, skip the header generation */
1640 hdr = logheader_short;
1641 hdr[0] = '<';
1642 hdr[1] = '0' + MAX(level, logsrv->minlvl);
1643 hdr[2] = '>';
1644 hdr_ptr = hdr;
1645 hdr_max = 3;
1646 maxlen = logsrv->maxlen - hdr_max;
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 Tarreau204e3f12018-12-15 15:48:48 +01001649
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001650 case LOG_FORMAT_RAW:
1651 /* all fields are known, skip the header generation */
1652 hdr_ptr = hdr = "";
1653 hdr_max = 0;
1654 maxlen = logsrv->maxlen;
Emeric Brun9e8ea0a2020-05-12 19:33:15 +02001655 max = MIN(size, maxlen - 1);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001656 goto send;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001657
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001658 default:
1659 return; /* must never happen */
1660 }
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001661
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001662 hdr_size = hdr_ptr - hdr;
Dragan Dosen1322d092015-09-22 16:05:32 +02001663
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001664 /* For each target, we may have a different facility.
1665 * We can also have a different log level for each message.
1666 * This induces variations in the message header length.
1667 * Since we don't want to recompute it each time, nor copy it every
1668 * time, we only change the facility in the pre-computed header,
1669 * and we change the pointer to the header accordingly.
1670 */
1671 fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
1672 hdr_ptr = hdr + 3; /* last digit of the log level */
1673 do {
1674 *hdr_ptr = '0' + fac_level % 10;
1675 fac_level /= 10;
1676 hdr_ptr--;
1677 } while (fac_level && hdr_ptr > hdr);
1678 *hdr_ptr = '<';
Dragan Dosen1322d092015-09-22 16:05:32 +02001679
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001680 hdr_max = hdr_size - (hdr_ptr - hdr);
Willy Tarreaue8746a02018-11-12 08:45:00 +01001681
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001682 /* time-based header */
1683 if (unlikely(hdr_size >= logsrv->maxlen)) {
1684 hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
1685 sd_max = 0;
1686 goto send;
1687 }
Willy Tarreauc1b06452018-11-12 11:57:56 +01001688
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001689 maxlen = logsrv->maxlen - hdr_max;
Dragan Dosen1322d092015-09-22 16:05:32 +02001690
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001691 /* tag */
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001692 tag_max = tag_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001693 if (unlikely(tag_max >= maxlen)) {
1694 tag_max = maxlen - 1;
1695 sd_max = 0;
1696 goto send;
1697 }
Dragan Dosen1322d092015-09-22 16:05:32 +02001698
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001699 maxlen -= tag_max;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001700
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001701 /* first pid separator */
1702 pid_sep1_max = log_formats[logsrv->format].pid.sep1.data;
1703 if (unlikely(pid_sep1_max >= maxlen)) {
1704 pid_sep1_max = maxlen - 1;
1705 sd_max = 0;
1706 goto send;
1707 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001708
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001709 pid_sep1 = log_formats[logsrv->format].pid.sep1.area;
1710 maxlen -= pid_sep1_max;
Dragan Dosen59cee972015-09-19 22:09:02 +02001711
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001712 /* pid */
Frédéric Lécaille90a10ae2019-05-14 10:57:58 +02001713 pid_max = pid_size;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001714 if (unlikely(pid_size >= maxlen)) {
1715 pid_size = maxlen - 1;
1716 sd_max = 0;
1717 goto send;
1718 }
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001719
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001720 maxlen -= pid_size;
Dragan Dosen43885c72015-10-01 13:18:13 +02001721
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001722 /* second pid separator */
1723 pid_sep2_max = log_formats[logsrv->format].pid.sep2.data;
1724 if (unlikely(pid_sep2_max >= maxlen)) {
1725 pid_sep2_max = maxlen - 1;
1726 sd_max = 0;
1727 goto send;
1728 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001729
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001730 pid_sep2 = log_formats[logsrv->format].pid.sep2.area;
1731 maxlen -= pid_sep2_max;
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001732
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001733 /* structured-data */
1734 if (sd_max >= maxlen) {
1735 sd_max = maxlen - 1;
1736 goto send;
1737 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001738
Emeric Brun9e8ea0a2020-05-12 19:33:15 +02001739 max = MIN(size, maxlen - sd_max - 1);
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001740send:
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001741 if (logsrv->addr.ss_family == AF_UNSPEC) {
Willy Tarreauc046d162019-08-30 15:24:59 +02001742 /* the target is a file descriptor or a ring buffer */
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001743 struct ist msg[7];
1744
Emeric Brune709e1e2020-05-06 17:23:59 +02001745 if (logsrv->type == LOG_TARGET_BUFFER) {
1746 msg[0] = ist2(message, MIN(size, logsrv->maxlen));
1747 msg[1] = ist2(tag_str, tag_size);
1748 msg[2] = ist2(pid_str, pid_size);
1749 msg[3] = ist2(sd, sd_size);
1750 sent = sink_write(logsrv->sink, msg, 1, level, logsrv->facility, &msg[1], &msg[2], &msg[3]);
1751 }
1752 else /* LOG_TARGET_FD */ {
1753 msg[0] = ist2(hdr_ptr, hdr_max);
1754 msg[1] = ist2(tag_str, tag_max);
1755 msg[2] = ist2(pid_sep1, pid_sep1_max);
1756 msg[3] = ist2(pid_str, pid_max);
1757 msg[4] = ist2(pid_sep2, pid_sep2_max);
1758 msg[5] = ist2(sd, sd_max);
1759 msg[6] = ist2(dataptr, max);
Willy Tarreauc046d162019-08-30 15:24:59 +02001760 sent = fd_write_frag_line(*plogfd, ~0, NULL, 0, msg, 7, 1);
Emeric Brune709e1e2020-05-06 17:23:59 +02001761 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001762 }
1763 else {
Willy Tarreaud52a7f82019-08-30 14:05:35 +02001764 iovec[0].iov_base = hdr_ptr;
1765 iovec[0].iov_len = hdr_max;
1766 iovec[1].iov_base = tag_str;
1767 iovec[1].iov_len = tag_max;
1768 iovec[2].iov_base = pid_sep1;
1769 iovec[2].iov_len = pid_sep1_max;
1770 iovec[3].iov_base = pid_str;
1771 iovec[3].iov_len = pid_max;
1772 iovec[4].iov_base = pid_sep2;
1773 iovec[4].iov_len = pid_sep2_max;
1774 iovec[5].iov_base = sd;
1775 iovec[5].iov_len = sd_max;
1776 iovec[6].iov_base = dataptr;
1777 iovec[6].iov_len = max;
1778 iovec[7].iov_base = "\n"; /* insert a \n at the end of the message */
1779 iovec[7].iov_len = 1;
1780
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001781 msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
1782 msghdr.msg_namelen = get_addr_len(&logsrv->addr);
Dragan Dosen43885c72015-10-01 13:18:13 +02001783
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001784 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1785 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001786
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001787 if (sent < 0) {
1788 static char once;
Dragan Dosen43885c72015-10-01 13:18:13 +02001789
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001790 if (errno == EAGAIN)
1791 _HA_ATOMIC_ADD(&dropped_logs, 1);
1792 else if (!once) {
1793 once = 1; /* note: no need for atomic ops here */
1794 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
1795 nblogger, strerror(errno), errno);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001796 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001797 }
1798}
Dragan Dosen59cee972015-09-19 22:09:02 +02001799
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001800/*
1801 * This function sends a syslog message.
1802 * It doesn't care about errors nor does it report them.
1803 * The arguments <sd> and <sd_size> are used for the structured-data part
1804 * in RFC5424 formatted syslog messages.
1805 */
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001806void __send_log(struct list *logsrvs, struct buffer *tag, int level,
1807 char *message, size_t size, char *sd, size_t sd_size)
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001808{
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001809 struct logsrv *logsrv;
1810 int nblogger;
1811 static THREAD_LOCAL int curr_pid;
1812 static THREAD_LOCAL char pidstr[100];
1813 static THREAD_LOCAL struct buffer pid;
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001814
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001815 if (logsrvs == NULL) {
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001816 if (!LIST_ISEMPTY(&global.logsrvs)) {
1817 logsrvs = &global.logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001818 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001819 }
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001820 if (!tag || !tag->area)
1821 tag = &global.log_tag;
Willy Tarreau18324f52014-06-27 18:10:07 +02001822
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02001823 if (!logsrvs || LIST_ISEMPTY(logsrvs))
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001824 return;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001825
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001826 if (unlikely(curr_pid != getpid())) {
1827 curr_pid = getpid();
1828 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1829 chunk_initstr(&pid, pidstr);
1830 }
1831
1832 /* Send log messages to syslog server. */
1833 nblogger = 0;
1834 list_for_each_entry(logsrv, logsrvs, list) {
Frédéric Lécailled803e472019-04-25 07:42:09 +02001835 static THREAD_LOCAL int in_range = 1;
1836
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001837 /* we can filter the level of the messages that are sent to each logger */
1838 if (level > logsrv->level)
1839 continue;
1840
Frédéric Lécailled803e472019-04-25 07:42:09 +02001841 if (logsrv->lb.smp_rgs) {
1842 struct smp_log_range *curr_rg;
1843
1844 HA_SPIN_LOCK(LOGSRV_LOCK, &logsrv->lock);
1845 curr_rg = &logsrv->lb.smp_rgs[logsrv->lb.curr_rg];
1846 in_range = in_smp_log_range(curr_rg, logsrv->lb.curr_idx);
1847 if (in_range) {
1848 /* Let's consume this range. */
1849 curr_rg->curr_idx = (curr_rg->curr_idx + 1) % curr_rg->sz;
1850 if (!curr_rg->curr_idx) {
1851 /* If consumed, let's select the next range. */
1852 logsrv->lb.curr_rg = (logsrv->lb.curr_rg + 1) % logsrv->lb.smp_rgs_sz;
1853 }
1854 }
1855 logsrv->lb.curr_idx = (logsrv->lb.curr_idx + 1) % logsrv->lb.smp_sz;
1856 HA_SPIN_UNLOCK(LOGSRV_LOCK, &logsrv->lock);
1857 }
1858 if (in_range)
1859 __do_send_log(logsrv, ++nblogger, pid.area, pid.data, level,
1860 message, size, sd, sd_size, tag->area, tag->data);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001861 }
1862}
1863
1864
Willy Tarreauc89ccb62012-04-05 21:18:22 +02001865const 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 +01001866const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1867 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1868 Set-cookie Updated, unknown, unknown */
1869
William Lallemand1d705562012-03-12 12:46:41 +01001870/*
1871 * try to write a character if there is enough space, or goto out
1872 */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001873#define LOGCHAR(x) do { \
William Lallemand1d705562012-03-12 12:46:41 +01001874 if (tmplog < dst + maxsize - 1) { \
William Lallemandbddd4fd2012-02-27 11:23:10 +01001875 *(tmplog++) = (x); \
1876 } else { \
1877 goto out; \
1878 } \
1879 } while(0)
1880
Dragan Dosen835b9212016-02-12 13:23:03 +01001881
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001882/* Initializes some log data at boot */
1883static void init_log()
Dragan Dosen835b9212016-02-12 13:23:03 +01001884{
1885 char *tmp;
Willy Tarreaue10cd482018-09-10 18:16:53 +02001886 int i;
Dragan Dosen835b9212016-02-12 13:23:03 +01001887
1888 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1889 * inside PARAM-VALUE should be escaped with '\' as prefix.
1890 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1891 * details.
1892 */
1893 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1894
1895 tmp = "\"\\]";
1896 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001897 ha_bit_set(*tmp, rfc5424_escape_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001898 tmp++;
1899 }
Willy Tarreaue10cd482018-09-10 18:16:53 +02001900
1901 /* initialize the log header encoding map : '{|}"#' should be encoded with
1902 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1903 * URL encoding only requires '"', '#' to be encoded as well as non-
1904 * printable characters above.
1905 */
1906 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1907 memset(url_encode_map, 0, sizeof(url_encode_map));
1908 for (i = 0; i < 32; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001909 ha_bit_set(i, hdr_encode_map);
1910 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001911 }
1912 for (i = 127; i < 256; i++) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001913 ha_bit_set(i, hdr_encode_map);
1914 ha_bit_set(i, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001915 }
1916
1917 tmp = "\"#{|}";
1918 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001919 ha_bit_set(*tmp, hdr_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001920 tmp++;
1921 }
1922
1923 tmp = "\"#";
1924 while (*tmp) {
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001925 ha_bit_set(*tmp, url_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001926 tmp++;
1927 }
1928
1929 /* initialize the http header encoding map. The draft httpbis define the
1930 * header content as:
1931 *
1932 * HTTP-message = start-line
1933 * *( header-field CRLF )
1934 * CRLF
1935 * [ message-body ]
1936 * header-field = field-name ":" OWS field-value OWS
1937 * field-value = *( field-content / obs-fold )
1938 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1939 * obs-fold = CRLF 1*( SP / HTAB )
1940 * field-vchar = VCHAR / obs-text
1941 * VCHAR = %x21-7E
1942 * obs-text = %x80-FF
1943 *
1944 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1945 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
Joseph Herlant85b40592018-11-15 12:10:04 -08001946 * "obs-fold" is voluntarily forgotten because haproxy remove this.
Willy Tarreaue10cd482018-09-10 18:16:53 +02001947 */
1948 memset(http_encode_map, 0, sizeof(http_encode_map));
1949 for (i = 0x00; i <= 0x08; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001950 ha_bit_set(i, http_encode_map);
Willy Tarreaue10cd482018-09-10 18:16:53 +02001951 for (i = 0x0a; i <= 0x1f; i++)
Willy Tarreau1bfd6022019-06-07 11:10:07 +02001952 ha_bit_set(i, http_encode_map);
1953 ha_bit_set(0x7f, http_encode_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001954}
William Lallemand1d705562012-03-12 12:46:41 +01001955
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001956INITCALL0(STG_PREPARE, init_log);
1957
Christopher Faulet0132d062017-07-26 15:33:35 +02001958/* Initialize log buffers used for syslog messages */
1959int init_log_buffers()
1960{
1961 logheader = my_realloc2(logheader, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001962 logheader_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001963 logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001964 logheader_rfc5424_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001965 logline = my_realloc2(logline, global.max_syslog_len + 1);
1966 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
1967 if (!logheader || !logline_rfc5424 || !logline || !logline_rfc5424)
1968 return 0;
1969 return 1;
1970}
1971
1972/* Deinitialize log buffers used for syslog messages */
1973void deinit_log_buffers()
1974{
1975 free(logheader);
1976 free(logheader_rfc5424);
1977 free(logline);
1978 free(logline_rfc5424);
Willy Tarreau869efd52019-11-15 15:16:57 +01001979 ring_free(_HA_ATOMIC_XCHG(&startup_logs, NULL));
Christopher Faulet0132d062017-07-26 15:33:35 +02001980 logheader = NULL;
1981 logheader_rfc5424 = NULL;
1982 logline = NULL;
1983 logline_rfc5424 = NULL;
1984}
1985
Willy Tarreaudf974472012-12-28 02:44:01 +01001986/* Builds a log line in <dst> based on <list_format>, and stops before reaching
1987 * <maxsize> characters. Returns the size of the output string in characters,
1988 * not counting the trailing zero which is always added if the resulting size
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001989 * is not zero. It requires a valid session and optionally a stream. If the
1990 * stream is NULL, default values will be assumed for the stream part.
Willy Tarreaudf974472012-12-28 02:44:01 +01001991 */
Willy Tarreau43c538e2018-09-05 14:58:15 +02001992int 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 +02001993{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02001994 struct proxy *fe = sess->fe;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001995 struct proxy *be;
1996 struct http_txn *txn;
1997 const struct strm_logs *logs;
Willy Tarreau8fa99842019-07-17 11:47:11 +02001998 struct connection *be_conn;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001999 unsigned int s_flags;
2000 unsigned int uniq_id;
Willy Tarreau83061a82018-07-13 11:56:34 +02002001 struct buffer chunk;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002002 char *uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002003 char *spc;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002004 char *qmark;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002005 char *end;
Willy Tarreaufe944602007-10-25 10:34:16 +02002006 struct tm tm;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002007 int t_request;
2008 int hdr;
2009 int last_isspace = 1;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002010 int nspaces = 0;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01002011 char *tmplog;
William Lallemand1d705562012-03-12 12:46:41 +01002012 char *ret;
2013 int iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002014 struct logformat_node *tmp;
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002015 struct timeval tv;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002016 struct strm_logs tmp_strm_log;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002017
William Lallemandbddd4fd2012-02-27 11:23:10 +01002018 /* FIXME: let's limit ourselves to frontend logging for now. */
Willy Tarreaubaaee002006-06-26 02:48:02 +02002019
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002020 if (likely(s)) {
2021 be = s->be;
2022 txn = s->txn;
2023 be_conn = cs_conn(objt_cs(s->si[1].end));
2024 s_flags = s->flags;
2025 uniq_id = s->uniq_id;
2026 logs = &s->logs;
2027 } else {
2028 /* we have no stream so we first need to initialize a few
2029 * things that are needed later. We do increment the request
2030 * ID so that it's uniquely assigned to this request just as
2031 * if the request had reached the point of being processed.
2032 * A request error is reported as it's the only element we have
2033 * here and which justifies emitting such a log.
2034 */
2035 be = fe;
2036 txn = NULL;
2037 be_conn = NULL;
2038 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002039 uniq_id = _HA_ATOMIC_XADD(&global.req_count, 1);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02002040
2041 /* prepare a valid log structure */
2042 tmp_strm_log.tv_accept = sess->tv_accept;
2043 tmp_strm_log.accept_date = sess->accept_date;
2044 tmp_strm_log.t_handshake = sess->t_handshake;
2045 tmp_strm_log.t_idle = tv_ms_elapsed(&sess->tv_accept, &now) - sess->t_handshake;
2046 tv_zero(&tmp_strm_log.tv_request);
2047 tmp_strm_log.t_queue = -1;
2048 tmp_strm_log.t_connect = -1;
2049 tmp_strm_log.t_data = -1;
2050 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
2051 tmp_strm_log.bytes_in = 0;
2052 tmp_strm_log.bytes_out = 0;
2053 tmp_strm_log.prx_queue_pos = 0;
2054 tmp_strm_log.srv_queue_pos = 0;
2055
2056 logs = &tmp_strm_log;
2057 }
2058
William Lallemandbddd4fd2012-02-27 11:23:10 +01002059 t_request = -1;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002060 if (tv_isge(&logs->tv_request, &logs->tv_accept))
2061 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
William Lallemandbddd4fd2012-02-27 11:23:10 +01002062
William Lallemand1d705562012-03-12 12:46:41 +01002063 tmplog = dst;
Willy Tarreauc9bd0cc2009-05-10 11:57:02 +02002064
William Lallemandbddd4fd2012-02-27 11:23:10 +01002065 /* fill logbuffer */
William Lallemand1d705562012-03-12 12:46:41 +01002066 if (LIST_ISEMPTY(list_format))
2067 return 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002068
William Lallemand1d705562012-03-12 12:46:41 +01002069 list_for_each_entry(tmp, list_format, list) {
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002070 struct connection *conn;
Willy Tarreau4f653562012-10-12 19:48:16 +02002071 const char *src = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +01002072 struct sample *key;
Willy Tarreau83061a82018-07-13 11:56:34 +02002073 const struct buffer empty = { };
William Lallemandbddd4fd2012-02-27 11:23:10 +01002074
Willy Tarreauc8368452012-12-21 00:09:23 +01002075 switch (tmp->type) {
William Lallemand1d705562012-03-12 12:46:41 +01002076 case LOG_FMT_SEPARATOR:
William Lallemandbddd4fd2012-02-27 11:23:10 +01002077 if (!last_isspace) {
2078 LOGCHAR(' ');
2079 last_isspace = 1;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002080 }
2081 break;
2082
William Lallemand1d705562012-03-12 12:46:41 +01002083 case LOG_FMT_TEXT: // text
William Lallemandbddd4fd2012-02-27 11:23:10 +01002084 src = tmp->arg;
William Lallemand5f232402012-04-05 18:02:55 +02002085 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002086 if (iret == 0)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002087 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002088 tmplog += iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002089 last_isspace = 0;
2090 break;
2091
Willy Tarreauc8368452012-12-21 00:09:23 +01002092 case LOG_FMT_EXPR: // sample expression, may be request or response
2093 key = NULL;
Christopher Faulet5f940702020-04-06 10:40:02 +02002094 if (tmp->options & LOG_OPT_REQ_CAP)
Adis Nezirovic79beb242015-07-06 15:41:02 +02002095 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 +02002096 if (!key && (tmp->options & LOG_OPT_RES_CAP))
Adis Nezirovic79beb242015-07-06 15:41:02 +02002097 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 +01002098 if (tmp->options & LOG_OPT_HTTP)
Dragan Dosen835b9212016-02-12 13:23:03 +01002099 ret = lf_encode_chunk(tmplog, dst + maxsize,
2100 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01002101 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002102 ret = lf_text_len(tmplog,
2103 key ? key->data.u.str.area : NULL,
2104 key ? key->data.u.str.data : 0,
2105 dst + maxsize - tmplog,
2106 tmp);
Willy Tarreauc8368452012-12-21 00:09:23 +01002107 if (ret == 0)
2108 goto out;
2109 tmplog = ret;
2110 last_isspace = 0;
2111 break;
2112
Willy Tarreau2beef582012-12-20 17:22:52 +01002113 case LOG_FMT_CLIENTIP: // %ci
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002114 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002115 if (conn && conn_get_src(conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002116 ret = lf_ip(tmplog, (struct sockaddr *)conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002117 else
2118 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002119 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002120 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002121 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002122 last_isspace = 0;
2123 break;
2124
Willy Tarreau2beef582012-12-20 17:22:52 +01002125 case LOG_FMT_CLIENTPORT: // %cp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002126 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002127 if (conn && conn_get_src(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002128 if (conn->src->ss_family == AF_UNIX) {
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002129 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002130 } else {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002131 ret = lf_port(tmplog, (struct sockaddr *)conn->src,
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002132 dst + maxsize - tmplog, tmp);
2133 }
William Lallemand5f232402012-04-05 18:02:55 +02002134 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002135 else
2136 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2137
William Lallemand5f232402012-04-05 18:02:55 +02002138 if (ret == NULL)
2139 goto out;
2140 tmplog = ret;
2141 last_isspace = 0;
2142 break;
2143
Willy Tarreau2beef582012-12-20 17:22:52 +01002144 case LOG_FMT_FRONTENDIP: // %fi
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002145 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002146 if (conn && conn_get_dst(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002147 ret = lf_ip(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002148 }
2149 else
2150 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2151
William Lallemand1d705562012-03-12 12:46:41 +01002152 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002153 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002154 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002155 last_isspace = 0;
2156 break;
2157
Willy Tarreau2beef582012-12-20 17:22:52 +01002158 case LOG_FMT_FRONTENDPORT: // %fp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002159 conn = objt_conn(sess->origin);
Willy Tarreau8fa99842019-07-17 11:47:11 +02002160 if (conn && conn_get_dst(conn)) {
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002161 if (conn->dst->ss_family == AF_UNIX)
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002162 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002163 else
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002164 ret = lf_port(tmplog, (struct sockaddr *)conn->dst, dst + maxsize - tmplog, tmp);
William Lallemand5f232402012-04-05 18:02:55 +02002165 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002166 else
2167 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2168
William Lallemand5f232402012-04-05 18:02:55 +02002169 if (ret == NULL)
2170 goto out;
2171 tmplog = ret;
2172 last_isspace = 0;
2173 break;
2174
Willy Tarreau2beef582012-12-20 17:22:52 +01002175 case LOG_FMT_BACKENDIP: // %bi
Willy Tarreau8fa99842019-07-17 11:47:11 +02002176 if (be_conn && conn_get_src(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002177 ret = lf_ip(tmplog, (const struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002178 else
2179 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2180
William Lallemand1d705562012-03-12 12:46:41 +01002181 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002182 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002183 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002184 last_isspace = 0;
2185 break;
2186
Willy Tarreau2beef582012-12-20 17:22:52 +01002187 case LOG_FMT_BACKENDPORT: // %bp
Willy Tarreau8fa99842019-07-17 11:47:11 +02002188 if (be_conn && conn_get_src(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002189 ret = lf_port(tmplog, (struct sockaddr *)be_conn->src, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002190 else
2191 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2192
William Lallemand5f232402012-04-05 18:02:55 +02002193 if (ret == NULL)
2194 goto out;
2195 tmplog = ret;
2196 last_isspace = 0;
2197 break;
2198
Willy Tarreau2beef582012-12-20 17:22:52 +01002199 case LOG_FMT_SERVERIP: // %si
Willy Tarreau8fa99842019-07-17 11:47:11 +02002200 if (be_conn && conn_get_dst(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002201 ret = lf_ip(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002202 else
2203 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2204
William Lallemand5f232402012-04-05 18:02:55 +02002205 if (ret == NULL)
2206 goto out;
2207 tmplog = ret;
2208 last_isspace = 0;
2209 break;
2210
Willy Tarreau2beef582012-12-20 17:22:52 +01002211 case LOG_FMT_SERVERPORT: // %sp
Willy Tarreau8fa99842019-07-17 11:47:11 +02002212 if (be_conn && conn_get_dst(be_conn))
Willy Tarreau6c6365f2019-07-17 16:48:18 +02002213 ret = lf_port(tmplog, (struct sockaddr *)be_conn->dst, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002214 else
2215 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2216
William Lallemand1d705562012-03-12 12:46:41 +01002217 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002218 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002219 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002220 last_isspace = 0;
2221 break;
2222
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002223 case LOG_FMT_DATE: // %t = accept date
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002224 get_localtime(logs->accept_date.tv_sec, &tm);
2225 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002226 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002227 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002228 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002229 last_isspace = 0;
2230 break;
2231
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002232 case LOG_FMT_tr: // %tr = start of request date
2233 /* Note that the timers are valid if we get here */
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002234 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 +02002235 get_localtime(tv.tv_sec, &tm);
2236 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
2237 if (ret == NULL)
2238 goto out;
2239 tmplog = ret;
2240 last_isspace = 0;
2241 break;
2242
2243 case LOG_FMT_DATEGMT: // %T = accept date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002244 get_gmtime(logs->accept_date.tv_sec, &tm);
William Lallemand5f232402012-04-05 18:02:55 +02002245 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002246 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002247 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002248 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002249 last_isspace = 0;
2250 break;
2251
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002252 case LOG_FMT_trg: // %trg = start of request date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002253 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 +02002254 get_gmtime(tv.tv_sec, &tm);
2255 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2256 if (ret == NULL)
2257 goto out;
2258 tmplog = ret;
2259 last_isspace = 0;
2260 break;
2261
2262 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002263 get_localtime(logs->accept_date.tv_sec, &tm);
2264 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
Yuxans Yao4e25b012012-10-19 10:36:09 +08002265 if (ret == NULL)
2266 goto out;
2267 tmplog = ret;
2268 last_isspace = 0;
2269 break;
2270
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002271 case LOG_FMT_trl: // %trl = start of request date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002272 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 +02002273 get_localtime(tv.tv_sec, &tm);
2274 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
2275 if (ret == NULL)
2276 goto out;
2277 tmplog = ret;
2278 last_isspace = 0;
2279 break;
2280
William Lallemand5f232402012-04-05 18:02:55 +02002281 case LOG_FMT_TS: // %Ts
William Lallemand5f232402012-04-05 18:02:55 +02002282 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002283 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
William Lallemand5f232402012-04-05 18:02:55 +02002284 if (iret < 0 || iret > dst + maxsize - tmplog)
2285 goto out;
2286 last_isspace = 0;
2287 tmplog += iret;
2288 } else {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002289 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002290 if (ret == NULL)
2291 goto out;
2292 tmplog = ret;
2293 last_isspace = 0;
2294 }
2295 break;
2296
William Lallemand1d705562012-03-12 12:46:41 +01002297 case LOG_FMT_MS: // %ms
William Lallemand5f232402012-04-05 18:02:55 +02002298 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002299 iret = snprintf(tmplog, dst + maxsize - tmplog, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
William Lallemand5f232402012-04-05 18:02:55 +02002300 if (iret < 0 || iret > dst + maxsize - tmplog)
2301 goto out;
2302 last_isspace = 0;
2303 tmplog += iret;
2304 } else {
2305 if ((dst + maxsize - tmplog) < 4)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002306 goto out;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002307 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002308 tmplog, 4);
2309 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002310 goto out;
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002311 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002312 last_isspace = 0;
William Lallemand5f232402012-04-05 18:02:55 +02002313 }
2314 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002315
William Lallemand1d705562012-03-12 12:46:41 +01002316 case LOG_FMT_FRONTEND: // %f
William Lallemandbddd4fd2012-02-27 11:23:10 +01002317 src = fe->id;
William Lallemand5f232402012-04-05 18:02:55 +02002318 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002319 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002320 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002321 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002322 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002323 break;
2324
Willy Tarreau773d65f2012-10-12 14:56:11 +02002325 case LOG_FMT_FRONTEND_XPRT: // %ft
2326 src = fe->id;
2327 if (tmp->options & LOG_OPT_QUOTE)
2328 LOGCHAR('"');
2329 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2330 if (iret == 0)
2331 goto out;
2332 tmplog += iret;
Willy Tarreaua261e9b2016-12-22 20:44:00 +01002333 if (sess->listener->bind_conf->xprt == xprt_get(XPRT_SSL))
Willy Tarreau773d65f2012-10-12 14:56:11 +02002334 LOGCHAR('~');
Willy Tarreau773d65f2012-10-12 14:56:11 +02002335 if (tmp->options & LOG_OPT_QUOTE)
2336 LOGCHAR('"');
2337 last_isspace = 0;
2338 break;
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002339#ifdef USE_OPENSSL
2340 case LOG_FMT_SSL_CIPHER: // %sslc
2341 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002342 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002343 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002344 src = ssl_sock_get_cipher_name(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002345 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002346 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2347 if (ret == NULL)
2348 goto out;
2349 tmplog = ret;
2350 last_isspace = 0;
2351 break;
Willy Tarreau773d65f2012-10-12 14:56:11 +02002352
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002353 case LOG_FMT_SSL_VERSION: // %sslv
2354 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002355 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002356 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002357 src = ssl_sock_get_proto_version(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002358 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002359 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2360 if (ret == NULL)
2361 goto out;
2362 tmplog = ret;
2363 last_isspace = 0;
2364 break;
2365#endif
William Lallemand1d705562012-03-12 12:46:41 +01002366 case LOG_FMT_BACKEND: // %b
William Lallemandbddd4fd2012-02-27 11:23:10 +01002367 src = be->id;
William Lallemand5f232402012-04-05 18:02:55 +02002368 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002369 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002370 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002371 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002372 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002373 break;
2374
William Lallemand1d705562012-03-12 12:46:41 +01002375 case LOG_FMT_SERVER: // %s
Willy Tarreaue1809df2018-09-05 15:30:16 +02002376 switch (obj_type(s ? s->target : NULL)) {
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002377 case OBJ_TYPE_SERVER:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002378 src = __objt_server(s->target)->id;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002379 break;
2380 case OBJ_TYPE_APPLET:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002381 src = __objt_applet(s->target)->name;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002382 break;
2383 default:
2384 src = "<NOSRV>";
2385 break;
2386 }
William Lallemand5f232402012-04-05 18:02:55 +02002387 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002388 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002389 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002390 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002391 last_isspace = 0;
2392 break;
2393
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002394 case LOG_FMT_Th: // %Th = handshake time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002395 ret = ltoa_o(logs->t_handshake, 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_Ti: // %Ti = HTTP idle time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002403 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002404 if (ret == NULL)
2405 goto out;
2406 tmplog = ret;
2407 last_isspace = 0;
2408 break;
2409
2410 case LOG_FMT_TR: // %TR = HTTP request time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002411 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002412 tmplog, dst + maxsize - tmplog);
2413 if (ret == NULL)
2414 goto out;
2415 tmplog = ret;
2416 last_isspace = 0;
2417 break;
2418
2419 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
William Lallemand5f232402012-04-05 18:02:55 +02002420 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002421 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002422 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002423 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002424 last_isspace = 0;
2425 break;
2426
William Lallemand1d705562012-03-12 12:46:41 +01002427 case LOG_FMT_TW: // %Tw
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002428 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002429 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002430 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002431 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002432 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002433 last_isspace = 0;
2434 break;
2435
William Lallemand1d705562012-03-12 12:46:41 +01002436 case LOG_FMT_TC: // %Tc
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002437 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002438 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002439 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002440 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002441 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002442 last_isspace = 0;
2443 break;
2444
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002445 case LOG_FMT_Tr: // %Tr
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002446 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002447 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002448 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002449 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002450 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002451 last_isspace = 0;
2452 break;
2453
Willy Tarreau27b639d2016-05-17 17:55:27 +02002454 case LOG_FMT_TD: // %Td
Willy Tarreaua21c0e62018-09-05 15:07:15 +02002455 if (be->mode == PR_MODE_HTTP)
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002456 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002457 tmplog, dst + maxsize - tmplog);
2458 else
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002459 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002460 tmplog, dst + maxsize - tmplog);
2461 if (ret == NULL)
2462 goto out;
2463 tmplog = ret;
2464 last_isspace = 0;
2465 break;
2466
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002467 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2468 if (!(fe->to_log & LW_BYTES))
2469 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002470 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 +02002471 tmplog, dst + maxsize - tmplog);
2472 if (ret == NULL)
2473 goto out;
2474 tmplog = ret;
2475 last_isspace = 0;
2476 break;
2477
2478 case LOG_FMT_TT: // %Tt = total time
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002479 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002480 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002481 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002482 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002483 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002484 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002485 last_isspace = 0;
2486 break;
2487
Damien Claisse57c8eb92020-04-28 12:09:19 +00002488 case LOG_FMT_TU: // %Tu = total time seen by user = Tt - Ti
2489 if (!(fe->to_log & LW_BYTES))
2490 LOGCHAR('+');
2491 ret = ltoa_o(logs->t_close - (logs->t_idle >= 0 ? logs->t_idle : 0),
2492 tmplog, dst + maxsize - tmplog);
2493 if (ret == NULL)
2494 goto out;
2495 tmplog = ret;
2496 last_isspace = 0;
2497 break;
2498
Willy Tarreau2beef582012-12-20 17:22:52 +01002499 case LOG_FMT_STATUS: // %ST
Willy Tarreau57bc8912016-04-25 17:09:40 +02002500 ret = ltoa_o(txn ? txn->status : 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002501 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002502 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002503 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002504 last_isspace = 0;
2505 break;
2506
William Lallemand1d705562012-03-12 12:46:41 +01002507 case LOG_FMT_BYTES: // %B
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002508 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002509 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002510 ret = lltoa(logs->bytes_out, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002511 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002512 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002513 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002514 last_isspace = 0;
2515 break;
2516
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002517 case LOG_FMT_BYTES_UP: // %U
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002518 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002519 if (ret == NULL)
2520 goto out;
2521 tmplog = ret;
2522 last_isspace = 0;
2523 break;
2524
Willy Tarreau2beef582012-12-20 17:22:52 +01002525 case LOG_FMT_CCLIENT: // %CC
Willy Tarreau57bc8912016-04-25 17:09:40 +02002526 src = txn ? txn->cli_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002527 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002528 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002529 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002530 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002531 last_isspace = 0;
2532 break;
2533
Willy Tarreau2beef582012-12-20 17:22:52 +01002534 case LOG_FMT_CSERVER: // %CS
Willy Tarreau57bc8912016-04-25 17:09:40 +02002535 src = txn ? txn->srv_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002536 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002537 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002538 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002539 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002540 last_isspace = 0;
2541 break;
2542
William Lallemand1d705562012-03-12 12:46:41 +01002543 case LOG_FMT_TERMSTATE: // %ts
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002544 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2545 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau6580c062012-03-12 15:09:42 +01002546 *tmplog = '\0';
2547 last_isspace = 0;
2548 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002549
William Lallemand1d705562012-03-12 12:46:41 +01002550 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002551 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2552 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau57bc8912016-04-25 17:09:40 +02002553 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2554 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 +01002555 last_isspace = 0;
2556 break;
2557
William Lallemand1d705562012-03-12 12:46:41 +01002558 case LOG_FMT_ACTCONN: // %ac
William Lallemand5f232402012-04-05 18:02:55 +02002559 ret = ltoa_o(actconn, 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_FECONN: // %fc
William Lallemand5f232402012-04-05 18:02:55 +02002567 ret = ltoa_o(fe->feconn, 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_BECONN: // %bc
William Lallemand5f232402012-04-05 18:02:55 +02002575 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002576 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002577 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002578 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002579 last_isspace = 0;
2580 break;
2581
William Lallemand1d705562012-03-12 12:46:41 +01002582 case LOG_FMT_SRVCONN: // %sc
Willy Tarreaue1809df2018-09-05 15:30:16 +02002583 ret = ultoa_o(objt_server(s ? s->target : NULL) ?
Willy Tarreau3fdb3662012-11-12 00:42:33 +01002584 objt_server(s->target)->cur_sess :
William Lallemand5f232402012-04-05 18:02:55 +02002585 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002586 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002587 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002588 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002589 last_isspace = 0;
2590 break;
2591
William Lallemand1d705562012-03-12 12:46:41 +01002592 case LOG_FMT_RETRIES: // %rq
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002593 if (s_flags & SF_REDISP)
William Lallemand1d705562012-03-12 12:46:41 +01002594 LOGCHAR('+');
Willy Tarreauabd71a52018-09-04 19:21:44 +02002595 ret = ltoa_o((s && s->si[1].conn_retries > 0) ?
Willy Tarreau350f4872014-11-28 14:42:25 +01002596 (be->conn_retries - s->si[1].conn_retries) :
William Lallemand5f232402012-04-05 18:02:55 +02002597 be->conn_retries, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002598 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002599 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_SRVQUEUE: // %sq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002605 ret = ltoa_o(logs->srv_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_BCKQUEUE: // %bq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002613 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002614 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002615 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002616 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002617 last_isspace = 0;
2618 break;
2619
William Lallemand1d705562012-03-12 12:46:41 +01002620 case LOG_FMT_HDRREQUEST: // %hr
William Lallemandbddd4fd2012-02-27 11:23:10 +01002621 /* request header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002622 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002623 if (tmp->options & LOG_OPT_QUOTE)
2624 LOGCHAR('"');
2625 LOGCHAR('{');
2626 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2627 if (hdr)
2628 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002629 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002630 ret = lf_encode_string(tmplog, dst + maxsize,
2631 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002632 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002633 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002634 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002635 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002636 }
2637 LOGCHAR('}');
William Lallemand51b5dca2012-03-26 17:52:55 +02002638 if (tmp->options & LOG_OPT_QUOTE)
2639 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002640 last_isspace = 0;
2641 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002642 break;
2643
William Lallemand1d705562012-03-12 12:46:41 +01002644 case LOG_FMT_HDRREQUESTLIST: // %hrl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002645 /* request header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002646 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002647 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2648 if (hdr > 0)
2649 LOGCHAR(' ');
2650 if (tmp->options & LOG_OPT_QUOTE)
2651 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002652 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002653 ret = lf_encode_string(tmplog, dst + maxsize,
2654 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002655 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002656 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002657 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002658 } else if (!(tmp->options & LOG_OPT_QUOTE))
2659 LOGCHAR('-');
2660 if (tmp->options & LOG_OPT_QUOTE)
2661 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002662 last_isspace = 0;
2663 }
2664 }
2665 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002666
William Lallemand1d705562012-03-12 12:46:41 +01002667
2668 case LOG_FMT_HDRRESPONS: // %hs
William Lallemandbddd4fd2012-02-27 11:23:10 +01002669 /* response header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002670 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002671 if (tmp->options & LOG_OPT_QUOTE)
2672 LOGCHAR('"');
2673 LOGCHAR('{');
2674 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2675 if (hdr)
2676 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002677 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002678 ret = lf_encode_string(tmplog, dst + maxsize,
2679 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002680 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002681 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002682 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002683 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002684 }
2685 LOGCHAR('}');
2686 last_isspace = 0;
2687 if (tmp->options & LOG_OPT_QUOTE)
2688 LOGCHAR('"');
2689 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002690 break;
2691
William Lallemand1d705562012-03-12 12:46:41 +01002692 case LOG_FMT_HDRRESPONSLIST: // %hsl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002693 /* response header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002694 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002695 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2696 if (hdr > 0)
2697 LOGCHAR(' ');
2698 if (tmp->options & LOG_OPT_QUOTE)
2699 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002700 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002701 ret = lf_encode_string(tmplog, dst + maxsize,
2702 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002703 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002704 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002705 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002706 } else if (!(tmp->options & LOG_OPT_QUOTE))
2707 LOGCHAR('-');
2708 if (tmp->options & LOG_OPT_QUOTE)
2709 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002710 last_isspace = 0;
2711 }
2712 }
2713 break;
2714
William Lallemand1d705562012-03-12 12:46:41 +01002715 case LOG_FMT_REQ: // %r
William Lallemandbddd4fd2012-02-27 11:23:10 +01002716 /* Request */
2717 if (tmp->options & LOG_OPT_QUOTE)
2718 LOGCHAR('"');
Willy Tarreau57bc8912016-04-25 17:09:40 +02002719 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Dragan Dosen835b9212016-02-12 13:23:03 +01002720 ret = lf_encode_string(tmplog, dst + maxsize,
2721 '#', url_encode_map, uri, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002722 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002723 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002724 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002725 if (tmp->options & LOG_OPT_QUOTE)
2726 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002727 last_isspace = 0;
2728 break;
William Lallemand5f232402012-04-05 18:02:55 +02002729
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002730 case LOG_FMT_HTTP_PATH: // %HP
Willy Tarreau57bc8912016-04-25 17:09:40 +02002731 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002732
Willy Tarreaub7636d12015-06-17 19:58:02 +02002733 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002734 LOGCHAR('"');
2735
2736 end = uri + strlen(uri);
2737 // look for the first whitespace character
2738 while (uri < end && !HTTP_IS_SPHT(*uri))
2739 uri++;
2740
2741 // keep advancing past multiple spaces
2742 while (uri < end && HTTP_IS_SPHT(*uri)) {
2743 uri++; nspaces++;
2744 }
2745
2746 // look for first space or question mark after url
2747 spc = uri;
2748 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2749 spc++;
2750
Nenad Merdanovic54e439f2016-04-26 01:39:02 +02002751 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002752 chunk.area = "<BADREQ>";
2753 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002754 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002755 chunk.area = uri;
2756 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002757 }
2758
Dragan Dosen835b9212016-02-12 13:23:03 +01002759 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002760 if (ret == NULL || *ret != '\0')
2761 goto out;
2762
2763 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002764 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002765 LOGCHAR('"');
2766
2767 last_isspace = 0;
2768 break;
2769
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002770 case LOG_FMT_HTTP_QUERY: // %HQ
2771 if (tmp->options & LOG_OPT_QUOTE)
2772 LOGCHAR('"');
2773
Willy Tarreau57bc8912016-04-25 17:09:40 +02002774 if (!txn || !txn->uri) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002775 chunk.area = "<BADREQ>";
2776 chunk.data = strlen("<BADREQ>");
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002777 } else {
2778 uri = txn->uri;
2779 end = uri + strlen(uri);
2780 // look for the first question mark
2781 while (uri < end && *uri != '?')
2782 uri++;
2783
2784 qmark = uri;
2785 // look for first space or question mark after url
2786 while (uri < end && !HTTP_IS_SPHT(*uri))
2787 uri++;
2788
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002789 chunk.area = qmark;
2790 chunk.data = uri - qmark;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002791 }
2792
Dragan Dosen835b9212016-02-12 13:23:03 +01002793 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002794 if (ret == NULL || *ret != '\0')
2795 goto out;
2796
2797 tmplog = ret;
2798 if (tmp->options & LOG_OPT_QUOTE)
2799 LOGCHAR('"');
2800
2801 last_isspace = 0;
2802 break;
2803
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002804 case LOG_FMT_HTTP_URI: // %HU
Willy Tarreau57bc8912016-04-25 17:09:40 +02002805 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002806
Willy Tarreaub7636d12015-06-17 19:58:02 +02002807 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002808 LOGCHAR('"');
2809
2810 end = uri + strlen(uri);
2811 // look for the first whitespace character
2812 while (uri < end && !HTTP_IS_SPHT(*uri))
2813 uri++;
2814
2815 // keep advancing past multiple spaces
2816 while (uri < end && HTTP_IS_SPHT(*uri)) {
2817 uri++; nspaces++;
2818 }
2819
2820 // look for first space after url
2821 spc = uri;
2822 while (spc < end && !HTTP_IS_SPHT(*spc))
2823 spc++;
2824
Willy Tarreau57bc8912016-04-25 17:09:40 +02002825 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002826 chunk.area = "<BADREQ>";
2827 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002828 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002829 chunk.area = uri;
2830 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002831 }
2832
Dragan Dosen835b9212016-02-12 13:23:03 +01002833 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002834 if (ret == NULL || *ret != '\0')
2835 goto out;
2836
2837 tmplog = ret;
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 last_isspace = 0;
2842 break;
2843
2844 case LOG_FMT_HTTP_METHOD: // %HM
Willy Tarreau57bc8912016-04-25 17:09:40 +02002845 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002846 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002847 LOGCHAR('"');
2848
2849 end = uri + strlen(uri);
2850 // look for the first whitespace character
2851 spc = uri;
2852 while (spc < end && !HTTP_IS_SPHT(*spc))
2853 spc++;
2854
2855 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002856 chunk.area = "<BADREQ>";
2857 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002858 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002859 chunk.area = uri;
2860 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002861 }
2862
Dragan Dosen835b9212016-02-12 13:23:03 +01002863 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002864 if (ret == NULL || *ret != '\0')
2865 goto out;
2866
2867 tmplog = ret;
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 last_isspace = 0;
2872 break;
2873
2874 case LOG_FMT_HTTP_VERSION: // %HV
Willy Tarreau57bc8912016-04-25 17:09:40 +02002875 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002876 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002877 LOGCHAR('"');
2878
2879 end = uri + strlen(uri);
2880 // look for the first whitespace character
2881 while (uri < end && !HTTP_IS_SPHT(*uri))
2882 uri++;
2883
2884 // keep advancing past multiple spaces
2885 while (uri < end && HTTP_IS_SPHT(*uri)) {
2886 uri++; nspaces++;
2887 }
2888
2889 // look for the next whitespace character
2890 while (uri < end && !HTTP_IS_SPHT(*uri))
2891 uri++;
2892
2893 // keep advancing past multiple spaces
2894 while (uri < end && HTTP_IS_SPHT(*uri))
2895 uri++;
2896
Willy Tarreau57bc8912016-04-25 17:09:40 +02002897 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002898 chunk.area = "<BADREQ>";
2899 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002900 } else if (uri == end) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002901 chunk.area = "HTTP/0.9";
2902 chunk.data = strlen("HTTP/0.9");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002903 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002904 chunk.area = uri;
2905 chunk.data = end - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002906 }
2907
Dragan Dosen835b9212016-02-12 13:23:03 +01002908 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002909 if (ret == NULL || *ret != '\0')
2910 goto out;
2911
2912 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002913 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002914 LOGCHAR('"');
2915
2916 last_isspace = 0;
2917 break;
2918
William Lallemand5f232402012-04-05 18:02:55 +02002919 case LOG_FMT_COUNTER: // %rt
2920 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002921 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
William Lallemand5f232402012-04-05 18:02:55 +02002922 if (iret < 0 || iret > dst + maxsize - tmplog)
2923 goto out;
2924 last_isspace = 0;
2925 tmplog += iret;
2926 } else {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002927 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002928 if (ret == NULL)
2929 goto out;
2930 tmplog = ret;
2931 last_isspace = 0;
2932 }
2933 break;
2934
Willy Tarreau7346acb2014-08-28 15:03:15 +02002935 case LOG_FMT_LOGCNT: // %lc
2936 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002937 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002938 if (iret < 0 || iret > dst + maxsize - tmplog)
2939 goto out;
2940 last_isspace = 0;
2941 tmplog += iret;
2942 } else {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002943 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002944 if (ret == NULL)
2945 goto out;
2946 tmplog = ret;
2947 last_isspace = 0;
2948 }
2949 break;
2950
William Lallemand5f232402012-04-05 18:02:55 +02002951 case LOG_FMT_HOSTNAME: // %H
2952 src = hostname;
2953 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2954 if (ret == NULL)
2955 goto out;
2956 tmplog = ret;
2957 last_isspace = 0;
2958 break;
2959
2960 case LOG_FMT_PID: // %pid
2961 if (tmp->options & LOG_OPT_HEXA) {
2962 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
2963 if (iret < 0 || iret > dst + maxsize - tmplog)
2964 goto out;
2965 last_isspace = 0;
2966 tmplog += iret;
2967 } else {
2968 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
2969 if (ret == NULL)
2970 goto out;
2971 tmplog = ret;
2972 last_isspace = 0;
2973 }
2974 break;
William Lallemanda73203e2012-03-12 12:48:57 +01002975
2976 case LOG_FMT_UNIQUEID: // %ID
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002977 ret = NULL;
Tim Duesterhusa17e6622020-03-05 20:19:02 +01002978 if (s)
2979 ret = lf_text_len(tmplog, s->unique_id.ptr, s->unique_id.len, maxsize - (tmplog - dst), tmp);
2980 else
2981 ret = lf_text_len(tmplog, NULL, 0, maxsize - (tmplog - dst), tmp);
William Lallemanda73203e2012-03-12 12:48:57 +01002982 if (ret == NULL)
2983 goto out;
2984 tmplog = ret;
2985 last_isspace = 0;
2986 break;
2987
William Lallemandbddd4fd2012-02-27 11:23:10 +01002988 }
2989 }
2990
2991out:
William Lallemand1d705562012-03-12 12:46:41 +01002992 /* *tmplog is a unused character */
2993 *tmplog = '\0';
Willy Tarreaudf974472012-12-28 02:44:01 +01002994 return tmplog - dst;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002995
Willy Tarreaubaaee002006-06-26 02:48:02 +02002996}
2997
William Lallemand1d705562012-03-12 12:46:41 +01002998/*
Willy Tarreau87b09662015-04-03 00:22:06 +02002999 * send a log for the stream when we have enough info about it.
William Lallemand1d705562012-03-12 12:46:41 +01003000 * Will not log if the frontend has no log defined.
3001 */
Willy Tarreau87b09662015-04-03 00:22:06 +02003002void strm_log(struct stream *s)
William Lallemand1d705562012-03-12 12:46:41 +01003003{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003004 struct session *sess = s->sess;
William Lallemand1d705562012-03-12 12:46:41 +01003005 int size, err, level;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02003006 int sd_size = 0;
William Lallemand1d705562012-03-12 12:46:41 +01003007
3008 /* if we don't want to log normal traffic, return now */
Willy Tarreaue7dff022015-04-03 01:14:29 +02003009 err = (s->flags & SF_REDISP) ||
3010 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
3011 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
Willy Tarreau350f4872014-11-28 14:42:25 +01003012 (s->si[1].conn_retries != s->be->conn_retries)) ||
Willy Tarreaueee5b512015-04-03 23:46:31 +02003013 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
Willy Tarreaubaaee002006-06-26 02:48:02 +02003014
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003015 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
William Lallemand1d705562012-03-12 12:46:41 +01003016 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003017
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003018 if (LIST_ISEMPTY(&sess->fe->logsrvs))
William Lallemand1d705562012-03-12 12:46:41 +01003019 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01003020
Willy Tarreauabcd5142013-06-11 17:18:02 +02003021 if (s->logs.level) { /* loglevel was overridden */
3022 if (s->logs.level == -1) {
3023 s->logs.logwait = 0; /* logs disabled */
3024 return;
3025 }
3026 level = s->logs.level - 1;
3027 }
3028 else {
3029 level = LOG_INFO;
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02003030 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
Willy Tarreauabcd5142013-06-11 17:18:02 +02003031 level = LOG_ERR;
3032 }
William Lallemand1d705562012-03-12 12:46:41 +01003033
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003034 /* if unique-id was not generated */
Tim Duesterhusa17e6622020-03-05 20:19:02 +01003035 if (!isttest(s->unique_id) && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
Tim Duesterhus2825b4b2020-02-28 15:13:34 +01003036 stream_generate_unique_id(s, &sess->fe->format_unique_id);
William Lallemand5b7ea3a2013-08-28 15:44:19 +02003037 }
3038
Dragan Dosen0b85ece2015-09-25 19:17:44 +02003039 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3040 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
3041 &sess->fe->logformat_sd);
3042 }
3043
Dragan Dosen59cee972015-09-19 22:09:02 +02003044 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
William Lallemand1d705562012-03-12 12:46:41 +01003045 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01003046 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003047 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3048 logline, size + 1, logline_rfc5424, sd_size);
William Lallemand1d705562012-03-12 12:46:41 +01003049 s->logs.logwait = 0;
3050 }
3051}
William Lallemandbddd4fd2012-02-27 11:23:10 +01003052
Willy Tarreau53839352018-09-05 19:51:10 +02003053/*
3054 * send a minimalist log for the session. Will not log if the frontend has no
3055 * log defined. It is assumed that this is only used to report anomalies that
3056 * cannot lead to the creation of a regular stream. Because of this the log
3057 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
3058 * in the frontend. The caller must simply know that it should not call this
Willy Tarreau9fa267d2018-10-05 10:22:27 +02003059 * function to report unimportant events. It is safe to call this function with
3060 * sess==NULL (will not do anything).
Willy Tarreau53839352018-09-05 19:51:10 +02003061 */
3062void sess_log(struct session *sess)
3063{
3064 int size, level;
3065 int sd_size = 0;
3066
Willy Tarreau9fa267d2018-10-05 10:22:27 +02003067 if (!sess)
3068 return;
3069
Willy Tarreau53839352018-09-05 19:51:10 +02003070 if (LIST_ISEMPTY(&sess->fe->logsrvs))
3071 return;
3072
3073 level = LOG_INFO;
3074 if (sess->fe->options2 & PR_O2_LOGERRORS)
3075 level = LOG_ERR;
3076
3077 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
3078 sd_size = sess_build_logline(sess, NULL,
3079 logline_rfc5424, global.max_syslog_len,
3080 &sess->fe->logformat_sd);
3081 }
3082
3083 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
3084 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01003085 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003086 __send_log(&sess->fe->logsrvs, &sess->fe->log_tag, level,
3087 logline, size + 1, logline_rfc5424, sd_size);
Willy Tarreau53839352018-09-05 19:51:10 +02003088 }
3089}
3090
Christopher Faulet5c6fefc2019-08-11 19:40:12 +02003091void app_log(struct list *logsrvs, struct buffer *tag, int level, const char *format, ...)
3092{
3093 va_list argp;
3094 int data_len;
3095
3096 if (level < 0 || format == NULL || logline == NULL)
3097 return;
3098
3099 va_start(argp, format);
3100 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
3101 if (data_len < 0 || data_len > global.max_syslog_len)
3102 data_len = global.max_syslog_len;
3103 va_end(argp);
3104
3105 __send_log(logsrvs, tag, level, logline, data_len, default_rfc5424_sd_log_format, 2);
3106}
3107
Willy Tarreau869efd52019-11-15 15:16:57 +01003108/* parse the "show startup-logs" command, returns 1 if a message is returned, otherwise zero */
3109static int cli_parse_show_startup_logs(char **args, char *payload, struct appctx *appctx, void *private)
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003110{
Willy Tarreau869efd52019-11-15 15:16:57 +01003111 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
3112 return 1;
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003113
Willy Tarreau869efd52019-11-15 15:16:57 +01003114 if (!startup_logs)
3115 return cli_msg(appctx, LOG_INFO, "\n"); // nothing to print
3116
3117 return ring_attach_cli(startup_logs, appctx);
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003118}
3119
3120/* register cli keywords */
3121static struct cli_kw_list cli_kws = {{ },{
3122 { { "show", "startup-logs", NULL },
Willy Tarreau869efd52019-11-15 15:16:57 +01003123 "show startup-logs : report logs emitted during HAProxy startup", cli_parse_show_startup_logs, NULL, NULL },
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003124 {{},}
3125}};
3126
Willy Tarreau0108d902018-11-25 19:14:37 +01003127INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
3128
Willy Tarreau082b6282019-05-22 14:42:12 +02003129REGISTER_PER_THREAD_ALLOC(init_log_buffers);
3130REGISTER_PER_THREAD_FREE(deinit_log_buffers);
Willy Tarreau172f5ce2018-11-26 11:21:50 +01003131
Willy Tarreaubaaee002006-06-26 02:48:02 +02003132/*
3133 * Local variables:
3134 * c-indent-level: 8
3135 * c-basic-offset: 8
3136 * End:
3137 */