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