blob: 6af34644ea89c5d6deb0dc4e6631cb6aabf9a948 [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * General logging functions.
3 *
4 * Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
5 *
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
13#include <stdarg.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <syslog.h>
18#include <time.h>
19#include <unistd.h>
20
21#include <sys/time.h>
22
Willy Tarreaue3ba5f02006-06-29 18:54:54 +020023#include <common/config.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020024#include <common/standard.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020025
26#include <types/backend.h>
27#include <types/global.h>
28#include <types/httperr.h>
29#include <types/log.h>
30#include <types/proxy.h>
31#include <types/session.h>
32
33
34const char *log_facilities[NB_LOG_FACILITIES] = {
35 "kern", "user", "mail", "daemon",
36 "auth", "syslog", "lpr", "news",
37 "uucp", "cron", "auth2", "ftp",
38 "ntp", "audit", "alert", "cron2",
39 "local0", "local1", "local2", "local3",
40 "local4", "local5", "local6", "local7"
41};
42
43
44const char *log_levels[NB_LOG_LEVELS] = {
45 "emerg", "alert", "crit", "err",
46 "warning", "notice", "info", "debug"
47};
48
49const char *monthname[12] = {
50 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
51 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
52};
53
54const char sess_term_cond[8] = "-cCsSPRI"; /* normal, CliTo, CliErr, SrvTo, SrvErr, PxErr, Resource, Internal */
Willy Tarreaub8750a82006-09-03 09:56:00 +020055const char sess_fin_state[8] = "-RCHDLQT"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, Tarpit */
Willy Tarreaubaaee002006-06-26 02:48:02 +020056const char sess_cookie[4] = "NIDV"; /* No cookie, Invalid cookie, cookie for a Down server, Valid cookie */
57const char sess_set_cookie[8] = "N1I3PD5R"; /* No set-cookie, unknown, Set-Cookie Inserted, unknown,
58 Set-cookie seen and left unchanged (passive), Set-cookie Deleted,
59 unknown, Set-cookie Rewritten */
60void **pool_requri = NULL;
61
62
63/*
64 * Displays the message on stderr with the date and pid. Overrides the quiet
65 * mode during startup.
66 */
Willy Tarreaub17916e2006-10-15 15:17:57 +020067void Alert(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +020068{
69 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +020070 struct tm *tm;
71
72 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
73 va_start(argp, fmt);
74
Willy Tarreau2b35c952006-10-15 15:25:48 +020075 tm = localtime(&now.tv_sec);
Willy Tarreaubaaee002006-06-26 02:48:02 +020076 fprintf(stderr, "[ALERT] %03d/%02d%02d%02d (%d) : ",
77 tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)getpid());
78 vfprintf(stderr, fmt, argp);
79 fflush(stderr);
80 va_end(argp);
81 }
82}
83
84
85/*
86 * Displays the message on stderr with the date and pid.
87 */
Willy Tarreaub17916e2006-10-15 15:17:57 +020088void Warning(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +020089{
90 va_list argp;
Willy Tarreaubaaee002006-06-26 02:48:02 +020091 struct tm *tm;
92
93 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
94 va_start(argp, fmt);
95
Willy Tarreau2b35c952006-10-15 15:25:48 +020096 tm = localtime(&now.tv_sec);
Willy Tarreaubaaee002006-06-26 02:48:02 +020097 fprintf(stderr, "[WARNING] %03d/%02d%02d%02d (%d) : ",
98 tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)getpid());
99 vfprintf(stderr, fmt, argp);
100 fflush(stderr);
101 va_end(argp);
102 }
103}
104
105/*
106 * Displays the message on <out> only if quiet mode is not set.
107 */
Willy Tarreaub17916e2006-10-15 15:17:57 +0200108void qfprintf(FILE *out, const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200109{
110 va_list argp;
111
112 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
113 va_start(argp, fmt);
114 vfprintf(out, fmt, argp);
115 fflush(out);
116 va_end(argp);
117 }
118}
119
120/*
121 * returns log level for <lev> or -1 if not found.
122 */
123int get_log_level(const char *lev)
124{
125 int level;
126
127 level = NB_LOG_LEVELS - 1;
128 while (level >= 0 && strcmp(log_levels[level], lev))
129 level--;
130
131 return level;
132}
133
134
135/*
136 * returns log facility for <fac> or -1 if not found.
137 */
138int get_log_facility(const char *fac)
139{
140 int facility;
141
142 facility = NB_LOG_FACILITIES - 1;
143 while (facility >= 0 && strcmp(log_facilities[facility], fac))
144 facility--;
145
146 return facility;
147}
148
149
150#define FD_SETS_ARE_BITFIELDS
151#ifdef FD_SETS_ARE_BITFIELDS
152/*
153 * This map is used with all the FD_* macros to check whether a particular bit
154 * is set or not. Each bit represents an ACSII code. FD_SET() sets those bytes
155 * which should be encoded. When FD_ISSET() returns non-zero, it means that the
156 * byte should be encoded. Be careful to always pass bytes from 0 to 255
157 * exclusively to the macros.
158 */
159fd_set hdr_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
160fd_set url_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
161
162#else
163#error "Check if your OS uses bitfields for fd_sets"
164#endif
165
166/*
167 * This function sends a syslog message to both log servers of a proxy,
168 * or to global log servers if the proxy is NULL.
169 * It also tries not to waste too much time computing the message header.
170 * It doesn't care about errors nor does it report them.
171 */
Willy Tarreaub17916e2006-10-15 15:17:57 +0200172void send_log(struct proxy *p, int level, const char *message, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200173{
174 static int logfd = -1; /* syslog UDP socket */
175 static long tvsec = -1; /* to force the string to be initialized */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200176 va_list argp;
177 static char logmsg[MAX_SYSLOG_LEN];
178 static char *dataptr = NULL;
179 int fac_level;
180 int hdr_len, data_len;
181 struct sockaddr_in *sa[2];
182 int facilities[2], loglevel[2];
183 int nbloggers = 0;
184 char *log_ptr;
185
186 if (logfd < 0) {
187 if ((logfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
188 return;
189 }
190
191 if (level < 0 || progname == NULL || message == NULL)
192 return;
193
Willy Tarreau2b35c952006-10-15 15:25:48 +0200194 if (now.tv_sec != tvsec || dataptr == NULL) {
Willy Tarreaubaaee002006-06-26 02:48:02 +0200195 /* this string is rebuild only once a second */
Willy Tarreau2b35c952006-10-15 15:25:48 +0200196 struct tm *tm = localtime(&now.tv_sec);
197 tvsec = now.tv_sec;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200198
199 hdr_len = snprintf(logmsg, sizeof(logmsg),
200 "<<<<>%s %2d %02d:%02d:%02d %s[%d]: ",
201 monthname[tm->tm_mon],
202 tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
203 progname, pid);
204 /* WARNING: depending upon implementations, snprintf may return
205 * either -1 or the number of bytes that would be needed to store
206 * the total message. In both cases, we must adjust it.
207 */
208 if (hdr_len < 0 || hdr_len > sizeof(logmsg))
209 hdr_len = sizeof(logmsg);
210
211 dataptr = logmsg + hdr_len;
212 }
213
214 va_start(argp, message);
215 data_len = vsnprintf(dataptr, logmsg + sizeof(logmsg) - dataptr, message, argp);
216 if (data_len < 0 || data_len > (logmsg + sizeof(logmsg) - dataptr))
217 data_len = logmsg + sizeof(logmsg) - dataptr;
218 va_end(argp);
219 dataptr[data_len - 1] = '\n'; /* force a break on ultra-long lines */
220
221 if (p == NULL) {
222 if (global.logfac1 >= 0) {
223 sa[nbloggers] = &global.logsrv1;
224 facilities[nbloggers] = global.logfac1;
225 loglevel[nbloggers] = global.loglev1;
226 nbloggers++;
227 }
228 if (global.logfac2 >= 0) {
229 sa[nbloggers] = &global.logsrv2;
230 facilities[nbloggers] = global.logfac2;
231 loglevel[nbloggers] = global.loglev2;
232 nbloggers++;
233 }
234 } else {
235 if (p->logfac1 >= 0) {
236 sa[nbloggers] = &p->logsrv1;
237 facilities[nbloggers] = p->logfac1;
238 loglevel[nbloggers] = p->loglev1;
239 nbloggers++;
240 }
241 if (p->logfac2 >= 0) {
242 sa[nbloggers] = &p->logsrv2;
243 facilities[nbloggers] = p->logfac2;
244 loglevel[nbloggers] = p->loglev2;
245 nbloggers++;
246 }
247 }
248
249 while (nbloggers-- > 0) {
250 /* we can filter the level of the messages that are sent to each logger */
251 if (level > loglevel[nbloggers])
252 continue;
253
254 /* For each target, we may have a different facility.
255 * We can also have a different log level for each message.
256 * This induces variations in the message header length.
257 * Since we don't want to recompute it each time, nor copy it every
258 * time, we only change the facility in the pre-computed header,
259 * and we change the pointer to the header accordingly.
260 */
261 fac_level = (facilities[nbloggers] << 3) + level;
262 log_ptr = logmsg + 3; /* last digit of the log level */
263 do {
264 *log_ptr = '0' + fac_level % 10;
265 fac_level /= 10;
266 log_ptr--;
267 } while (fac_level && log_ptr > logmsg);
268 *log_ptr = '<';
269
270 /* the total syslog message now starts at logptr, for dataptr+data_len-logptr */
271
272#ifndef MSG_NOSIGNAL
273 sendto(logfd, log_ptr, dataptr + data_len - log_ptr, MSG_DONTWAIT,
274 (struct sockaddr *)sa[nbloggers], sizeof(**sa));
275#else
276 sendto(logfd, log_ptr, dataptr + data_len - log_ptr, MSG_DONTWAIT | MSG_NOSIGNAL,
277 (struct sockaddr *)sa[nbloggers], sizeof(**sa));
278#endif
279 }
280}
281
282
283/*
284 * send a log for the session when we have enough info about it
285 */
286void sess_log(struct session *s)
287{
288 char pn[INET6_ADDRSTRLEN + strlen(":65535")];
289 struct proxy *p = s->proxy;
290 int log;
291 char *uri;
292 char *pxid;
293 char *srv;
294 struct tm *tm;
295
296 /* This is a first attempt at a better logging system.
297 * For now, we rely on send_log() to provide the date, although it obviously
298 * is the date of the log and not of the request, and most fields are not
299 * computed.
300 */
301
302 log = p->to_log & ~s->logs.logwait;
303
304 if (s->cli_addr.ss_family == AF_INET)
305 inet_ntop(AF_INET,
306 (const void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr,
307 pn, sizeof(pn));
308 else
309 inet_ntop(AF_INET6,
310 (const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr,
311 pn, sizeof(pn));
312
313 uri = (log & LW_REQ) ? s->logs.uri ? s->logs.uri : "<BADREQ>" : "";
314 pxid = p->id;
315 srv = (p->to_log & LW_SVID) ?
316 (s->data_source != DATA_SRC_STATS) ?
317 (s->srv != NULL) ? s->srv->id : "<NOSRV>" : "<STATS>" : "-";
318
319 tm = localtime(&s->logs.tv_accept.tv_sec);
320 if (p->to_log & LW_REQ) {
321 char tmpline[MAX_SYSLOG_LEN], *h;
322 int hdr;
323
324 h = tmpline;
325 if (p->to_log & LW_REQHDR && (h < tmpline + sizeof(tmpline) - 10)) {
326 *(h++) = ' ';
327 *(h++) = '{';
328 for (hdr = 0; hdr < p->nb_req_cap; hdr++) {
329 if (hdr)
330 *(h++) = '|';
331 if (s->req_cap[hdr] != NULL)
332 h = encode_string(h, tmpline + sizeof(tmpline) - 7,
333 '#', hdr_encode_map, s->req_cap[hdr]);
334 }
335 *(h++) = '}';
336 }
337
338 if (p->to_log & LW_RSPHDR && (h < tmpline + sizeof(tmpline) - 7)) {
339 *(h++) = ' ';
340 *(h++) = '{';
341 for (hdr = 0; hdr < p->nb_rsp_cap; hdr++) {
342 if (hdr)
343 *(h++) = '|';
344 if (s->rsp_cap[hdr] != NULL)
345 h = encode_string(h, tmpline + sizeof(tmpline) - 4,
346 '#', hdr_encode_map, s->rsp_cap[hdr]);
347 }
348 *(h++) = '}';
349 }
350
351 if (h < tmpline + sizeof(tmpline) - 4) {
352 *(h++) = ' ';
353 *(h++) = '"';
354 h = encode_string(h, tmpline + sizeof(tmpline) - 1,
355 '#', url_encode_map, uri);
356 *(h++) = '"';
357 }
358 *h = '\0';
359
360 send_log(p, LOG_INFO,
361 "%s:%d [%02d/%s/%04d:%02d:%02d:%02d]"
362 " %s %s %d/%d/%d/%d/%s%d %d %s%lld"
363 " %s %s %c%c%c%c %d/%d/%d %d/%d%s\n",
364 pn,
365 (s->cli_addr.ss_family == AF_INET) ?
366 ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port) :
367 ntohs(((struct sockaddr_in6 *)&s->cli_addr)->sin6_port),
368 tm->tm_mday, monthname[tm->tm_mon], tm->tm_year+1900,
369 tm->tm_hour, tm->tm_min, tm->tm_sec,
370 pxid, srv,
371 s->logs.t_request,
372 (s->logs.t_queue >= 0) ? s->logs.t_queue - s->logs.t_request : -1,
373 (s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_queue : -1,
374 (s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
375 (p->to_log & LW_BYTES) ? "" : "+", s->logs.t_close,
376 s->logs.status,
377 (p->to_log & LW_BYTES) ? "" : "+", s->logs.bytes,
378 s->logs.cli_cookie ? s->logs.cli_cookie : "-",
379 s->logs.srv_cookie ? s->logs.srv_cookie : "-",
380 sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT],
381 sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT],
382 (p->options & PR_O_COOK_ANY) ? sess_cookie[(s->flags & SN_CK_MASK) >> SN_CK_SHIFT] : '-',
383 (p->options & PR_O_COOK_ANY) ? sess_set_cookie[(s->flags & SN_SCK_MASK) >> SN_SCK_SHIFT] : '-',
384 s->srv ? s->srv->cur_sess : 0, p->nbconn, actconn,
385 s->logs.srv_queue_size, s->logs.prx_queue_size, tmpline);
386 }
387 else {
388 send_log(p, LOG_INFO, "%s:%d [%02d/%s/%04d:%02d:%02d:%02d]"
389 " %s %s %d/%d/%s%d %s%lld"
390 " %c%c %d/%d/%d %d/%d\n",
391 pn,
392 (s->cli_addr.ss_family == AF_INET) ?
393 ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port) :
394 ntohs(((struct sockaddr_in6 *)&s->cli_addr)->sin6_port),
395 tm->tm_mday, monthname[tm->tm_mon], tm->tm_year+1900,
396 tm->tm_hour, tm->tm_min, tm->tm_sec,
397 pxid, srv,
398 (s->logs.t_queue >= 0) ? s->logs.t_queue : -1,
399 (s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_queue : -1,
400 (p->to_log & LW_BYTES) ? "" : "+", s->logs.t_close,
401 (p->to_log & LW_BYTES) ? "" : "+", s->logs.bytes,
402 sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT],
403 sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT],
404 s->srv ? s->srv->cur_sess : 0, p->nbconn, actconn,
405 s->logs.srv_queue_size, s->logs.prx_queue_size);
406 }
407
408 s->logs.logwait = 0;
409}
410
411
412/*
413 * Initializes some data needed later.
414 */
415void init_log()
416{
417 int i;
418 char *tmp;
419
420 /* initialize the log header encoding map : '{|}"#' should be encoded with
421 * '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
422 * URL encoding only requires '"', '#' to be encoded as well as non-
423 * printable characters above.
424 */
425 memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
426 memset(url_encode_map, 0, sizeof(url_encode_map));
427 for (i = 0; i < 32; i++) {
428 FD_SET(i, hdr_encode_map);
429 FD_SET(i, url_encode_map);
430 }
431 for (i = 127; i < 256; i++) {
432 FD_SET(i, hdr_encode_map);
433 FD_SET(i, url_encode_map);
434 }
435
436 tmp = "\"#{|}";
437 while (*tmp) {
438 FD_SET(*tmp, hdr_encode_map);
439 tmp++;
440 }
441
442 tmp = "\"#";
443 while (*tmp) {
444 FD_SET(*tmp, url_encode_map);
445 tmp++;
446 }
447}
448
449/*
450 * Local variables:
451 * c-indent-level: 8
452 * c-basic-offset: 8
453 * End:
454 */