blob: 32ca6709937f053c43599fa1297410988abaa6ff [file] [log] [blame]
Willy Tarreau72c28532009-01-22 18:56:50 +01001/*
Willy Tarreaud8fc1102010-09-12 17:56:16 +02002 * haproxy log statistics reporter
Willy Tarreau72c28532009-01-22 18:56:50 +01003 *
Willy Tarreaud2201062010-05-27 18:17:30 +02004 * Copyright 2000-2010 Willy Tarreau <w@1wt.eu>
Willy Tarreau72c28532009-01-22 18:56:50 +01005 *
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 Tarreau72c28532009-01-22 18:56:50 +010013#include <errno.h>
14#include <fcntl.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <syslog.h>
18#include <string.h>
19#include <unistd.h>
20#include <ctype.h>
21
Willy Tarreau45cb4fb2009-10-26 21:10:04 +010022#include <eb32tree.h>
Willy Tarreauabe45b62010-10-28 20:33:46 +020023#include <eb64tree.h>
24#include <ebistree.h>
Willy Tarreaud2201062010-05-27 18:17:30 +020025#include <ebsttree.h>
Willy Tarreau72c28532009-01-22 18:56:50 +010026
Willy Tarreaud2201062010-05-27 18:17:30 +020027#define SOURCE_FIELD 5
Willy Tarreau72c28532009-01-22 18:56:50 +010028#define ACCEPT_FIELD 6
Willy Tarreaud2201062010-05-27 18:17:30 +020029#define SERVER_FIELD 8
Willy Tarreau72c28532009-01-22 18:56:50 +010030#define TIME_FIELD 9
31#define STATUS_FIELD 10
Willy Tarreaud8fc1102010-09-12 17:56:16 +020032#define TERM_CODES_FIELD 14
Willy Tarreau72c28532009-01-22 18:56:50 +010033#define CONN_FIELD 15
Willy Tarreauabe45b62010-10-28 20:33:46 +020034#define METH_FIELD 17
35#define URL_FIELD 18
Willy Tarreau72c28532009-01-22 18:56:50 +010036#define MAXLINE 16384
37#define QBITS 4
38
Willy Tarreaudf6f0d12011-07-10 18:15:08 +020039#define SEP(c) ((unsigned char)(c) <= ' ')
40#define SKIP_CHAR(p,c) do { while (1) { int __c = (unsigned char)*p++; if (__c == c) break; if (__c <= ' ') { p--; break; } } } while (0)
Willy Tarreau72c28532009-01-22 18:56:50 +010041
42/* [0] = err/date, [1] = req, [2] = conn, [3] = resp, [4] = data */
43static struct eb_root timers[5] = {
44 EB_ROOT_UNIQUE, EB_ROOT_UNIQUE, EB_ROOT_UNIQUE,
45 EB_ROOT_UNIQUE, EB_ROOT_UNIQUE,
46};
47
48struct timer {
49 struct eb32_node node;
50 unsigned int count;
51};
52
Willy Tarreaud2201062010-05-27 18:17:30 +020053struct srv_st {
54 unsigned int st_cnt[6]; /* 0xx to 5xx */
55 unsigned int nb_ct, nb_rt, nb_ok;
56 unsigned long long cum_ct, cum_rt;
57 struct ebmb_node node;
58 /* don't put anything else here, the server name will be there */
59};
Willy Tarreau72c28532009-01-22 18:56:50 +010060
Willy Tarreauabe45b62010-10-28 20:33:46 +020061struct url_stat {
62 union {
63 struct ebpt_node url;
64 struct eb64_node val;
65 } node;
66 char *url;
67 unsigned long long total_time; /* sum(all reqs' times) */
68 unsigned long long total_time_ok; /* sum(all OK reqs' times) */
69 unsigned int nb_err, nb_req;
70};
71
Willy Tarreau72c28532009-01-22 18:56:50 +010072#define FILT_COUNT_ONLY 0x01
73#define FILT_INVERT 0x02
74#define FILT_QUIET 0x04
75#define FILT_ERRORS_ONLY 0x08
76#define FILT_ACC_DELAY 0x10
77#define FILT_ACC_COUNT 0x20
78#define FILT_GRAPH_TIMERS 0x40
Willy Tarreau214c2032009-02-20 11:02:32 +010079#define FILT_PERCENTILE 0x80
Willy Tarreau5bdfd962009-10-14 15:16:29 +020080#define FILT_TIME_RESP 0x100
81
82#define FILT_INVERT_ERRORS 0x200
83#define FILT_INVERT_TIME_RESP 0x400
Willy Tarreau72c28532009-01-22 18:56:50 +010084
Willy Tarreau0f423a72010-05-03 10:50:54 +020085#define FILT_COUNT_STATUS 0x800
Willy Tarreaud2201062010-05-27 18:17:30 +020086#define FILT_COUNT_SRV_STATUS 0x1000
Willy Tarreaud8fc1102010-09-12 17:56:16 +020087#define FILT_COUNT_TERM_CODES 0x2000
Willy Tarreau0f423a72010-05-03 10:50:54 +020088
Willy Tarreauabe45b62010-10-28 20:33:46 +020089#define FILT_COUNT_URL_ONLY 0x004000
90#define FILT_COUNT_URL_COUNT 0x008000
91#define FILT_COUNT_URL_ERR 0x010000
92#define FILT_COUNT_URL_TTOT 0x020000
93#define FILT_COUNT_URL_TAVG 0x040000
94#define FILT_COUNT_URL_TTOTO 0x080000
95#define FILT_COUNT_URL_TAVGO 0x100000
96#define FILT_COUNT_URL_ANY (FILT_COUNT_URL_ONLY|FILT_COUNT_URL_COUNT|FILT_COUNT_URL_ERR| \
97 FILT_COUNT_URL_TTOT|FILT_COUNT_URL_TAVG|FILT_COUNT_URL_TTOTO|FILT_COUNT_URL_TAVGO)
98
Willy Tarreau70c428f2011-07-10 17:27:40 +020099#define FILT_HTTP_ONLY 0x200000
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200100#define FILT_TERM_CODE_NAME 0x400000
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200101#define FILT_INVERT_TERM_CODE_NAME 0x800000
Willy Tarreau70c428f2011-07-10 17:27:40 +0200102
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200103#define FILT_HTTP_STATUS 0x1000000
104#define FILT_INVERT_HTTP_STATUS 0x2000000
105
Willy Tarreau72c28532009-01-22 18:56:50 +0100106unsigned int filter = 0;
107unsigned int filter_invert = 0;
Willy Tarreau214c2032009-02-20 11:02:32 +0100108const char *line;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200109int linenum = 0;
110int parse_err = 0;
111int lines_out = 0;
Willy Tarreau72c28532009-01-22 18:56:50 +0100112
Willy Tarreau214c2032009-02-20 11:02:32 +0100113const char *fgets2(FILE *stream);
Willy Tarreau72c28532009-01-22 18:56:50 +0100114
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200115void filter_count_url(const char *accept_field, const char *time_field, struct timer **tptr);
116void filter_count_srv_status(const char *accept_field, const char *time_field, struct timer **tptr);
117void filter_count_term_codes(const char *accept_field, const char *time_field, struct timer **tptr);
118void filter_count_status(const char *accept_field, const char *time_field, struct timer **tptr);
119void filter_graphs(const char *accept_field, const char *time_field, struct timer **tptr);
120void filter_output_line(const char *accept_field, const char *time_field, struct timer **tptr);
121void filter_accept_holes(const char *accept_field, const char *time_field, struct timer **tptr);
122
Willy Tarreau72c28532009-01-22 18:56:50 +0100123void die(const char *msg)
124{
125 fprintf(stderr,
126 "%s"
Willy Tarreauabe45b62010-10-28 20:33:46 +0200127 "Usage: halog [-q] [-c] [-v] {-gt|-pct|-st|-tc|-srv|-u|-uc|-ue|-ua|-ut|-uao|-uto}\n"
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200128 " [-s <skip>] [-e|-E] [-H] [-rt|-RT <time>] [-ad <delay>] [-ac <count>]\n"
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200129 " [-tcn|-TCN <termcode>] [ -hs|-HS [min][:[max]] ] < log\n"
Willy Tarreau72c28532009-01-22 18:56:50 +0100130 "\n",
131 msg ? msg : ""
132 );
133 exit(1);
134}
135
136
137/* return pointer to first char not part of current field starting at <p>. */
138const char *field_stop(const char *p)
139{
140 unsigned char c;
141
142 while (1) {
143 c = *(p++);
144 if (c > ' ')
145 continue;
Willy Tarreau14389e72011-07-10 22:11:17 +0200146 if (c == ' ' || c == 0)
Willy Tarreau72c28532009-01-22 18:56:50 +0100147 break;
148 }
149 return p - 1;
150}
151
152/* return field <field> (starting from 1) in string <p>. Only consider
153 * contiguous spaces (or tabs) as one delimiter. May return pointer to
154 * last char if field is not found. Equivalent to awk '{print $field}'.
155 */
156const char *field_start(const char *p, int field)
157{
158 unsigned char c;
159 while (1) {
160 /* skip spaces */
161 while (1) {
162 c = *p;
163 if (c > ' ')
164 break;
Willy Tarreau14389e72011-07-10 22:11:17 +0200165 if (c == ' ')
Willy Tarreau72c28532009-01-22 18:56:50 +0100166 goto next;
167 if (!c) /* end of line */
168 return p;
169 /* other char => new field */
170 break;
171 next:
172 p++;
173 }
174
175 /* start of field */
176 field--;
177 if (!field)
178 return p;
179
180 /* skip this field */
181 while (1) {
182 c = *(p++);
183 if (c > ' ')
184 continue;
Willy Tarreau14389e72011-07-10 22:11:17 +0200185 if (c == ' ')
Willy Tarreau72c28532009-01-22 18:56:50 +0100186 break;
187 if (c == '\0')
188 return p;
189 }
190 }
191}
192
193/* keep only the <bits> higher bits of <i> */
194static inline unsigned int quantify_u32(unsigned int i, int bits)
195{
196 int high;
197
198 if (!bits)
199 return 0;
200
201 if (i)
202 high = fls_auto(i); // 1 to 32
203 else
204 high = 0;
205
206 if (high <= bits)
207 return i;
208
209 return i & ~((1 << (high - bits)) - 1);
210}
211
212/* keep only the <bits> higher bits of the absolute value of <i>, as well as
213 * its sign. */
214static inline int quantify(int i, int bits)
215{
216 if (i >= 0)
217 return quantify_u32(i, bits);
218 else
219 return -quantify_u32(-i, bits);
220}
221
222/* Insert timer value <v> into tree <r>. A pre-allocated node must be passed
223 * in <alloc>. It may be NULL, in which case the function will allocate it
224 * itself. It will be reset to NULL once consumed. The caller is responsible
225 * for freeing the node once not used anymore. The node where the value was
226 * inserted is returned.
227 */
228struct timer *insert_timer(struct eb_root *r, struct timer **alloc, int v)
229{
230 struct timer *t = *alloc;
231 struct eb32_node *n;
232
233 if (!t) {
234 t = calloc(sizeof(*t), 1);
235 if (unlikely(!t)) {
236 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
237 exit(1);
238 }
239 }
240 t->node.key = quantify(v, QBITS); // keep only the higher QBITS bits
241
242 n = eb32i_insert(r, &t->node);
243 if (n == &t->node)
244 t = NULL; /* node inserted, will malloc next time */
245
246 *alloc = t;
247 return container_of(n, struct timer, node);
248}
249
250/* Insert value value <v> into tree <r>. A pre-allocated node must be passed
251 * in <alloc>. It may be NULL, in which case the function will allocate it
252 * itself. It will be reset to NULL once consumed. The caller is responsible
253 * for freeing the node once not used anymore. The node where the value was
254 * inserted is returned.
255 */
256struct timer *insert_value(struct eb_root *r, struct timer **alloc, int v)
257{
258 struct timer *t = *alloc;
259 struct eb32_node *n;
260
261 if (!t) {
262 t = calloc(sizeof(*t), 1);
263 if (unlikely(!t)) {
264 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
265 exit(1);
266 }
267 }
268 t->node.key = v;
269
270 n = eb32i_insert(r, &t->node);
271 if (n == &t->node)
272 t = NULL; /* node inserted, will malloc next time */
273
274 *alloc = t;
275 return container_of(n, struct timer, node);
276}
277
278int str2ic(const char *s)
279{
280 int i = 0;
281 int j, k;
282
283 if (*s != '-') {
284 /* positive number */
285 while (1) {
286 j = (*s++) - '0';
287 k = i * 10;
288 if ((unsigned)j > 9)
289 break;
290 i = k + j;
291 }
292 } else {
293 /* negative number */
294 s++;
295 while (1) {
296 j = (*s++) - '0';
297 k = i * 10;
298 if ((unsigned)j > 9)
299 break;
300 i = k - j;
301 }
302 }
303
304 return i;
305}
306
307
308/* Equivalent to strtoul with a length. */
309static inline unsigned int __strl2ui(const char *s, int len)
310{
311 unsigned int i = 0;
312 while (len-- > 0) {
313 i = i * 10 - '0';
314 i += (unsigned char)*s++;
315 }
316 return i;
317}
318
319unsigned int strl2ui(const char *s, int len)
320{
321 return __strl2ui(s, len);
322}
323
324/* Convert "[04/Dec/2008:09:49:40.555]" to an integer equivalent to the time of
325 * the day in milliseconds. It returns -1 for all unparsable values. The parser
326 * looks ugly but gcc emits far better code that way.
327 */
328int convert_date(const char *field)
329{
330 unsigned int h, m, s, ms;
331 unsigned char c;
332 const char *b, *e;
333
334 h = m = s = ms = 0;
335 e = field;
336
337 /* skip the date */
338 while (1) {
339 c = *(e++);
340 if (c == ':')
341 break;
342 if (!c)
343 goto out_err;
344 }
345
346 /* hour + ':' */
347 b = e;
348 while (1) {
349 c = *(e++) - '0';
350 if (c > 9)
351 break;
352 h = h * 10 + c;
353 }
354 if (c == (unsigned char)(0 - '0'))
355 goto out_err;
356
357 /* minute + ':' */
358 b = e;
359 while (1) {
360 c = *(e++) - '0';
361 if (c > 9)
362 break;
363 m = m * 10 + c;
364 }
365 if (c == (unsigned char)(0 - '0'))
366 goto out_err;
367
368 /* second + '.' or ']' */
369 b = e;
370 while (1) {
371 c = *(e++) - '0';
372 if (c > 9)
373 break;
374 s = s * 10 + c;
375 }
376 if (c == (unsigned char)(0 - '0'))
377 goto out_err;
378
379 /* if there's a '.', we have milliseconds */
380 if (c == (unsigned char)('.' - '0')) {
381 /* millisecond second + ']' */
382 b = e;
383 while (1) {
384 c = *(e++) - '0';
385 if (c > 9)
386 break;
387 ms = ms * 10 + c;
388 }
389 if (c == (unsigned char)(0 - '0'))
390 goto out_err;
391 }
392 return (((h * 60) + m) * 60 + s) * 1000 + ms;
393 out_err:
394 return -1;
395}
396
397void truncated_line(int linenum, const char *line)
398{
399 if (!(filter & FILT_QUIET))
400 fprintf(stderr, "Truncated line %d: %s\n", linenum, line);
401}
402
403int main(int argc, char **argv)
404{
Willy Tarreau26deaf52011-07-10 19:47:48 +0200405 const char *b, *e, *p, *time_field, *accept_field;
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200406 const char *filter_term_code_name = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100407 const char *output_file = NULL;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200408 int f, last, err;
409 struct timer *t = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100410 struct eb32_node *n;
Willy Tarreauabe45b62010-10-28 20:33:46 +0200411 struct url_stat *ustat = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100412 int val, test;
Willy Tarreau72c28532009-01-22 18:56:50 +0100413 int filter_acc_delay = 0, filter_acc_count = 0;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200414 int filter_time_resp = 0;
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200415 int filt_http_status_low = 0, filt_http_status_high = 0;
Willy Tarreau72c28532009-01-22 18:56:50 +0100416 int skip_fields = 1;
417
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200418 void (*line_filter)(const char *accept_field, const char *time_field, struct timer **tptr) = NULL;
419
Willy Tarreau72c28532009-01-22 18:56:50 +0100420 argc--; argv++;
421 while (argc > 0) {
422 if (*argv[0] != '-')
423 break;
424
425 if (strcmp(argv[0], "-ad") == 0) {
426 if (argc < 2) die("missing option for -ad");
427 argc--; argv++;
428 filter |= FILT_ACC_DELAY;
429 filter_acc_delay = atol(*argv);
430 }
431 else if (strcmp(argv[0], "-ac") == 0) {
432 if (argc < 2) die("missing option for -ac");
433 argc--; argv++;
434 filter |= FILT_ACC_COUNT;
435 filter_acc_count = atol(*argv);
436 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200437 else if (strcmp(argv[0], "-rt") == 0) {
438 if (argc < 2) die("missing option for -rt");
439 argc--; argv++;
440 filter |= FILT_TIME_RESP;
441 filter_time_resp = atol(*argv);
442 }
443 else if (strcmp(argv[0], "-RT") == 0) {
444 if (argc < 2) die("missing option for -RT");
445 argc--; argv++;
446 filter |= FILT_TIME_RESP | FILT_INVERT_TIME_RESP;
447 filter_time_resp = atol(*argv);
448 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100449 else if (strcmp(argv[0], "-s") == 0) {
450 if (argc < 2) die("missing option for -s");
451 argc--; argv++;
452 skip_fields = atol(*argv);
453 }
454 else if (strcmp(argv[0], "-e") == 0)
455 filter |= FILT_ERRORS_ONLY;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200456 else if (strcmp(argv[0], "-E") == 0)
457 filter |= FILT_ERRORS_ONLY | FILT_INVERT_ERRORS;
Willy Tarreau70c428f2011-07-10 17:27:40 +0200458 else if (strcmp(argv[0], "-H") == 0)
459 filter |= FILT_HTTP_ONLY;
Willy Tarreau72c28532009-01-22 18:56:50 +0100460 else if (strcmp(argv[0], "-c") == 0)
461 filter |= FILT_COUNT_ONLY;
462 else if (strcmp(argv[0], "-q") == 0)
463 filter |= FILT_QUIET;
464 else if (strcmp(argv[0], "-v") == 0)
465 filter_invert = !filter_invert;
466 else if (strcmp(argv[0], "-gt") == 0)
467 filter |= FILT_GRAPH_TIMERS;
Willy Tarreau214c2032009-02-20 11:02:32 +0100468 else if (strcmp(argv[0], "-pct") == 0)
469 filter |= FILT_PERCENTILE;
Willy Tarreau0f423a72010-05-03 10:50:54 +0200470 else if (strcmp(argv[0], "-st") == 0)
471 filter |= FILT_COUNT_STATUS;
Willy Tarreaud2201062010-05-27 18:17:30 +0200472 else if (strcmp(argv[0], "-srv") == 0)
473 filter |= FILT_COUNT_SRV_STATUS;
Willy Tarreaud8fc1102010-09-12 17:56:16 +0200474 else if (strcmp(argv[0], "-tc") == 0)
475 filter |= FILT_COUNT_TERM_CODES;
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200476 else if (strcmp(argv[0], "-tcn") == 0) {
477 if (argc < 2) die("missing option for -tcn");
478 argc--; argv++;
479 filter |= FILT_TERM_CODE_NAME;
480 filter_term_code_name = *argv;
481 }
482 else if (strcmp(argv[0], "-TCN") == 0) {
483 if (argc < 2) die("missing option for -TCN");
484 argc--; argv++;
485 filter |= FILT_TERM_CODE_NAME | FILT_INVERT_TERM_CODE_NAME;
486 filter_term_code_name = *argv;
487 }
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200488 else if (strcmp(argv[0], "-hs") == 0 || strcmp(argv[0], "-HS") == 0) {
489 char *sep, *str;
490
491 if (argc < 2) die("missing option for -hs/-HS ([min]:[max])");
492 filter |= FILT_HTTP_STATUS;
493 if (argv[0][1] == 'H')
494 filter |= FILT_INVERT_HTTP_STATUS;
495
496 argc--; argv++;
497 str = *argv;
498 sep = strchr(str, ':'); /* [min]:[max] */
499 if (!sep)
500 sep = str; /* make max point to min */
501 else
502 *sep++ = 0;
503 filt_http_status_low = *str ? atol(str) : 0;
504 filt_http_status_high = *sep ? atol(sep) : 65535;
505 }
Willy Tarreauabe45b62010-10-28 20:33:46 +0200506 else if (strcmp(argv[0], "-u") == 0)
507 filter |= FILT_COUNT_URL_ONLY;
508 else if (strcmp(argv[0], "-uc") == 0)
509 filter |= FILT_COUNT_URL_COUNT;
510 else if (strcmp(argv[0], "-ue") == 0)
511 filter |= FILT_COUNT_URL_ERR;
512 else if (strcmp(argv[0], "-ua") == 0)
513 filter |= FILT_COUNT_URL_TAVG;
514 else if (strcmp(argv[0], "-ut") == 0)
515 filter |= FILT_COUNT_URL_TTOT;
516 else if (strcmp(argv[0], "-uao") == 0)
517 filter |= FILT_COUNT_URL_TAVGO;
518 else if (strcmp(argv[0], "-uto") == 0)
519 filter |= FILT_COUNT_URL_TTOTO;
Willy Tarreau72c28532009-01-22 18:56:50 +0100520 else if (strcmp(argv[0], "-o") == 0) {
521 if (output_file)
522 die("Fatal: output file name already specified.\n");
523 if (argc < 2)
524 die("Fatal: missing output file name.\n");
525 output_file = argv[1];
526 }
527 argc--;
528 argv++;
529 }
530
531 if (!filter)
532 die("No action specified.\n");
533
534 if (filter & FILT_ACC_COUNT && !filter_acc_count)
535 filter_acc_count=1;
536
537 if (filter & FILT_ACC_DELAY && !filter_acc_delay)
538 filter_acc_delay = 1;
539
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200540
541 /* by default, all lines are printed */
542 line_filter = filter_output_line;
543 if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY))
544 line_filter = filter_accept_holes;
545 else if (filter & (FILT_GRAPH_TIMERS|FILT_PERCENTILE))
546 line_filter = filter_graphs;
547 else if (filter & FILT_COUNT_STATUS)
548 line_filter = filter_count_status;
549 else if (filter & FILT_COUNT_TERM_CODES)
550 line_filter = filter_count_term_codes;
551 else if (filter & FILT_COUNT_SRV_STATUS)
552 line_filter = filter_count_srv_status;
553 else if (filter & FILT_COUNT_URL_ANY)
554 line_filter = filter_count_url;
555 else if (filter & FILT_COUNT_ONLY)
556 line_filter = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100557
Willy Tarreau214c2032009-02-20 11:02:32 +0100558 while ((line = fgets2(stdin)) != NULL) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100559 linenum++;
Willy Tarreau26deaf52011-07-10 19:47:48 +0200560 time_field = NULL; accept_field = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100561
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200562 test = 1;
Willy Tarreau26deaf52011-07-10 19:47:48 +0200563
564 /* for any line we process, we first ensure that there is a field
565 * looking like the accept date field (beginning with a '[').
566 */
567 accept_field = field_start(line, ACCEPT_FIELD + skip_fields);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200568 if (unlikely(*accept_field != '[')) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200569 parse_err++;
570 continue;
571 }
572
573 /* the day of month field is begin 01 and 31 */
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200574 if (accept_field[1] < '0' || accept_field[1] > '3') {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200575 parse_err++;
576 continue;
577 }
578
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200579 if (filter & FILT_HTTP_ONLY) {
Willy Tarreau70c428f2011-07-10 17:27:40 +0200580 /* only report lines with at least 4 timers */
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200581 if (!time_field) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200582 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200583 if (unlikely(!*time_field)) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200584 truncated_line(linenum, line);
585 continue;
586 }
Willy Tarreau70c428f2011-07-10 17:27:40 +0200587 }
588
Willy Tarreau758a6ea2011-07-10 18:53:44 +0200589 e = field_stop(time_field + 1);
590 /* we have field TIME_FIELD in [time_field]..[e-1] */
591 p = time_field;
Willy Tarreau70c428f2011-07-10 17:27:40 +0200592 f = 0;
Willy Tarreaudf6f0d12011-07-10 18:15:08 +0200593 while (!SEP(*p)) {
Willy Tarreau70c428f2011-07-10 17:27:40 +0200594 if (++f == 4)
595 break;
596 SKIP_CHAR(p, '/');
597 }
598 test &= (f >= 4);
599 }
600
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200601 if (filter & FILT_TIME_RESP) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200602 int tps;
603
604 /* only report lines with response times larger than filter_time_resp */
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200605 if (!time_field) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200606 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200607 if (unlikely(!*time_field)) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200608 truncated_line(linenum, line);
609 continue;
610 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200611 }
612
Willy Tarreau758a6ea2011-07-10 18:53:44 +0200613 e = field_stop(time_field + 1);
614 /* we have field TIME_FIELD in [time_field]..[e-1], let's check only the response time */
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200615
Willy Tarreau758a6ea2011-07-10 18:53:44 +0200616 p = time_field;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200617 err = 0;
Willy Tarreau24bcb4f2010-10-28 20:39:50 +0200618 f = 0;
Willy Tarreaudf6f0d12011-07-10 18:15:08 +0200619 while (!SEP(*p)) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200620 tps = str2ic(p);
621 if (tps < 0) {
622 tps = -1;
623 err = 1;
624 }
Willy Tarreau24bcb4f2010-10-28 20:39:50 +0200625 if (++f == 4)
626 break;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200627 SKIP_CHAR(p, '/');
628 }
629
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200630 if (unlikely(f < 4)) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200631 parse_err++;
632 continue;
633 }
634
635 test &= (tps >= filter_time_resp) ^ !!(filter & FILT_INVERT_TIME_RESP);
636 }
637
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200638 if (filter & (FILT_ERRORS_ONLY | FILT_HTTP_STATUS)) {
639 /* Check both error codes (-1, 5xx) and status code ranges */
Willy Tarreau26deaf52011-07-10 19:47:48 +0200640 if (time_field)
641 b = field_start(time_field, STATUS_FIELD - TIME_FIELD + 1);
642 else
643 b = field_start(accept_field, STATUS_FIELD - ACCEPT_FIELD + 1);
644
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200645 if (unlikely(!*b)) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100646 truncated_line(linenum, line);
647 continue;
648 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200649
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200650 val = str2ic(b);
651 if (filter & FILT_ERRORS_ONLY)
652 test &= (val < 0 || (val >= 500 && val <= 599)) ^ !!(filter & FILT_INVERT_ERRORS);
653
654 if (filter & FILT_HTTP_STATUS)
655 test &= (val >= filt_http_status_low && val <= filt_http_status_high) ^ !!(filter & FILT_INVERT_HTTP_STATUS);
Willy Tarreau72c28532009-01-22 18:56:50 +0100656 }
657
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200658 if (filter & FILT_TERM_CODE_NAME) {
659 /* only report corresponding termination code name */
660 if (time_field)
661 b = field_start(time_field, TERM_CODES_FIELD - TIME_FIELD + 1);
662 else
663 b = field_start(accept_field, TERM_CODES_FIELD - ACCEPT_FIELD + 1);
664
665 if (unlikely(!*b)) {
666 truncated_line(linenum, line);
667 continue;
668 }
669
670 test &= (b[0] == filter_term_code_name[0] && b[1] == filter_term_code_name[1]) ^ !!(filter & FILT_INVERT_TERM_CODE_NAME);
671 }
672
673
Willy Tarreau0f423a72010-05-03 10:50:54 +0200674 test ^= filter_invert;
675 if (!test)
676 continue;
677
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200678 /************** here we process inputs *******************/
Willy Tarreau72c28532009-01-22 18:56:50 +0100679
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200680 if (line_filter)
681 line_filter(accept_field, time_field, &t);
682 else
683 lines_out++; /* we're just counting lines */
684 }
Willy Tarreauabe45b62010-10-28 20:33:46 +0200685
Willy Tarreauabe45b62010-10-28 20:33:46 +0200686
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200687 /*****************************************************
688 * Here we've finished reading all input. Depending on the
689 * filters, we may still have some analysis to run on the
690 * collected data and to output data in a new format.
691 *************************************************** */
Willy Tarreau72c28532009-01-22 18:56:50 +0100692
693 if (t)
694 free(t);
695
696 if (filter & FILT_COUNT_ONLY) {
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200697 printf("%d\n", lines_out);
Willy Tarreau72c28532009-01-22 18:56:50 +0100698 exit(0);
699 }
700
Willy Tarreau72c28532009-01-22 18:56:50 +0100701 if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY)) {
702 /* sort and count all timers. Output will look like this :
703 * <accept_date> <delta_ms from previous one> <nb entries>
704 */
705 n = eb32_first(&timers[0]);
706
707 if (n)
708 last = n->key;
709 while (n) {
710 unsigned int d, h, m, s, ms;
711
712 t = container_of(n, struct timer, node);
713 h = n->key;
714 d = h - last;
715 last = h;
716
717 if (d >= filter_acc_delay && t->count >= filter_acc_count) {
718 ms = h % 1000; h = h / 1000;
719 s = h % 60; h = h / 60;
720 m = h % 60; h = h / 60;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200721 lines_out++;
Willy Tarreau72c28532009-01-22 18:56:50 +0100722 printf("%02d:%02d:%02d.%03d %d %d %d\n", h, m, s, ms, last, d, t->count);
723 }
724 n = eb32_next(n);
725 }
726 }
727 else if (filter & FILT_GRAPH_TIMERS) {
728 /* sort all timers */
729 for (f = 0; f < 5; f++) {
730 struct eb32_node *n;
731 int val;
732
733 val = 0;
734 n = eb32_first(&timers[f]);
735 while (n) {
736 int i;
737 double d;
738
739 t = container_of(n, struct timer, node);
740 last = n->key;
741 val = t->count;
742
743 i = (last < 0) ? -last : last;
744 i = fls_auto(i) - QBITS;
745
746 if (i > 0)
747 d = val / (double)(1 << i);
748 else
749 d = val;
750
751 if (d > 0.0) {
752 printf("%d %d %f\n", f, last, d+1.0);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200753 lines_out++;
Willy Tarreau72c28532009-01-22 18:56:50 +0100754 }
755
756 n = eb32_next(n);
757 }
Willy Tarreau214c2032009-02-20 11:02:32 +0100758 }
759 }
760 else if (filter & FILT_PERCENTILE) {
761 /* report timers by percentile :
762 * <percent> <total> <max_req_time> <max_conn_time> <max_resp_time> <max_data_time>
763 * We don't count errs.
764 */
765 struct eb32_node *n[5];
766 unsigned long cum[5];
767 double step;
768
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200769 if (!lines_out)
Willy Tarreau910ba4b2009-11-17 10:16:19 +0100770 goto empty;
771
Willy Tarreau214c2032009-02-20 11:02:32 +0100772 for (f = 1; f < 5; f++) {
773 n[f] = eb32_first(&timers[f]);
774 cum[f] = container_of(n[f], struct timer, node)->count;
775 }
776
777 for (step = 1; step <= 1000;) {
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200778 unsigned int thres = lines_out * (step / 1000.0);
Willy Tarreau214c2032009-02-20 11:02:32 +0100779
780 printf("%3.1f %d ", step/10.0, thres);
781 for (f = 1; f < 5; f++) {
782 struct eb32_node *next;
783 while (cum[f] < thres) {
784 /* need to find other keys */
785 next = eb32_next(n[f]);
786 if (!next)
787 break;
788 n[f] = next;
789 cum[f] += container_of(next, struct timer, node)->count;
790 }
791
792 /* value still within $step % of total */
793 printf("%d ", n[f]->key);
794 }
795 putchar('\n');
796 if (step >= 100 && step < 900)
797 step += 50; // jump 5% by 5% between those steps.
798 else if (step >= 20 && step < 980)
799 step += 10;
800 else
801 step += 1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100802 }
803 }
Willy Tarreau0f423a72010-05-03 10:50:54 +0200804 else if (filter & FILT_COUNT_STATUS) {
805 /* output all statuses in the form of <status> <occurrences> */
806 n = eb32_first(&timers[0]);
807 while (n) {
808 t = container_of(n, struct timer, node);
809 printf("%d %d\n", n->key, t->count);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200810 lines_out++;
Willy Tarreau0f423a72010-05-03 10:50:54 +0200811 n = eb32_next(n);
812 }
813 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200814 else if (filter & FILT_COUNT_SRV_STATUS) {
Willy Tarreaud2201062010-05-27 18:17:30 +0200815 struct ebmb_node *srv_node;
816 struct srv_st *srv;
817
818 printf("#srv_name 1xx 2xx 3xx 4xx 5xx other tot_req req_ok pct_ok avg_ct avg_rt\n");
819
820 srv_node = ebmb_first(&timers[0]);
821 while (srv_node) {
822 int tot_rq;
823
824 srv = container_of(srv_node, struct srv_st, node);
825
826 tot_rq = 0;
827 for (f = 0; f <= 5; f++)
828 tot_rq += srv->st_cnt[f];
829
830 printf("%s %d %d %d %d %d %d %d %d %.1f %d %d\n",
831 srv_node->key, srv->st_cnt[1], srv->st_cnt[2],
832 srv->st_cnt[3], srv->st_cnt[4], srv->st_cnt[5], srv->st_cnt[0],
833 tot_rq,
834 srv->nb_ok, (double)srv->nb_ok * 100.0 / (tot_rq?tot_rq:1),
835 (int)(srv->cum_ct / (srv->nb_ct?srv->nb_ct:1)), (int)(srv->cum_rt / (srv->nb_rt?srv->nb_rt:1)));
836 srv_node = ebmb_next(srv_node);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200837 lines_out++;
Willy Tarreaud2201062010-05-27 18:17:30 +0200838 }
839 }
Willy Tarreaud8fc1102010-09-12 17:56:16 +0200840 else if (filter & FILT_COUNT_TERM_CODES) {
841 /* output all statuses in the form of <code> <occurrences> */
842 n = eb32_first(&timers[0]);
843 while (n) {
844 t = container_of(n, struct timer, node);
845 printf("%c%c %d\n", (n->key >> 8), (n->key) & 255, t->count);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200846 lines_out++;
Willy Tarreaud8fc1102010-09-12 17:56:16 +0200847 n = eb32_next(n);
848 }
849 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200850 else if (filter & FILT_COUNT_URL_ANY) {
Willy Tarreauabe45b62010-10-28 20:33:46 +0200851 struct eb_node *node, *next;
852
853 if (!(filter & FILT_COUNT_URL_ONLY)) {
854 /* we have to sort on another criterion. We'll use timers[1] for the
855 * destination tree.
856 */
857
858 timers[1] = EB_ROOT; /* reconfigure to accept duplicates */
859 for (node = eb_first(&timers[0]); node; node = next) {
860 next = eb_next(node);
861 eb_delete(node);
862
863 ustat = container_of(node, struct url_stat, node.url.node);
864
865 if (filter & FILT_COUNT_URL_COUNT)
866 ustat->node.val.key = ustat->nb_req;
867 else if (filter & FILT_COUNT_URL_ERR)
868 ustat->node.val.key = ustat->nb_err;
869 else if (filter & FILT_COUNT_URL_TTOT)
870 ustat->node.val.key = ustat->total_time;
871 else if (filter & FILT_COUNT_URL_TAVG)
872 ustat->node.val.key = ustat->nb_req ? ustat->total_time / ustat->nb_req : 0;
873 else if (filter & FILT_COUNT_URL_TTOTO)
874 ustat->node.val.key = ustat->total_time_ok;
875 else if (filter & FILT_COUNT_URL_TAVGO)
876 ustat->node.val.key = (ustat->nb_req - ustat->nb_err) ? ustat->total_time_ok / (ustat->nb_req - ustat->nb_err) : 0;
877 else
878 ustat->node.val.key = 0;
879
880 eb64_insert(&timers[1], &ustat->node.val);
881 }
882 /* switch trees */
883 timers[0] = timers[1];
884 }
885
886 printf("#req err ttot tavg oktot okavg url\n");
887
888 /* scan the tree in its reverse sorting order */
889 node = eb_last(&timers[0]);
890 while (node) {
891 ustat = container_of(node, struct url_stat, node.url.node);
892 printf("%d %d %Ld %Ld %Ld %Ld %s\n",
893 ustat->nb_req,
894 ustat->nb_err,
895 ustat->total_time,
896 ustat->nb_req ? ustat->total_time / ustat->nb_req : 0,
897 ustat->total_time_ok,
898 (ustat->nb_req - ustat->nb_err) ? ustat->total_time_ok / (ustat->nb_req - ustat->nb_err) : 0,
899 ustat->url);
900
901 node = eb_prev(node);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200902 lines_out++;
Willy Tarreauabe45b62010-10-28 20:33:46 +0200903 }
904 }
Willy Tarreaud2201062010-05-27 18:17:30 +0200905
Willy Tarreau910ba4b2009-11-17 10:16:19 +0100906 empty:
Willy Tarreau72c28532009-01-22 18:56:50 +0100907 if (!(filter & FILT_QUIET))
908 fprintf(stderr, "%d lines in, %d lines out, %d parsing errors\n",
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200909 linenum, lines_out, parse_err);
Willy Tarreau72c28532009-01-22 18:56:50 +0100910 exit(0);
911}
912
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200913void filter_output_line(const char *accept_field, const char *time_field, struct timer **tptr)
914{
915 puts(line);
916 lines_out++;
917}
918
919void filter_accept_holes(const char *accept_field, const char *time_field, struct timer **tptr)
920{
921 struct timer *t2;
922 int val;
923
924 val = convert_date(accept_field);
925 if (unlikely(val < 0)) {
926 truncated_line(linenum, line);
927 return;
928 }
929
930 t2 = insert_value(&timers[0], tptr, val);
931 t2->count++;
932 lines_out++;
933 return;
934}
935
936void filter_count_status(const char *accept_field, const char *time_field, struct timer **tptr)
937{
938 struct timer *t2;
939 const char *b;
940 int val;
941
942 if (time_field)
943 b = field_start(time_field, STATUS_FIELD - TIME_FIELD + 1);
944 else
945 b = field_start(accept_field, STATUS_FIELD - ACCEPT_FIELD + 1);
946
947 if (unlikely(!*b)) {
948 truncated_line(linenum, line);
949 return;
950 }
951
952 val = str2ic(b);
953
954 t2 = insert_value(&timers[0], tptr, val);
955 t2->count++;
956}
957
958void filter_count_term_codes(const char *accept_field, const char *time_field, struct timer **tptr)
959{
960 struct timer *t2;
961 const char *b;
962 int val;
963
964 if (time_field)
965 b = field_start(time_field, TERM_CODES_FIELD - TIME_FIELD + 1);
966 else
967 b = field_start(accept_field, TERM_CODES_FIELD - ACCEPT_FIELD + 1);
968
969 if (unlikely(!*b)) {
970 truncated_line(linenum, line);
971 return;
972 }
973
974 val = 256 * b[0] + b[1];
975
976 t2 = insert_value(&timers[0], tptr, val);
977 t2->count++;
978}
979
980void filter_count_srv_status(const char *accept_field, const char *time_field, struct timer **tptr)
981{
982 const char *b, *e, *p;
983 int f, err, array[5];
984 struct ebmb_node *srv_node;
985 struct srv_st *srv;
986 int val;
987
988 /* the server field is before the status field, so let's
989 * parse them in the proper order.
990 */
991 b = field_start(accept_field, SERVER_FIELD - ACCEPT_FIELD + 1);
992 if (unlikely(!*b)) {
993 truncated_line(linenum, line);
994 return;
995 }
996
997 e = field_stop(b + 1); /* we have the server name in [b]..[e-1] */
998
999 /* the chance that a server name already exists is extremely high,
1000 * so let's perform a normal lookup first.
1001 */
1002 srv_node = ebst_lookup_len(&timers[0], b, e - b);
1003 srv = container_of(srv_node, struct srv_st, node);
1004
1005 if (!srv_node) {
1006 /* server not yet in the tree, let's create it */
1007 srv = (void *)calloc(1, sizeof(struct srv_st) + e - b + 1);
1008 srv_node = &srv->node;
1009 memcpy(&srv_node->key, b, e - b);
1010 srv_node->key[e - b] = '\0';
1011 ebst_insert(&timers[0], srv_node);
1012 }
1013
1014 /* let's collect the connect and response times */
1015 if (!time_field) {
1016 time_field = field_start(e, TIME_FIELD - SERVER_FIELD);
1017 if (unlikely(!*time_field)) {
1018 truncated_line(linenum, line);
1019 return;
1020 }
1021 }
1022
1023 e = field_stop(time_field + 1);
1024 /* we have field TIME_FIELD in [time_field]..[e-1] */
1025
1026 p = time_field;
1027 err = 0;
1028 f = 0;
1029 while (!SEP(*p)) {
1030 array[f] = str2ic(p);
1031 if (array[f] < 0) {
1032 array[f] = -1;
1033 err = 1;
1034 }
1035 if (++f == 5)
1036 break;
1037 SKIP_CHAR(p, '/');
1038 }
1039
1040 if (unlikely(f < 5)){
1041 parse_err++;
1042 return;
1043 }
1044
1045 /* OK we have our timers in array[2,3] */
1046 if (!err)
1047 srv->nb_ok++;
1048
1049 if (array[2] >= 0) {
1050 srv->cum_ct += array[2];
1051 srv->nb_ct++;
1052 }
1053
1054 if (array[3] >= 0) {
1055 srv->cum_rt += array[3];
1056 srv->nb_rt++;
1057 }
1058
1059 /* we're interested in the 5 HTTP status classes (1xx ... 5xx), and
1060 * the invalid ones which will be reported as 0.
1061 */
1062 b = field_start(e, STATUS_FIELD - TIME_FIELD);
1063 if (unlikely(!*b)) {
1064 truncated_line(linenum, line);
1065 return;
1066 }
1067
1068 val = 0;
1069 if (*b >= '1' && *b <= '5')
1070 val = *b - '0';
1071
1072 srv->st_cnt[val]++;
1073}
1074
1075void filter_count_url(const char *accept_field, const char *time_field, struct timer **tptr)
1076{
1077 struct url_stat *ustat = NULL;
1078 struct ebpt_node *ebpt_old;
1079 const char *b, *e;
1080 int f, err, array[5];
1081
1082 /* let's collect the response time */
1083 if (!time_field) {
1084 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1); // avg 115 ns per line
1085 if (unlikely(!*time_field)) {
1086 truncated_line(linenum, line);
1087 return;
1088 }
1089 }
1090
1091 /* we have the field TIME_FIELD starting at <time_field>. We'll
1092 * parse the 5 timers to detect errors, it takes avg 55 ns per line.
1093 */
1094 e = time_field; err = 0; f = 0;
1095 while (!SEP(*e)) {
1096 array[f] = str2ic(e);
1097 if (array[f] < 0) {
1098 array[f] = -1;
1099 err = 1;
1100 }
1101 if (++f == 5)
1102 break;
1103 SKIP_CHAR(e, '/');
1104 }
1105 if (f < 5) {
1106 parse_err++;
1107 return;
1108 }
1109
1110 /* OK we have our timers in array[3], and err is >0 if at
1111 * least one -1 was seen. <e> points to the first char of
1112 * the last timer. Let's prepare a new node with that.
1113 */
1114 if (unlikely(!ustat))
1115 ustat = calloc(1, sizeof(*ustat));
1116
1117 ustat->nb_err = err;
1118 ustat->nb_req = 1;
1119
1120 /* use array[4] = total time in case of error */
1121 ustat->total_time = (array[3] >= 0) ? array[3] : array[4];
1122 ustat->total_time_ok = (array[3] >= 0) ? array[3] : 0;
1123
1124 /* the line may be truncated because of a bad request or anything like this,
1125 * without a method. Also, if it does not begin with an quote, let's skip to
1126 * the next field because it's a capture. Let's fall back to the "method" itself
1127 * if there's nothing else.
1128 */
1129 e = field_start(e, METH_FIELD - TIME_FIELD + 1); // avg 100 ns per line
Willy Tarreau61a40c72011-09-06 08:11:27 +02001130 while (*e != '"' && *e) {
1131 /* Note: some syslog servers escape quotes ! */
1132 if (*e == '\\' && e[1] == '"')
1133 break;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001134 e = field_start(e, 2);
Willy Tarreau61a40c72011-09-06 08:11:27 +02001135 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001136
1137 if (unlikely(!*e)) {
1138 truncated_line(linenum, line);
1139 return;
1140 }
1141
1142 b = field_start(e, URL_FIELD - METH_FIELD + 1); // avg 40 ns per line
1143 if (!*b)
1144 b = e;
1145
1146 /* stop at end of field or first ';' or '?', takes avg 64 ns per line */
1147 e = b;
1148 do {
Willy Tarreau14389e72011-07-10 22:11:17 +02001149 if (*e == ' ' || *e == '?' || *e == ';') {
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001150 *(char *)e = 0;
1151 break;
1152 }
1153 e++;
1154 } while (*e);
1155
1156 /* now instead of copying the URL for a simple lookup, we'll link
1157 * to it from the node we're trying to insert. If it returns a
1158 * different value, it was already there. Otherwise we just have
1159 * to dynamically realloc an entry using strdup().
1160 */
1161 ustat->node.url.key = (char *)b;
1162 ebpt_old = ebis_insert(&timers[0], &ustat->node.url);
1163
1164 if (ebpt_old != &ustat->node.url) {
1165 struct url_stat *ustat_old;
1166 /* node was already there, let's update previous one */
1167 ustat_old = container_of(ebpt_old, struct url_stat, node.url);
1168 ustat_old->nb_req ++;
1169 ustat_old->nb_err += ustat->nb_err;
1170 ustat_old->total_time += ustat->total_time;
1171 ustat_old->total_time_ok += ustat->total_time_ok;
1172 } else {
1173 ustat->url = ustat->node.url.key = strdup(ustat->node.url.key);
1174 ustat = NULL; /* node was used */
1175 }
1176}
1177
1178void filter_graphs(const char *accept_field, const char *time_field, struct timer **tptr)
1179{
1180 struct timer *t2;
1181 const char *e, *p;
1182 int f, err, array[5];
1183
1184 if (!time_field) {
1185 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1);
1186 if (unlikely(!*time_field)) {
1187 truncated_line(linenum, line);
1188 return;
1189 }
1190 }
1191
1192 e = field_stop(time_field + 1);
1193 /* we have field TIME_FIELD in [time_field]..[e-1] */
1194
1195 p = time_field;
1196 err = 0;
1197 f = 0;
1198 while (!SEP(*p)) {
1199 array[f] = str2ic(p);
1200 if (array[f] < 0) {
1201 array[f] = -1;
1202 err = 1;
1203 }
1204 if (++f == 5)
1205 break;
1206 SKIP_CHAR(p, '/');
1207 }
1208
1209 if (unlikely(f < 5)) {
1210 parse_err++;
1211 return;
1212 }
1213
1214 /* if we find at least one negative time, we count one error
1215 * with a time equal to the total session time. This will
1216 * emphasize quantum timing effects associated to known
1217 * timeouts. Note that on some buggy machines, it is possible
1218 * that the total time is negative, hence the reason to reset
1219 * it.
1220 */
1221
1222 if (filter & FILT_GRAPH_TIMERS) {
1223 if (err) {
1224 if (array[4] < 0)
1225 array[4] = -1;
1226 t2 = insert_timer(&timers[0], tptr, array[4]); // total time
1227 t2->count++;
1228 } else {
1229 int v;
1230
1231 t2 = insert_timer(&timers[1], tptr, array[0]); t2->count++; // req
1232 t2 = insert_timer(&timers[2], tptr, array[2]); t2->count++; // conn
1233 t2 = insert_timer(&timers[3], tptr, array[3]); t2->count++; // resp
1234
1235 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
1236 if (v < 0 && !(filter & FILT_QUIET))
1237 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
1238 line, array[0], array[1], array[2], array[3], array[4], v);
1239 t2 = insert_timer(&timers[4], tptr, v); t2->count++;
1240 lines_out++;
1241 }
1242 } else { /* percentile */
1243 if (err) {
1244 if (array[4] < 0)
1245 array[4] = -1;
1246 t2 = insert_value(&timers[0], tptr, array[4]); // total time
1247 t2->count++;
1248 } else {
1249 int v;
1250
1251 t2 = insert_value(&timers[1], tptr, array[0]); t2->count++; // req
1252 t2 = insert_value(&timers[2], tptr, array[2]); t2->count++; // conn
1253 t2 = insert_value(&timers[3], tptr, array[3]); t2->count++; // resp
1254
1255 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
1256 if (v < 0 && !(filter & FILT_QUIET))
1257 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
1258 line, array[0], array[1], array[2], array[3], array[4], v);
1259 t2 = insert_value(&timers[4], tptr, v); t2->count++;
1260 lines_out++;
1261 }
1262 }
1263}
1264
1265
Willy Tarreau72c28532009-01-22 18:56:50 +01001266/*
1267 * Local variables:
1268 * c-indent-level: 8
1269 * c-basic-offset: 8
1270 * End:
1271 */