blob: b9da76155a5590abd2228dfcf3aa748f806f9fdf [file] [log] [blame]
Willy Tarreau72c28532009-01-22 18:56:50 +01001/*
2 * haproxy log time reporter
3 *
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
13/*
Willy Tarreau214c2032009-02-20 11:02:32 +010014 * gcc -O2 -o halog2 halog2.c -Iinclude src/ebtree.c src/eb32tree.c fgets2.c
Willy Tarreau72c28532009-01-22 18:56:50 +010015 *
16 * Usage:
17 * $0 [ min_delay [ min_count [ field_shift ]]] < haproxy.log
18 * Note: if min_delay < 0, it only outputs lines with status codes 5xx.
19 */
20
21#include <errno.h>
22#include <fcntl.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <syslog.h>
26#include <string.h>
27#include <unistd.h>
28#include <ctype.h>
29
Willy Tarreau45cb4fb2009-10-26 21:10:04 +010030#include <eb32tree.h>
Willy Tarreaud2201062010-05-27 18:17:30 +020031#include <ebsttree.h>
Willy Tarreau72c28532009-01-22 18:56:50 +010032
Willy Tarreaud2201062010-05-27 18:17:30 +020033#define SOURCE_FIELD 5
Willy Tarreau72c28532009-01-22 18:56:50 +010034#define ACCEPT_FIELD 6
Willy Tarreaud2201062010-05-27 18:17:30 +020035#define SERVER_FIELD 8
Willy Tarreau72c28532009-01-22 18:56:50 +010036#define TIME_FIELD 9
37#define STATUS_FIELD 10
38#define CONN_FIELD 15
39#define MAXLINE 16384
40#define QBITS 4
41
42#define SKIP_CHAR(p,c) do { while (1) if (!*p) break; else if (*(p++) == c) break; } while (0)
43
44/* [0] = err/date, [1] = req, [2] = conn, [3] = resp, [4] = data */
45static struct eb_root timers[5] = {
46 EB_ROOT_UNIQUE, EB_ROOT_UNIQUE, EB_ROOT_UNIQUE,
47 EB_ROOT_UNIQUE, EB_ROOT_UNIQUE,
48};
49
50struct timer {
51 struct eb32_node node;
52 unsigned int count;
53};
54
Willy Tarreaud2201062010-05-27 18:17:30 +020055struct srv_st {
56 unsigned int st_cnt[6]; /* 0xx to 5xx */
57 unsigned int nb_ct, nb_rt, nb_ok;
58 unsigned long long cum_ct, cum_rt;
59 struct ebmb_node node;
60 /* don't put anything else here, the server name will be there */
61};
Willy Tarreau72c28532009-01-22 18:56:50 +010062
63#define FILT_COUNT_ONLY 0x01
64#define FILT_INVERT 0x02
65#define FILT_QUIET 0x04
66#define FILT_ERRORS_ONLY 0x08
67#define FILT_ACC_DELAY 0x10
68#define FILT_ACC_COUNT 0x20
69#define FILT_GRAPH_TIMERS 0x40
Willy Tarreau214c2032009-02-20 11:02:32 +010070#define FILT_PERCENTILE 0x80
Willy Tarreau5bdfd962009-10-14 15:16:29 +020071#define FILT_TIME_RESP 0x100
72
73#define FILT_INVERT_ERRORS 0x200
74#define FILT_INVERT_TIME_RESP 0x400
Willy Tarreau72c28532009-01-22 18:56:50 +010075
Willy Tarreau0f423a72010-05-03 10:50:54 +020076#define FILT_COUNT_STATUS 0x800
Willy Tarreaud2201062010-05-27 18:17:30 +020077#define FILT_COUNT_SRV_STATUS 0x1000
Willy Tarreau0f423a72010-05-03 10:50:54 +020078
Willy Tarreau72c28532009-01-22 18:56:50 +010079unsigned int filter = 0;
80unsigned int filter_invert = 0;
Willy Tarreau214c2032009-02-20 11:02:32 +010081const char *line;
Willy Tarreau72c28532009-01-22 18:56:50 +010082
Willy Tarreau214c2032009-02-20 11:02:32 +010083const char *fgets2(FILE *stream);
Willy Tarreau72c28532009-01-22 18:56:50 +010084
85void die(const char *msg)
86{
87 fprintf(stderr,
88 "%s"
Willy Tarreaud2201062010-05-27 18:17:30 +020089 "Usage: halog [-q] [-c] [-v] [-gt] [-pct] [-st] [-srv] [-s <skip>] [-e|-E] [-rt|-RT <time>] [-ad <delay>] [-ac <count>] < file.log\n"
Willy Tarreau72c28532009-01-22 18:56:50 +010090 "\n",
91 msg ? msg : ""
92 );
93 exit(1);
94}
95
96
97/* return pointer to first char not part of current field starting at <p>. */
98const char *field_stop(const char *p)
99{
100 unsigned char c;
101
102 while (1) {
103 c = *(p++);
104 if (c > ' ')
105 continue;
106 if (c == ' ' || c == '\t' || c == 0)
107 break;
108 }
109 return p - 1;
110}
111
112/* return field <field> (starting from 1) in string <p>. Only consider
113 * contiguous spaces (or tabs) as one delimiter. May return pointer to
114 * last char if field is not found. Equivalent to awk '{print $field}'.
115 */
116const char *field_start(const char *p, int field)
117{
118 unsigned char c;
119 while (1) {
120 /* skip spaces */
121 while (1) {
122 c = *p;
123 if (c > ' ')
124 break;
125 if (c == ' ' || c == '\t')
126 goto next;
127 if (!c) /* end of line */
128 return p;
129 /* other char => new field */
130 break;
131 next:
132 p++;
133 }
134
135 /* start of field */
136 field--;
137 if (!field)
138 return p;
139
140 /* skip this field */
141 while (1) {
142 c = *(p++);
143 if (c > ' ')
144 continue;
145 if (c == ' ' || c == '\t')
146 break;
147 if (c == '\0')
148 return p;
149 }
150 }
151}
152
153/* keep only the <bits> higher bits of <i> */
154static inline unsigned int quantify_u32(unsigned int i, int bits)
155{
156 int high;
157
158 if (!bits)
159 return 0;
160
161 if (i)
162 high = fls_auto(i); // 1 to 32
163 else
164 high = 0;
165
166 if (high <= bits)
167 return i;
168
169 return i & ~((1 << (high - bits)) - 1);
170}
171
172/* keep only the <bits> higher bits of the absolute value of <i>, as well as
173 * its sign. */
174static inline int quantify(int i, int bits)
175{
176 if (i >= 0)
177 return quantify_u32(i, bits);
178 else
179 return -quantify_u32(-i, bits);
180}
181
182/* Insert timer value <v> into tree <r>. A pre-allocated node must be passed
183 * in <alloc>. It may be NULL, in which case the function will allocate it
184 * itself. It will be reset to NULL once consumed. The caller is responsible
185 * for freeing the node once not used anymore. The node where the value was
186 * inserted is returned.
187 */
188struct timer *insert_timer(struct eb_root *r, struct timer **alloc, int v)
189{
190 struct timer *t = *alloc;
191 struct eb32_node *n;
192
193 if (!t) {
194 t = calloc(sizeof(*t), 1);
195 if (unlikely(!t)) {
196 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
197 exit(1);
198 }
199 }
200 t->node.key = quantify(v, QBITS); // keep only the higher QBITS bits
201
202 n = eb32i_insert(r, &t->node);
203 if (n == &t->node)
204 t = NULL; /* node inserted, will malloc next time */
205
206 *alloc = t;
207 return container_of(n, struct timer, node);
208}
209
210/* Insert value value <v> into tree <r>. A pre-allocated node must be passed
211 * in <alloc>. It may be NULL, in which case the function will allocate it
212 * itself. It will be reset to NULL once consumed. The caller is responsible
213 * for freeing the node once not used anymore. The node where the value was
214 * inserted is returned.
215 */
216struct timer *insert_value(struct eb_root *r, struct timer **alloc, int v)
217{
218 struct timer *t = *alloc;
219 struct eb32_node *n;
220
221 if (!t) {
222 t = calloc(sizeof(*t), 1);
223 if (unlikely(!t)) {
224 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
225 exit(1);
226 }
227 }
228 t->node.key = v;
229
230 n = eb32i_insert(r, &t->node);
231 if (n == &t->node)
232 t = NULL; /* node inserted, will malloc next time */
233
234 *alloc = t;
235 return container_of(n, struct timer, node);
236}
237
238int str2ic(const char *s)
239{
240 int i = 0;
241 int j, k;
242
243 if (*s != '-') {
244 /* positive number */
245 while (1) {
246 j = (*s++) - '0';
247 k = i * 10;
248 if ((unsigned)j > 9)
249 break;
250 i = k + j;
251 }
252 } else {
253 /* negative number */
254 s++;
255 while (1) {
256 j = (*s++) - '0';
257 k = i * 10;
258 if ((unsigned)j > 9)
259 break;
260 i = k - j;
261 }
262 }
263
264 return i;
265}
266
267
268/* Equivalent to strtoul with a length. */
269static inline unsigned int __strl2ui(const char *s, int len)
270{
271 unsigned int i = 0;
272 while (len-- > 0) {
273 i = i * 10 - '0';
274 i += (unsigned char)*s++;
275 }
276 return i;
277}
278
279unsigned int strl2ui(const char *s, int len)
280{
281 return __strl2ui(s, len);
282}
283
284/* Convert "[04/Dec/2008:09:49:40.555]" to an integer equivalent to the time of
285 * the day in milliseconds. It returns -1 for all unparsable values. The parser
286 * looks ugly but gcc emits far better code that way.
287 */
288int convert_date(const char *field)
289{
290 unsigned int h, m, s, ms;
291 unsigned char c;
292 const char *b, *e;
293
294 h = m = s = ms = 0;
295 e = field;
296
297 /* skip the date */
298 while (1) {
299 c = *(e++);
300 if (c == ':')
301 break;
302 if (!c)
303 goto out_err;
304 }
305
306 /* hour + ':' */
307 b = e;
308 while (1) {
309 c = *(e++) - '0';
310 if (c > 9)
311 break;
312 h = h * 10 + c;
313 }
314 if (c == (unsigned char)(0 - '0'))
315 goto out_err;
316
317 /* minute + ':' */
318 b = e;
319 while (1) {
320 c = *(e++) - '0';
321 if (c > 9)
322 break;
323 m = m * 10 + c;
324 }
325 if (c == (unsigned char)(0 - '0'))
326 goto out_err;
327
328 /* second + '.' or ']' */
329 b = e;
330 while (1) {
331 c = *(e++) - '0';
332 if (c > 9)
333 break;
334 s = s * 10 + c;
335 }
336 if (c == (unsigned char)(0 - '0'))
337 goto out_err;
338
339 /* if there's a '.', we have milliseconds */
340 if (c == (unsigned char)('.' - '0')) {
341 /* millisecond second + ']' */
342 b = e;
343 while (1) {
344 c = *(e++) - '0';
345 if (c > 9)
346 break;
347 ms = ms * 10 + c;
348 }
349 if (c == (unsigned char)(0 - '0'))
350 goto out_err;
351 }
352 return (((h * 60) + m) * 60 + s) * 1000 + ms;
353 out_err:
354 return -1;
355}
356
357void truncated_line(int linenum, const char *line)
358{
359 if (!(filter & FILT_QUIET))
360 fprintf(stderr, "Truncated line %d: %s\n", linenum, line);
361}
362
363int main(int argc, char **argv)
364{
365 const char *b, *e, *p;
366 const char *output_file = NULL;
367 int f, tot, last, linenum, err, parse_err;
368 struct timer *t = NULL, *t2;
369 struct eb32_node *n;
370 int val, test;
371 int array[5];
372 int filter_acc_delay = 0, filter_acc_count = 0;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200373 int filter_time_resp = 0;
Willy Tarreau72c28532009-01-22 18:56:50 +0100374 int skip_fields = 1;
375
376 argc--; argv++;
377 while (argc > 0) {
378 if (*argv[0] != '-')
379 break;
380
381 if (strcmp(argv[0], "-ad") == 0) {
382 if (argc < 2) die("missing option for -ad");
383 argc--; argv++;
384 filter |= FILT_ACC_DELAY;
385 filter_acc_delay = atol(*argv);
386 }
387 else if (strcmp(argv[0], "-ac") == 0) {
388 if (argc < 2) die("missing option for -ac");
389 argc--; argv++;
390 filter |= FILT_ACC_COUNT;
391 filter_acc_count = atol(*argv);
392 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200393 else if (strcmp(argv[0], "-rt") == 0) {
394 if (argc < 2) die("missing option for -rt");
395 argc--; argv++;
396 filter |= FILT_TIME_RESP;
397 filter_time_resp = atol(*argv);
398 }
399 else if (strcmp(argv[0], "-RT") == 0) {
400 if (argc < 2) die("missing option for -RT");
401 argc--; argv++;
402 filter |= FILT_TIME_RESP | FILT_INVERT_TIME_RESP;
403 filter_time_resp = atol(*argv);
404 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100405 else if (strcmp(argv[0], "-s") == 0) {
406 if (argc < 2) die("missing option for -s");
407 argc--; argv++;
408 skip_fields = atol(*argv);
409 }
410 else if (strcmp(argv[0], "-e") == 0)
411 filter |= FILT_ERRORS_ONLY;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200412 else if (strcmp(argv[0], "-E") == 0)
413 filter |= FILT_ERRORS_ONLY | FILT_INVERT_ERRORS;
Willy Tarreau72c28532009-01-22 18:56:50 +0100414 else if (strcmp(argv[0], "-c") == 0)
415 filter |= FILT_COUNT_ONLY;
416 else if (strcmp(argv[0], "-q") == 0)
417 filter |= FILT_QUIET;
418 else if (strcmp(argv[0], "-v") == 0)
419 filter_invert = !filter_invert;
420 else if (strcmp(argv[0], "-gt") == 0)
421 filter |= FILT_GRAPH_TIMERS;
Willy Tarreau214c2032009-02-20 11:02:32 +0100422 else if (strcmp(argv[0], "-pct") == 0)
423 filter |= FILT_PERCENTILE;
Willy Tarreau0f423a72010-05-03 10:50:54 +0200424 else if (strcmp(argv[0], "-st") == 0)
425 filter |= FILT_COUNT_STATUS;
Willy Tarreaud2201062010-05-27 18:17:30 +0200426 else if (strcmp(argv[0], "-srv") == 0)
427 filter |= FILT_COUNT_SRV_STATUS;
Willy Tarreau72c28532009-01-22 18:56:50 +0100428 else if (strcmp(argv[0], "-o") == 0) {
429 if (output_file)
430 die("Fatal: output file name already specified.\n");
431 if (argc < 2)
432 die("Fatal: missing output file name.\n");
433 output_file = argv[1];
434 }
435 argc--;
436 argv++;
437 }
438
439 if (!filter)
440 die("No action specified.\n");
441
442 if (filter & FILT_ACC_COUNT && !filter_acc_count)
443 filter_acc_count=1;
444
445 if (filter & FILT_ACC_DELAY && !filter_acc_delay)
446 filter_acc_delay = 1;
447
448 linenum = 0;
449 tot = 0;
450 parse_err = 0;
451
Willy Tarreau214c2032009-02-20 11:02:32 +0100452 while ((line = fgets2(stdin)) != NULL) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100453 linenum++;
454
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200455 test = 1;
Willy Tarreau2651ac32010-05-05 12:20:19 +0200456 if (unlikely(filter & FILT_TIME_RESP)) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200457 int tps;
458
459 /* only report lines with response times larger than filter_time_resp */
460 b = field_start(line, TIME_FIELD + skip_fields);
461 if (!*b) {
462 truncated_line(linenum, line);
463 continue;
464 }
465
466 e = field_stop(b + 1);
467 /* we have field TIME_FIELD in [b]..[e-1] */
468
469 p = b;
470 err = 0;
471 for (f = 0; f < 4 && *p; f++) {
472 tps = str2ic(p);
473 if (tps < 0) {
474 tps = -1;
475 err = 1;
476 }
477
478 SKIP_CHAR(p, '/');
479 }
480
481 if (f < 4) {
482 parse_err++;
483 continue;
484 }
485
486 test &= (tps >= filter_time_resp) ^ !!(filter & FILT_INVERT_TIME_RESP);
487 }
488
Willy Tarreau2651ac32010-05-05 12:20:19 +0200489 if (unlikely(filter & FILT_ERRORS_ONLY)) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100490 /* only report erroneous status codes */
491 b = field_start(line, STATUS_FIELD + skip_fields);
492 if (!*b) {
493 truncated_line(linenum, line);
494 continue;
495 }
496 if (*b == '-') {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200497 test &= !!(filter & FILT_INVERT_ERRORS);
Willy Tarreau72c28532009-01-22 18:56:50 +0100498 } else {
499 val = strl2ui(b, 3);
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200500 test &= (val >= 500 && val <= 599) ^ !!(filter & FILT_INVERT_ERRORS);
Willy Tarreau72c28532009-01-22 18:56:50 +0100501 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100502 }
503
Willy Tarreau0f423a72010-05-03 10:50:54 +0200504 test ^= filter_invert;
505 if (!test)
506 continue;
507
Willy Tarreau2651ac32010-05-05 12:20:19 +0200508 if (unlikely(filter & (FILT_ACC_COUNT|FILT_ACC_DELAY))) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100509 b = field_start(line, ACCEPT_FIELD + skip_fields);
510 if (!*b) {
511 truncated_line(linenum, line);
512 continue;
513 }
514
Willy Tarreau214c2032009-02-20 11:02:32 +0100515 tot++;
Willy Tarreau72c28532009-01-22 18:56:50 +0100516 val = convert_date(b);
517 //printf("date=%s => %d\n", b, val);
518 if (val < 0) {
519 parse_err++;
520 continue;
521 }
522
523 t2 = insert_value(&timers[0], &t, val);
524 t2->count++;
525 continue;
526 }
527
Willy Tarreau2651ac32010-05-05 12:20:19 +0200528 if (unlikely(filter & (FILT_GRAPH_TIMERS|FILT_PERCENTILE))) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100529 int f;
530
531 b = field_start(line, TIME_FIELD + skip_fields);
532 if (!*b) {
533 truncated_line(linenum, line);
534 continue;
535 }
536
537 e = field_stop(b + 1);
538 /* we have field TIME_FIELD in [b]..[e-1] */
539
540 p = b;
541 err = 0;
542 for (f = 0; f < 5 && *p; f++) {
543 array[f] = str2ic(p);
544 if (array[f] < 0) {
545 array[f] = -1;
546 err = 1;
547 }
548
549 SKIP_CHAR(p, '/');
550 }
551
552 if (f < 5) {
553 parse_err++;
554 continue;
555 }
556
557 /* if we find at least one negative time, we count one error
558 * with a time equal to the total session time. This will
559 * emphasize quantum timing effects associated to known
560 * timeouts. Note that on some buggy machines, it is possible
561 * that the total time is negative, hence the reason to reset
562 * it.
563 */
Willy Tarreau214c2032009-02-20 11:02:32 +0100564
565 if (filter & FILT_GRAPH_TIMERS) {
566 if (err) {
567 if (array[4] < 0)
568 array[4] = -1;
569 t2 = insert_timer(&timers[0], &t, array[4]); // total time
570 t2->count++;
571 } else {
572 int v;
Willy Tarreau72c28532009-01-22 18:56:50 +0100573
Willy Tarreau214c2032009-02-20 11:02:32 +0100574 t2 = insert_timer(&timers[1], &t, array[0]); t2->count++; // req
575 t2 = insert_timer(&timers[2], &t, array[2]); t2->count++; // conn
576 t2 = insert_timer(&timers[3], &t, array[3]); t2->count++; // resp
577
578 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
579 if (v < 0 && !(filter & FILT_QUIET))
580 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
581 line, array[0], array[1], array[2], array[3], array[4], v);
582 t2 = insert_timer(&timers[4], &t, v); t2->count++;
583 tot++;
584 }
585 } else { /* percentile */
586 if (err) {
587 if (array[4] < 0)
588 array[4] = -1;
589 t2 = insert_value(&timers[0], &t, array[4]); // total time
590 t2->count++;
591 } else {
592 int v;
Willy Tarreau72c28532009-01-22 18:56:50 +0100593
Willy Tarreau214c2032009-02-20 11:02:32 +0100594 t2 = insert_value(&timers[1], &t, array[0]); t2->count++; // req
595 t2 = insert_value(&timers[2], &t, array[2]); t2->count++; // conn
596 t2 = insert_value(&timers[3], &t, array[3]); t2->count++; // resp
597
598 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
599 if (v < 0 && !(filter & FILT_QUIET))
600 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
601 line, array[0], array[1], array[2], array[3], array[4], v);
602 t2 = insert_value(&timers[4], &t, v); t2->count++;
603 tot++;
604 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100605 }
606 continue;
607 }
608
Willy Tarreau2651ac32010-05-05 12:20:19 +0200609 if (unlikely(filter & FILT_COUNT_STATUS)) {
Willy Tarreau0f423a72010-05-03 10:50:54 +0200610 b = field_start(line, STATUS_FIELD + skip_fields);
611 if (!*b) {
612 truncated_line(linenum, line);
613 continue;
614 }
615 val = str2ic(b);
616
617 t2 = insert_value(&timers[0], &t, val);
618 t2->count++;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200619 continue;
Willy Tarreau0f423a72010-05-03 10:50:54 +0200620 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200621
Willy Tarreaud2201062010-05-27 18:17:30 +0200622 if (unlikely(filter & FILT_COUNT_SRV_STATUS)) {
623 char *srv_name;
624 struct ebmb_node *srv_node;
625 struct srv_st *srv;
626
627 /* first, let's ensure that the line is a traffic line (beginning
628 * with an IP address)
629 */
630 b = field_start(line, SOURCE_FIELD + skip_fields);
631 if (*b < '0' || *b > '9') {
632 parse_err++;
633 continue;
634 }
635
636 /* the server field is before the status field, so let's
637 * parse them in the proper order.
638 */
639 b = field_start(b, SERVER_FIELD - SOURCE_FIELD + 1);
640 if (!*b) {
641 truncated_line(linenum, line);
642 continue;
643 }
644
645 e = field_stop(b + 1); /* we have the server name in [b]..[e-1] */
646
647 /* the chance that a server name already exists is extremely high,
648 * so let's perform a normal lookup first.
649 */
650 srv_node = ebst_lookup_len(&timers[0], b, e - b);
651 srv = container_of(srv_node, struct srv_st, node);
652
653 if (!srv_node) {
654 /* server not yet in the tree, let's create it */
655 srv = (void *)calloc(1, sizeof(struct srv_st) + e - b + 1);
656 srv_node = &srv->node;
657 memcpy(&srv_node->key, b, e - b);
658 srv_node->key[e - b] = '\0';
659 ebst_insert(&timers[0], srv_node);
660 }
661
662 /* let's collect the connect and response times */
663 b = field_start(e, TIME_FIELD - SERVER_FIELD);
664 if (!*b) {
665 truncated_line(linenum, line);
666 continue;
667 }
668
669 e = field_stop(b + 1);
670 /* we have field TIME_FIELD in [b]..[e-1] */
671
672 p = b;
673 err = 0;
674 for (f = 0; f < 5 && *p; f++) {
675 array[f] = str2ic(p);
676 if (array[f] < 0) {
677 array[f] = -1;
678 err = 1;
679 }
680
681 SKIP_CHAR(p, '/');
682 }
683
684 if (f < 5) {
685 parse_err++;
686 continue;
687 }
688
689 /* OK we have our timers in array[2,3] */
690 if (!err)
691 srv->nb_ok++;
692
693 if (array[2] >= 0) {
694 srv->cum_ct += array[2];
695 srv->nb_ct++;
696 }
697
698 if (array[3] >= 0) {
699 srv->cum_rt += array[3];
700 srv->nb_rt++;
701 }
702
703 /* we're interested in the 5 HTTP status classes (1xx ... 5xx), and
704 * the invalid ones which will be reported as 0.
705 */
706 b = field_start(e, STATUS_FIELD - TIME_FIELD);
707 if (!*b) {
708 truncated_line(linenum, line);
709 continue;
710 }
711
712 val = 0;
713 if (*b >= '1' && *b <= '5')
714 val = *b - '0';
715
716 srv->st_cnt[val]++;
717 continue;
718 }
719
Willy Tarreau72c28532009-01-22 18:56:50 +0100720 /* all other cases mean we just want to count lines */
721 tot++;
Willy Tarreau2651ac32010-05-05 12:20:19 +0200722 if (unlikely(!(filter & FILT_COUNT_ONLY)))
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200723 puts(line);
Willy Tarreau72c28532009-01-22 18:56:50 +0100724 }
725
726 if (t)
727 free(t);
728
729 if (filter & FILT_COUNT_ONLY) {
730 printf("%d\n", tot);
731 exit(0);
732 }
733
Willy Tarreau72c28532009-01-22 18:56:50 +0100734 if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY)) {
735 /* sort and count all timers. Output will look like this :
736 * <accept_date> <delta_ms from previous one> <nb entries>
737 */
738 n = eb32_first(&timers[0]);
739
740 if (n)
741 last = n->key;
742 while (n) {
743 unsigned int d, h, m, s, ms;
744
745 t = container_of(n, struct timer, node);
746 h = n->key;
747 d = h - last;
748 last = h;
749
750 if (d >= filter_acc_delay && t->count >= filter_acc_count) {
751 ms = h % 1000; h = h / 1000;
752 s = h % 60; h = h / 60;
753 m = h % 60; h = h / 60;
754 tot++;
755 printf("%02d:%02d:%02d.%03d %d %d %d\n", h, m, s, ms, last, d, t->count);
756 }
757 n = eb32_next(n);
758 }
759 }
760 else if (filter & FILT_GRAPH_TIMERS) {
761 /* sort all timers */
762 for (f = 0; f < 5; f++) {
763 struct eb32_node *n;
764 int val;
765
766 val = 0;
767 n = eb32_first(&timers[f]);
768 while (n) {
769 int i;
770 double d;
771
772 t = container_of(n, struct timer, node);
773 last = n->key;
774 val = t->count;
775
776 i = (last < 0) ? -last : last;
777 i = fls_auto(i) - QBITS;
778
779 if (i > 0)
780 d = val / (double)(1 << i);
781 else
782 d = val;
783
784 if (d > 0.0) {
785 printf("%d %d %f\n", f, last, d+1.0);
786 tot++;
787 }
788
789 n = eb32_next(n);
790 }
Willy Tarreau214c2032009-02-20 11:02:32 +0100791 }
792 }
793 else if (filter & FILT_PERCENTILE) {
794 /* report timers by percentile :
795 * <percent> <total> <max_req_time> <max_conn_time> <max_resp_time> <max_data_time>
796 * We don't count errs.
797 */
798 struct eb32_node *n[5];
799 unsigned long cum[5];
800 double step;
801
Willy Tarreau910ba4b2009-11-17 10:16:19 +0100802 if (!tot)
803 goto empty;
804
Willy Tarreau214c2032009-02-20 11:02:32 +0100805 for (f = 1; f < 5; f++) {
806 n[f] = eb32_first(&timers[f]);
807 cum[f] = container_of(n[f], struct timer, node)->count;
808 }
809
810 for (step = 1; step <= 1000;) {
811 unsigned int thres = tot * (step / 1000.0);
812
813 printf("%3.1f %d ", step/10.0, thres);
814 for (f = 1; f < 5; f++) {
815 struct eb32_node *next;
816 while (cum[f] < thres) {
817 /* need to find other keys */
818 next = eb32_next(n[f]);
819 if (!next)
820 break;
821 n[f] = next;
822 cum[f] += container_of(next, struct timer, node)->count;
823 }
824
825 /* value still within $step % of total */
826 printf("%d ", n[f]->key);
827 }
828 putchar('\n');
829 if (step >= 100 && step < 900)
830 step += 50; // jump 5% by 5% between those steps.
831 else if (step >= 20 && step < 980)
832 step += 10;
833 else
834 step += 1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100835 }
836 }
Willy Tarreau0f423a72010-05-03 10:50:54 +0200837 else if (filter & FILT_COUNT_STATUS) {
838 /* output all statuses in the form of <status> <occurrences> */
839 n = eb32_first(&timers[0]);
840 while (n) {
841 t = container_of(n, struct timer, node);
842 printf("%d %d\n", n->key, t->count);
843 n = eb32_next(n);
844 }
845 }
Willy Tarreaud2201062010-05-27 18:17:30 +0200846 else if (unlikely(filter & FILT_COUNT_SRV_STATUS)) {
847 char *srv_name;
848 struct ebmb_node *srv_node;
849 struct srv_st *srv;
850
851 printf("#srv_name 1xx 2xx 3xx 4xx 5xx other tot_req req_ok pct_ok avg_ct avg_rt\n");
852
853 srv_node = ebmb_first(&timers[0]);
854 while (srv_node) {
855 int tot_rq;
856
857 srv = container_of(srv_node, struct srv_st, node);
858
859 tot_rq = 0;
860 for (f = 0; f <= 5; f++)
861 tot_rq += srv->st_cnt[f];
862
863 printf("%s %d %d %d %d %d %d %d %d %.1f %d %d\n",
864 srv_node->key, srv->st_cnt[1], srv->st_cnt[2],
865 srv->st_cnt[3], srv->st_cnt[4], srv->st_cnt[5], srv->st_cnt[0],
866 tot_rq,
867 srv->nb_ok, (double)srv->nb_ok * 100.0 / (tot_rq?tot_rq:1),
868 (int)(srv->cum_ct / (srv->nb_ct?srv->nb_ct:1)), (int)(srv->cum_rt / (srv->nb_rt?srv->nb_rt:1)));
869 srv_node = ebmb_next(srv_node);
870 tot++;
871 }
872 }
873
Willy Tarreau910ba4b2009-11-17 10:16:19 +0100874 empty:
Willy Tarreau72c28532009-01-22 18:56:50 +0100875 if (!(filter & FILT_QUIET))
876 fprintf(stderr, "%d lines in, %d lines out, %d parsing errors\n",
877 linenum, tot, parse_err);
878 exit(0);
879}
880
881/*
882 * Local variables:
883 * c-indent-level: 8
884 * c-basic-offset: 8
885 * End:
886 */