blob: 016227987cd65ec45faaa81a4f2bcc8f4f094d60 [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 Tarreaud2201062010-05-27 18:17:30 +020023#include <ebsttree.h>
Willy Tarreau72c28532009-01-22 18:56:50 +010024
Willy Tarreaud2201062010-05-27 18:17:30 +020025#define SOURCE_FIELD 5
Willy Tarreau72c28532009-01-22 18:56:50 +010026#define ACCEPT_FIELD 6
Willy Tarreaud2201062010-05-27 18:17:30 +020027#define SERVER_FIELD 8
Willy Tarreau72c28532009-01-22 18:56:50 +010028#define TIME_FIELD 9
29#define STATUS_FIELD 10
Willy Tarreaud8fc1102010-09-12 17:56:16 +020030#define TERM_CODES_FIELD 14
Willy Tarreau72c28532009-01-22 18:56:50 +010031#define CONN_FIELD 15
32#define MAXLINE 16384
33#define QBITS 4
34
35#define SKIP_CHAR(p,c) do { while (1) if (!*p) break; else if (*(p++) == c) break; } while (0)
36
37/* [0] = err/date, [1] = req, [2] = conn, [3] = resp, [4] = data */
38static struct eb_root timers[5] = {
39 EB_ROOT_UNIQUE, EB_ROOT_UNIQUE, EB_ROOT_UNIQUE,
40 EB_ROOT_UNIQUE, EB_ROOT_UNIQUE,
41};
42
43struct timer {
44 struct eb32_node node;
45 unsigned int count;
46};
47
Willy Tarreaud2201062010-05-27 18:17:30 +020048struct srv_st {
49 unsigned int st_cnt[6]; /* 0xx to 5xx */
50 unsigned int nb_ct, nb_rt, nb_ok;
51 unsigned long long cum_ct, cum_rt;
52 struct ebmb_node node;
53 /* don't put anything else here, the server name will be there */
54};
Willy Tarreau72c28532009-01-22 18:56:50 +010055
56#define FILT_COUNT_ONLY 0x01
57#define FILT_INVERT 0x02
58#define FILT_QUIET 0x04
59#define FILT_ERRORS_ONLY 0x08
60#define FILT_ACC_DELAY 0x10
61#define FILT_ACC_COUNT 0x20
62#define FILT_GRAPH_TIMERS 0x40
Willy Tarreau214c2032009-02-20 11:02:32 +010063#define FILT_PERCENTILE 0x80
Willy Tarreau5bdfd962009-10-14 15:16:29 +020064#define FILT_TIME_RESP 0x100
65
66#define FILT_INVERT_ERRORS 0x200
67#define FILT_INVERT_TIME_RESP 0x400
Willy Tarreau72c28532009-01-22 18:56:50 +010068
Willy Tarreau0f423a72010-05-03 10:50:54 +020069#define FILT_COUNT_STATUS 0x800
Willy Tarreaud2201062010-05-27 18:17:30 +020070#define FILT_COUNT_SRV_STATUS 0x1000
Willy Tarreaud8fc1102010-09-12 17:56:16 +020071#define FILT_COUNT_TERM_CODES 0x2000
Willy Tarreau0f423a72010-05-03 10:50:54 +020072
Willy Tarreau72c28532009-01-22 18:56:50 +010073unsigned int filter = 0;
74unsigned int filter_invert = 0;
Willy Tarreau214c2032009-02-20 11:02:32 +010075const char *line;
Willy Tarreau72c28532009-01-22 18:56:50 +010076
Willy Tarreau214c2032009-02-20 11:02:32 +010077const char *fgets2(FILE *stream);
Willy Tarreau72c28532009-01-22 18:56:50 +010078
79void die(const char *msg)
80{
81 fprintf(stderr,
82 "%s"
Willy Tarreaud8fc1102010-09-12 17:56:16 +020083 "Usage: halog [-q] [-c] [-v] {-gt|-pct|-st|-tc|-srv} [-s <skip>] [-e|-E] [-rt|-RT <time>] [-ad <delay>] [-ac <count>] < file.log\n"
Willy Tarreau72c28532009-01-22 18:56:50 +010084 "\n",
85 msg ? msg : ""
86 );
87 exit(1);
88}
89
90
91/* return pointer to first char not part of current field starting at <p>. */
92const char *field_stop(const char *p)
93{
94 unsigned char c;
95
96 while (1) {
97 c = *(p++);
98 if (c > ' ')
99 continue;
100 if (c == ' ' || c == '\t' || c == 0)
101 break;
102 }
103 return p - 1;
104}
105
106/* return field <field> (starting from 1) in string <p>. Only consider
107 * contiguous spaces (or tabs) as one delimiter. May return pointer to
108 * last char if field is not found. Equivalent to awk '{print $field}'.
109 */
110const char *field_start(const char *p, int field)
111{
112 unsigned char c;
113 while (1) {
114 /* skip spaces */
115 while (1) {
116 c = *p;
117 if (c > ' ')
118 break;
119 if (c == ' ' || c == '\t')
120 goto next;
121 if (!c) /* end of line */
122 return p;
123 /* other char => new field */
124 break;
125 next:
126 p++;
127 }
128
129 /* start of field */
130 field--;
131 if (!field)
132 return p;
133
134 /* skip this field */
135 while (1) {
136 c = *(p++);
137 if (c > ' ')
138 continue;
139 if (c == ' ' || c == '\t')
140 break;
141 if (c == '\0')
142 return p;
143 }
144 }
145}
146
147/* keep only the <bits> higher bits of <i> */
148static inline unsigned int quantify_u32(unsigned int i, int bits)
149{
150 int high;
151
152 if (!bits)
153 return 0;
154
155 if (i)
156 high = fls_auto(i); // 1 to 32
157 else
158 high = 0;
159
160 if (high <= bits)
161 return i;
162
163 return i & ~((1 << (high - bits)) - 1);
164}
165
166/* keep only the <bits> higher bits of the absolute value of <i>, as well as
167 * its sign. */
168static inline int quantify(int i, int bits)
169{
170 if (i >= 0)
171 return quantify_u32(i, bits);
172 else
173 return -quantify_u32(-i, bits);
174}
175
176/* Insert timer value <v> into tree <r>. A pre-allocated node must be passed
177 * in <alloc>. It may be NULL, in which case the function will allocate it
178 * itself. It will be reset to NULL once consumed. The caller is responsible
179 * for freeing the node once not used anymore. The node where the value was
180 * inserted is returned.
181 */
182struct timer *insert_timer(struct eb_root *r, struct timer **alloc, int v)
183{
184 struct timer *t = *alloc;
185 struct eb32_node *n;
186
187 if (!t) {
188 t = calloc(sizeof(*t), 1);
189 if (unlikely(!t)) {
190 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
191 exit(1);
192 }
193 }
194 t->node.key = quantify(v, QBITS); // keep only the higher QBITS bits
195
196 n = eb32i_insert(r, &t->node);
197 if (n == &t->node)
198 t = NULL; /* node inserted, will malloc next time */
199
200 *alloc = t;
201 return container_of(n, struct timer, node);
202}
203
204/* Insert value value <v> into tree <r>. A pre-allocated node must be passed
205 * in <alloc>. It may be NULL, in which case the function will allocate it
206 * itself. It will be reset to NULL once consumed. The caller is responsible
207 * for freeing the node once not used anymore. The node where the value was
208 * inserted is returned.
209 */
210struct timer *insert_value(struct eb_root *r, struct timer **alloc, int v)
211{
212 struct timer *t = *alloc;
213 struct eb32_node *n;
214
215 if (!t) {
216 t = calloc(sizeof(*t), 1);
217 if (unlikely(!t)) {
218 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
219 exit(1);
220 }
221 }
222 t->node.key = v;
223
224 n = eb32i_insert(r, &t->node);
225 if (n == &t->node)
226 t = NULL; /* node inserted, will malloc next time */
227
228 *alloc = t;
229 return container_of(n, struct timer, node);
230}
231
232int str2ic(const char *s)
233{
234 int i = 0;
235 int j, k;
236
237 if (*s != '-') {
238 /* positive number */
239 while (1) {
240 j = (*s++) - '0';
241 k = i * 10;
242 if ((unsigned)j > 9)
243 break;
244 i = k + j;
245 }
246 } else {
247 /* negative number */
248 s++;
249 while (1) {
250 j = (*s++) - '0';
251 k = i * 10;
252 if ((unsigned)j > 9)
253 break;
254 i = k - j;
255 }
256 }
257
258 return i;
259}
260
261
262/* Equivalent to strtoul with a length. */
263static inline unsigned int __strl2ui(const char *s, int len)
264{
265 unsigned int i = 0;
266 while (len-- > 0) {
267 i = i * 10 - '0';
268 i += (unsigned char)*s++;
269 }
270 return i;
271}
272
273unsigned int strl2ui(const char *s, int len)
274{
275 return __strl2ui(s, len);
276}
277
278/* Convert "[04/Dec/2008:09:49:40.555]" to an integer equivalent to the time of
279 * the day in milliseconds. It returns -1 for all unparsable values. The parser
280 * looks ugly but gcc emits far better code that way.
281 */
282int convert_date(const char *field)
283{
284 unsigned int h, m, s, ms;
285 unsigned char c;
286 const char *b, *e;
287
288 h = m = s = ms = 0;
289 e = field;
290
291 /* skip the date */
292 while (1) {
293 c = *(e++);
294 if (c == ':')
295 break;
296 if (!c)
297 goto out_err;
298 }
299
300 /* hour + ':' */
301 b = e;
302 while (1) {
303 c = *(e++) - '0';
304 if (c > 9)
305 break;
306 h = h * 10 + c;
307 }
308 if (c == (unsigned char)(0 - '0'))
309 goto out_err;
310
311 /* minute + ':' */
312 b = e;
313 while (1) {
314 c = *(e++) - '0';
315 if (c > 9)
316 break;
317 m = m * 10 + c;
318 }
319 if (c == (unsigned char)(0 - '0'))
320 goto out_err;
321
322 /* second + '.' or ']' */
323 b = e;
324 while (1) {
325 c = *(e++) - '0';
326 if (c > 9)
327 break;
328 s = s * 10 + c;
329 }
330 if (c == (unsigned char)(0 - '0'))
331 goto out_err;
332
333 /* if there's a '.', we have milliseconds */
334 if (c == (unsigned char)('.' - '0')) {
335 /* millisecond second + ']' */
336 b = e;
337 while (1) {
338 c = *(e++) - '0';
339 if (c > 9)
340 break;
341 ms = ms * 10 + c;
342 }
343 if (c == (unsigned char)(0 - '0'))
344 goto out_err;
345 }
346 return (((h * 60) + m) * 60 + s) * 1000 + ms;
347 out_err:
348 return -1;
349}
350
351void truncated_line(int linenum, const char *line)
352{
353 if (!(filter & FILT_QUIET))
354 fprintf(stderr, "Truncated line %d: %s\n", linenum, line);
355}
356
357int main(int argc, char **argv)
358{
359 const char *b, *e, *p;
360 const char *output_file = NULL;
361 int f, tot, last, linenum, err, parse_err;
362 struct timer *t = NULL, *t2;
363 struct eb32_node *n;
364 int val, test;
365 int array[5];
366 int filter_acc_delay = 0, filter_acc_count = 0;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200367 int filter_time_resp = 0;
Willy Tarreau72c28532009-01-22 18:56:50 +0100368 int skip_fields = 1;
369
370 argc--; argv++;
371 while (argc > 0) {
372 if (*argv[0] != '-')
373 break;
374
375 if (strcmp(argv[0], "-ad") == 0) {
376 if (argc < 2) die("missing option for -ad");
377 argc--; argv++;
378 filter |= FILT_ACC_DELAY;
379 filter_acc_delay = atol(*argv);
380 }
381 else if (strcmp(argv[0], "-ac") == 0) {
382 if (argc < 2) die("missing option for -ac");
383 argc--; argv++;
384 filter |= FILT_ACC_COUNT;
385 filter_acc_count = atol(*argv);
386 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200387 else if (strcmp(argv[0], "-rt") == 0) {
388 if (argc < 2) die("missing option for -rt");
389 argc--; argv++;
390 filter |= FILT_TIME_RESP;
391 filter_time_resp = atol(*argv);
392 }
393 else if (strcmp(argv[0], "-RT") == 0) {
394 if (argc < 2) die("missing option for -RT");
395 argc--; argv++;
396 filter |= FILT_TIME_RESP | FILT_INVERT_TIME_RESP;
397 filter_time_resp = atol(*argv);
398 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100399 else if (strcmp(argv[0], "-s") == 0) {
400 if (argc < 2) die("missing option for -s");
401 argc--; argv++;
402 skip_fields = atol(*argv);
403 }
404 else if (strcmp(argv[0], "-e") == 0)
405 filter |= FILT_ERRORS_ONLY;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200406 else if (strcmp(argv[0], "-E") == 0)
407 filter |= FILT_ERRORS_ONLY | FILT_INVERT_ERRORS;
Willy Tarreau72c28532009-01-22 18:56:50 +0100408 else if (strcmp(argv[0], "-c") == 0)
409 filter |= FILT_COUNT_ONLY;
410 else if (strcmp(argv[0], "-q") == 0)
411 filter |= FILT_QUIET;
412 else if (strcmp(argv[0], "-v") == 0)
413 filter_invert = !filter_invert;
414 else if (strcmp(argv[0], "-gt") == 0)
415 filter |= FILT_GRAPH_TIMERS;
Willy Tarreau214c2032009-02-20 11:02:32 +0100416 else if (strcmp(argv[0], "-pct") == 0)
417 filter |= FILT_PERCENTILE;
Willy Tarreau0f423a72010-05-03 10:50:54 +0200418 else if (strcmp(argv[0], "-st") == 0)
419 filter |= FILT_COUNT_STATUS;
Willy Tarreaud2201062010-05-27 18:17:30 +0200420 else if (strcmp(argv[0], "-srv") == 0)
421 filter |= FILT_COUNT_SRV_STATUS;
Willy Tarreaud8fc1102010-09-12 17:56:16 +0200422 else if (strcmp(argv[0], "-tc") == 0)
423 filter |= FILT_COUNT_TERM_CODES;
Willy Tarreau72c28532009-01-22 18:56:50 +0100424 else if (strcmp(argv[0], "-o") == 0) {
425 if (output_file)
426 die("Fatal: output file name already specified.\n");
427 if (argc < 2)
428 die("Fatal: missing output file name.\n");
429 output_file = argv[1];
430 }
431 argc--;
432 argv++;
433 }
434
435 if (!filter)
436 die("No action specified.\n");
437
438 if (filter & FILT_ACC_COUNT && !filter_acc_count)
439 filter_acc_count=1;
440
441 if (filter & FILT_ACC_DELAY && !filter_acc_delay)
442 filter_acc_delay = 1;
443
444 linenum = 0;
445 tot = 0;
446 parse_err = 0;
447
Willy Tarreau214c2032009-02-20 11:02:32 +0100448 while ((line = fgets2(stdin)) != NULL) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100449 linenum++;
450
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200451 test = 1;
Willy Tarreau2651ac32010-05-05 12:20:19 +0200452 if (unlikely(filter & FILT_TIME_RESP)) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200453 int tps;
454
455 /* only report lines with response times larger than filter_time_resp */
456 b = field_start(line, TIME_FIELD + skip_fields);
457 if (!*b) {
458 truncated_line(linenum, line);
459 continue;
460 }
461
462 e = field_stop(b + 1);
463 /* we have field TIME_FIELD in [b]..[e-1] */
464
465 p = b;
466 err = 0;
467 for (f = 0; f < 4 && *p; f++) {
468 tps = str2ic(p);
469 if (tps < 0) {
470 tps = -1;
471 err = 1;
472 }
473
474 SKIP_CHAR(p, '/');
475 }
476
477 if (f < 4) {
478 parse_err++;
479 continue;
480 }
481
482 test &= (tps >= filter_time_resp) ^ !!(filter & FILT_INVERT_TIME_RESP);
483 }
484
Willy Tarreau2651ac32010-05-05 12:20:19 +0200485 if (unlikely(filter & FILT_ERRORS_ONLY)) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100486 /* only report erroneous status codes */
487 b = field_start(line, STATUS_FIELD + skip_fields);
488 if (!*b) {
489 truncated_line(linenum, line);
490 continue;
491 }
492 if (*b == '-') {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200493 test &= !!(filter & FILT_INVERT_ERRORS);
Willy Tarreau72c28532009-01-22 18:56:50 +0100494 } else {
495 val = strl2ui(b, 3);
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200496 test &= (val >= 500 && val <= 599) ^ !!(filter & FILT_INVERT_ERRORS);
Willy Tarreau72c28532009-01-22 18:56:50 +0100497 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100498 }
499
Willy Tarreau0f423a72010-05-03 10:50:54 +0200500 test ^= filter_invert;
501 if (!test)
502 continue;
503
Willy Tarreau2651ac32010-05-05 12:20:19 +0200504 if (unlikely(filter & (FILT_ACC_COUNT|FILT_ACC_DELAY))) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100505 b = field_start(line, ACCEPT_FIELD + skip_fields);
506 if (!*b) {
507 truncated_line(linenum, line);
508 continue;
509 }
510
Willy Tarreau214c2032009-02-20 11:02:32 +0100511 tot++;
Willy Tarreau72c28532009-01-22 18:56:50 +0100512 val = convert_date(b);
513 //printf("date=%s => %d\n", b, val);
514 if (val < 0) {
515 parse_err++;
516 continue;
517 }
518
519 t2 = insert_value(&timers[0], &t, val);
520 t2->count++;
521 continue;
522 }
523
Willy Tarreau2651ac32010-05-05 12:20:19 +0200524 if (unlikely(filter & (FILT_GRAPH_TIMERS|FILT_PERCENTILE))) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100525 int f;
526
527 b = field_start(line, TIME_FIELD + skip_fields);
528 if (!*b) {
529 truncated_line(linenum, line);
530 continue;
531 }
532
533 e = field_stop(b + 1);
534 /* we have field TIME_FIELD in [b]..[e-1] */
535
536 p = b;
537 err = 0;
538 for (f = 0; f < 5 && *p; f++) {
539 array[f] = str2ic(p);
540 if (array[f] < 0) {
541 array[f] = -1;
542 err = 1;
543 }
544
545 SKIP_CHAR(p, '/');
546 }
547
548 if (f < 5) {
549 parse_err++;
550 continue;
551 }
552
553 /* if we find at least one negative time, we count one error
554 * with a time equal to the total session time. This will
555 * emphasize quantum timing effects associated to known
556 * timeouts. Note that on some buggy machines, it is possible
557 * that the total time is negative, hence the reason to reset
558 * it.
559 */
Willy Tarreau214c2032009-02-20 11:02:32 +0100560
561 if (filter & FILT_GRAPH_TIMERS) {
562 if (err) {
563 if (array[4] < 0)
564 array[4] = -1;
565 t2 = insert_timer(&timers[0], &t, array[4]); // total time
566 t2->count++;
567 } else {
568 int v;
Willy Tarreau72c28532009-01-22 18:56:50 +0100569
Willy Tarreau214c2032009-02-20 11:02:32 +0100570 t2 = insert_timer(&timers[1], &t, array[0]); t2->count++; // req
571 t2 = insert_timer(&timers[2], &t, array[2]); t2->count++; // conn
572 t2 = insert_timer(&timers[3], &t, array[3]); t2->count++; // resp
573
574 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
575 if (v < 0 && !(filter & FILT_QUIET))
576 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
577 line, array[0], array[1], array[2], array[3], array[4], v);
578 t2 = insert_timer(&timers[4], &t, v); t2->count++;
579 tot++;
580 }
581 } else { /* percentile */
582 if (err) {
583 if (array[4] < 0)
584 array[4] = -1;
585 t2 = insert_value(&timers[0], &t, array[4]); // total time
586 t2->count++;
587 } else {
588 int v;
Willy Tarreau72c28532009-01-22 18:56:50 +0100589
Willy Tarreau214c2032009-02-20 11:02:32 +0100590 t2 = insert_value(&timers[1], &t, array[0]); t2->count++; // req
591 t2 = insert_value(&timers[2], &t, array[2]); t2->count++; // conn
592 t2 = insert_value(&timers[3], &t, array[3]); t2->count++; // resp
593
594 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
595 if (v < 0 && !(filter & FILT_QUIET))
596 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
597 line, array[0], array[1], array[2], array[3], array[4], v);
598 t2 = insert_value(&timers[4], &t, v); t2->count++;
599 tot++;
600 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100601 }
602 continue;
603 }
604
Willy Tarreau2651ac32010-05-05 12:20:19 +0200605 if (unlikely(filter & FILT_COUNT_STATUS)) {
Willy Tarreau54170812010-09-13 22:50:49 +0200606 /* first, let's ensure that the line is a traffic line (beginning
607 * with an IP address)
608 */
609 b = field_start(line, SOURCE_FIELD + skip_fields);
610 if (*b < '0' || *b > '9') {
611 parse_err++;
612 continue;
613 }
614
615 b = field_start(b, STATUS_FIELD - SOURCE_FIELD + 1);
Willy Tarreau0f423a72010-05-03 10:50:54 +0200616 if (!*b) {
617 truncated_line(linenum, line);
618 continue;
619 }
620 val = str2ic(b);
621
622 t2 = insert_value(&timers[0], &t, val);
623 t2->count++;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200624 continue;
Willy Tarreau0f423a72010-05-03 10:50:54 +0200625 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200626
Willy Tarreaud8fc1102010-09-12 17:56:16 +0200627 if (unlikely(filter & FILT_COUNT_TERM_CODES)) {
Willy Tarreau54170812010-09-13 22:50:49 +0200628 /* first, let's ensure that the line is a traffic line (beginning
629 * with an IP address)
630 */
631 b = field_start(line, SOURCE_FIELD + skip_fields);
632 if (*b < '0' || *b > '9') {
633 parse_err++;
634 continue;
635 }
636
637 b = field_start(b, TERM_CODES_FIELD - SOURCE_FIELD + 1);
Willy Tarreaud8fc1102010-09-12 17:56:16 +0200638 if (!*b) {
639 truncated_line(linenum, line);
640 continue;
641 }
642 val = 256 * b[0] + b[1];
643
644 t2 = insert_value(&timers[0], &t, val);
645 t2->count++;
646 continue;
647 }
648
Willy Tarreaud2201062010-05-27 18:17:30 +0200649 if (unlikely(filter & FILT_COUNT_SRV_STATUS)) {
650 char *srv_name;
651 struct ebmb_node *srv_node;
652 struct srv_st *srv;
653
654 /* first, let's ensure that the line is a traffic line (beginning
655 * with an IP address)
656 */
657 b = field_start(line, SOURCE_FIELD + skip_fields);
658 if (*b < '0' || *b > '9') {
659 parse_err++;
660 continue;
661 }
662
663 /* the server field is before the status field, so let's
664 * parse them in the proper order.
665 */
666 b = field_start(b, SERVER_FIELD - SOURCE_FIELD + 1);
667 if (!*b) {
668 truncated_line(linenum, line);
669 continue;
670 }
671
672 e = field_stop(b + 1); /* we have the server name in [b]..[e-1] */
673
674 /* the chance that a server name already exists is extremely high,
675 * so let's perform a normal lookup first.
676 */
677 srv_node = ebst_lookup_len(&timers[0], b, e - b);
678 srv = container_of(srv_node, struct srv_st, node);
679
680 if (!srv_node) {
681 /* server not yet in the tree, let's create it */
682 srv = (void *)calloc(1, sizeof(struct srv_st) + e - b + 1);
683 srv_node = &srv->node;
684 memcpy(&srv_node->key, b, e - b);
685 srv_node->key[e - b] = '\0';
686 ebst_insert(&timers[0], srv_node);
687 }
688
689 /* let's collect the connect and response times */
690 b = field_start(e, TIME_FIELD - SERVER_FIELD);
691 if (!*b) {
692 truncated_line(linenum, line);
693 continue;
694 }
695
696 e = field_stop(b + 1);
697 /* we have field TIME_FIELD in [b]..[e-1] */
698
699 p = b;
700 err = 0;
701 for (f = 0; f < 5 && *p; f++) {
702 array[f] = str2ic(p);
703 if (array[f] < 0) {
704 array[f] = -1;
705 err = 1;
706 }
707
708 SKIP_CHAR(p, '/');
709 }
710
711 if (f < 5) {
712 parse_err++;
713 continue;
714 }
715
716 /* OK we have our timers in array[2,3] */
717 if (!err)
718 srv->nb_ok++;
719
720 if (array[2] >= 0) {
721 srv->cum_ct += array[2];
722 srv->nb_ct++;
723 }
724
725 if (array[3] >= 0) {
726 srv->cum_rt += array[3];
727 srv->nb_rt++;
728 }
729
730 /* we're interested in the 5 HTTP status classes (1xx ... 5xx), and
731 * the invalid ones which will be reported as 0.
732 */
733 b = field_start(e, STATUS_FIELD - TIME_FIELD);
734 if (!*b) {
735 truncated_line(linenum, line);
736 continue;
737 }
738
739 val = 0;
740 if (*b >= '1' && *b <= '5')
741 val = *b - '0';
742
743 srv->st_cnt[val]++;
744 continue;
745 }
746
Willy Tarreau72c28532009-01-22 18:56:50 +0100747 /* all other cases mean we just want to count lines */
748 tot++;
Willy Tarreau2651ac32010-05-05 12:20:19 +0200749 if (unlikely(!(filter & FILT_COUNT_ONLY)))
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200750 puts(line);
Willy Tarreau72c28532009-01-22 18:56:50 +0100751 }
752
753 if (t)
754 free(t);
755
756 if (filter & FILT_COUNT_ONLY) {
757 printf("%d\n", tot);
758 exit(0);
759 }
760
Willy Tarreau72c28532009-01-22 18:56:50 +0100761 if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY)) {
762 /* sort and count all timers. Output will look like this :
763 * <accept_date> <delta_ms from previous one> <nb entries>
764 */
765 n = eb32_first(&timers[0]);
766
767 if (n)
768 last = n->key;
769 while (n) {
770 unsigned int d, h, m, s, ms;
771
772 t = container_of(n, struct timer, node);
773 h = n->key;
774 d = h - last;
775 last = h;
776
777 if (d >= filter_acc_delay && t->count >= filter_acc_count) {
778 ms = h % 1000; h = h / 1000;
779 s = h % 60; h = h / 60;
780 m = h % 60; h = h / 60;
781 tot++;
782 printf("%02d:%02d:%02d.%03d %d %d %d\n", h, m, s, ms, last, d, t->count);
783 }
784 n = eb32_next(n);
785 }
786 }
787 else if (filter & FILT_GRAPH_TIMERS) {
788 /* sort all timers */
789 for (f = 0; f < 5; f++) {
790 struct eb32_node *n;
791 int val;
792
793 val = 0;
794 n = eb32_first(&timers[f]);
795 while (n) {
796 int i;
797 double d;
798
799 t = container_of(n, struct timer, node);
800 last = n->key;
801 val = t->count;
802
803 i = (last < 0) ? -last : last;
804 i = fls_auto(i) - QBITS;
805
806 if (i > 0)
807 d = val / (double)(1 << i);
808 else
809 d = val;
810
811 if (d > 0.0) {
812 printf("%d %d %f\n", f, last, d+1.0);
813 tot++;
814 }
815
816 n = eb32_next(n);
817 }
Willy Tarreau214c2032009-02-20 11:02:32 +0100818 }
819 }
820 else if (filter & FILT_PERCENTILE) {
821 /* report timers by percentile :
822 * <percent> <total> <max_req_time> <max_conn_time> <max_resp_time> <max_data_time>
823 * We don't count errs.
824 */
825 struct eb32_node *n[5];
826 unsigned long cum[5];
827 double step;
828
Willy Tarreau910ba4b2009-11-17 10:16:19 +0100829 if (!tot)
830 goto empty;
831
Willy Tarreau214c2032009-02-20 11:02:32 +0100832 for (f = 1; f < 5; f++) {
833 n[f] = eb32_first(&timers[f]);
834 cum[f] = container_of(n[f], struct timer, node)->count;
835 }
836
837 for (step = 1; step <= 1000;) {
838 unsigned int thres = tot * (step / 1000.0);
839
840 printf("%3.1f %d ", step/10.0, thres);
841 for (f = 1; f < 5; f++) {
842 struct eb32_node *next;
843 while (cum[f] < thres) {
844 /* need to find other keys */
845 next = eb32_next(n[f]);
846 if (!next)
847 break;
848 n[f] = next;
849 cum[f] += container_of(next, struct timer, node)->count;
850 }
851
852 /* value still within $step % of total */
853 printf("%d ", n[f]->key);
854 }
855 putchar('\n');
856 if (step >= 100 && step < 900)
857 step += 50; // jump 5% by 5% between those steps.
858 else if (step >= 20 && step < 980)
859 step += 10;
860 else
861 step += 1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100862 }
863 }
Willy Tarreau0f423a72010-05-03 10:50:54 +0200864 else if (filter & FILT_COUNT_STATUS) {
865 /* output all statuses in the form of <status> <occurrences> */
866 n = eb32_first(&timers[0]);
867 while (n) {
868 t = container_of(n, struct timer, node);
869 printf("%d %d\n", n->key, t->count);
870 n = eb32_next(n);
871 }
872 }
Willy Tarreaud2201062010-05-27 18:17:30 +0200873 else if (unlikely(filter & FILT_COUNT_SRV_STATUS)) {
874 char *srv_name;
875 struct ebmb_node *srv_node;
876 struct srv_st *srv;
877
878 printf("#srv_name 1xx 2xx 3xx 4xx 5xx other tot_req req_ok pct_ok avg_ct avg_rt\n");
879
880 srv_node = ebmb_first(&timers[0]);
881 while (srv_node) {
882 int tot_rq;
883
884 srv = container_of(srv_node, struct srv_st, node);
885
886 tot_rq = 0;
887 for (f = 0; f <= 5; f++)
888 tot_rq += srv->st_cnt[f];
889
890 printf("%s %d %d %d %d %d %d %d %d %.1f %d %d\n",
891 srv_node->key, srv->st_cnt[1], srv->st_cnt[2],
892 srv->st_cnt[3], srv->st_cnt[4], srv->st_cnt[5], srv->st_cnt[0],
893 tot_rq,
894 srv->nb_ok, (double)srv->nb_ok * 100.0 / (tot_rq?tot_rq:1),
895 (int)(srv->cum_ct / (srv->nb_ct?srv->nb_ct:1)), (int)(srv->cum_rt / (srv->nb_rt?srv->nb_rt:1)));
896 srv_node = ebmb_next(srv_node);
897 tot++;
898 }
899 }
Willy Tarreaud8fc1102010-09-12 17:56:16 +0200900 else if (filter & FILT_COUNT_TERM_CODES) {
901 /* output all statuses in the form of <code> <occurrences> */
902 n = eb32_first(&timers[0]);
903 while (n) {
904 t = container_of(n, struct timer, node);
905 printf("%c%c %d\n", (n->key >> 8), (n->key) & 255, t->count);
906 n = eb32_next(n);
907 }
908 }
Willy Tarreaud2201062010-05-27 18:17:30 +0200909
Willy Tarreau910ba4b2009-11-17 10:16:19 +0100910 empty:
Willy Tarreau72c28532009-01-22 18:56:50 +0100911 if (!(filter & FILT_QUIET))
912 fprintf(stderr, "%d lines in, %d lines out, %d parsing errors\n",
913 linenum, tot, parse_err);
914 exit(0);
915}
916
917/*
918 * Local variables:
919 * c-indent-level: 8
920 * c-basic-offset: 8
921 * End:
922 */