blob: a075b85e46a34df141dceed66f6583667bd370af [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * General logging functions.
3 *
Willy Tarreaub7f694f2008-06-22 17:18:02 +02004 * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
Willy Tarreaubaaee002006-06-26 02:48:02 +02005 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
Willy Tarreau8a3f52f2012-12-20 21:23:42 +010013#include <ctype.h>
Willy Tarreauc8f24f82007-11-30 18:38:35 +010014#include <fcntl.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020015#include <stdarg.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <syslog.h>
20#include <time.h>
21#include <unistd.h>
Robert Tsai81ae1952007-12-05 10:47:29 +010022#include <errno.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020023
24#include <sys/time.h>
Willy Tarreau077edcb2016-08-10 18:30:56 +020025#include <sys/uio.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026
Willy Tarreaue3ba5f02006-06-29 18:54:54 +020027#include <common/config.h>
Willy Tarreaud6d06902009-08-19 11:22:33 +020028#include <common/compat.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010029#include <common/initcall.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020030#include <common/standard.h>
Willy Tarreaufb278672006-10-15 15:38:50 +020031#include <common/time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020032
Christopher Fauletc1b730a2017-10-24 12:00:51 +020033#include <types/cli.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020034#include <types/global.h>
William Lallemand723b73a2012-02-08 16:37:49 +010035#include <types/log.h>
Willy Tarreauec6c5df2008-07-15 00:22:45 +020036
Christopher Fauletc1b730a2017-10-24 12:00:51 +020037#include <proto/applet.h>
38#include <proto/cli.h>
William Lallemand5f232402012-04-05 18:02:55 +020039#include <proto/frontend.h>
Willy Tarreauec6c5df2008-07-15 00:22:45 +020040#include <proto/log.h>
Willy Tarreauc8368452012-12-21 00:09:23 +010041#include <proto/sample.h>
Willy Tarreaufb0afa72015-04-03 14:46:27 +020042#include <proto/stream.h>
Willy Tarreau827aee92011-03-10 16:55:02 +010043#include <proto/stream_interface.h>
Willy Tarreau773d65f2012-10-12 14:56:11 +020044#ifdef USE_OPENSSL
45#include <proto/ssl_sock.h>
46#endif
Willy Tarreaubaaee002006-06-26 02:48:02 +020047
Dragan Dosen43885c72015-10-01 13:18:13 +020048struct log_fmt {
49 char *name;
50 struct {
Willy Tarreau83061a82018-07-13 11:56:34 +020051 struct buffer sep1; /* first pid separator */
52 struct buffer sep2; /* second pid separator */
Dragan Dosen43885c72015-10-01 13:18:13 +020053 } pid;
54};
55
56static const struct log_fmt log_formats[LOG_FORMATS] = {
57 [LOG_FORMAT_RFC3164] = {
58 .name = "rfc3164",
59 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020060 .sep1 = { .area = "[", .data = 1 },
61 .sep2 = { .area = "]: ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020062 }
63 },
64 [LOG_FORMAT_RFC5424] = {
65 .name = "rfc5424",
66 .pid = {
Willy Tarreau843b7cb2018-07-13 10:54:26 +020067 .sep1 = { .area = " ", .data = 1 },
68 .sep2 = { .area = " - ", .data = 3 }
Dragan Dosen43885c72015-10-01 13:18:13 +020069 }
Willy Tarreaue8746a02018-11-12 08:45:00 +010070 },
71 [LOG_FORMAT_SHORT] = {
72 .name = "short",
73 .pid = {
74 .sep1 = { .area = "", .data = 0 },
75 .sep2 = { .area = " ", .data = 1 },
76 }
77 },
Willy Tarreauc1b06452018-11-12 11:57:56 +010078 [LOG_FORMAT_RAW] = {
79 .name = "raw",
80 .pid = {
81 .sep1 = { .area = "", .data = 0 },
82 .sep2 = { .area = "", .data = 0 },
83 }
84 },
Dragan Dosen1322d092015-09-22 16:05:32 +020085};
86
Dragan Dosen835b9212016-02-12 13:23:03 +010087#define FD_SETS_ARE_BITFIELDS
88#ifdef FD_SETS_ARE_BITFIELDS
89/*
90 * This map is used with all the FD_* macros to check whether a particular bit
91 * is set or not. Each bit represents an ACSII code. FD_SET() sets those bytes
92 * which should be escaped. When FD_ISSET() returns non-zero, it means that the
93 * byte should be escaped. Be careful to always pass bytes from 0 to 255
94 * exclusively to the macros.
95 */
96fd_set rfc5424_escape_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
Willy Tarreaue10cd482018-09-10 18:16:53 +020097fd_set hdr_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
98fd_set url_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
99fd_set http_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
Dragan Dosen835b9212016-02-12 13:23:03 +0100100
101#else
102#error "Check if your OS uses bitfields for fd_sets"
103#endif
104
Willy Tarreaubaaee002006-06-26 02:48:02 +0200105const char *log_facilities[NB_LOG_FACILITIES] = {
106 "kern", "user", "mail", "daemon",
107 "auth", "syslog", "lpr", "news",
108 "uucp", "cron", "auth2", "ftp",
109 "ntp", "audit", "alert", "cron2",
110 "local0", "local1", "local2", "local3",
111 "local4", "local5", "local6", "local7"
112};
113
Willy Tarreaubaaee002006-06-26 02:48:02 +0200114const char *log_levels[NB_LOG_LEVELS] = {
115 "emerg", "alert", "crit", "err",
116 "warning", "notice", "info", "debug"
117};
118
Willy Tarreau570f2212013-06-10 16:42:09 +0200119const 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 +0200120const char sess_fin_state[8] = "-RCHDLQT"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, Tarpit */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200121
William Lallemand723b73a2012-02-08 16:37:49 +0100122
123/* log_format */
124struct logformat_type {
125 char *name;
126 int type;
William Lallemandbddd4fd2012-02-27 11:23:10 +0100127 int mode;
William Lallemand5e19a282012-04-02 16:22:10 +0200128 int lw; /* logwait bitsfield */
William Lallemandb7ff6a32012-03-02 14:35:21 +0100129 int (*config_callback)(struct logformat_node *node, struct proxy *curproxy);
Willy Tarreau2beef582012-12-20 17:22:52 +0100130 const char *replace_by; /* new option to use instead of old one */
William Lallemand723b73a2012-02-08 16:37:49 +0100131};
132
William Lallemandb7ff6a32012-03-02 14:35:21 +0100133int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy);
134
William Lallemand723b73a2012-02-08 16:37:49 +0100135/* log_format variable names */
136static const struct logformat_type logformat_keywords[] = {
William Lallemand5e19a282012-04-02 16:22:10 +0200137 { "o", LOG_FMT_GLOBAL, PR_MODE_TCP, 0, NULL }, /* global option */
Willy Tarreau2beef582012-12-20 17:22:52 +0100138
139 /* please keep these lines sorted ! */
140 { "B", LOG_FMT_BYTES, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from server to client */
141 { "CC", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL }, /* client cookie */
142 { "CS", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL }, /* server cookie */
143 { "H", LOG_FMT_HOSTNAME, PR_MODE_TCP, LW_INIT, NULL }, /* Hostname */
144 { "ID", LOG_FMT_UNIQUEID, PR_MODE_HTTP, LW_BYTES, NULL }, /* Unique ID */
Willy Tarreau4bf99632014-06-13 12:21:40 +0200145 { "ST", LOG_FMT_STATUS, PR_MODE_TCP, LW_RESP, NULL }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200146 { "T", LOG_FMT_DATEGMT, PR_MODE_TCP, LW_INIT, NULL }, /* date GMT */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200147 { "Ta", LOG_FMT_Ta, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time active (tr to end) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100148 { "Tc", LOG_FMT_TC, PR_MODE_TCP, LW_BYTES, NULL }, /* Tc */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200149 { "Th", LOG_FMT_Th, PR_MODE_TCP, LW_BYTES, NULL }, /* Time handshake */
150 { "Ti", LOG_FMT_Ti, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time idle */
151 { "Tl", LOG_FMT_DATELOCAL, PR_MODE_TCP, LW_INIT, NULL }, /* date local timezone */
152 { "Tq", LOG_FMT_TQ, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tq=Th+Ti+TR */
153 { "Tr", LOG_FMT_Tr, PR_MODE_HTTP, LW_BYTES, NULL }, /* Tr */
154 { "TR", LOG_FMT_TR, PR_MODE_HTTP, LW_BYTES, NULL }, /* Time to receive a valid request */
Willy Tarreau27b639d2016-05-17 17:55:27 +0200155 { "Td", LOG_FMT_TD, PR_MODE_TCP, LW_BYTES, NULL }, /* Td = Tt - (Tq + Tw + Tc + Tr) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100156 { "Ts", LOG_FMT_TS, PR_MODE_TCP, LW_INIT, NULL }, /* timestamp GMT */
William Lallemand5e19a282012-04-02 16:22:10 +0200157 { "Tt", LOG_FMT_TT, PR_MODE_TCP, LW_BYTES, NULL }, /* Tt */
Willy Tarreau2beef582012-12-20 17:22:52 +0100158 { "Tw", LOG_FMT_TW, PR_MODE_TCP, LW_BYTES, NULL }, /* Tw */
159 { "U", LOG_FMT_BYTES_UP, PR_MODE_TCP, LW_BYTES, NULL }, /* bytes from client to server */
William Lallemand5e19a282012-04-02 16:22:10 +0200160 { "ac", LOG_FMT_ACTCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* actconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100161 { "b", LOG_FMT_BACKEND, PR_MODE_TCP, LW_INIT, NULL }, /* backend */
William Lallemand5e19a282012-04-02 16:22:10 +0200162 { "bc", LOG_FMT_BECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* beconn */
Willy Tarreau2beef582012-12-20 17:22:52 +0100163 { "bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source ip */
164 { "bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource }, /* backend source port */
William Lallemand5e19a282012-04-02 16:22:10 +0200165 { "bq", LOG_FMT_BCKQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* backend_queue */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200166 { "ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client ip */
167 { "cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL }, /* client port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100168 { "f", LOG_FMT_FRONTEND, PR_MODE_TCP, LW_INIT, NULL }, /* frontend */
169 { "fc", LOG_FMT_FECONN, PR_MODE_TCP, LW_BYTES, NULL }, /* feconn */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200170 { "fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend ip */
171 { "fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100172 { "ft", LOG_FMT_FRONTEND_XPRT, PR_MODE_TCP, LW_INIT, NULL }, /* frontend with transport mode */
Willy Tarreaud9ed3d22014-06-13 12:23:06 +0200173 { "hr", LOG_FMT_HDRREQUEST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request */
174 { "hrl", LOG_FMT_HDRREQUESTLIST, PR_MODE_TCP, LW_REQHDR, NULL }, /* header request list */
175 { "hs", LOG_FMT_HDRRESPONS, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response */
176 { "hsl", LOG_FMT_HDRRESPONSLIST, PR_MODE_TCP, LW_RSPHDR, NULL }, /* header response list */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000177 { "HM", LOG_FMT_HTTP_METHOD, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP method */
178 { "HP", LOG_FMT_HTTP_PATH, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP path */
Andrew Hayworthe63ac872015-07-31 16:14:16 +0000179 { "HQ", LOG_FMT_HTTP_QUERY, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP query */
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +0000180 { "HU", LOG_FMT_HTTP_URI, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP full URI */
181 { "HV", LOG_FMT_HTTP_VERSION, PR_MODE_HTTP, LW_REQ, NULL }, /* HTTP version */
Willy Tarreau7346acb2014-08-28 15:03:15 +0200182 { "lc", LOG_FMT_LOGCNT, PR_MODE_TCP, LW_INIT, NULL }, /* log counter */
Willy Tarreau2beef582012-12-20 17:22:52 +0100183 { "ms", LOG_FMT_MS, PR_MODE_TCP, LW_INIT, NULL }, /* accept date millisecond */
William Lallemand5e19a282012-04-02 16:22:10 +0200184 { "pid", LOG_FMT_PID, PR_MODE_TCP, LW_INIT, NULL }, /* log pid */
Willy Tarreau2beef582012-12-20 17:22:52 +0100185 { "r", LOG_FMT_REQ, PR_MODE_HTTP, LW_REQ, NULL }, /* request */
186 { "rc", LOG_FMT_RETRIES, PR_MODE_TCP, LW_BYTES, NULL }, /* retries */
Willy Tarreau1f0da242014-01-25 11:01:50 +0100187 { "rt", LOG_FMT_COUNTER, PR_MODE_TCP, LW_REQ, NULL }, /* request counter (HTTP or TCP session) */
Willy Tarreau2beef582012-12-20 17:22:52 +0100188 { "s", LOG_FMT_SERVER, PR_MODE_TCP, LW_SVID, NULL }, /* server */
189 { "sc", LOG_FMT_SRVCONN, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_conn */
190 { "si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination ip */
191 { "sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL }, /* server destination port */
192 { "sq", LOG_FMT_SRVQUEUE, PR_MODE_TCP, LW_BYTES, NULL }, /* srv_queue */
Willy Tarreauffc3fcd2012-10-12 20:17:54 +0200193 { "sslc", LOG_FMT_SSL_CIPHER, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL ciphers */
194 { "sslv", LOG_FMT_SSL_VERSION, PR_MODE_TCP, LW_XPRT, NULL }, /* client-side SSL protocol version */
Willy Tarreau2beef582012-12-20 17:22:52 +0100195 { "t", LOG_FMT_DATE, PR_MODE_TCP, LW_INIT, NULL }, /* date */
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200196 { "tr", LOG_FMT_tr, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request */
197 { "trg",LOG_FMT_trg, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, GMT */
198 { "trl",LOG_FMT_trl, PR_MODE_HTTP, LW_INIT, NULL }, /* date of start of request, local */
Willy Tarreau2beef582012-12-20 17:22:52 +0100199 { "ts", LOG_FMT_TERMSTATE, PR_MODE_TCP, LW_BYTES, NULL },/* termination state */
200 { "tsc", LOG_FMT_TERMSTATE_CK, PR_MODE_TCP, LW_INIT, NULL },/* termination state */
201
202 /* The following tags are deprecated and will be removed soon */
203 { "Bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bi" }, /* backend source ip */
204 { "Bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, LW_BCKIP, prepare_addrsource, "bp" }, /* backend source port */
Willy Tarreaud02286d2017-06-23 11:23:43 +0200205 { "Ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "ci" }, /* client ip */
206 { "Cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, LW_CLIP | LW_XPRT, NULL, "cp" }, /* client port */
207 { "Fi", LOG_FMT_FRONTENDIP, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fi" }, /* frontend ip */
208 { "Fp", LOG_FMT_FRONTENDPORT, PR_MODE_TCP, LW_FRTIP | LW_XPRT, NULL, "fp" }, /* frontend port */
Willy Tarreau2beef582012-12-20 17:22:52 +0100209 { "Si", LOG_FMT_SERVERIP, PR_MODE_TCP, LW_SVIP, NULL, "si" }, /* server destination ip */
210 { "Sp", LOG_FMT_SERVERPORT, PR_MODE_TCP, LW_SVIP, NULL, "sp" }, /* server destination port */
211 { "cc", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL, "CC" }, /* client cookie */
212 { "cs", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL, "CS" }, /* server cookie */
213 { "st", LOG_FMT_STATUS, PR_MODE_HTTP, LW_RESP, NULL, "ST" }, /* status code */
William Lallemand5e19a282012-04-02 16:22:10 +0200214 { 0, 0, 0, 0, NULL }
William Lallemand723b73a2012-02-08 16:37:49 +0100215};
216
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +0200217char 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
218char 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 +0100219char 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 +0100220char *log_format = NULL;
221
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200222/* Default string used for structured-data part in RFC5424 formatted
223 * syslog messages.
224 */
225char default_rfc5424_sd_log_format[] = "- ";
Dragan Dosen1322d092015-09-22 16:05:32 +0200226
Willy Tarreau13ef7732018-11-12 07:25:28 +0100227/* total number of dropped logs */
228unsigned int dropped_logs = 0;
229
Dragan Dosen1322d092015-09-22 16:05:32 +0200230/* This is a global syslog header, common to all outgoing messages in
231 * RFC3164 format. It begins with time-based part and is updated by
232 * update_log_hdr().
Dragan Dosen59cee972015-09-19 22:09:02 +0200233 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200234THREAD_LOCAL char *logheader = NULL;
Willy Tarreau55e2f5a2019-05-05 10:11:39 +0200235THREAD_LOCAL char *logheader_end = NULL;
Dragan Dosen59cee972015-09-19 22:09:02 +0200236
Dragan Dosen1322d092015-09-22 16:05:32 +0200237/* This is a global syslog header for messages in RFC5424 format. It is
238 * updated by update_log_hdr_rfc5424().
239 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200240THREAD_LOCAL char *logheader_rfc5424 = NULL;
Willy Tarreau55e2f5a2019-05-05 10:11:39 +0200241THREAD_LOCAL char *logheader_rfc5424_end = NULL;
Dragan Dosen1322d092015-09-22 16:05:32 +0200242
Dragan Dosen59cee972015-09-19 22:09:02 +0200243/* This is a global syslog message buffer, common to all outgoing
244 * messages. It contains only the data part.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100245 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200246THREAD_LOCAL char *logline = NULL;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +0100247
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200248/* A global syslog message buffer, common to all RFC5424 syslog messages.
249 * Currently, it is used for generating the structured-data part.
250 */
Christopher Fauletf8188c62017-06-02 16:20:16 +0200251THREAD_LOCAL char *logline_rfc5424 = NULL;
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200252
Christopher Fauletd4696382017-10-24 11:44:05 +0200253/* A global buffer used to store all startup alerts/warnings. It will then be
254 * retrieve on the CLI. */
Willy Tarreaua6483992018-12-15 16:55:36 +0100255static char *startup_logs = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +0200256
William Lallemand723b73a2012-02-08 16:37:49 +0100257struct logformat_var_args {
258 char *name;
259 int mask;
260};
261
262struct logformat_var_args var_args_list[] = {
263// global
264 { "M", LOG_OPT_MANDATORY },
265 { "Q", LOG_OPT_QUOTE },
William Lallemand5f232402012-04-05 18:02:55 +0200266 { "X", LOG_OPT_HEXA },
Dragan Dosen835b9212016-02-12 13:23:03 +0100267 { "E", LOG_OPT_ESC },
William Lallemand723b73a2012-02-08 16:37:49 +0100268 { 0, 0 }
269};
270
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200271/* return the name of the directive used in the current proxy for which we're
272 * currently parsing a header, when it is known.
273 */
274static inline const char *fmt_directive(const struct proxy *curproxy)
275{
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100276 switch (curproxy->conf.args.ctx) {
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200277 case ARGC_ACL:
278 return "acl";
279 case ARGC_STK:
280 return "stick";
281 case ARGC_TRK:
282 return "track-sc";
283 case ARGC_LOG:
284 return "log-format";
Dragan Dosen0b85ece2015-09-25 19:17:44 +0200285 case ARGC_LOGSD:
286 return "log-format-sd";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100287 case ARGC_HRQ:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100288 return "http-request";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100289 case ARGC_HRS:
Thierry FOURNIER1c0054f2013-11-20 15:09:52 +0100290 return "http-response";
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200291 case ARGC_UIF:
292 return "unique-id-format";
Thierry FOURNIERd18cd0f2013-11-29 12:15:45 +0100293 case ARGC_RDR:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200294 return "redirect";
295 case ARGC_CAP:
296 return "capture";
Willy Tarreau28d976d2015-07-09 11:39:33 +0200297 case ARGC_SRV:
298 return "server";
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200299 case ARGC_SPOE:
300 return "spoe-message";
Thierry FOURNIER / OZON.IO4ed1c952016-11-24 23:57:54 +0100301 case ARGC_UBK:
302 return "use_backend";
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100303 default:
Willy Tarreau53e1a6d2015-07-09 11:20:00 +0200304 return "undefined(please report this bug)"; /* must never happen */
Willy Tarreaubf0addb2013-12-02 12:24:54 +0100305 }
Willy Tarreaub1f3af22013-04-12 18:30:32 +0200306}
307
William Lallemand723b73a2012-02-08 16:37:49 +0100308/*
William Lallemandb7ff6a32012-03-02 14:35:21 +0100309 * callback used to configure addr source retrieval
310 */
311int prepare_addrsource(struct logformat_node *node, struct proxy *curproxy)
312{
313 curproxy->options2 |= PR_O2_SRC_ADDR;
314
315 return 0;
316}
317
318
319/*
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100320 * Parse args in a logformat_var. Returns 0 in error
321 * case, otherwise, it returns 1.
William Lallemand723b73a2012-02-08 16:37:49 +0100322 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100323int parse_logformat_var_args(char *args, struct logformat_node *node, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100324{
325 int i = 0;
326 int end = 0;
327 int flags = 0; // 1 = + 2 = -
328 char *sp = NULL; // start pointer
329
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100330 if (args == NULL) {
331 memprintf(err, "internal error: parse_logformat_var_args() expects non null 'args'");
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100332 return 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100333 }
William Lallemand723b73a2012-02-08 16:37:49 +0100334
335 while (1) {
336 if (*args == '\0')
337 end = 1;
338
339 if (*args == '+') {
340 // add flag
341 sp = args + 1;
342 flags = 1;
343 }
344 if (*args == '-') {
345 // delete flag
346 sp = args + 1;
347 flags = 2;
348 }
349
350 if (*args == '\0' || *args == ',') {
351 *args = '\0';
Willy Tarreau254d44c2012-12-20 18:19:26 +0100352 for (i = 0; sp && var_args_list[i].name; i++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100353 if (strcmp(sp, var_args_list[i].name) == 0) {
354 if (flags == 1) {
355 node->options |= var_args_list[i].mask;
356 break;
357 } else if (flags == 2) {
358 node->options &= ~var_args_list[i].mask;
359 break;
360 }
361 }
362 }
363 sp = NULL;
364 if (end)
365 break;
366 }
Willy Tarreau254d44c2012-12-20 18:19:26 +0100367 args++;
William Lallemand723b73a2012-02-08 16:37:49 +0100368 }
Thierry FOURNIER / OZON.IObca46f02016-11-22 23:13:04 +0100369 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100370}
371
372/*
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100373 * Parse a variable '%varname' or '%{args}varname' in log-format. The caller
374 * must pass the args part in the <arg> pointer with its length in <arg_len>,
375 * and varname with its length in <var> and <var_len> respectively. <arg> is
376 * ignored when arg_len is 0. Neither <var> nor <var_len> may be null.
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100377 * Returns false in error case and err is filled, otherwise returns true.
William Lallemand723b73a2012-02-08 16:37:49 +0100378 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100379int 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 +0100380{
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100381 int j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200382 struct logformat_node *node = NULL;
William Lallemand723b73a2012-02-08 16:37:49 +0100383
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100384 for (j = 0; logformat_keywords[j].name; j++) { // search a log type
385 if (strlen(logformat_keywords[j].name) == var_len &&
386 strncmp(var, logformat_keywords[j].name, var_len) == 0) {
387 if (logformat_keywords[j].mode != PR_MODE_HTTP || curproxy->mode == PR_MODE_HTTP) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200388 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100389 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100390 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200391 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100392 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100393 node->type = logformat_keywords[j].type;
394 node->options = *defoptions;
395 if (arg_len) {
396 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100397 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200398 goto error_free;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100399 }
400 if (node->type == LOG_FMT_GLOBAL) {
401 *defoptions = node->options;
402 free(node->arg);
403 free(node);
404 } else {
405 if (logformat_keywords[j].config_callback &&
406 logformat_keywords[j].config_callback(node, curproxy) != 0) {
Dragan Dosen61302da2019-04-30 00:40:02 +0200407 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100408 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100409 curproxy->to_log |= logformat_keywords[j].lw;
410 LIST_ADDQ(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100411 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100412 if (logformat_keywords[j].replace_by)
Christopher Faulet767a84b2017-11-24 16:50:31 +0100413 ha_warning("parsing [%s:%d] : deprecated variable '%s' in '%s', please replace it with '%s'.\n",
414 curproxy->conf.args.file, curproxy->conf.args.line,
415 logformat_keywords[j].name, fmt_directive(curproxy), logformat_keywords[j].replace_by);
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100416 return 1;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100417 } else {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100418 memprintf(err, "format variable '%s' is reserved for HTTP mode",
419 logformat_keywords[j].name);
Dragan Dosen61302da2019-04-30 00:40:02 +0200420 goto error_free;
William Lallemand723b73a2012-02-08 16:37:49 +0100421 }
William Lallemand723b73a2012-02-08 16:37:49 +0100422 }
423 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100424
425 j = var[var_len];
426 var[var_len] = 0;
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100427 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 +0100428 var[var_len] = j;
Dragan Dosen61302da2019-04-30 00:40:02 +0200429
430 error_free:
431 if (node) {
432 free(node->arg);
433 free(node);
434 }
Thierry FOURNIER / OZON.IOeca4d952016-11-22 22:06:04 +0100435 return 0;
William Lallemand723b73a2012-02-08 16:37:49 +0100436}
437
438/*
439 * push to the logformat linked list
440 *
441 * start: start pointer
442 * end: end text pointer
443 * type: string type
William Lallemand1d705562012-03-12 12:46:41 +0100444 * list_format: destination list
William Lallemand723b73a2012-02-08 16:37:49 +0100445 *
446 * LOG_TEXT: copy chars from start to end excluding end.
447 *
448*/
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100449int add_to_logformat_list(char *start, char *end, int type, struct list *list_format, char **err)
William Lallemand723b73a2012-02-08 16:37:49 +0100450{
451 char *str;
452
Willy Tarreaua3571662012-12-20 21:59:12 +0100453 if (type == LF_TEXT) { /* type text */
Vincent Bernat02779b62016-04-03 13:48:43 +0200454 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100455 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100456 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100457 return 0;
458 }
Vincent Bernat02779b62016-04-03 13:48:43 +0200459 str = calloc(1, end - start + 1);
William Lallemand723b73a2012-02-08 16:37:49 +0100460 strncpy(str, start, end - start);
William Lallemand723b73a2012-02-08 16:37:49 +0100461 str[end - start] = '\0';
462 node->arg = str;
William Lallemand1d705562012-03-12 12:46:41 +0100463 node->type = LOG_FMT_TEXT; // type string
464 LIST_ADDQ(list_format, &node->list);
Willy Tarreaua3571662012-12-20 21:59:12 +0100465 } else if (type == LF_SEPARATOR) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200466 struct logformat_node *node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100467 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100468 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100469 return 0;
470 }
William Lallemand1d705562012-03-12 12:46:41 +0100471 node->type = LOG_FMT_SEPARATOR;
472 LIST_ADDQ(list_format, &node->list);
William Lallemand723b73a2012-02-08 16:37:49 +0100473 }
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100474 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100475}
476
477/*
Willy Tarreauc8368452012-12-21 00:09:23 +0100478 * Parse the sample fetch expression <text> and add a node to <list_format> upon
479 * success. At the moment, sample converters are not yet supported but fetch arguments
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200480 * should work. The curpx->conf.args.ctx must be set by the caller.
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100481 *
482 * In error case, the function returns 0, otherwise it returns 1.
Willy Tarreauc8368452012-12-21 00:09:23 +0100483 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100484int add_sample_to_logformat_list(char *text, char *arg, int arg_len, struct proxy *curpx, struct list *list_format, int options, int cap, char **err)
Willy Tarreauc8368452012-12-21 00:09:23 +0100485{
486 char *cmd[2];
Dragan Dosen61302da2019-04-30 00:40:02 +0200487 struct sample_expr *expr = NULL;
488 struct logformat_node *node = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +0100489 int cmd_arg;
490
491 cmd[0] = text;
492 cmd[1] = "";
493 cmd_arg = 0;
494
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100495 expr = sample_parse_expr(cmd, &cmd_arg, curpx->conf.args.file, curpx->conf.args.line, err, &curpx->conf.args);
Willy Tarreauc8368452012-12-21 00:09:23 +0100496 if (!expr) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100497 memprintf(err, "failed to parse sample expression <%s> : %s", text, *err);
Dragan Dosen61302da2019-04-30 00:40:02 +0200498 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100499 }
500
Vincent Bernat02779b62016-04-03 13:48:43 +0200501 node = calloc(1, sizeof(*node));
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100502 if (!node) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100503 memprintf(err, "out of memory error");
Dragan Dosen61302da2019-04-30 00:40:02 +0200504 goto error_free;
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100505 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100506 node->type = LOG_FMT_EXPR;
507 node->expr = expr;
508 node->options = options;
509
510 if (arg_len) {
511 node->arg = my_strndup(arg, arg_len);
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100512 if (!parse_logformat_var_args(node->arg, node, err))
Dragan Dosen61302da2019-04-30 00:40:02 +0200513 goto error_free;
Willy Tarreauc8368452012-12-21 00:09:23 +0100514 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100515 if (expr->fetch->val & cap & SMP_VAL_REQUEST)
Willy Tarreauc8368452012-12-21 00:09:23 +0100516 node->options |= LOG_OPT_REQ_CAP; /* fetch method is request-compatible */
517
Willy Tarreau434c57c2013-01-08 01:10:24 +0100518 if (expr->fetch->val & cap & SMP_VAL_RESPONSE)
Willy Tarreauc8368452012-12-21 00:09:23 +0100519 node->options |= LOG_OPT_RES_CAP; /* fetch method is response-compatible */
520
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100521 if (!(expr->fetch->val & cap)) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100522 memprintf(err, "sample fetch <%s> may not be reliably used here because it needs '%s' which is not available here",
523 text, sample_src_names(expr->fetch->use));
Dragan Dosen61302da2019-04-30 00:40:02 +0200524 goto error_free;
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100525 }
Willy Tarreau434c57c2013-01-08 01:10:24 +0100526
Willy Tarreauc8368452012-12-21 00:09:23 +0100527 /* check if we need to allocate an hdr_idx struct for HTTP parsing */
528 /* Note, we may also need to set curpx->to_log with certain fetches */
Willy Tarreau25320b22013-03-24 07:22:08 +0100529 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
Willy Tarreauc8368452012-12-21 00:09:23 +0100530
William Lallemand65ad6e12014-01-31 15:08:02 +0100531 /* FIXME: temporary workaround for missing LW_XPRT and LW_REQ flags
532 * needed with some sample fetches (eg: ssl*). We always set it for
533 * now on, but this will leave with sample capabilities soon.
Willy Tarreau1f31c732013-01-10 16:22:27 +0100534 */
535 curpx->to_log |= LW_XPRT;
William Lallemand65ad6e12014-01-31 15:08:02 +0100536 curpx->to_log |= LW_REQ;
Willy Tarreauc8368452012-12-21 00:09:23 +0100537 LIST_ADDQ(list_format, &node->list);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100538 return 1;
Dragan Dosen61302da2019-04-30 00:40:02 +0200539
540 error_free:
541 release_sample_expr(expr);
542 if (node) {
543 free(node->arg);
544 free(node);
545 }
546 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100547}
548
549/*
William Lallemand723b73a2012-02-08 16:37:49 +0100550 * Parse the log_format string and fill a linked list.
551 * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200552 * You can set arguments using { } : %{many arguments}varname.
553 * The curproxy->conf.args.ctx must be set by the caller.
William Lallemand1d705562012-03-12 12:46:41 +0100554 *
555 * str: the string to parse
556 * curproxy: the proxy affected
557 * list_format: the destination list
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +0100558 * options: LOG_OPT_* to force on every node
Willy Tarreau434c57c2013-01-08 01:10:24 +0100559 * cap: all SMP_VAL_* flags supported by the consumer
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100560 *
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100561 * The function returns 1 in success case, otherwise, it returns 0 and err is filled.
William Lallemand723b73a2012-02-08 16:37:49 +0100562 */
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100563int 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 +0100564{
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100565 char *sp, *str, *backfmt; /* start pointer for text parts */
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100566 char *arg = NULL; /* start pointer for args */
567 char *var = NULL; /* start pointer for vars */
568 int arg_len = 0;
569 int var_len = 0;
570 int cformat; /* current token format */
571 int pformat; /* previous token format */
William Lallemand723b73a2012-02-08 16:37:49 +0100572 struct logformat_node *tmplf, *back;
573
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100574 sp = str = backfmt = strdup(fmt);
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100575 if (!str) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100576 memprintf(err, "out of memory error");
Thierry FOURNIER / OZON.IO9cbfef22016-11-22 23:24:10 +0100577 return 0;
578 }
William Lallemand1dc00ef2012-08-09 16:41:35 +0200579 curproxy->to_log |= LW_INIT;
William Lallemand5e19a282012-04-02 16:22:10 +0200580
William Lallemand723b73a2012-02-08 16:37:49 +0100581 /* flush the list first. */
William Lallemand1d705562012-03-12 12:46:41 +0100582 list_for_each_entry_safe(tmplf, back, list_format, list) {
William Lallemand723b73a2012-02-08 16:37:49 +0100583 LIST_DEL(&tmplf->list);
Dragan Dosen61302da2019-04-30 00:40:02 +0200584 release_sample_expr(tmplf->expr);
585 free(tmplf->arg);
William Lallemand723b73a2012-02-08 16:37:49 +0100586 free(tmplf);
587 }
588
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100589 for (cformat = LF_INIT; cformat != LF_END; str++) {
William Lallemand723b73a2012-02-08 16:37:49 +0100590 pformat = cformat;
591
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100592 if (!*str)
593 cformat = LF_END; // preset it to save all states from doing this
William Lallemand723b73a2012-02-08 16:37:49 +0100594
Joseph Herlant85b40592018-11-15 12:10:04 -0800595 /* The principle of the two-step state machine below is to first detect a change, and
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100596 * second have all common paths processed at one place. The common paths are the ones
597 * encountered in text areas (LF_INIT, LF_TEXT, LF_SEPARATOR) and at the end (LF_END).
598 * We use the common LF_INIT state to dispatch to the different final states.
599 */
600 switch (pformat) {
601 case LF_STARTVAR: // text immediately following a '%'
Willy Tarreauc8368452012-12-21 00:09:23 +0100602 arg = NULL; var = NULL;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100603 arg_len = var_len = 0;
604 if (*str == '{') { // optional argument
605 cformat = LF_STARG;
606 arg = str + 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100607 }
Willy Tarreauc8368452012-12-21 00:09:23 +0100608 else if (*str == '[') {
609 cformat = LF_STEXPR;
610 var = str + 1; // store expr in variable name
611 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100612 else if (isalpha((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100613 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100614 var = str;
William Lallemand723b73a2012-02-08 16:37:49 +0100615 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100616 else if (*str == '%')
617 cformat = LF_TEXT; // convert this character to a litteral (useful for '%')
Willy Tarreau0f28f822013-12-16 01:38:33 +0100618 else if (isdigit((unsigned char)*str) || *str == ' ' || *str == '\t') {
Willy Tarreau06d97f92013-12-02 17:45:48 +0100619 /* single '%' followed by blank or digit, send them both */
620 cformat = LF_TEXT;
621 pformat = LF_TEXT; /* finally we include the previous char as well */
622 sp = str - 1; /* send both the '%' and the current char */
Jim Freemana2278c82017-04-15 08:01:59 -0600623 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 +0100624 *str, (int)(str - backfmt), fmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100625 return 0;
Willy Tarreau06d97f92013-12-02 17:45:48 +0100626
627 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100628 else
629 cformat = LF_INIT; // handle other cases of litterals
630 break;
631
632 case LF_STARG: // text immediately following '%{'
633 if (*str == '}') { // end of arg
William Lallemand723b73a2012-02-08 16:37:49 +0100634 cformat = LF_EDARG;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100635 arg_len = str - arg;
636 *str = 0; // used for reporting errors
William Lallemand723b73a2012-02-08 16:37:49 +0100637 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100638 break;
639
640 case LF_EDARG: // text immediately following '%{arg}'
Willy Tarreauc8368452012-12-21 00:09:23 +0100641 if (*str == '[') {
642 cformat = LF_STEXPR;
643 var = str + 1; // store expr in variable name
644 break;
645 }
Willy Tarreau0f28f822013-12-16 01:38:33 +0100646 else if (isalnum((unsigned char)*str)) { // variable name
William Lallemand723b73a2012-02-08 16:37:49 +0100647 cformat = LF_VAR;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100648 var = str;
649 break;
650 }
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100651 memprintf(err, "parse argument modifier without variable name near '%%{%s}'", arg);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100652 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100653
Willy Tarreauc8368452012-12-21 00:09:23 +0100654 case LF_STEXPR: // text immediately following '%['
655 if (*str == ']') { // end of arg
656 cformat = LF_EDEXPR;
657 var_len = str - var;
658 *str = 0; // needed for parsing the expression
659 }
660 break;
661
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100662 case LF_VAR: // text part of a variable name
663 var_len = str - var;
Willy Tarreau0f28f822013-12-16 01:38:33 +0100664 if (!isalnum((unsigned char)*str))
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100665 cformat = LF_INIT; // not variable name anymore
666 break;
667
Willy Tarreauc8368452012-12-21 00:09:23 +0100668 default: // LF_INIT, LF_TEXT, LF_SEPARATOR, LF_END, LF_EDEXPR
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100669 cformat = LF_INIT;
670 }
671
672 if (cformat == LF_INIT) { /* resynchronize state to text/sep/startvar */
673 switch (*str) {
674 case '%': cformat = LF_STARTVAR; break;
675 case ' ': cformat = LF_SEPARATOR; break;
676 case 0 : cformat = LF_END; break;
677 default : cformat = LF_TEXT; break;
William Lallemand723b73a2012-02-08 16:37:49 +0100678 }
679 }
680
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100681 if (cformat != pformat || pformat == LF_SEPARATOR) {
682 switch (pformat) {
683 case LF_VAR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100684 if (!parse_logformat_var(arg, arg_len, var, var_len, curproxy, list_format, &options, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100685 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100686 break;
Willy Tarreauc8368452012-12-21 00:09:23 +0100687 case LF_STEXPR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100688 if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100689 return 0;
Willy Tarreauc8368452012-12-21 00:09:23 +0100690 break;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100691 case LF_TEXT:
692 case LF_SEPARATOR:
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100693 if (!add_to_logformat_list(sp, str, pformat, list_format, err))
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100694 return 0;
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100695 break;
696 }
697 sp = str; /* new start of text at every state switch and at every separator */
William Lallemand723b73a2012-02-08 16:37:49 +0100698 }
699 }
Willy Tarreau8a3f52f2012-12-20 21:23:42 +0100700
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100701 if (pformat == LF_STARTVAR || pformat == LF_STARG || pformat == LF_STEXPR) {
Thierry FOURNIER / OZON.IO8a4e4422016-11-23 00:41:28 +0100702 memprintf(err, "truncated line after '%s'", var ? var : arg ? arg : "%");
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100703 return 0;
704 }
Willy Tarreaub83bc1e2012-12-24 12:36:33 +0100705 free(backfmt);
Thierry FOURNIER / OZON.IOa2c38d72016-11-22 23:11:21 +0100706
707 return 1;
William Lallemand723b73a2012-02-08 16:37:49 +0100708}
709
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200710/*
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200711 * Parse the first range of indexes from a string made of a list of comma seperated
712 * ranges of indexes. Note that an index may be considered as a particular range
713 * with a high limit to the low limit.
714 */
715int get_logsrv_smp_range(unsigned int *low, unsigned int *high, char **arg, char **err)
716{
717 char *end, *p;
718
719 *low = *high = 0;
720
721 p = *arg;
722 end = strchr(p, ',');
723 if (!end)
724 end = p + strlen(p);
725
726 *high = *low = read_uint((const char **)&p, end);
727 if (!*low || (p != end && *p != '-'))
728 goto err;
729
730 if (p == end)
731 goto done;
732
733 p++;
734 *high = read_uint((const char **)&p, end);
735 if (!*high || *high <= *low || p != end)
736 goto err;
737
738 done:
739 if (*end == ',')
740 end++;
741 *arg = end;
742 return 1;
743
744 err:
745 memprintf(err, "wrong sample range '%s'", *arg);
746 return 0;
747}
748
749/*
750 * Returns 1 if the range defined by <low> and <high> overlaps
751 * one of them in <rgs> array of ranges with <sz> the size of this
752 * array, 0 if not.
753 */
754int smp_log_ranges_overlap(struct smp_log_range *rgs, size_t sz,
755 unsigned int low, unsigned int high, char **err)
756{
757 size_t i;
758
759 for (i = 0; i < sz; i++) {
760 if ((low >= rgs[i].low && low <= rgs[i].high) ||
761 (high >= rgs[i].low && high <= rgs[i].high)) {
762 memprintf(err, "ranges are overlapping");
763 return 1;
764 }
765 }
766
767 return 0;
768}
769
770int smp_log_range_cmp(const void *a, const void *b)
771{
772 const struct smp_log_range *rg_a = a;
773 const struct smp_log_range *rg_b = b;
774
775 if (rg_a->high < rg_b->low)
776 return -1;
777 else if (rg_a->low > rg_b->high)
778 return 1;
779
780 return 0;
781}
782
783/*
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200784 * Parse "log" keyword and update <logsrvs> list accordingly.
785 *
786 * When <do_del> is set, it means the "no log" line was parsed, so all log
787 * servers in <logsrvs> are released.
788 *
789 * Otherwise, we try to parse the "log" line. First of all, when the list is not
790 * the global one, we look for the parameter "global". If we find it,
791 * global.logsrvs is copied. Else we parse each arguments.
792 *
793 * The function returns 1 in success case, otherwise, it returns 0 and err is
794 * filled.
795 */
796int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err)
797{
798 struct sockaddr_storage *sk;
799 struct logsrv *logsrv = NULL;
800 int port1, port2;
801 int cur_arg;
802
803 /*
804 * "no log": delete previous herited or defined syslog
805 * servers.
806 */
807 if (do_del) {
808 struct logsrv *back;
809
810 if (*(args[1]) != 0) {
811 memprintf(err, "'no log' does not expect arguments");
812 goto error;
813 }
814
815 list_for_each_entry_safe(logsrv, back, logsrvs, list) {
816 LIST_DEL(&logsrv->list);
817 free(logsrv);
818 }
819 return 1;
820 }
821
822 /*
823 * "log global": copy global.logrsvs linked list to the end of logsrvs
824 * list. But first, we check (logsrvs != global.logsrvs).
825 */
826 if (*(args[1]) && *(args[2]) == 0 && !strcmp(args[1], "global")) {
827 if (logsrvs == &global.logsrvs) {
828 memprintf(err, "'global' is not supported for a global syslog server");
829 goto error;
830 }
831 list_for_each_entry(logsrv, &global.logsrvs, list) {
Christopher Faulet28ac0992018-03-26 16:09:19 +0200832 struct logsrv *node;
833
834 list_for_each_entry(node, logsrvs, list) {
835 if (node->ref == logsrv)
836 goto skip_logsrv;
837 }
838
839 node = malloc(sizeof(*node));
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200840 memcpy(node, logsrv, sizeof(struct logsrv));
Christopher Faulet28ac0992018-03-26 16:09:19 +0200841 node->ref = logsrv;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200842 LIST_INIT(&node->list);
843 LIST_ADDQ(logsrvs, &node->list);
Christopher Faulet28ac0992018-03-26 16:09:19 +0200844
845 skip_logsrv:
846 continue;
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200847 }
848 return 1;
849 }
850
851 /*
852 * "log <address> ...: parse a syslog server line
853 */
854 if (*(args[1]) == 0 || *(args[2]) == 0) {
855 memprintf(err, "expects <address> and <facility> %s as arguments",
856 ((logsrvs == &global.logsrvs) ? "" : "or global"));
857 goto error;
858 }
859
Willy Tarreau5a32ecc2018-11-12 07:34:59 +0100860 /* take care of "stdout" and "stderr" as regular aliases for fd@1 / fd@2 */
861 if (strcmp(args[1], "stdout") == 0)
862 args[1] = "fd@1";
863 else if (strcmp(args[1], "stderr") == 0)
864 args[1] = "fd@2";
865
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200866 logsrv = calloc(1, sizeof(*logsrv));
867 if (!logsrv) {
868 memprintf(err, "out of memory");
869 goto error;
870 }
871
872 /* skip address for now, it will be parsed at the end */
873 cur_arg = 2;
874
875 /* just after the address, a length may be specified */
876 logsrv->maxlen = MAX_SYSLOG_LEN;
877 if (strcmp(args[cur_arg], "len") == 0) {
878 int len = atoi(args[cur_arg+1]);
879 if (len < 80 || len > 65535) {
880 memprintf(err, "invalid log length '%s', must be between 80 and 65535",
881 args[cur_arg+1]);
882 goto error;
883 }
884 logsrv->maxlen = len;
885 cur_arg += 2;
886 }
887 if (logsrv->maxlen > global.max_syslog_len)
888 global.max_syslog_len = logsrv->maxlen;
889
890 /* after the length, a format may be specified */
891 if (strcmp(args[cur_arg], "format") == 0) {
892 logsrv->format = get_log_format(args[cur_arg+1]);
893 if (logsrv->format < 0) {
894 memprintf(err, "unknown log format '%s'", args[cur_arg+1]);
895 goto error;
896 }
897 cur_arg += 2;
898 }
899
Frédéric Lécailled95ea282019-04-24 16:14:33 +0200900 if (strcmp(args[cur_arg], "sample") == 0) {
901 unsigned low, high;
902 char *p, *beg, *end, *smp_sz_str;
903 struct smp_log_range *smp_rgs = NULL;
904 size_t smp_rgs_sz = 0, smp_sz = 0, new_smp_sz;
905
906 p = args[cur_arg+1];
907 smp_sz_str = strchr(p, ':');
908 if (!smp_sz_str) {
909 memprintf(err, "Missing sample size");
910 goto error;
911 }
912
913 *smp_sz_str++ = '\0';
914
915 end = p + strlen(p);
916
917 while (p != end) {
918 if (!get_logsrv_smp_range(&low, &high, &p, err))
919 goto error;
920
921 if (smp_rgs && smp_log_ranges_overlap(smp_rgs, smp_rgs_sz, low, high, err))
922 goto error;
923
924 smp_rgs = my_realloc2(smp_rgs, (smp_rgs_sz + 1) * sizeof *smp_rgs);
925 if (!smp_rgs) {
926 memprintf(err, "out of memory error");
927 goto error;
928 }
929
930 smp_rgs[smp_rgs_sz].low = low;
931 smp_rgs[smp_rgs_sz].high = high;
932 smp_rgs[smp_rgs_sz].sz = high - low + 1;
933 smp_rgs[smp_rgs_sz].curr_idx = 0;
934 if (smp_rgs[smp_rgs_sz].high > smp_sz)
935 smp_sz = smp_rgs[smp_rgs_sz].high;
936 smp_rgs_sz++;
937 }
938
939 beg = smp_sz_str;
940 end = beg + strlen(beg);
941 new_smp_sz = read_uint((const char **)&beg, end);
942 if (!new_smp_sz || beg != end) {
943 memprintf(err, "wrong sample size '%s' for sample range '%s'",
944 smp_sz_str, args[cur_arg+1]);
945 goto error;
946 }
947
948 if (new_smp_sz < smp_sz) {
949 memprintf(err, "sample size %zu should be greater or equal to "
950 "%zu the maximum of the high ranges limits",
951 new_smp_sz, smp_sz);
952 goto error;
953 }
954 smp_sz = new_smp_sz;
955
956 /* Let's order <smp_rgs> array. */
957 qsort(smp_rgs, smp_rgs_sz, sizeof(struct smp_log_range), smp_log_range_cmp);
958
959 logsrv->lb.smp_rgs = smp_rgs;
960 logsrv->lb.smp_rgs_sz = smp_rgs_sz;
961 logsrv->lb.smp_sz = smp_sz;
962
963 cur_arg += 2;
964 }
Frédéric Lécailled803e472019-04-25 07:42:09 +0200965 HA_SPIN_INIT(&logsrv->lock);
Christopher Faulet4b0b79d2018-03-26 15:54:32 +0200966 /* parse the facility */
967 logsrv->facility = get_log_facility(args[cur_arg]);
968 if (logsrv->facility < 0) {
969 memprintf(err, "unknown log facility '%s'", args[cur_arg]);
970 goto error;
971 }
972 cur_arg++;
973
974 /* parse the max syslog level (default: debug) */
975 logsrv->level = 7;
976 if (*(args[cur_arg])) {
977 logsrv->level = get_log_level(args[cur_arg]);
978 if (logsrv->level < 0) {
979 memprintf(err, "unknown optional log level '%s'", args[cur_arg]);
980 goto error;
981 }
982 cur_arg++;
983 }
984
985 /* parse the limit syslog level (default: emerg) */
986 logsrv->minlvl = 0;
987 if (*(args[cur_arg])) {
988 logsrv->minlvl = get_log_level(args[cur_arg]);
989 if (logsrv->minlvl < 0) {
990 memprintf(err, "unknown optional minimum log level '%s'", args[cur_arg]);
991 goto error;
992 }
993 cur_arg++;
994 }
995
996 /* Too many args */
997 if (*(args[cur_arg])) {
998 memprintf(err, "cannot handle unexpected argument '%s'", args[cur_arg]);
999 goto error;
1000 }
1001
1002 /* now, back to the address */
1003 sk = str2sa_range(args[1], NULL, &port1, &port2, err, NULL, NULL, 1);
1004 if (!sk)
1005 goto error;
1006 logsrv->addr = *sk;
1007
1008 if (sk->ss_family == AF_INET || sk->ss_family == AF_INET6) {
1009 if (port1 != port2) {
1010 memprintf(err, "port ranges and offsets are not allowed in '%s'", args[1]);
1011 goto error;
1012 }
1013 logsrv->addr = *sk;
1014 if (!port1)
1015 set_host_port(&logsrv->addr, SYSLOG_PORT);
1016 }
1017 LIST_ADDQ(logsrvs, &logsrv->list);
1018 return 1;
1019
1020 error:
1021 free(logsrv);
1022 return 0;
1023}
1024
1025
Christopher Fauletd4696382017-10-24 11:44:05 +02001026/* Generic function to display messages prefixed by a label */
1027static void print_message(const char *label, const char *fmt, va_list argp)
1028{
1029 struct tm tm;
1030 char *head, *msg;
1031
1032 head = msg = NULL;
1033
1034 get_localtime(date.tv_sec, &tm);
1035 memprintf(&head, "[%s] %03d/%02d%02d%02d (%d) : ",
1036 label, tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
1037 memvprintf(&msg, fmt, argp);
1038
1039 if (global.mode & MODE_STARTING)
1040 memprintf(&startup_logs, "%s%s%s", (startup_logs ? startup_logs : ""), head, msg);
1041
1042 fprintf(stderr, "%s%s", head, msg);
1043 fflush(stderr);
1044
1045 free(head);
1046 free(msg);
1047}
1048
Willy Tarreaubaaee002006-06-26 02:48:02 +02001049/*
1050 * Displays the message on stderr with the date and pid. Overrides the quiet
1051 * mode during startup.
1052 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001053void ha_alert(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001054{
1055 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001056
1057 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
1058 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001059 print_message("ALERT", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001060 va_end(argp);
1061 }
1062}
1063
1064
1065/*
1066 * Displays the message on stderr with the date and pid.
1067 */
Christopher Faulet767a84b2017-11-24 16:50:31 +01001068void ha_warning(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001069{
1070 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001071
1072 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1073 va_start(argp, fmt);
Christopher Fauletd4696382017-10-24 11:44:05 +02001074 print_message("WARNING", fmt, argp);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001075 va_end(argp);
1076 }
1077}
1078
1079/*
William Lallemand9c56a222018-11-21 18:04:52 +01001080 * Displays the message on stderr with the date and pid.
1081 */
1082void ha_notice(const char *fmt, ...)
1083{
1084 va_list argp;
1085
1086 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1087 va_start(argp, fmt);
1088 print_message("NOTICE", fmt, argp);
1089 va_end(argp);
1090 }
1091}
1092
1093/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001094 * Displays the message on <out> only if quiet mode is not set.
1095 */
Willy Tarreaub17916e2006-10-15 15:17:57 +02001096void qfprintf(FILE *out, const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001097{
1098 va_list argp;
1099
1100 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
1101 va_start(argp, fmt);
1102 vfprintf(out, fmt, argp);
1103 fflush(out);
1104 va_end(argp);
1105 }
1106}
1107
1108/*
Dragan Dosen1322d092015-09-22 16:05:32 +02001109 * returns log format for <fmt> or -1 if not found.
1110 */
1111int get_log_format(const char *fmt)
1112{
1113 int format;
1114
1115 format = LOG_FORMATS - 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001116 while (format >= 0 && strcmp(log_formats[format].name, fmt))
Dragan Dosen1322d092015-09-22 16:05:32 +02001117 format--;
1118
1119 return format;
1120}
1121
1122/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02001123 * returns log level for <lev> or -1 if not found.
1124 */
1125int get_log_level(const char *lev)
1126{
1127 int level;
1128
1129 level = NB_LOG_LEVELS - 1;
1130 while (level >= 0 && strcmp(log_levels[level], lev))
1131 level--;
1132
1133 return level;
1134}
1135
Willy Tarreaubaaee002006-06-26 02:48:02 +02001136/*
1137 * returns log facility for <fac> or -1 if not found.
1138 */
1139int get_log_facility(const char *fac)
1140{
1141 int facility;
1142
1143 facility = NB_LOG_FACILITIES - 1;
1144 while (facility >= 0 && strcmp(log_facilities[facility], fac))
1145 facility--;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001146
Willy Tarreaubaaee002006-06-26 02:48:02 +02001147 return facility;
1148}
1149
William Lallemanda1cc3812012-02-08 16:38:44 +01001150/*
Dragan Dosen835b9212016-02-12 13:23:03 +01001151 * Encode the string.
1152 *
1153 * When using the +E log format option, it will try to escape '"\]'
1154 * characters with '\' as prefix. The same prefix should not be used as
1155 * <escape>.
1156 */
1157static char *lf_encode_string(char *start, char *stop,
1158 const char escape, const fd_set *map,
1159 const char *string,
1160 struct logformat_node *node)
1161{
1162 if (node->options & LOG_OPT_ESC) {
1163 if (start < stop) {
1164 stop--; /* reserve one byte for the final '\0' */
1165 while (start < stop && *string != '\0') {
1166 if (!FD_ISSET((unsigned char)(*string), map)) {
1167 if (!FD_ISSET((unsigned char)(*string), rfc5424_escape_map))
1168 *start++ = *string;
1169 else {
1170 if (start + 2 >= stop)
1171 break;
1172 *start++ = '\\';
1173 *start++ = *string;
1174 }
1175 }
1176 else {
1177 if (start + 3 >= stop)
1178 break;
1179 *start++ = escape;
1180 *start++ = hextab[(*string >> 4) & 15];
1181 *start++ = hextab[*string & 15];
1182 }
1183 string++;
1184 }
1185 *start = '\0';
1186 }
1187 }
1188 else {
1189 return encode_string(start, stop, escape, map, string);
1190 }
1191
1192 return start;
1193}
1194
1195/*
1196 * Encode the chunk.
1197 *
1198 * When using the +E log format option, it will try to escape '"\]'
1199 * characters with '\' as prefix. The same prefix should not be used as
1200 * <escape>.
1201 */
1202static char *lf_encode_chunk(char *start, char *stop,
1203 const char escape, const fd_set *map,
Willy Tarreau83061a82018-07-13 11:56:34 +02001204 const struct buffer *chunk,
Dragan Dosen835b9212016-02-12 13:23:03 +01001205 struct logformat_node *node)
1206{
1207 char *str, *end;
1208
1209 if (node->options & LOG_OPT_ESC) {
1210 if (start < stop) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001211 str = chunk->area;
1212 end = chunk->area + chunk->data;
Dragan Dosen835b9212016-02-12 13:23:03 +01001213
1214 stop--; /* reserve one byte for the final '\0' */
1215 while (start < stop && str < end) {
1216 if (!FD_ISSET((unsigned char)(*str), map)) {
1217 if (!FD_ISSET((unsigned char)(*str), rfc5424_escape_map))
1218 *start++ = *str;
1219 else {
1220 if (start + 2 >= stop)
1221 break;
1222 *start++ = '\\';
1223 *start++ = *str;
1224 }
1225 }
1226 else {
1227 if (start + 3 >= stop)
1228 break;
1229 *start++ = escape;
1230 *start++ = hextab[(*str >> 4) & 15];
1231 *start++ = hextab[*str & 15];
1232 }
1233 str++;
1234 }
1235 *start = '\0';
1236 }
1237 }
1238 else {
1239 return encode_chunk(start, stop, escape, map, chunk);
1240 }
1241
1242 return start;
1243}
1244
1245/*
William Lallemanda1cc3812012-02-08 16:38:44 +01001246 * Write a string in the log string
Dragan Dosen835b9212016-02-12 13:23:03 +01001247 * Take cares of quote and escape options
William Lallemanda1cc3812012-02-08 16:38:44 +01001248 *
Joseph Herlant85b40592018-11-15 12:10:04 -08001249 * Return the address of the \0 character, or NULL on error
William Lallemanda1cc3812012-02-08 16:38:44 +01001250 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001251char *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 +01001252{
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001253 if (size < 2)
1254 return NULL;
William Lallemanda1cc3812012-02-08 16:38:44 +01001255
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001256 if (node->options & LOG_OPT_QUOTE) {
1257 *(dst++) = '"';
1258 size--;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001259 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001260
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001261 if (src && len) {
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001262 if (++len > size)
1263 len = size;
Dragan Dosen835b9212016-02-12 13:23:03 +01001264 if (node->options & LOG_OPT_ESC) {
Dragan Dosen835b9212016-02-12 13:23:03 +01001265 char *ret;
1266
Dragan Dosendb1b6f92016-07-25 11:35:02 +02001267 ret = escape_string(dst, dst + len, '\\', rfc5424_escape_map, src);
Dragan Dosen835b9212016-02-12 13:23:03 +01001268 if (ret == NULL || *ret != '\0')
1269 return NULL;
1270 len = ret - dst;
1271 }
1272 else {
Dragan Dosen835b9212016-02-12 13:23:03 +01001273 len = strlcpy2(dst, src, len);
1274 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001275
1276 size -= len;
1277 dst += len;
1278 }
Willy Tarreau6cbbdbf2013-02-05 18:52:25 +01001279 else if ((node->options & (LOG_OPT_QUOTE|LOG_OPT_MANDATORY)) == LOG_OPT_MANDATORY) {
1280 if (size < 2)
1281 return NULL;
1282 *(dst++) = '-';
1283 }
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001284
1285 if (node->options & LOG_OPT_QUOTE) {
1286 if (size < 2)
1287 return NULL;
1288 *(dst++) = '"';
1289 }
1290
1291 *dst = '\0';
William Lallemandbddd4fd2012-02-27 11:23:10 +01001292 return dst;
William Lallemanda1cc3812012-02-08 16:38:44 +01001293}
1294
Willy Tarreau26ffa852018-09-05 15:23:10 +02001295static inline char *lf_text(char *dst, const char *src, size_t size, const struct logformat_node *node)
Willy Tarreau2b0108a2012-12-21 19:23:44 +01001296{
1297 return lf_text_len(dst, src, size, size, node);
1298}
1299
William Lallemand5f232402012-04-05 18:02:55 +02001300/*
Joseph Herlant85b40592018-11-15 12:10:04 -08001301 * Write a IP address to the log string
William Lallemand5f232402012-04-05 18:02:55 +02001302 * +X option write in hexadecimal notation, most signifant byte on the left
1303 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001304char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001305{
1306 char *ret = dst;
1307 int iret;
1308 char pn[INET6_ADDRSTRLEN];
1309
1310 if (node->options & LOG_OPT_HEXA) {
Radek Zajic594c4562019-03-22 10:21:54 +00001311 unsigned char *addr = NULL;
1312 switch (sockaddr->sa_family) {
1313 case AF_INET:
1314 addr = (unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
1315 iret = snprintf(dst, size, "%02X%02X%02X%02X", addr[0], addr[1], addr[2], addr[3]);
1316 break;
1317 case AF_INET6:
1318 addr = (unsigned char *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr;
1319 iret = snprintf(dst, size, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
1320 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
1321 addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]);
1322 break;
1323 default:
1324 return NULL;
1325 }
William Lallemand5f232402012-04-05 18:02:55 +02001326 if (iret < 0 || iret > size)
1327 return NULL;
1328 ret += iret;
1329 } else {
1330 addr_to_str((struct sockaddr_storage *)sockaddr, pn, sizeof(pn));
1331 ret = lf_text(dst, pn, size, node);
1332 if (ret == NULL)
1333 return NULL;
1334 }
1335 return ret;
1336}
1337
1338/*
1339 * Write a port to the log
1340 * +X option write in hexadecimal notation, most signifant byte on the left
1341 */
Willy Tarreau26ffa852018-09-05 15:23:10 +02001342char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node)
William Lallemand5f232402012-04-05 18:02:55 +02001343{
1344 char *ret = dst;
1345 int iret;
1346
1347 if (node->options & LOG_OPT_HEXA) {
1348 const unsigned char *port = (const unsigned char *)&((struct sockaddr_in *)sockaddr)->sin_port;
1349 iret = snprintf(dst, size, "%02X%02X", port[0], port[1]);
1350 if (iret < 0 || iret > size)
1351 return NULL;
1352 ret += iret;
1353 } else {
1354 ret = ltoa_o(get_host_port((struct sockaddr_storage *)sockaddr), dst, size);
1355 if (ret == NULL)
1356 return NULL;
1357 }
1358 return ret;
1359}
1360
Dragan Dosen1322d092015-09-22 16:05:32 +02001361/* Re-generate time-based part of the syslog header in RFC3164 format at
1362 * the beginning of logheader once a second and return the pointer to the
1363 * first character after it.
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001364 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001365static char *update_log_hdr(const time_t time)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001366{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001367 static THREAD_LOCAL long tvsec;
Willy Tarreau83061a82018-07-13 11:56:34 +02001368 static THREAD_LOCAL struct buffer host = { };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001369 static THREAD_LOCAL int sep = 0;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001370
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001371 if (unlikely(time != tvsec || logheader_end == NULL)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +02001372 /* this string is rebuild only once a second */
Willy Tarreaufe944602007-10-25 10:34:16 +02001373 struct tm tm;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001374 int hdr_len;
Willy Tarreaufe944602007-10-25 10:34:16 +02001375
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001376 tvsec = time;
Willy Tarreaufe944602007-10-25 10:34:16 +02001377 get_localtime(tvsec, &tm);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001378
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001379 if (unlikely(global.log_send_hostname != host.area)) {
1380 host.area = global.log_send_hostname;
1381 host.data = host.area ? strlen(host.area) : 0;
1382 sep = host.data ? 1 : 0;
Dragan Dosen43885c72015-10-01 13:18:13 +02001383 }
1384
Dragan Dosen59cee972015-09-19 22:09:02 +02001385 hdr_len = snprintf(logheader, global.max_syslog_len,
Dragan Dosen43885c72015-10-01 13:18:13 +02001386 "<<<<>%s %2d %02d:%02d:%02d %.*s%*s",
Willy Tarreaufe944602007-10-25 10:34:16 +02001387 monthname[tm.tm_mon],
Dragan Dosen43885c72015-10-01 13:18:13 +02001388 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001389 (int)host.data, host.area, sep, "");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001390 /* WARNING: depending upon implementations, snprintf may return
1391 * either -1 or the number of bytes that would be needed to store
1392 * the total message. In both cases, we must adjust it.
1393 */
Willy Tarreau18324f52014-06-27 18:10:07 +02001394 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1395 hdr_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001396
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001397 logheader_end = logheader + hdr_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001398 }
1399
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001400 logheader_end[0] = 0; // ensure we get rid of any previous attempt
Willy Tarreau094af4e2015-01-07 15:03:42 +01001401
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001402 return logheader_end;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001403}
1404
Dragan Dosen1322d092015-09-22 16:05:32 +02001405/* Re-generate time-based part of the syslog header in RFC5424 format at
1406 * the beginning of logheader_rfc5424 once a second and return the pointer
1407 * to the first character after it.
1408 */
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001409static char *update_log_hdr_rfc5424(const time_t time)
Dragan Dosen1322d092015-09-22 16:05:32 +02001410{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001411 static THREAD_LOCAL long tvsec;
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001412 const char *gmt_offset;
Dragan Dosen1322d092015-09-22 16:05:32 +02001413
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001414 if (unlikely(time != tvsec || logheader_rfc5424_end == NULL)) {
Dragan Dosen1322d092015-09-22 16:05:32 +02001415 /* this string is rebuild only once a second */
1416 struct tm tm;
1417 int hdr_len;
1418
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001419 tvsec = time;
Dragan Dosen1322d092015-09-22 16:05:32 +02001420 get_localtime(tvsec, &tm);
Benoit GARNIERe2e5bde2016-03-27 03:04:16 +02001421 gmt_offset = get_gmt_offset(time, &tm);
Dragan Dosen1322d092015-09-22 16:05:32 +02001422
1423 hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len,
Dragan Dosen17def462015-10-09 21:31:43 +02001424 "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%.3s:%.2s %s ",
Dragan Dosen1322d092015-09-22 16:05:32 +02001425 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
Dragan Dosen17def462015-10-09 21:31:43 +02001426 tm.tm_hour, tm.tm_min, tm.tm_sec,
Benoit GARNIERb413c2a2016-03-27 11:08:03 +02001427 gmt_offset, gmt_offset+3,
Dragan Dosen43885c72015-10-01 13:18:13 +02001428 global.log_send_hostname ? global.log_send_hostname : hostname);
Dragan Dosen1322d092015-09-22 16:05:32 +02001429 /* WARNING: depending upon implementations, snprintf may return
1430 * either -1 or the number of bytes that would be needed to store
1431 * the total message. In both cases, we must adjust it.
1432 */
1433 if (hdr_len < 0 || hdr_len > global.max_syslog_len)
1434 hdr_len = global.max_syslog_len;
1435
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001436 logheader_rfc5424_end = logheader_rfc5424 + hdr_len;
Dragan Dosen1322d092015-09-22 16:05:32 +02001437 }
1438
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001439 logheader_rfc5424_end[0] = 0; // ensure we get rid of any previous attempt
Dragan Dosen1322d092015-09-22 16:05:32 +02001440
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001441 return logheader_rfc5424_end;
Dragan Dosen1322d092015-09-22 16:05:32 +02001442}
1443
William Lallemand2a4a44f2012-02-06 16:00:33 +01001444/*
Dragan Dosen59cee972015-09-19 22:09:02 +02001445 * This function sends the syslog message using a printf format string. It
1446 * expects an LF-terminated message.
William Lallemand2a4a44f2012-02-06 16:00:33 +01001447 */
1448void send_log(struct proxy *p, int level, const char *format, ...)
1449{
1450 va_list argp;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001451 int data_len;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001452
Willy Tarreau8c97ab52015-01-15 16:29:53 +01001453 if (level < 0 || format == NULL || logline == NULL)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001454 return;
1455
William Lallemand2a4a44f2012-02-06 16:00:33 +01001456 va_start(argp, format);
Dragan Dosen59cee972015-09-19 22:09:02 +02001457 data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
Willy Tarreau18324f52014-06-27 18:10:07 +02001458 if (data_len < 0 || data_len > global.max_syslog_len)
1459 data_len = global.max_syslog_len;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001460 va_end(argp);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001461
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001462 __send_log(p, level, logline, data_len, default_rfc5424_sd_log_format, 2);
William Lallemand2a4a44f2012-02-06 16:00:33 +01001463}
1464
1465/*
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001466 * This function sends a syslog message to <logsrv>.
1467 * <pid_str> is the string to be used for the PID of the caller, <pid_size> is length.
1468 * Same thing for <sd> and <sd_size> which are used for the structured-data part
1469 * in RFC5424 formatted syslog messages, and <tag_str> and <tag_size> the syslog tag.
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001470 * It overrides the last byte of the message vector with an LF character.
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001471 * Does not return any error,
William Lallemand2a4a44f2012-02-06 16:00:33 +01001472 */
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001473static inline void __do_send_log(struct logsrv *logsrv, int nblogger, char *pid_str, size_t pid_size,
1474 int level, char *message, size_t size, char *sd, size_t sd_size,
1475 char *tag_str, size_t tag_size)
William Lallemand2a4a44f2012-02-06 16:00:33 +01001476{
Christopher Fauletf8188c62017-06-02 16:20:16 +02001477 static THREAD_LOCAL struct iovec iovec[NB_MSG_IOVEC_ELEMENTS] = { };
1478 static THREAD_LOCAL struct msghdr msghdr = {
1479 //.msg_iov = iovec,
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001480 .msg_iovlen = NB_MSG_IOVEC_ELEMENTS
1481 };
Christopher Fauletf8188c62017-06-02 16:20:16 +02001482 static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
1483 static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
1484 static THREAD_LOCAL char *dataptr = NULL;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001485 time_t time = date.tv_sec;
Dragan Dosen1322d092015-09-22 16:05:32 +02001486 char *hdr, *hdr_ptr;
Dragan Dosen59cee972015-09-19 22:09:02 +02001487 size_t hdr_size;
Willy Tarreau83061a82018-07-13 11:56:34 +02001488 struct buffer *tag = &global.log_tag;
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001489 int fac_level;
1490 int *plogfd;
1491 char *pid_sep1 = "", *pid_sep2 = "";
1492 char logheader_short[3];
1493 int sent;
1494 int maxlen;
1495 int hdr_max = 0;
1496 int tag_max = 0;
1497 int pid_sep1_max = 0;
1498 int pid_sep2_max = 0;
1499 int sd_max = 0;
1500 int max = 0;
Christopher Fauletf8188c62017-06-02 16:20:16 +02001501
1502 msghdr.msg_iov = iovec;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001503
1504 dataptr = message;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001505
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001506 if (logsrv->addr.ss_family == AF_UNSPEC) {
1507 /* the socket's address is a file descriptor */
1508 plogfd = (int *)&((struct sockaddr_in *)&logsrv->addr)->sin_addr.s_addr;
1509 if (unlikely(!((struct sockaddr_in *)&logsrv->addr)->sin_port)) {
1510 /* FD not yet initialized to non-blocking mode.
1511 * DON'T DO IT ON A TERMINAL!
1512 */
1513 if (!isatty(*plogfd))
1514 fcntl(*plogfd, F_SETFL, O_NONBLOCK);
1515 ((struct sockaddr_in *)&logsrv->addr)->sin_port = 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001516 }
Robert Tsai81ae1952007-12-05 10:47:29 +01001517 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001518 else if (logsrv->addr.ss_family == AF_UNIX)
1519 plogfd = &logfdunix;
1520 else
1521 plogfd = &logfdinet;
Robert Tsai81ae1952007-12-05 10:47:29 +01001522
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001523 if (unlikely(*plogfd < 0)) {
1524 /* socket not successfully initialized yet */
1525 if ((*plogfd = socket(logsrv->addr.ss_family, SOCK_DGRAM,
1526 (logsrv->addr.ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
1527 static char once;
William Lallemand0f99e342011-10-12 17:50:54 +02001528
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001529 if (!once) {
1530 once = 1; /* note: no need for atomic ops here */
1531 ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
1532 nblogger, strerror(errno), errno);
1533 }
1534 return;
1535 } else {
1536 /* we don't want to receive anything on this socket */
1537 setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
1538 /* does nothing under Linux, maybe needed for others */
1539 shutdown(*plogfd, SHUT_RD);
1540 fcntl(*plogfd, F_SETFD, fcntl(*plogfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1541 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001542 }
1543
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001544 switch (logsrv->format) {
1545 case LOG_FORMAT_RFC3164:
1546 hdr = logheader;
1547 hdr_ptr = update_log_hdr(time);
1548 break;
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001549
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001550 case LOG_FORMAT_RFC5424:
1551 hdr = logheader_rfc5424;
1552 hdr_ptr = update_log_hdr_rfc5424(time);
1553 sd_max = sd_size; /* the SD part allowed only in RFC5424 */
1554 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001555
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001556 case LOG_FORMAT_SHORT:
1557 /* all fields are known, skip the header generation */
1558 hdr = logheader_short;
1559 hdr[0] = '<';
1560 hdr[1] = '0' + MAX(level, logsrv->minlvl);
1561 hdr[2] = '>';
1562 hdr_ptr = hdr;
1563 hdr_max = 3;
1564 maxlen = logsrv->maxlen - hdr_max;
1565 max = MIN(size, maxlen) - 1;
1566 goto send;
Willy Tarreau204e3f12018-12-15 15:48:48 +01001567
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001568 case LOG_FORMAT_RAW:
1569 /* all fields are known, skip the header generation */
1570 hdr_ptr = hdr = "";
1571 hdr_max = 0;
1572 maxlen = logsrv->maxlen;
1573 max = MIN(size, maxlen) - 1;
1574 goto send;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001575
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001576 default:
1577 return; /* must never happen */
1578 }
Willy Tarreauc7c7be22014-06-23 18:07:15 +02001579
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001580 hdr_size = hdr_ptr - hdr;
Dragan Dosen1322d092015-09-22 16:05:32 +02001581
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001582 /* For each target, we may have a different facility.
1583 * We can also have a different log level for each message.
1584 * This induces variations in the message header length.
1585 * Since we don't want to recompute it each time, nor copy it every
1586 * time, we only change the facility in the pre-computed header,
1587 * and we change the pointer to the header accordingly.
1588 */
1589 fac_level = (logsrv->facility << 3) + MAX(level, logsrv->minlvl);
1590 hdr_ptr = hdr + 3; /* last digit of the log level */
1591 do {
1592 *hdr_ptr = '0' + fac_level % 10;
1593 fac_level /= 10;
1594 hdr_ptr--;
1595 } while (fac_level && hdr_ptr > hdr);
1596 *hdr_ptr = '<';
Dragan Dosen1322d092015-09-22 16:05:32 +02001597
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001598 hdr_max = hdr_size - (hdr_ptr - hdr);
Willy Tarreaue8746a02018-11-12 08:45:00 +01001599
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001600 /* time-based header */
1601 if (unlikely(hdr_size >= logsrv->maxlen)) {
1602 hdr_max = MIN(hdr_max, logsrv->maxlen) - 1;
1603 sd_max = 0;
1604 goto send;
1605 }
Willy Tarreauc1b06452018-11-12 11:57:56 +01001606
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001607 maxlen = logsrv->maxlen - hdr_max;
Dragan Dosen1322d092015-09-22 16:05:32 +02001608
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001609 /* tag */
1610 tag_max = tag->data;
1611 if (unlikely(tag_max >= maxlen)) {
1612 tag_max = maxlen - 1;
1613 sd_max = 0;
1614 goto send;
1615 }
Dragan Dosen1322d092015-09-22 16:05:32 +02001616
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001617 maxlen -= tag_max;
William Lallemand2a4a44f2012-02-06 16:00:33 +01001618
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001619 /* first pid separator */
1620 pid_sep1_max = log_formats[logsrv->format].pid.sep1.data;
1621 if (unlikely(pid_sep1_max >= maxlen)) {
1622 pid_sep1_max = maxlen - 1;
1623 sd_max = 0;
1624 goto send;
1625 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001626
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001627 pid_sep1 = log_formats[logsrv->format].pid.sep1.area;
1628 maxlen -= pid_sep1_max;
Dragan Dosen59cee972015-09-19 22:09:02 +02001629
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001630 /* pid */
1631 if (unlikely(pid_size >= maxlen)) {
1632 pid_size = maxlen - 1;
1633 sd_max = 0;
1634 goto send;
1635 }
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001636
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001637 maxlen -= pid_size;
Dragan Dosen43885c72015-10-01 13:18:13 +02001638
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001639 /* second pid separator */
1640 pid_sep2_max = log_formats[logsrv->format].pid.sep2.data;
1641 if (unlikely(pid_sep2_max >= maxlen)) {
1642 pid_sep2_max = maxlen - 1;
1643 sd_max = 0;
1644 goto send;
1645 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001646
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001647 pid_sep2 = log_formats[logsrv->format].pid.sep2.area;
1648 maxlen -= pid_sep2_max;
Dragan Dosen68d2e3a2015-09-19 22:35:44 +02001649
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001650 /* structured-data */
1651 if (sd_max >= maxlen) {
1652 sd_max = maxlen - 1;
1653 goto send;
1654 }
Dragan Dosen59cee972015-09-19 22:09:02 +02001655
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001656 max = MIN(size, maxlen - sd_max) - 1;
1657send:
1658 iovec[0].iov_base = hdr_ptr;
1659 iovec[0].iov_len = hdr_max;
1660 iovec[1].iov_base = tag_str;
1661 iovec[1].iov_len = tag_size;
1662 iovec[2].iov_base = pid_sep1;
1663 iovec[2].iov_len = pid_sep1_max;
1664 iovec[3].iov_base = pid_str;
1665 iovec[3].iov_len = pid_size;
1666 iovec[4].iov_base = pid_sep2;
1667 iovec[4].iov_len = pid_sep2_max;
1668 iovec[5].iov_base = sd;
1669 iovec[5].iov_len = sd_max;
1670 iovec[6].iov_base = dataptr;
1671 iovec[6].iov_len = max;
1672 iovec[7].iov_base = "\n"; /* insert a \n at the end of the message */
1673 iovec[7].iov_len = 1;
Dragan Dosen43885c72015-10-01 13:18:13 +02001674
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001675 if (logsrv->addr.ss_family == AF_UNSPEC) {
1676 /* the target is a direct file descriptor */
1677 sent = writev(*plogfd, iovec, 8);
1678 }
1679 else {
1680 msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
1681 msghdr.msg_namelen = get_addr_len(&logsrv->addr);
Dragan Dosen43885c72015-10-01 13:18:13 +02001682
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001683 sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
1684 }
Dragan Dosen43885c72015-10-01 13:18:13 +02001685
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001686 if (sent < 0) {
1687 static char once;
Dragan Dosen43885c72015-10-01 13:18:13 +02001688
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001689 if (errno == EAGAIN)
1690 _HA_ATOMIC_ADD(&dropped_logs, 1);
1691 else if (!once) {
1692 once = 1; /* note: no need for atomic ops here */
1693 ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
1694 nblogger, strerror(errno), errno);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02001695 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001696 }
1697}
Dragan Dosen59cee972015-09-19 22:09:02 +02001698
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001699/*
1700 * This function sends a syslog message.
1701 * It doesn't care about errors nor does it report them.
1702 * The arguments <sd> and <sd_size> are used for the structured-data part
1703 * in RFC5424 formatted syslog messages.
1704 */
1705void __send_log(struct proxy *p, int level, char *message, size_t size, char *sd, size_t sd_size)
1706{
1707 struct list *logsrvs = NULL;
1708 struct logsrv *logsrv;
1709 int nblogger;
1710 static THREAD_LOCAL int curr_pid;
1711 static THREAD_LOCAL char pidstr[100];
1712 static THREAD_LOCAL struct buffer pid;
1713 struct buffer *tag = &global.log_tag;
Dragan Dosen609ac2a2015-09-16 18:25:42 +02001714
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001715 if (p == NULL) {
1716 if (!LIST_ISEMPTY(&global.logsrvs)) {
1717 logsrvs = &global.logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001718 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001719 } else {
1720 if (!LIST_ISEMPTY(&p->logsrvs)) {
1721 logsrvs = &p->logsrvs;
Willy Tarreau5a32ecc2018-11-12 07:34:59 +01001722 }
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001723 if (p->log_tag.area) {
1724 tag = &p->log_tag;
1725 }
1726 }
Willy Tarreau18324f52014-06-27 18:10:07 +02001727
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001728 if (!logsrvs)
1729 return;
Willy Tarreauc98aebc2018-03-20 11:17:29 +01001730
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001731 if (unlikely(curr_pid != getpid())) {
1732 curr_pid = getpid();
1733 ltoa_o(curr_pid, pidstr, sizeof(pidstr));
1734 chunk_initstr(&pid, pidstr);
1735 }
1736
1737 /* Send log messages to syslog server. */
1738 nblogger = 0;
1739 list_for_each_entry(logsrv, logsrvs, list) {
Frédéric Lécailled803e472019-04-25 07:42:09 +02001740 static THREAD_LOCAL int in_range = 1;
1741
Frédéric Lécaille0bad8402019-04-10 08:22:17 +02001742 /* we can filter the level of the messages that are sent to each logger */
1743 if (level > logsrv->level)
1744 continue;
1745
Frédéric Lécailled803e472019-04-25 07:42:09 +02001746 if (logsrv->lb.smp_rgs) {
1747 struct smp_log_range *curr_rg;
1748
1749 HA_SPIN_LOCK(LOGSRV_LOCK, &logsrv->lock);
1750 curr_rg = &logsrv->lb.smp_rgs[logsrv->lb.curr_rg];
1751 in_range = in_smp_log_range(curr_rg, logsrv->lb.curr_idx);
1752 if (in_range) {
1753 /* Let's consume this range. */
1754 curr_rg->curr_idx = (curr_rg->curr_idx + 1) % curr_rg->sz;
1755 if (!curr_rg->curr_idx) {
1756 /* If consumed, let's select the next range. */
1757 logsrv->lb.curr_rg = (logsrv->lb.curr_rg + 1) % logsrv->lb.smp_rgs_sz;
1758 }
1759 }
1760 logsrv->lb.curr_idx = (logsrv->lb.curr_idx + 1) % logsrv->lb.smp_sz;
1761 HA_SPIN_UNLOCK(LOGSRV_LOCK, &logsrv->lock);
1762 }
1763 if (in_range)
1764 __do_send_log(logsrv, ++nblogger, pid.area, pid.data, level,
1765 message, size, sd, sd_size, tag->area, tag->data);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001766 }
1767}
1768
William Lallemandbddd4fd2012-02-27 11:23:10 +01001769extern fd_set hdr_encode_map[];
1770extern fd_set url_encode_map[];
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01001771extern fd_set http_encode_map[];
William Lallemandbddd4fd2012-02-27 11:23:10 +01001772
Willy Tarreaubaaee002006-06-26 02:48:02 +02001773
Willy Tarreauc89ccb62012-04-05 21:18:22 +02001774const 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 +01001775const char sess_set_cookie[8] = "NPDIRU67"; /* No set-cookie, Set-cookie found and left unchanged (passive),
1776 Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
1777 Set-cookie Updated, unknown, unknown */
1778
William Lallemand1d705562012-03-12 12:46:41 +01001779/*
1780 * try to write a character if there is enough space, or goto out
1781 */
William Lallemandbddd4fd2012-02-27 11:23:10 +01001782#define LOGCHAR(x) do { \
William Lallemand1d705562012-03-12 12:46:41 +01001783 if (tmplog < dst + maxsize - 1) { \
William Lallemandbddd4fd2012-02-27 11:23:10 +01001784 *(tmplog++) = (x); \
1785 } else { \
1786 goto out; \
1787 } \
1788 } while(0)
1789
Dragan Dosen835b9212016-02-12 13:23:03 +01001790
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001791/* Initializes some log data at boot */
1792static void init_log()
Dragan Dosen835b9212016-02-12 13:23:03 +01001793{
1794 char *tmp;
Willy Tarreaue10cd482018-09-10 18:16:53 +02001795 int i;
Dragan Dosen835b9212016-02-12 13:23:03 +01001796
1797 /* Initialize the escape map for the RFC5424 structured-data : '"\]'
1798 * inside PARAM-VALUE should be escaped with '\' as prefix.
1799 * See https://tools.ietf.org/html/rfc5424#section-6.3.3 for more
1800 * details.
1801 */
1802 memset(rfc5424_escape_map, 0, sizeof(rfc5424_escape_map));
1803
1804 tmp = "\"\\]";
1805 while (*tmp) {
1806 FD_SET(*tmp, rfc5424_escape_map);
1807 tmp++;
1808 }
Willy Tarreaue10cd482018-09-10 18:16:53 +02001809
1810 /* initialize the log header encoding map : '{|}"#' should be encoded with
1811 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
1812 * URL encoding only requires '"', '#' to be encoded as well as non-
1813 * printable characters above.
1814 */
1815 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
1816 memset(url_encode_map, 0, sizeof(url_encode_map));
1817 for (i = 0; i < 32; i++) {
1818 FD_SET(i, hdr_encode_map);
1819 FD_SET(i, url_encode_map);
1820 }
1821 for (i = 127; i < 256; i++) {
1822 FD_SET(i, hdr_encode_map);
1823 FD_SET(i, url_encode_map);
1824 }
1825
1826 tmp = "\"#{|}";
1827 while (*tmp) {
1828 FD_SET(*tmp, hdr_encode_map);
1829 tmp++;
1830 }
1831
1832 tmp = "\"#";
1833 while (*tmp) {
1834 FD_SET(*tmp, url_encode_map);
1835 tmp++;
1836 }
1837
1838 /* initialize the http header encoding map. The draft httpbis define the
1839 * header content as:
1840 *
1841 * HTTP-message = start-line
1842 * *( header-field CRLF )
1843 * CRLF
1844 * [ message-body ]
1845 * header-field = field-name ":" OWS field-value OWS
1846 * field-value = *( field-content / obs-fold )
1847 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1848 * obs-fold = CRLF 1*( SP / HTAB )
1849 * field-vchar = VCHAR / obs-text
1850 * VCHAR = %x21-7E
1851 * obs-text = %x80-FF
1852 *
1853 * All the chars are encoded except "VCHAR", "obs-text", SP and HTAB.
1854 * The encoded chars are form 0x00 to 0x08, 0x0a to 0x1f and 0x7f. The
Joseph Herlant85b40592018-11-15 12:10:04 -08001855 * "obs-fold" is voluntarily forgotten because haproxy remove this.
Willy Tarreaue10cd482018-09-10 18:16:53 +02001856 */
1857 memset(http_encode_map, 0, sizeof(http_encode_map));
1858 for (i = 0x00; i <= 0x08; i++)
1859 FD_SET(i, http_encode_map);
1860 for (i = 0x0a; i <= 0x1f; i++)
1861 FD_SET(i, http_encode_map);
1862 FD_SET(0x7f, http_encode_map);
Dragan Dosen835b9212016-02-12 13:23:03 +01001863}
William Lallemand1d705562012-03-12 12:46:41 +01001864
Willy Tarreaub6b3df32018-11-26 16:31:20 +01001865INITCALL0(STG_PREPARE, init_log);
1866
Christopher Fauletcd7879a2017-10-27 13:53:47 +02001867static int init_log_buffers_per_thread()
1868{
1869 return init_log_buffers();
1870}
1871
1872static void deinit_log_buffers_per_thread()
1873{
1874 deinit_log_buffers();
1875}
1876
Christopher Faulet0132d062017-07-26 15:33:35 +02001877/* Initialize log buffers used for syslog messages */
1878int init_log_buffers()
1879{
1880 logheader = my_realloc2(logheader, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001881 logheader_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001882 logheader_rfc5424 = my_realloc2(logheader_rfc5424, global.max_syslog_len + 1);
Willy Tarreau55e2f5a2019-05-05 10:11:39 +02001883 logheader_rfc5424_end = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001884 logline = my_realloc2(logline, global.max_syslog_len + 1);
1885 logline_rfc5424 = my_realloc2(logline_rfc5424, global.max_syslog_len + 1);
1886 if (!logheader || !logline_rfc5424 || !logline || !logline_rfc5424)
1887 return 0;
1888 return 1;
1889}
1890
1891/* Deinitialize log buffers used for syslog messages */
1892void deinit_log_buffers()
1893{
Olivier Houchard7c497112019-03-07 14:19:24 +01001894 void *tmp_startup_logs;
1895
Christopher Faulet0132d062017-07-26 15:33:35 +02001896 free(logheader);
1897 free(logheader_rfc5424);
1898 free(logline);
1899 free(logline_rfc5424);
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01001900 tmp_startup_logs = _HA_ATOMIC_XCHG(&startup_logs, NULL);
Olivier Houchard7c497112019-03-07 14:19:24 +01001901 free(tmp_startup_logs);
1902
Christopher Faulet0132d062017-07-26 15:33:35 +02001903 logheader = NULL;
1904 logheader_rfc5424 = NULL;
1905 logline = NULL;
1906 logline_rfc5424 = NULL;
Christopher Fauletd4696382017-10-24 11:44:05 +02001907 startup_logs = NULL;
Christopher Faulet0132d062017-07-26 15:33:35 +02001908}
1909
Willy Tarreaudf974472012-12-28 02:44:01 +01001910/* Builds a log line in <dst> based on <list_format>, and stops before reaching
1911 * <maxsize> characters. Returns the size of the output string in characters,
1912 * not counting the trailing zero which is always added if the resulting size
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001913 * is not zero. It requires a valid session and optionally a stream. If the
1914 * stream is NULL, default values will be assumed for the stream part.
Willy Tarreaudf974472012-12-28 02:44:01 +01001915 */
Willy Tarreau43c538e2018-09-05 14:58:15 +02001916int 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 +02001917{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02001918 struct proxy *fe = sess->fe;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001919 struct proxy *be;
1920 struct http_txn *txn;
1921 const struct strm_logs *logs;
1922 const struct connection *be_conn;
1923 unsigned int s_flags;
1924 unsigned int uniq_id;
Willy Tarreau83061a82018-07-13 11:56:34 +02001925 struct buffer chunk;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001926 char *uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001927 char *spc;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00001928 char *qmark;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001929 char *end;
Willy Tarreaufe944602007-10-25 10:34:16 +02001930 struct tm tm;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001931 int t_request;
1932 int hdr;
1933 int last_isspace = 1;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00001934 int nspaces = 0;
Willy Tarreaub1a2faf2012-03-19 16:51:53 +01001935 char *tmplog;
William Lallemand1d705562012-03-12 12:46:41 +01001936 char *ret;
1937 int iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001938 struct logformat_node *tmp;
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02001939 struct timeval tv;
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001940 struct strm_logs tmp_strm_log;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001941
William Lallemandbddd4fd2012-02-27 11:23:10 +01001942 /* FIXME: let's limit ourselves to frontend logging for now. */
Willy Tarreaubaaee002006-06-26 02:48:02 +02001943
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001944 if (likely(s)) {
1945 be = s->be;
1946 txn = s->txn;
1947 be_conn = cs_conn(objt_cs(s->si[1].end));
1948 s_flags = s->flags;
1949 uniq_id = s->uniq_id;
1950 logs = &s->logs;
1951 } else {
1952 /* we have no stream so we first need to initialize a few
1953 * things that are needed later. We do increment the request
1954 * ID so that it's uniquely assigned to this request just as
1955 * if the request had reached the point of being processed.
1956 * A request error is reported as it's the only element we have
1957 * here and which justifies emitting such a log.
1958 */
1959 be = fe;
1960 txn = NULL;
1961 be_conn = NULL;
1962 s_flags = SF_ERR_PRXCOND | SF_FINST_R;
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01001963 uniq_id = _HA_ATOMIC_XADD(&global.req_count, 1);
Willy Tarreau09bb27c2018-09-05 16:55:15 +02001964
1965 /* prepare a valid log structure */
1966 tmp_strm_log.tv_accept = sess->tv_accept;
1967 tmp_strm_log.accept_date = sess->accept_date;
1968 tmp_strm_log.t_handshake = sess->t_handshake;
1969 tmp_strm_log.t_idle = tv_ms_elapsed(&sess->tv_accept, &now) - sess->t_handshake;
1970 tv_zero(&tmp_strm_log.tv_request);
1971 tmp_strm_log.t_queue = -1;
1972 tmp_strm_log.t_connect = -1;
1973 tmp_strm_log.t_data = -1;
1974 tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
1975 tmp_strm_log.bytes_in = 0;
1976 tmp_strm_log.bytes_out = 0;
1977 tmp_strm_log.prx_queue_pos = 0;
1978 tmp_strm_log.srv_queue_pos = 0;
1979
1980 logs = &tmp_strm_log;
1981 }
1982
William Lallemandbddd4fd2012-02-27 11:23:10 +01001983 t_request = -1;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02001984 if (tv_isge(&logs->tv_request, &logs->tv_accept))
1985 t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);
William Lallemandbddd4fd2012-02-27 11:23:10 +01001986
William Lallemand1d705562012-03-12 12:46:41 +01001987 tmplog = dst;
Willy Tarreauc9bd0cc2009-05-10 11:57:02 +02001988
William Lallemandbddd4fd2012-02-27 11:23:10 +01001989 /* fill logbuffer */
William Lallemand1d705562012-03-12 12:46:41 +01001990 if (LIST_ISEMPTY(list_format))
1991 return 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01001992
William Lallemand1d705562012-03-12 12:46:41 +01001993 list_for_each_entry(tmp, list_format, list) {
Willy Tarreaub363a1f2013-10-01 10:45:07 +02001994 struct connection *conn;
Willy Tarreau4f653562012-10-12 19:48:16 +02001995 const char *src = NULL;
Willy Tarreauc8368452012-12-21 00:09:23 +01001996 struct sample *key;
Willy Tarreau83061a82018-07-13 11:56:34 +02001997 const struct buffer empty = { };
William Lallemandbddd4fd2012-02-27 11:23:10 +01001998
Willy Tarreauc8368452012-12-21 00:09:23 +01001999 switch (tmp->type) {
William Lallemand1d705562012-03-12 12:46:41 +01002000 case LOG_FMT_SEPARATOR:
William Lallemandbddd4fd2012-02-27 11:23:10 +01002001 if (!last_isspace) {
2002 LOGCHAR(' ');
2003 last_isspace = 1;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002004 }
2005 break;
2006
William Lallemand1d705562012-03-12 12:46:41 +01002007 case LOG_FMT_TEXT: // text
William Lallemandbddd4fd2012-02-27 11:23:10 +01002008 src = tmp->arg;
William Lallemand5f232402012-04-05 18:02:55 +02002009 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002010 if (iret == 0)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002011 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002012 tmplog += iret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002013 last_isspace = 0;
2014 break;
2015
Willy Tarreauc8368452012-12-21 00:09:23 +01002016 case LOG_FMT_EXPR: // sample expression, may be request or response
2017 key = NULL;
Olivier Houchardf90db442018-12-15 14:00:06 +01002018 if (tmp->options & LOG_OPT_REQ_CAP && s)
Adis Nezirovic79beb242015-07-06 15:41:02 +02002019 key = sample_fetch_as_type(be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, tmp->expr, SMP_T_STR);
Olivier Houchardf90db442018-12-15 14:00:06 +01002020 if (!key && (tmp->options & LOG_OPT_RES_CAP) && s)
Adis Nezirovic79beb242015-07-06 15:41:02 +02002021 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 +01002022 if (tmp->options & LOG_OPT_HTTP)
Dragan Dosen835b9212016-02-12 13:23:03 +01002023 ret = lf_encode_chunk(tmplog, dst + maxsize,
2024 '%', http_encode_map, key ? &key->data.u.str : &empty, tmp);
Thierry FOURNIERd048d8b2014-03-13 16:46:18 +01002025 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002026 ret = lf_text_len(tmplog,
2027 key ? key->data.u.str.area : NULL,
2028 key ? key->data.u.str.data : 0,
2029 dst + maxsize - tmplog,
2030 tmp);
Willy Tarreauc8368452012-12-21 00:09:23 +01002031 if (ret == 0)
2032 goto out;
2033 tmplog = ret;
2034 last_isspace = 0;
2035 break;
2036
Willy Tarreau2beef582012-12-20 17:22:52 +01002037 case LOG_FMT_CLIENTIP: // %ci
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002038 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002039 if (conn)
2040 ret = lf_ip(tmplog, (struct sockaddr *)&conn->addr.from, dst + maxsize - tmplog, tmp);
2041 else
2042 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002043 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002044 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002045 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002046 last_isspace = 0;
2047 break;
2048
Willy Tarreau2beef582012-12-20 17:22:52 +01002049 case LOG_FMT_CLIENTPORT: // %cp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002050 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002051 if (conn) {
2052 if (conn->addr.from.ss_family == AF_UNIX) {
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002053 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002054 } else {
2055 ret = lf_port(tmplog, (struct sockaddr *)&conn->addr.from,
2056 dst + maxsize - tmplog, tmp);
2057 }
William Lallemand5f232402012-04-05 18:02:55 +02002058 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002059 else
2060 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2061
William Lallemand5f232402012-04-05 18:02:55 +02002062 if (ret == NULL)
2063 goto out;
2064 tmplog = ret;
2065 last_isspace = 0;
2066 break;
2067
Willy Tarreau2beef582012-12-20 17:22:52 +01002068 case LOG_FMT_FRONTENDIP: // %fi
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002069 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002070 if (conn) {
2071 conn_get_to_addr(conn);
2072 ret = lf_ip(tmplog, (struct sockaddr *)&conn->addr.to, dst + maxsize - tmplog, tmp);
2073 }
2074 else
2075 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2076
William Lallemand1d705562012-03-12 12:46:41 +01002077 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002078 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002079 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002080 last_isspace = 0;
2081 break;
2082
Willy Tarreau2beef582012-12-20 17:22:52 +01002083 case LOG_FMT_FRONTENDPORT: // %fp
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002084 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002085 if (conn) {
2086 conn_get_to_addr(conn);
2087 if (conn->addr.to.ss_family == AF_UNIX)
Willy Tarreaufb0afa72015-04-03 14:46:27 +02002088 ret = ltoa_o(sess->listener->luid, tmplog, dst + maxsize - tmplog);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002089 else
2090 ret = lf_port(tmplog, (struct sockaddr *)&conn->addr.to, dst + maxsize - tmplog, tmp);
William Lallemand5f232402012-04-05 18:02:55 +02002091 }
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002092 else
2093 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2094
William Lallemand5f232402012-04-05 18:02:55 +02002095 if (ret == NULL)
2096 goto out;
2097 tmplog = ret;
2098 last_isspace = 0;
2099 break;
2100
Willy Tarreau2beef582012-12-20 17:22:52 +01002101 case LOG_FMT_BACKENDIP: // %bi
Willy Tarreau2393c5b2018-09-05 15:24:56 +02002102 if (be_conn)
2103 ret = lf_ip(tmplog, (const struct sockaddr *)&be_conn->addr.from, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002104 else
2105 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2106
William Lallemand1d705562012-03-12 12:46:41 +01002107 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002108 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002109 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002110 last_isspace = 0;
2111 break;
2112
Willy Tarreau2beef582012-12-20 17:22:52 +01002113 case LOG_FMT_BACKENDPORT: // %bp
Willy Tarreau2393c5b2018-09-05 15:24:56 +02002114 if (be_conn)
2115 ret = lf_port(tmplog, (struct sockaddr *)&be_conn->addr.from, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002116 else
2117 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2118
William Lallemand5f232402012-04-05 18:02:55 +02002119 if (ret == NULL)
2120 goto out;
2121 tmplog = ret;
2122 last_isspace = 0;
2123 break;
2124
Willy Tarreau2beef582012-12-20 17:22:52 +01002125 case LOG_FMT_SERVERIP: // %si
Willy Tarreau2393c5b2018-09-05 15:24:56 +02002126 if (be_conn)
2127 ret = lf_ip(tmplog, (struct sockaddr *)&be_conn->addr.to, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002128 else
2129 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2130
William Lallemand5f232402012-04-05 18:02:55 +02002131 if (ret == NULL)
2132 goto out;
2133 tmplog = ret;
2134 last_isspace = 0;
2135 break;
2136
Willy Tarreau2beef582012-12-20 17:22:52 +01002137 case LOG_FMT_SERVERPORT: // %sp
Willy Tarreau2393c5b2018-09-05 15:24:56 +02002138 if (be_conn)
2139 ret = lf_port(tmplog, (struct sockaddr *)&be_conn->addr.to, dst + maxsize - tmplog, tmp);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002140 else
2141 ret = lf_text_len(tmplog, NULL, 0, dst + maxsize - tmplog, tmp);
2142
William Lallemand1d705562012-03-12 12:46:41 +01002143 if (ret == NULL)
William Lallemandb7ff6a32012-03-02 14:35:21 +01002144 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002145 tmplog = ret;
William Lallemandb7ff6a32012-03-02 14:35:21 +01002146 last_isspace = 0;
2147 break;
2148
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002149 case LOG_FMT_DATE: // %t = accept date
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002150 get_localtime(logs->accept_date.tv_sec, &tm);
2151 ret = date2str_log(tmplog, &tm, &logs->accept_date, dst + maxsize - tmplog);
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
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002158 case LOG_FMT_tr: // %tr = start of request date
2159 /* Note that the timers are valid if we get here */
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002160 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 +02002161 get_localtime(tv.tv_sec, &tm);
2162 ret = date2str_log(tmplog, &tm, &tv, dst + maxsize - tmplog);
2163 if (ret == NULL)
2164 goto out;
2165 tmplog = ret;
2166 last_isspace = 0;
2167 break;
2168
2169 case LOG_FMT_DATEGMT: // %T = accept date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002170 get_gmtime(logs->accept_date.tv_sec, &tm);
William Lallemand5f232402012-04-05 18:02:55 +02002171 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002172 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002173 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002174 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002175 last_isspace = 0;
2176 break;
2177
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002178 case LOG_FMT_trg: // %trg = start of request date, GMT
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002179 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 +02002180 get_gmtime(tv.tv_sec, &tm);
2181 ret = gmt2str_log(tmplog, &tm, dst + maxsize - tmplog);
2182 if (ret == NULL)
2183 goto out;
2184 tmplog = ret;
2185 last_isspace = 0;
2186 break;
2187
2188 case LOG_FMT_DATELOCAL: // %Tl = accept date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002189 get_localtime(logs->accept_date.tv_sec, &tm);
2190 ret = localdate2str_log(tmplog, logs->accept_date.tv_sec, &tm, dst + maxsize - tmplog);
Yuxans Yao4e25b012012-10-19 10:36:09 +08002191 if (ret == NULL)
2192 goto out;
2193 tmplog = ret;
2194 last_isspace = 0;
2195 break;
2196
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002197 case LOG_FMT_trl: // %trl = start of request date, local
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002198 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 +02002199 get_localtime(tv.tv_sec, &tm);
2200 ret = localdate2str_log(tmplog, tv.tv_sec, &tm, dst + maxsize - tmplog);
2201 if (ret == NULL)
2202 goto out;
2203 tmplog = ret;
2204 last_isspace = 0;
2205 break;
2206
William Lallemand5f232402012-04-05 18:02:55 +02002207 case LOG_FMT_TS: // %Ts
William Lallemand5f232402012-04-05 18:02:55 +02002208 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002209 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", (unsigned int)logs->accept_date.tv_sec);
William Lallemand5f232402012-04-05 18:02:55 +02002210 if (iret < 0 || iret > dst + maxsize - tmplog)
2211 goto out;
2212 last_isspace = 0;
2213 tmplog += iret;
2214 } else {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002215 ret = ltoa_o(logs->accept_date.tv_sec, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002216 if (ret == NULL)
2217 goto out;
2218 tmplog = ret;
2219 last_isspace = 0;
2220 }
2221 break;
2222
William Lallemand1d705562012-03-12 12:46:41 +01002223 case LOG_FMT_MS: // %ms
William Lallemand5f232402012-04-05 18:02:55 +02002224 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002225 iret = snprintf(tmplog, dst + maxsize - tmplog, "%02X",(unsigned int)logs->accept_date.tv_usec/1000);
William Lallemand5f232402012-04-05 18:02:55 +02002226 if (iret < 0 || iret > dst + maxsize - tmplog)
2227 goto out;
2228 last_isspace = 0;
2229 tmplog += iret;
2230 } else {
2231 if ((dst + maxsize - tmplog) < 4)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002232 goto out;
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002233 ret = utoa_pad((unsigned int)logs->accept_date.tv_usec/1000,
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002234 tmplog, 4);
2235 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002236 goto out;
Willy Tarreau9e60cd82013-01-24 01:18:16 +01002237 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002238 last_isspace = 0;
William Lallemand5f232402012-04-05 18:02:55 +02002239 }
2240 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002241
William Lallemand1d705562012-03-12 12:46:41 +01002242 case LOG_FMT_FRONTEND: // %f
William Lallemandbddd4fd2012-02-27 11:23:10 +01002243 src = fe->id;
William Lallemand5f232402012-04-05 18:02:55 +02002244 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002245 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002246 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002247 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002248 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002249 break;
2250
Willy Tarreau773d65f2012-10-12 14:56:11 +02002251 case LOG_FMT_FRONTEND_XPRT: // %ft
2252 src = fe->id;
2253 if (tmp->options & LOG_OPT_QUOTE)
2254 LOGCHAR('"');
2255 iret = strlcpy2(tmplog, src, dst + maxsize - tmplog);
2256 if (iret == 0)
2257 goto out;
2258 tmplog += iret;
Willy Tarreaua261e9b2016-12-22 20:44:00 +01002259 if (sess->listener->bind_conf->xprt == xprt_get(XPRT_SSL))
Willy Tarreau773d65f2012-10-12 14:56:11 +02002260 LOGCHAR('~');
Willy Tarreau773d65f2012-10-12 14:56:11 +02002261 if (tmp->options & LOG_OPT_QUOTE)
2262 LOGCHAR('"');
2263 last_isspace = 0;
2264 break;
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002265#ifdef USE_OPENSSL
2266 case LOG_FMT_SSL_CIPHER: // %sslc
2267 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002268 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002269 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002270 src = ssl_sock_get_cipher_name(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002271 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002272 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2273 if (ret == NULL)
2274 goto out;
2275 tmplog = ret;
2276 last_isspace = 0;
2277 break;
Willy Tarreau773d65f2012-10-12 14:56:11 +02002278
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002279 case LOG_FMT_SSL_VERSION: // %sslv
2280 src = NULL;
Willy Tarreau9ad7bd42015-04-03 19:19:59 +02002281 conn = objt_conn(sess->origin);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002282 if (conn) {
Emmanuel Hocdet01da5712017-10-13 16:59:49 +02002283 src = ssl_sock_get_proto_version(conn);
Willy Tarreaub363a1f2013-10-01 10:45:07 +02002284 }
Willy Tarreauffc3fcd2012-10-12 20:17:54 +02002285 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2286 if (ret == NULL)
2287 goto out;
2288 tmplog = ret;
2289 last_isspace = 0;
2290 break;
2291#endif
William Lallemand1d705562012-03-12 12:46:41 +01002292 case LOG_FMT_BACKEND: // %b
William Lallemandbddd4fd2012-02-27 11:23:10 +01002293 src = be->id;
William Lallemand5f232402012-04-05 18:02:55 +02002294 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002295 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002296 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002297 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002298 last_isspace = 0;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002299 break;
2300
William Lallemand1d705562012-03-12 12:46:41 +01002301 case LOG_FMT_SERVER: // %s
Willy Tarreaue1809df2018-09-05 15:30:16 +02002302 switch (obj_type(s ? s->target : NULL)) {
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002303 case OBJ_TYPE_SERVER:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002304 src = __objt_server(s->target)->id;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002305 break;
2306 case OBJ_TYPE_APPLET:
Willy Tarreau1aaf3242018-09-20 11:13:58 +02002307 src = __objt_applet(s->target)->name;
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002308 break;
2309 default:
2310 src = "<NOSRV>";
2311 break;
2312 }
William Lallemand5f232402012-04-05 18:02:55 +02002313 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002314 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002315 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002316 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002317 last_isspace = 0;
2318 break;
2319
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002320 case LOG_FMT_Th: // %Th = handshake time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002321 ret = ltoa_o(logs->t_handshake, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002322 if (ret == NULL)
2323 goto out;
2324 tmplog = ret;
2325 last_isspace = 0;
2326 break;
2327
2328 case LOG_FMT_Ti: // %Ti = HTTP idle time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002329 ret = ltoa_o(logs->t_idle, tmplog, dst + maxsize - tmplog);
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002330 if (ret == NULL)
2331 goto out;
2332 tmplog = ret;
2333 last_isspace = 0;
2334 break;
2335
2336 case LOG_FMT_TR: // %TR = HTTP request time
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002337 ret = ltoa_o((t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1,
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002338 tmplog, dst + maxsize - tmplog);
2339 if (ret == NULL)
2340 goto out;
2341 tmplog = ret;
2342 last_isspace = 0;
2343 break;
2344
2345 case LOG_FMT_TQ: // %Tq = Th + Ti + TR
William Lallemand5f232402012-04-05 18:02:55 +02002346 ret = ltoa_o(t_request, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002347 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002348 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002349 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002350 last_isspace = 0;
2351 break;
2352
William Lallemand1d705562012-03-12 12:46:41 +01002353 case LOG_FMT_TW: // %Tw
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002354 ret = ltoa_o((logs->t_queue >= 0) ? logs->t_queue - t_request : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002355 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002356 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002357 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002358 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002359 last_isspace = 0;
2360 break;
2361
William Lallemand1d705562012-03-12 12:46:41 +01002362 case LOG_FMT_TC: // %Tc
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002363 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002364 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002365 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002366 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002367 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002368 last_isspace = 0;
2369 break;
2370
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002371 case LOG_FMT_Tr: // %Tr
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002372 ret = ltoa_o((logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1,
William Lallemand5f232402012-04-05 18:02:55 +02002373 tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002374 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002375 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002376 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002377 last_isspace = 0;
2378 break;
2379
Willy Tarreau27b639d2016-05-17 17:55:27 +02002380 case LOG_FMT_TD: // %Td
Willy Tarreaua21c0e62018-09-05 15:07:15 +02002381 if (be->mode == PR_MODE_HTTP)
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002382 ret = ltoa_o((logs->t_data >= 0) ? logs->t_close - logs->t_data : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002383 tmplog, dst + maxsize - tmplog);
2384 else
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002385 ret = ltoa_o((logs->t_connect >= 0) ? logs->t_close - logs->t_connect : -1,
Willy Tarreau27b639d2016-05-17 17:55:27 +02002386 tmplog, dst + maxsize - tmplog);
2387 if (ret == NULL)
2388 goto out;
2389 tmplog = ret;
2390 last_isspace = 0;
2391 break;
2392
Thierry FOURNIER / OZON.IO4cac3592016-07-28 17:19:45 +02002393 case LOG_FMT_Ta: // %Ta = active time = Tt - Th - Ti
2394 if (!(fe->to_log & LW_BYTES))
2395 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002396 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 +02002397 tmplog, dst + maxsize - tmplog);
2398 if (ret == NULL)
2399 goto out;
2400 tmplog = ret;
2401 last_isspace = 0;
2402 break;
2403
2404 case LOG_FMT_TT: // %Tt = total time
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002405 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002406 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002407 ret = ltoa_o(logs->t_close, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002408 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002409 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002410 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002411 last_isspace = 0;
2412 break;
2413
Willy Tarreau2beef582012-12-20 17:22:52 +01002414 case LOG_FMT_STATUS: // %ST
Willy Tarreau57bc8912016-04-25 17:09:40 +02002415 ret = ltoa_o(txn ? txn->status : 0, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002416 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002417 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002418 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002419 last_isspace = 0;
2420 break;
2421
William Lallemand1d705562012-03-12 12:46:41 +01002422 case LOG_FMT_BYTES: // %B
Willy Tarreaud79a3b22012-12-28 09:40:16 +01002423 if (!(fe->to_log & LW_BYTES))
William Lallemand1d705562012-03-12 12:46:41 +01002424 LOGCHAR('+');
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002425 ret = lltoa(logs->bytes_out, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002426 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002427 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002428 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002429 last_isspace = 0;
2430 break;
2431
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002432 case LOG_FMT_BYTES_UP: // %U
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002433 ret = lltoa(logs->bytes_in, tmplog, dst + maxsize - tmplog);
Willy Tarreauc5259fd2012-12-20 15:38:04 +01002434 if (ret == NULL)
2435 goto out;
2436 tmplog = ret;
2437 last_isspace = 0;
2438 break;
2439
Willy Tarreau2beef582012-12-20 17:22:52 +01002440 case LOG_FMT_CCLIENT: // %CC
Willy Tarreau57bc8912016-04-25 17:09:40 +02002441 src = txn ? txn->cli_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002442 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002443 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002444 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002445 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002446 last_isspace = 0;
2447 break;
2448
Willy Tarreau2beef582012-12-20 17:22:52 +01002449 case LOG_FMT_CSERVER: // %CS
Willy Tarreau57bc8912016-04-25 17:09:40 +02002450 src = txn ? txn->srv_cookie : NULL;
William Lallemand5f232402012-04-05 18:02:55 +02002451 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002452 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002453 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002454 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002455 last_isspace = 0;
2456 break;
2457
William Lallemand1d705562012-03-12 12:46:41 +01002458 case LOG_FMT_TERMSTATE: // %ts
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002459 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2460 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau6580c062012-03-12 15:09:42 +01002461 *tmplog = '\0';
2462 last_isspace = 0;
2463 break;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002464
William Lallemand1d705562012-03-12 12:46:41 +01002465 case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002466 LOGCHAR(sess_term_cond[(s_flags & SF_ERR_MASK) >> SF_ERR_SHIFT]);
2467 LOGCHAR(sess_fin_state[(s_flags & SF_FINST_MASK) >> SF_FINST_SHIFT]);
Willy Tarreau57bc8912016-04-25 17:09:40 +02002468 LOGCHAR((txn && (be->ck_opts & PR_CK_ANY)) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
2469 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 +01002470 last_isspace = 0;
2471 break;
2472
William Lallemand1d705562012-03-12 12:46:41 +01002473 case LOG_FMT_ACTCONN: // %ac
William Lallemand5f232402012-04-05 18:02:55 +02002474 ret = ltoa_o(actconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002475 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002476 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002477 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002478 last_isspace = 0;
2479 break;
2480
William Lallemand1d705562012-03-12 12:46:41 +01002481 case LOG_FMT_FECONN: // %fc
William Lallemand5f232402012-04-05 18:02:55 +02002482 ret = ltoa_o(fe->feconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002483 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002484 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002485 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002486 last_isspace = 0;
2487 break;
2488
William Lallemand1d705562012-03-12 12:46:41 +01002489 case LOG_FMT_BECONN: // %bc
William Lallemand5f232402012-04-05 18:02:55 +02002490 ret = ltoa_o(be->beconn, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002491 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002492 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002493 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002494 last_isspace = 0;
2495 break;
2496
William Lallemand1d705562012-03-12 12:46:41 +01002497 case LOG_FMT_SRVCONN: // %sc
Willy Tarreaue1809df2018-09-05 15:30:16 +02002498 ret = ultoa_o(objt_server(s ? s->target : NULL) ?
Willy Tarreau3fdb3662012-11-12 00:42:33 +01002499 objt_server(s->target)->cur_sess :
William Lallemand5f232402012-04-05 18:02:55 +02002500 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_RETRIES: // %rq
Willy Tarreaub8bc5252018-09-05 15:51:28 +02002508 if (s_flags & SF_REDISP)
William Lallemand1d705562012-03-12 12:46:41 +01002509 LOGCHAR('+');
Willy Tarreauabd71a52018-09-04 19:21:44 +02002510 ret = ltoa_o((s && s->si[1].conn_retries > 0) ?
Willy Tarreau350f4872014-11-28 14:42:25 +01002511 (be->conn_retries - s->si[1].conn_retries) :
William Lallemand5f232402012-04-05 18:02:55 +02002512 be->conn_retries, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002513 if (ret == NULL)
William Lallemand51b5dca2012-03-26 17:52:55 +02002514 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002515 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002516 last_isspace = 0;
2517 break;
2518
William Lallemand1d705562012-03-12 12:46:41 +01002519 case LOG_FMT_SRVQUEUE: // %sq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002520 ret = ltoa_o(logs->srv_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002521 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002522 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002523 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002524 last_isspace = 0;
2525 break;
2526
William Lallemand1d705562012-03-12 12:46:41 +01002527 case LOG_FMT_BCKQUEUE: // %bq
Willy Tarreau372ac5a2018-09-05 15:16:23 +02002528 ret = ltoa_o(logs->prx_queue_pos, tmplog, dst + maxsize - tmplog);
William Lallemand1d705562012-03-12 12:46:41 +01002529 if (ret == NULL)
William Lallemandbddd4fd2012-02-27 11:23:10 +01002530 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002531 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002532 last_isspace = 0;
2533 break;
2534
William Lallemand1d705562012-03-12 12:46:41 +01002535 case LOG_FMT_HDRREQUEST: // %hr
William Lallemandbddd4fd2012-02-27 11:23:10 +01002536 /* request header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002537 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002538 if (tmp->options & LOG_OPT_QUOTE)
2539 LOGCHAR('"');
2540 LOGCHAR('{');
2541 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2542 if (hdr)
2543 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002544 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002545 ret = lf_encode_string(tmplog, dst + maxsize,
2546 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002547 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002548 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002549 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002550 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002551 }
2552 LOGCHAR('}');
William Lallemand51b5dca2012-03-26 17:52:55 +02002553 if (tmp->options & LOG_OPT_QUOTE)
2554 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002555 last_isspace = 0;
2556 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002557 break;
2558
William Lallemand1d705562012-03-12 12:46:41 +01002559 case LOG_FMT_HDRREQUESTLIST: // %hrl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002560 /* request header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002561 if (fe->nb_req_cap && s && s->req_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002562 for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
2563 if (hdr > 0)
2564 LOGCHAR(' ');
2565 if (tmp->options & LOG_OPT_QUOTE)
2566 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002567 if (s->req_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002568 ret = lf_encode_string(tmplog, dst + maxsize,
2569 '#', hdr_encode_map, s->req_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002570 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002571 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002572 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002573 } else if (!(tmp->options & LOG_OPT_QUOTE))
2574 LOGCHAR('-');
2575 if (tmp->options & LOG_OPT_QUOTE)
2576 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002577 last_isspace = 0;
2578 }
2579 }
2580 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002581
William Lallemand1d705562012-03-12 12:46:41 +01002582
2583 case LOG_FMT_HDRRESPONS: // %hs
William Lallemandbddd4fd2012-02-27 11:23:10 +01002584 /* response header */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002585 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002586 if (tmp->options & LOG_OPT_QUOTE)
2587 LOGCHAR('"');
2588 LOGCHAR('{');
2589 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2590 if (hdr)
2591 LOGCHAR('|');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002592 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002593 ret = lf_encode_string(tmplog, dst + maxsize,
2594 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002595 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002596 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002597 tmplog = ret;
William Lallemand51b5dca2012-03-26 17:52:55 +02002598 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002599 }
2600 LOGCHAR('}');
2601 last_isspace = 0;
2602 if (tmp->options & LOG_OPT_QUOTE)
2603 LOGCHAR('"');
2604 }
William Lallemandbddd4fd2012-02-27 11:23:10 +01002605 break;
2606
William Lallemand1d705562012-03-12 12:46:41 +01002607 case LOG_FMT_HDRRESPONSLIST: // %hsl
William Lallemandbddd4fd2012-02-27 11:23:10 +01002608 /* response header list */
Willy Tarreaud4f91662018-09-05 15:28:07 +02002609 if (fe->nb_rsp_cap && s && s->res_cap) {
William Lallemandbddd4fd2012-02-27 11:23:10 +01002610 for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
2611 if (hdr > 0)
2612 LOGCHAR(' ');
2613 if (tmp->options & LOG_OPT_QUOTE)
2614 LOGCHAR('"');
Willy Tarreaucb7dd012015-04-03 22:16:32 +02002615 if (s->res_cap[hdr] != NULL) {
Dragan Dosen835b9212016-02-12 13:23:03 +01002616 ret = lf_encode_string(tmplog, dst + maxsize,
2617 '#', hdr_encode_map, s->res_cap[hdr], tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002618 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002619 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002620 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002621 } else if (!(tmp->options & LOG_OPT_QUOTE))
2622 LOGCHAR('-');
2623 if (tmp->options & LOG_OPT_QUOTE)
2624 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002625 last_isspace = 0;
2626 }
2627 }
2628 break;
2629
William Lallemand1d705562012-03-12 12:46:41 +01002630 case LOG_FMT_REQ: // %r
William Lallemandbddd4fd2012-02-27 11:23:10 +01002631 /* Request */
2632 if (tmp->options & LOG_OPT_QUOTE)
2633 LOGCHAR('"');
Willy Tarreau57bc8912016-04-25 17:09:40 +02002634 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Dragan Dosen835b9212016-02-12 13:23:03 +01002635 ret = lf_encode_string(tmplog, dst + maxsize,
2636 '#', url_encode_map, uri, tmp);
William Lallemand1d705562012-03-12 12:46:41 +01002637 if (ret == NULL || *ret != '\0')
William Lallemand51b5dca2012-03-26 17:52:55 +02002638 goto out;
William Lallemand1d705562012-03-12 12:46:41 +01002639 tmplog = ret;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002640 if (tmp->options & LOG_OPT_QUOTE)
2641 LOGCHAR('"');
William Lallemandbddd4fd2012-02-27 11:23:10 +01002642 last_isspace = 0;
2643 break;
William Lallemand5f232402012-04-05 18:02:55 +02002644
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002645 case LOG_FMT_HTTP_PATH: // %HP
Willy Tarreau57bc8912016-04-25 17:09:40 +02002646 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002647
Willy Tarreaub7636d12015-06-17 19:58:02 +02002648 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002649 LOGCHAR('"');
2650
2651 end = uri + strlen(uri);
2652 // look for the first whitespace character
2653 while (uri < end && !HTTP_IS_SPHT(*uri))
2654 uri++;
2655
2656 // keep advancing past multiple spaces
2657 while (uri < end && HTTP_IS_SPHT(*uri)) {
2658 uri++; nspaces++;
2659 }
2660
2661 // look for first space or question mark after url
2662 spc = uri;
2663 while (spc < end && *spc != '?' && !HTTP_IS_SPHT(*spc))
2664 spc++;
2665
Nenad Merdanovic54e439f2016-04-26 01:39:02 +02002666 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002667 chunk.area = "<BADREQ>";
2668 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002669 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002670 chunk.area = uri;
2671 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002672 }
2673
Dragan Dosen835b9212016-02-12 13:23:03 +01002674 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002675 if (ret == NULL || *ret != '\0')
2676 goto out;
2677
2678 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002679 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002680 LOGCHAR('"');
2681
2682 last_isspace = 0;
2683 break;
2684
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002685 case LOG_FMT_HTTP_QUERY: // %HQ
2686 if (tmp->options & LOG_OPT_QUOTE)
2687 LOGCHAR('"');
2688
Willy Tarreau57bc8912016-04-25 17:09:40 +02002689 if (!txn || !txn->uri) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002690 chunk.area = "<BADREQ>";
2691 chunk.data = strlen("<BADREQ>");
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002692 } else {
2693 uri = txn->uri;
2694 end = uri + strlen(uri);
2695 // look for the first question mark
2696 while (uri < end && *uri != '?')
2697 uri++;
2698
2699 qmark = uri;
2700 // look for first space or question mark after url
2701 while (uri < end && !HTTP_IS_SPHT(*uri))
2702 uri++;
2703
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002704 chunk.area = qmark;
2705 chunk.data = uri - qmark;
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002706 }
2707
Dragan Dosen835b9212016-02-12 13:23:03 +01002708 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworthe63ac872015-07-31 16:14:16 +00002709 if (ret == NULL || *ret != '\0')
2710 goto out;
2711
2712 tmplog = ret;
2713 if (tmp->options & LOG_OPT_QUOTE)
2714 LOGCHAR('"');
2715
2716 last_isspace = 0;
2717 break;
2718
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002719 case LOG_FMT_HTTP_URI: // %HU
Willy Tarreau57bc8912016-04-25 17:09:40 +02002720 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002721
Willy Tarreaub7636d12015-06-17 19:58:02 +02002722 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002723 LOGCHAR('"');
2724
2725 end = uri + strlen(uri);
2726 // look for the first whitespace character
2727 while (uri < end && !HTTP_IS_SPHT(*uri))
2728 uri++;
2729
2730 // keep advancing past multiple spaces
2731 while (uri < end && HTTP_IS_SPHT(*uri)) {
2732 uri++; nspaces++;
2733 }
2734
2735 // look for first space after url
2736 spc = uri;
2737 while (spc < end && !HTTP_IS_SPHT(*spc))
2738 spc++;
2739
Willy Tarreau57bc8912016-04-25 17:09:40 +02002740 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002741 chunk.area = "<BADREQ>";
2742 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002743 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002744 chunk.area = uri;
2745 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002746 }
2747
Dragan Dosen835b9212016-02-12 13:23:03 +01002748 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002749 if (ret == NULL || *ret != '\0')
2750 goto out;
2751
2752 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002753 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002754 LOGCHAR('"');
2755
2756 last_isspace = 0;
2757 break;
2758
2759 case LOG_FMT_HTTP_METHOD: // %HM
Willy Tarreau57bc8912016-04-25 17:09:40 +02002760 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002761 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002762 LOGCHAR('"');
2763
2764 end = uri + strlen(uri);
2765 // look for the first whitespace character
2766 spc = uri;
2767 while (spc < end && !HTTP_IS_SPHT(*spc))
2768 spc++;
2769
2770 if (spc == end) { // odd case, we have txn->uri, but we only got a verb
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002771 chunk.area = "<BADREQ>";
2772 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002773 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002774 chunk.area = uri;
2775 chunk.data = spc - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002776 }
2777
Dragan Dosen835b9212016-02-12 13:23:03 +01002778 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002779 if (ret == NULL || *ret != '\0')
2780 goto out;
2781
2782 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002783 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002784 LOGCHAR('"');
2785
2786 last_isspace = 0;
2787 break;
2788
2789 case LOG_FMT_HTTP_VERSION: // %HV
Willy Tarreau57bc8912016-04-25 17:09:40 +02002790 uri = txn && txn->uri ? txn->uri : "<BADREQ>";
Willy Tarreaub7636d12015-06-17 19:58:02 +02002791 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002792 LOGCHAR('"');
2793
2794 end = uri + strlen(uri);
2795 // look for the first whitespace character
2796 while (uri < end && !HTTP_IS_SPHT(*uri))
2797 uri++;
2798
2799 // keep advancing past multiple spaces
2800 while (uri < end && HTTP_IS_SPHT(*uri)) {
2801 uri++; nspaces++;
2802 }
2803
2804 // look for the next whitespace character
2805 while (uri < end && !HTTP_IS_SPHT(*uri))
2806 uri++;
2807
2808 // keep advancing past multiple spaces
2809 while (uri < end && HTTP_IS_SPHT(*uri))
2810 uri++;
2811
Willy Tarreau57bc8912016-04-25 17:09:40 +02002812 if (!txn || !txn->uri || nspaces == 0) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002813 chunk.area = "<BADREQ>";
2814 chunk.data = strlen("<BADREQ>");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002815 } else if (uri == end) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002816 chunk.area = "HTTP/0.9";
2817 chunk.data = strlen("HTTP/0.9");
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002818 } else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02002819 chunk.area = uri;
2820 chunk.data = end - uri;
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002821 }
2822
Dragan Dosen835b9212016-02-12 13:23:03 +01002823 ret = lf_encode_chunk(tmplog, dst + maxsize, '#', url_encode_map, &chunk, tmp);
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002824 if (ret == NULL || *ret != '\0')
2825 goto out;
2826
2827 tmplog = ret;
Willy Tarreaub7636d12015-06-17 19:58:02 +02002828 if (tmp->options & LOG_OPT_QUOTE)
Andrew Hayworth0ebc55f2015-04-27 21:37:03 +00002829 LOGCHAR('"');
2830
2831 last_isspace = 0;
2832 break;
2833
William Lallemand5f232402012-04-05 18:02:55 +02002834 case LOG_FMT_COUNTER: // %rt
2835 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002836 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", uniq_id);
William Lallemand5f232402012-04-05 18:02:55 +02002837 if (iret < 0 || iret > dst + maxsize - tmplog)
2838 goto out;
2839 last_isspace = 0;
2840 tmplog += iret;
2841 } else {
Willy Tarreau5cacab62018-09-05 15:52:59 +02002842 ret = ltoa_o(uniq_id, tmplog, dst + maxsize - tmplog);
William Lallemand5f232402012-04-05 18:02:55 +02002843 if (ret == NULL)
2844 goto out;
2845 tmplog = ret;
2846 last_isspace = 0;
2847 }
2848 break;
2849
Willy Tarreau7346acb2014-08-28 15:03:15 +02002850 case LOG_FMT_LOGCNT: // %lc
2851 if (tmp->options & LOG_OPT_HEXA) {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002852 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", fe->log_count);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002853 if (iret < 0 || iret > dst + maxsize - tmplog)
2854 goto out;
2855 last_isspace = 0;
2856 tmplog += iret;
2857 } else {
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002858 ret = ultoa_o(fe->log_count, tmplog, dst + maxsize - tmplog);
Willy Tarreau7346acb2014-08-28 15:03:15 +02002859 if (ret == NULL)
2860 goto out;
2861 tmplog = ret;
2862 last_isspace = 0;
2863 }
2864 break;
2865
William Lallemand5f232402012-04-05 18:02:55 +02002866 case LOG_FMT_HOSTNAME: // %H
2867 src = hostname;
2868 ret = lf_text(tmplog, src, dst + maxsize - tmplog, tmp);
2869 if (ret == NULL)
2870 goto out;
2871 tmplog = ret;
2872 last_isspace = 0;
2873 break;
2874
2875 case LOG_FMT_PID: // %pid
2876 if (tmp->options & LOG_OPT_HEXA) {
2877 iret = snprintf(tmplog, dst + maxsize - tmplog, "%04X", pid);
2878 if (iret < 0 || iret > dst + maxsize - tmplog)
2879 goto out;
2880 last_isspace = 0;
2881 tmplog += iret;
2882 } else {
2883 ret = ltoa_o(pid, tmplog, dst + maxsize - tmplog);
2884 if (ret == NULL)
2885 goto out;
2886 tmplog = ret;
2887 last_isspace = 0;
2888 }
2889 break;
William Lallemanda73203e2012-03-12 12:48:57 +01002890
2891 case LOG_FMT_UNIQUEID: // %ID
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002892 ret = NULL;
Willy Tarreau02fdf4f2018-09-05 15:49:01 +02002893 src = s ? s->unique_id : NULL;
Thierry FOURNIER1be69102014-04-15 01:38:48 +02002894 ret = lf_text(tmplog, src, maxsize - (tmplog - dst), tmp);
William Lallemanda73203e2012-03-12 12:48:57 +01002895 if (ret == NULL)
2896 goto out;
2897 tmplog = ret;
2898 last_isspace = 0;
2899 break;
2900
William Lallemandbddd4fd2012-02-27 11:23:10 +01002901 }
2902 }
2903
2904out:
William Lallemand1d705562012-03-12 12:46:41 +01002905 /* *tmplog is a unused character */
2906 *tmplog = '\0';
Willy Tarreaudf974472012-12-28 02:44:01 +01002907 return tmplog - dst;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002908
Willy Tarreaubaaee002006-06-26 02:48:02 +02002909}
2910
William Lallemand1d705562012-03-12 12:46:41 +01002911/*
Willy Tarreau87b09662015-04-03 00:22:06 +02002912 * send a log for the stream when we have enough info about it.
William Lallemand1d705562012-03-12 12:46:41 +01002913 * Will not log if the frontend has no log defined.
2914 */
Willy Tarreau87b09662015-04-03 00:22:06 +02002915void strm_log(struct stream *s)
William Lallemand1d705562012-03-12 12:46:41 +01002916{
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002917 struct session *sess = s->sess;
William Lallemand1d705562012-03-12 12:46:41 +01002918 int size, err, level;
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002919 int sd_size = 0;
William Lallemand1d705562012-03-12 12:46:41 +01002920
2921 /* if we don't want to log normal traffic, return now */
Willy Tarreaue7dff022015-04-03 01:14:29 +02002922 err = (s->flags & SF_REDISP) ||
2923 ((s->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
2924 (((s->flags & SF_ERR_MASK) == SF_ERR_NONE) &&
Willy Tarreau350f4872014-11-28 14:42:25 +01002925 (s->si[1].conn_retries != s->be->conn_retries)) ||
Willy Tarreaueee5b512015-04-03 23:46:31 +02002926 ((sess->fe->mode == PR_MODE_HTTP) && s->txn && s->txn->status >= 500);
Willy Tarreaubaaee002006-06-26 02:48:02 +02002927
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002928 if (!err && (sess->fe->options2 & PR_O2_NOLOGNORM))
William Lallemand1d705562012-03-12 12:46:41 +01002929 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002930
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002931 if (LIST_ISEMPTY(&sess->fe->logsrvs))
William Lallemand1d705562012-03-12 12:46:41 +01002932 return;
William Lallemandbddd4fd2012-02-27 11:23:10 +01002933
Willy Tarreauabcd5142013-06-11 17:18:02 +02002934 if (s->logs.level) { /* loglevel was overridden */
2935 if (s->logs.level == -1) {
2936 s->logs.logwait = 0; /* logs disabled */
2937 return;
2938 }
2939 level = s->logs.level - 1;
2940 }
2941 else {
2942 level = LOG_INFO;
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002943 if (err && (sess->fe->options2 & PR_O2_LOGERRORS))
Willy Tarreauabcd5142013-06-11 17:18:02 +02002944 level = LOG_ERR;
2945 }
William Lallemand1d705562012-03-12 12:46:41 +01002946
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002947 /* if unique-id was not generated */
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002948 if (!s->unique_id && !LIST_ISEMPTY(&sess->fe->format_unique_id)) {
Willy Tarreaubafbe012017-11-24 17:34:44 +01002949 if ((s->unique_id = pool_alloc(pool_head_uniqueid)) != NULL)
Willy Tarreaue36cbcb2015-04-03 15:40:56 +02002950 build_logline(s, s->unique_id, UNIQUEID_LEN, &sess->fe->format_unique_id);
William Lallemand5b7ea3a2013-08-28 15:44:19 +02002951 }
2952
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002953 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2954 sd_size = build_logline(s, logline_rfc5424, global.max_syslog_len,
2955 &sess->fe->logformat_sd);
2956 }
2957
Dragan Dosen59cee972015-09-19 22:09:02 +02002958 size = build_logline(s, logline, global.max_syslog_len, &sess->fe->logformat);
William Lallemand1d705562012-03-12 12:46:41 +01002959 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002960 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Dragan Dosen0b85ece2015-09-25 19:17:44 +02002961 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
William Lallemand1d705562012-03-12 12:46:41 +01002962 s->logs.logwait = 0;
2963 }
2964}
William Lallemandbddd4fd2012-02-27 11:23:10 +01002965
Willy Tarreau53839352018-09-05 19:51:10 +02002966/*
2967 * send a minimalist log for the session. Will not log if the frontend has no
2968 * log defined. It is assumed that this is only used to report anomalies that
2969 * cannot lead to the creation of a regular stream. Because of this the log
2970 * level is LOG_INFO or LOG_ERR depending on the "log-separate-error" setting
2971 * in the frontend. The caller must simply know that it should not call this
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002972 * function to report unimportant events. It is safe to call this function with
2973 * sess==NULL (will not do anything).
Willy Tarreau53839352018-09-05 19:51:10 +02002974 */
2975void sess_log(struct session *sess)
2976{
2977 int size, level;
2978 int sd_size = 0;
2979
Willy Tarreau9fa267d2018-10-05 10:22:27 +02002980 if (!sess)
2981 return;
2982
Willy Tarreau53839352018-09-05 19:51:10 +02002983 if (LIST_ISEMPTY(&sess->fe->logsrvs))
2984 return;
2985
2986 level = LOG_INFO;
2987 if (sess->fe->options2 & PR_O2_LOGERRORS)
2988 level = LOG_ERR;
2989
2990 if (!LIST_ISEMPTY(&sess->fe->logformat_sd)) {
2991 sd_size = sess_build_logline(sess, NULL,
2992 logline_rfc5424, global.max_syslog_len,
2993 &sess->fe->logformat_sd);
2994 }
2995
2996 size = sess_build_logline(sess, NULL, logline, global.max_syslog_len, &sess->fe->logformat);
2997 if (size > 0) {
Olivier Houchardd2ee3e72019-03-08 18:53:21 +01002998 _HA_ATOMIC_ADD(&sess->fe->log_count, 1);
Willy Tarreau53839352018-09-05 19:51:10 +02002999 __send_log(sess->fe, level, logline, size + 1, logline_rfc5424, sd_size);
3000 }
3001}
3002
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003003static int cli_io_handler_show_startup_logs(struct appctx *appctx)
3004{
3005 struct stream_interface *si = appctx->owner;
3006 const char *msg = (startup_logs ? startup_logs : "No startup alerts/warnings.\n");
3007
3008 if (ci_putstr(si_ic(si), msg) == -1) {
Willy Tarreaudb398432018-11-15 11:08:52 +01003009 si_rx_room_blk(si);
Christopher Fauletc1b730a2017-10-24 12:00:51 +02003010 return 0;
3011 }
3012 return 1;
3013}
3014
3015/* register cli keywords */
3016static struct cli_kw_list cli_kws = {{ },{
3017 { { "show", "startup-logs", NULL },
3018 "show startup-logs : report logs emitted during HAProxy startup",
3019 NULL, cli_io_handler_show_startup_logs },
3020 {{},}
3021}};
3022
Willy Tarreau0108d902018-11-25 19:14:37 +01003023INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
3024
Willy Tarreau172f5ce2018-11-26 11:21:50 +01003025REGISTER_PER_THREAD_INIT(init_log_buffers_per_thread);
3026REGISTER_PER_THREAD_DEINIT(deinit_log_buffers_per_thread);
3027
Willy Tarreaubaaee002006-06-26 02:48:02 +02003028/*
3029 * Local variables:
3030 * c-indent-level: 8
3031 * c-basic-offset: 8
3032 * End:
3033 */