blob: 5566d1a449dd36f4612c615419e7e0615eb2293e [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 Tarreaufb278672006-10-15 15:38:50 +020025#include <common/time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026
27#include <types/backend.h>
28#include <types/global.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020029#include <types/log.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020030#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 */
Willy Tarreaub8750a82006-09-03 09:56:00 +020054const char sess_fin_state[8] = "-RCHDLQT"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, Tarpit */
Willy Tarreaubaaee002006-06-26 02:48:02 +020055
56/*
57 * Displays the message on stderr with the date and pid. Overrides the quiet
58 * mode during startup.
59 */
Willy Tarreaub17916e2006-10-15 15:17:57 +020060void Alert(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +020061{
62 va_list argp;
Willy Tarreaufe944602007-10-25 10:34:16 +020063 struct tm tm;
Willy Tarreaubaaee002006-06-26 02:48:02 +020064
65 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
66 va_start(argp, fmt);
67
Willy Tarreaufe944602007-10-25 10:34:16 +020068 get_localtime(now.tv_sec, &tm);
Willy Tarreaubaaee002006-06-26 02:48:02 +020069 fprintf(stderr, "[ALERT] %03d/%02d%02d%02d (%d) : ",
Willy Tarreaufe944602007-10-25 10:34:16 +020070 tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
Willy Tarreaubaaee002006-06-26 02:48:02 +020071 vfprintf(stderr, fmt, argp);
72 fflush(stderr);
73 va_end(argp);
74 }
75}
76
77
78/*
79 * Displays the message on stderr with the date and pid.
80 */
Willy Tarreaub17916e2006-10-15 15:17:57 +020081void Warning(const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +020082{
83 va_list argp;
Willy Tarreaufe944602007-10-25 10:34:16 +020084 struct tm tm;
Willy Tarreaubaaee002006-06-26 02:48:02 +020085
86 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
87 va_start(argp, fmt);
88
Willy Tarreaufe944602007-10-25 10:34:16 +020089 get_localtime(now.tv_sec, &tm);
Willy Tarreaubaaee002006-06-26 02:48:02 +020090 fprintf(stderr, "[WARNING] %03d/%02d%02d%02d (%d) : ",
Willy Tarreaufe944602007-10-25 10:34:16 +020091 tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)getpid());
Willy Tarreaubaaee002006-06-26 02:48:02 +020092 vfprintf(stderr, fmt, argp);
93 fflush(stderr);
94 va_end(argp);
95 }
96}
97
98/*
99 * Displays the message on <out> only if quiet mode is not set.
100 */
Willy Tarreaub17916e2006-10-15 15:17:57 +0200101void qfprintf(FILE *out, const char *fmt, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200102{
103 va_list argp;
104
105 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
106 va_start(argp, fmt);
107 vfprintf(out, fmt, argp);
108 fflush(out);
109 va_end(argp);
110 }
111}
112
113/*
114 * returns log level for <lev> or -1 if not found.
115 */
116int get_log_level(const char *lev)
117{
118 int level;
119
120 level = NB_LOG_LEVELS - 1;
121 while (level >= 0 && strcmp(log_levels[level], lev))
122 level--;
123
124 return level;
125}
126
127
128/*
129 * returns log facility for <fac> or -1 if not found.
130 */
131int get_log_facility(const char *fac)
132{
133 int facility;
134
135 facility = NB_LOG_FACILITIES - 1;
136 while (facility >= 0 && strcmp(log_facilities[facility], fac))
137 facility--;
138
139 return facility;
140}
141
142
Willy Tarreaubaaee002006-06-26 02:48:02 +0200143/*
144 * This function sends a syslog message to both log servers of a proxy,
145 * or to global log servers if the proxy is NULL.
146 * It also tries not to waste too much time computing the message header.
147 * It doesn't care about errors nor does it report them.
148 */
Willy Tarreaub17916e2006-10-15 15:17:57 +0200149void send_log(struct proxy *p, int level, const char *message, ...)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200150{
151 static int logfd = -1; /* syslog UDP socket */
152 static long tvsec = -1; /* to force the string to be initialized */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200153 va_list argp;
154 static char logmsg[MAX_SYSLOG_LEN];
155 static char *dataptr = NULL;
156 int fac_level;
157 int hdr_len, data_len;
158 struct sockaddr_in *sa[2];
159 int facilities[2], loglevel[2];
160 int nbloggers = 0;
161 char *log_ptr;
162
163 if (logfd < 0) {
164 if ((logfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
165 return;
Willy Tarreau3d089532007-09-17 10:56:13 +0200166 /* we don't want to receive anything on this socket */
167 setsockopt(logfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
168 shutdown(logfd, SHUT_RD); /* does nothing under Linux, maybe needed for others */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200169 }
170
171 if (level < 0 || progname == NULL || message == NULL)
172 return;
173
Willy Tarreau2b35c952006-10-15 15:25:48 +0200174 if (now.tv_sec != tvsec || dataptr == NULL) {
Willy Tarreaubaaee002006-06-26 02:48:02 +0200175 /* this string is rebuild only once a second */
Willy Tarreaufe944602007-10-25 10:34:16 +0200176 struct tm tm;
177
Willy Tarreau2b35c952006-10-15 15:25:48 +0200178 tvsec = now.tv_sec;
Willy Tarreaufe944602007-10-25 10:34:16 +0200179 get_localtime(tvsec, &tm);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200180
181 hdr_len = snprintf(logmsg, sizeof(logmsg),
182 "<<<<>%s %2d %02d:%02d:%02d %s[%d]: ",
Willy Tarreaufe944602007-10-25 10:34:16 +0200183 monthname[tm.tm_mon],
184 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
Willy Tarreaubaaee002006-06-26 02:48:02 +0200185 progname, pid);
186 /* WARNING: depending upon implementations, snprintf may return
187 * either -1 or the number of bytes that would be needed to store
188 * the total message. In both cases, we must adjust it.
189 */
190 if (hdr_len < 0 || hdr_len > sizeof(logmsg))
191 hdr_len = sizeof(logmsg);
192
193 dataptr = logmsg + hdr_len;
194 }
195
196 va_start(argp, message);
Willy Tarreau8d5d7f22007-01-21 19:16:41 +0100197 /*
198 * FIXME: we take a huge performance hit here. We might have to replace
199 * vsnprintf() for a hard-coded log writer.
200 */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200201 data_len = vsnprintf(dataptr, logmsg + sizeof(logmsg) - dataptr, message, argp);
202 if (data_len < 0 || data_len > (logmsg + sizeof(logmsg) - dataptr))
203 data_len = logmsg + sizeof(logmsg) - dataptr;
204 va_end(argp);
205 dataptr[data_len - 1] = '\n'; /* force a break on ultra-long lines */
206
207 if (p == NULL) {
208 if (global.logfac1 >= 0) {
209 sa[nbloggers] = &global.logsrv1;
210 facilities[nbloggers] = global.logfac1;
211 loglevel[nbloggers] = global.loglev1;
212 nbloggers++;
213 }
214 if (global.logfac2 >= 0) {
215 sa[nbloggers] = &global.logsrv2;
216 facilities[nbloggers] = global.logfac2;
217 loglevel[nbloggers] = global.loglev2;
218 nbloggers++;
219 }
220 } else {
221 if (p->logfac1 >= 0) {
222 sa[nbloggers] = &p->logsrv1;
223 facilities[nbloggers] = p->logfac1;
224 loglevel[nbloggers] = p->loglev1;
225 nbloggers++;
226 }
227 if (p->logfac2 >= 0) {
228 sa[nbloggers] = &p->logsrv2;
229 facilities[nbloggers] = p->logfac2;
230 loglevel[nbloggers] = p->loglev2;
231 nbloggers++;
232 }
233 }
234
235 while (nbloggers-- > 0) {
236 /* we can filter the level of the messages that are sent to each logger */
237 if (level > loglevel[nbloggers])
238 continue;
239
240 /* For each target, we may have a different facility.
241 * We can also have a different log level for each message.
242 * This induces variations in the message header length.
243 * Since we don't want to recompute it each time, nor copy it every
244 * time, we only change the facility in the pre-computed header,
245 * and we change the pointer to the header accordingly.
246 */
247 fac_level = (facilities[nbloggers] << 3) + level;
248 log_ptr = logmsg + 3; /* last digit of the log level */
249 do {
250 *log_ptr = '0' + fac_level % 10;
251 fac_level /= 10;
252 log_ptr--;
253 } while (fac_level && log_ptr > logmsg);
254 *log_ptr = '<';
255
256 /* the total syslog message now starts at logptr, for dataptr+data_len-logptr */
257
258#ifndef MSG_NOSIGNAL
259 sendto(logfd, log_ptr, dataptr + data_len - log_ptr, MSG_DONTWAIT,
260 (struct sockaddr *)sa[nbloggers], sizeof(**sa));
261#else
262 sendto(logfd, log_ptr, dataptr + data_len - log_ptr, MSG_DONTWAIT | MSG_NOSIGNAL,
263 (struct sockaddr *)sa[nbloggers], sizeof(**sa));
264#endif
265 }
266}
267
268
269/*
270 * send a log for the session when we have enough info about it
271 */
Willy Tarreau42250582007-04-01 01:30:43 +0200272void tcp_sess_log(struct session *s)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200273{
274 char pn[INET6_ADDRSTRLEN + strlen(":65535")];
Willy Tarreau73de9892006-11-30 11:40:23 +0100275 struct proxy *fe = s->fe;
Willy Tarreauddb358d2006-12-17 22:55:52 +0100276 struct proxy *be = s->be;
277 struct proxy *prx_log;
Willy Tarreau42250582007-04-01 01:30:43 +0200278 int tolog;
279 char *svid;
Willy Tarreaufe944602007-10-25 10:34:16 +0200280 struct tm tm;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200281
Willy Tarreaubaaee002006-06-26 02:48:02 +0200282 if (s->cli_addr.ss_family == AF_INET)
283 inet_ntop(AF_INET,
284 (const void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr,
285 pn, sizeof(pn));
286 else
287 inet_ntop(AF_INET6,
288 (const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr,
289 pn, sizeof(pn));
290
Willy Tarreaufe944602007-10-25 10:34:16 +0200291 get_localtime(s->logs.tv_accept.tv_sec, &tm);
Willy Tarreauddb358d2006-12-17 22:55:52 +0100292
293 if (fe->logfac1 >= 0)
294 prx_log = fe;
Willy Tarreau97de6242006-12-27 17:18:38 +0100295 /*
296 * FIXME: should we fall back to the backend if the frontend did not
297 * define any log ? It seems like we should not permit such complex
298 * setups because they would induce a debugging nightmare for the
299 * admin.
300 */
301 // else if (be->logfac1 >= 0)
302 // prx_log = be;
Willy Tarreauddb358d2006-12-17 22:55:52 +0100303 else
304 prx_log = NULL; /* global */
305
Willy Tarreau42250582007-04-01 01:30:43 +0200306 /* FIXME: let's limit ourselves to frontend logging for now. */
307 tolog = fe->to_log;
308 svid = (tolog & LW_SVID) ? (s->srv != NULL) ? s->srv->id : "<NOSRV>" : "-";
Willy Tarreaubaaee002006-06-26 02:48:02 +0200309
Willy Tarreau42250582007-04-01 01:30:43 +0200310 send_log(prx_log, LOG_INFO, "%s:%d [%02d/%s/%04d:%02d:%02d:%02d.%03d]"
311 " %s %s/%s %d/%d/%s%d %s%lld"
312 " %c%c %d/%d/%d/%d %d/%d\n",
313 pn,
314 (s->cli_addr.ss_family == AF_INET) ?
315 ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port) :
316 ntohs(((struct sockaddr_in6 *)&s->cli_addr)->sin6_port),
Willy Tarreaufe944602007-10-25 10:34:16 +0200317 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
318 tm.tm_hour, tm.tm_min, tm.tm_sec, s->logs.tv_accept.tv_usec/1000,
Willy Tarreau42250582007-04-01 01:30:43 +0200319 fe->id, be->id, svid,
320 (s->logs.t_queue >= 0) ? s->logs.t_queue : -1,
321 (s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_queue : -1,
322 (tolog & LW_BYTES) ? "" : "+", s->logs.t_close,
323 (tolog & LW_BYTES) ? "" : "+", s->logs.bytes_in,
324 sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT],
325 sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT],
326 actconn, fe->feconn, be->beconn, s->srv ? s->srv->cur_sess : 0,
327 s->logs.srv_queue_size, s->logs.prx_queue_size);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200328
329 s->logs.logwait = 0;
330}
331
Willy Tarreaubaaee002006-06-26 02:48:02 +0200332
333/*
334 * Local variables:
335 * c-indent-level: 8
336 * c-basic-offset: 8
337 * End:
338 */