blob: ad905101eea39160e4d8247b6823777af7e28942 [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>. */
Willy Tarreauf9042062011-09-10 12:26:35 +0200138
139#if defined(__i386__)
140/* this one is always faster on 32-bits */
141static inline const char *field_stop(const char *p)
142{
143 asm(
144 /* Look for spaces */
145 "4: \n\t"
146 "inc %0 \n\t"
147 "cmpb $0x20, -1(%0) \n\t"
148 "ja 4b \n\t"
149 "jz 3f \n\t"
150
151 /* we only get there for control chars 0..31. Leave if we find '\0' */
152 "cmpb $0x0, -1(%0) \n\t"
153 "jnz 4b \n\t"
154
155 /* return %0-1 = position of the last char we checked */
156 "3: \n\t"
157 "dec %0 \n\t"
158 : "=r" (p)
159 : "0" (p)
160 );
161 return p;
162}
163#else
Willy Tarreau72c28532009-01-22 18:56:50 +0100164const char *field_stop(const char *p)
165{
166 unsigned char c;
167
168 while (1) {
169 c = *(p++);
170 if (c > ' ')
171 continue;
Willy Tarreau14389e72011-07-10 22:11:17 +0200172 if (c == ' ' || c == 0)
Willy Tarreau72c28532009-01-22 18:56:50 +0100173 break;
174 }
175 return p - 1;
176}
Willy Tarreauf9042062011-09-10 12:26:35 +0200177#endif
Willy Tarreau72c28532009-01-22 18:56:50 +0100178
179/* return field <field> (starting from 1) in string <p>. Only consider
180 * contiguous spaces (or tabs) as one delimiter. May return pointer to
181 * last char if field is not found. Equivalent to awk '{print $field}'.
182 */
183const char *field_start(const char *p, int field)
184{
Willy Tarreauf9042062011-09-10 12:26:35 +0200185#ifndef PREFER_ASM
Willy Tarreau72c28532009-01-22 18:56:50 +0100186 unsigned char c;
187 while (1) {
188 /* skip spaces */
189 while (1) {
Willy Tarreauf9042062011-09-10 12:26:35 +0200190 c = *(p++);
Willy Tarreau72c28532009-01-22 18:56:50 +0100191 if (c > ' ')
192 break;
Willy Tarreau14389e72011-07-10 22:11:17 +0200193 if (c == ' ')
Willy Tarreauf9042062011-09-10 12:26:35 +0200194 continue;
Willy Tarreau72c28532009-01-22 18:56:50 +0100195 if (!c) /* end of line */
Willy Tarreauf9042062011-09-10 12:26:35 +0200196 return p-1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100197 /* other char => new field */
198 break;
Willy Tarreau72c28532009-01-22 18:56:50 +0100199 }
200
201 /* start of field */
202 field--;
203 if (!field)
Willy Tarreauf9042062011-09-10 12:26:35 +0200204 return p-1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100205
206 /* skip this field */
207 while (1) {
208 c = *(p++);
Willy Tarreau14389e72011-07-10 22:11:17 +0200209 if (c == ' ')
Willy Tarreau72c28532009-01-22 18:56:50 +0100210 break;
Willy Tarreauf9042062011-09-10 12:26:35 +0200211 if (c > ' ')
212 continue;
Willy Tarreau72c28532009-01-22 18:56:50 +0100213 if (c == '\0')
Willy Tarreauf9042062011-09-10 12:26:35 +0200214 return p - 1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100215 }
216 }
Willy Tarreauf9042062011-09-10 12:26:35 +0200217#else
218 /* This version works optimally on i386 and x86_64 but the code above
219 * shows similar performance. However, depending on the version of GCC
220 * used, inlining rules change and it may have difficulties to make
221 * efficient use of this code at other locations and could result in
222 * worse performance (eg: gcc 4.4). You may want to experience.
223 */
224 asm(
225 /* skip spaces */
226 "1: \n\t"
227 "inc %0 \n\t"
228 "cmpb $0x20, -1(%0) \n\t"
229 "ja 2f \n\t"
230 "jz 1b \n\t"
231
232 /* we only get there for control chars 0..31. Leave if we find '\0' */
233 "cmpb $0x0, -1(%0) \n\t"
234 "jz 3f \n\t"
235
236 /* start of field at [%0-1]. Check if we need to skip more fields */
237 "2: \n\t"
238 "dec %1 \n\t"
239 "jz 3f \n\t"
240
241 /* Look for spaces */
242 "4: \n\t"
243 "inc %0 \n\t"
244 "cmpb $0x20, -1(%0) \n\t"
245 "jz 1b \n\t"
246 "ja 4b \n\t"
247
248 /* we only get there for control chars 0..31. Leave if we find '\0' */
249 "cmpb $0x0, -1(%0) \n\t"
250 "jnz 4b \n\t"
251
252 /* return %0-1 = position of the last char we checked */
253 "3: \n\t"
254 "dec %0 \n\t"
255 : "=r" (p)
256 : "r" (field), "0" (p)
257 );
258 return p;
259#endif
Willy Tarreau72c28532009-01-22 18:56:50 +0100260}
261
262/* keep only the <bits> higher bits of <i> */
263static inline unsigned int quantify_u32(unsigned int i, int bits)
264{
265 int high;
266
267 if (!bits)
268 return 0;
269
270 if (i)
271 high = fls_auto(i); // 1 to 32
272 else
273 high = 0;
274
275 if (high <= bits)
276 return i;
277
278 return i & ~((1 << (high - bits)) - 1);
279}
280
281/* keep only the <bits> higher bits of the absolute value of <i>, as well as
282 * its sign. */
283static inline int quantify(int i, int bits)
284{
285 if (i >= 0)
286 return quantify_u32(i, bits);
287 else
288 return -quantify_u32(-i, bits);
289}
290
291/* Insert timer value <v> into tree <r>. A pre-allocated node must be passed
292 * in <alloc>. It may be NULL, in which case the function will allocate it
293 * itself. It will be reset to NULL once consumed. The caller is responsible
294 * for freeing the node once not used anymore. The node where the value was
295 * inserted is returned.
296 */
297struct timer *insert_timer(struct eb_root *r, struct timer **alloc, int v)
298{
299 struct timer *t = *alloc;
300 struct eb32_node *n;
301
302 if (!t) {
303 t = calloc(sizeof(*t), 1);
304 if (unlikely(!t)) {
305 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
306 exit(1);
307 }
308 }
309 t->node.key = quantify(v, QBITS); // keep only the higher QBITS bits
310
311 n = eb32i_insert(r, &t->node);
312 if (n == &t->node)
313 t = NULL; /* node inserted, will malloc next time */
314
315 *alloc = t;
316 return container_of(n, struct timer, node);
317}
318
319/* Insert value value <v> into tree <r>. A pre-allocated node must be passed
320 * in <alloc>. It may be NULL, in which case the function will allocate it
321 * itself. It will be reset to NULL once consumed. The caller is responsible
322 * for freeing the node once not used anymore. The node where the value was
323 * inserted is returned.
324 */
325struct timer *insert_value(struct eb_root *r, struct timer **alloc, int v)
326{
327 struct timer *t = *alloc;
328 struct eb32_node *n;
329
330 if (!t) {
331 t = calloc(sizeof(*t), 1);
332 if (unlikely(!t)) {
333 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
334 exit(1);
335 }
336 }
337 t->node.key = v;
338
339 n = eb32i_insert(r, &t->node);
340 if (n == &t->node)
341 t = NULL; /* node inserted, will malloc next time */
342
343 *alloc = t;
344 return container_of(n, struct timer, node);
345}
346
347int str2ic(const char *s)
348{
349 int i = 0;
350 int j, k;
351
352 if (*s != '-') {
353 /* positive number */
354 while (1) {
355 j = (*s++) - '0';
356 k = i * 10;
357 if ((unsigned)j > 9)
358 break;
359 i = k + j;
360 }
361 } else {
362 /* negative number */
363 s++;
364 while (1) {
365 j = (*s++) - '0';
366 k = i * 10;
367 if ((unsigned)j > 9)
368 break;
369 i = k - j;
370 }
371 }
372
373 return i;
374}
375
376
377/* Equivalent to strtoul with a length. */
378static inline unsigned int __strl2ui(const char *s, int len)
379{
380 unsigned int i = 0;
381 while (len-- > 0) {
382 i = i * 10 - '0';
383 i += (unsigned char)*s++;
384 }
385 return i;
386}
387
388unsigned int strl2ui(const char *s, int len)
389{
390 return __strl2ui(s, len);
391}
392
393/* Convert "[04/Dec/2008:09:49:40.555]" to an integer equivalent to the time of
394 * the day in milliseconds. It returns -1 for all unparsable values. The parser
395 * looks ugly but gcc emits far better code that way.
396 */
397int convert_date(const char *field)
398{
399 unsigned int h, m, s, ms;
400 unsigned char c;
401 const char *b, *e;
402
403 h = m = s = ms = 0;
404 e = field;
405
406 /* skip the date */
407 while (1) {
408 c = *(e++);
409 if (c == ':')
410 break;
411 if (!c)
412 goto out_err;
413 }
414
415 /* hour + ':' */
416 b = e;
417 while (1) {
418 c = *(e++) - '0';
419 if (c > 9)
420 break;
421 h = h * 10 + c;
422 }
423 if (c == (unsigned char)(0 - '0'))
424 goto out_err;
425
426 /* minute + ':' */
427 b = e;
428 while (1) {
429 c = *(e++) - '0';
430 if (c > 9)
431 break;
432 m = m * 10 + c;
433 }
434 if (c == (unsigned char)(0 - '0'))
435 goto out_err;
436
437 /* second + '.' or ']' */
438 b = e;
439 while (1) {
440 c = *(e++) - '0';
441 if (c > 9)
442 break;
443 s = s * 10 + c;
444 }
445 if (c == (unsigned char)(0 - '0'))
446 goto out_err;
447
448 /* if there's a '.', we have milliseconds */
449 if (c == (unsigned char)('.' - '0')) {
450 /* millisecond second + ']' */
451 b = e;
452 while (1) {
453 c = *(e++) - '0';
454 if (c > 9)
455 break;
456 ms = ms * 10 + c;
457 }
458 if (c == (unsigned char)(0 - '0'))
459 goto out_err;
460 }
461 return (((h * 60) + m) * 60 + s) * 1000 + ms;
462 out_err:
463 return -1;
464}
465
466void truncated_line(int linenum, const char *line)
467{
468 if (!(filter & FILT_QUIET))
469 fprintf(stderr, "Truncated line %d: %s\n", linenum, line);
470}
471
472int main(int argc, char **argv)
473{
Willy Tarreau26deaf52011-07-10 19:47:48 +0200474 const char *b, *e, *p, *time_field, *accept_field;
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200475 const char *filter_term_code_name = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100476 const char *output_file = NULL;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200477 int f, last, err;
478 struct timer *t = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100479 struct eb32_node *n;
Willy Tarreauabe45b62010-10-28 20:33:46 +0200480 struct url_stat *ustat = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100481 int val, test;
Willy Tarreau72c28532009-01-22 18:56:50 +0100482 int filter_acc_delay = 0, filter_acc_count = 0;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200483 int filter_time_resp = 0;
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200484 int filt_http_status_low = 0, filt_http_status_high = 0;
Willy Tarreau72c28532009-01-22 18:56:50 +0100485 int skip_fields = 1;
486
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200487 void (*line_filter)(const char *accept_field, const char *time_field, struct timer **tptr) = NULL;
488
Willy Tarreau72c28532009-01-22 18:56:50 +0100489 argc--; argv++;
490 while (argc > 0) {
491 if (*argv[0] != '-')
492 break;
493
494 if (strcmp(argv[0], "-ad") == 0) {
495 if (argc < 2) die("missing option for -ad");
496 argc--; argv++;
497 filter |= FILT_ACC_DELAY;
498 filter_acc_delay = atol(*argv);
499 }
500 else if (strcmp(argv[0], "-ac") == 0) {
501 if (argc < 2) die("missing option for -ac");
502 argc--; argv++;
503 filter |= FILT_ACC_COUNT;
504 filter_acc_count = atol(*argv);
505 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200506 else if (strcmp(argv[0], "-rt") == 0) {
507 if (argc < 2) die("missing option for -rt");
508 argc--; argv++;
509 filter |= FILT_TIME_RESP;
510 filter_time_resp = atol(*argv);
511 }
512 else if (strcmp(argv[0], "-RT") == 0) {
513 if (argc < 2) die("missing option for -RT");
514 argc--; argv++;
515 filter |= FILT_TIME_RESP | FILT_INVERT_TIME_RESP;
516 filter_time_resp = atol(*argv);
517 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100518 else if (strcmp(argv[0], "-s") == 0) {
519 if (argc < 2) die("missing option for -s");
520 argc--; argv++;
521 skip_fields = atol(*argv);
522 }
523 else if (strcmp(argv[0], "-e") == 0)
524 filter |= FILT_ERRORS_ONLY;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200525 else if (strcmp(argv[0], "-E") == 0)
526 filter |= FILT_ERRORS_ONLY | FILT_INVERT_ERRORS;
Willy Tarreau70c428f2011-07-10 17:27:40 +0200527 else if (strcmp(argv[0], "-H") == 0)
528 filter |= FILT_HTTP_ONLY;
Willy Tarreau72c28532009-01-22 18:56:50 +0100529 else if (strcmp(argv[0], "-c") == 0)
530 filter |= FILT_COUNT_ONLY;
531 else if (strcmp(argv[0], "-q") == 0)
532 filter |= FILT_QUIET;
533 else if (strcmp(argv[0], "-v") == 0)
534 filter_invert = !filter_invert;
535 else if (strcmp(argv[0], "-gt") == 0)
536 filter |= FILT_GRAPH_TIMERS;
Willy Tarreau214c2032009-02-20 11:02:32 +0100537 else if (strcmp(argv[0], "-pct") == 0)
538 filter |= FILT_PERCENTILE;
Willy Tarreau0f423a72010-05-03 10:50:54 +0200539 else if (strcmp(argv[0], "-st") == 0)
540 filter |= FILT_COUNT_STATUS;
Willy Tarreaud2201062010-05-27 18:17:30 +0200541 else if (strcmp(argv[0], "-srv") == 0)
542 filter |= FILT_COUNT_SRV_STATUS;
Willy Tarreaud8fc1102010-09-12 17:56:16 +0200543 else if (strcmp(argv[0], "-tc") == 0)
544 filter |= FILT_COUNT_TERM_CODES;
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200545 else if (strcmp(argv[0], "-tcn") == 0) {
546 if (argc < 2) die("missing option for -tcn");
547 argc--; argv++;
548 filter |= FILT_TERM_CODE_NAME;
549 filter_term_code_name = *argv;
550 }
551 else if (strcmp(argv[0], "-TCN") == 0) {
552 if (argc < 2) die("missing option for -TCN");
553 argc--; argv++;
554 filter |= FILT_TERM_CODE_NAME | FILT_INVERT_TERM_CODE_NAME;
555 filter_term_code_name = *argv;
556 }
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200557 else if (strcmp(argv[0], "-hs") == 0 || strcmp(argv[0], "-HS") == 0) {
558 char *sep, *str;
559
560 if (argc < 2) die("missing option for -hs/-HS ([min]:[max])");
561 filter |= FILT_HTTP_STATUS;
562 if (argv[0][1] == 'H')
563 filter |= FILT_INVERT_HTTP_STATUS;
564
565 argc--; argv++;
566 str = *argv;
567 sep = strchr(str, ':'); /* [min]:[max] */
568 if (!sep)
569 sep = str; /* make max point to min */
570 else
571 *sep++ = 0;
572 filt_http_status_low = *str ? atol(str) : 0;
573 filt_http_status_high = *sep ? atol(sep) : 65535;
574 }
Willy Tarreauabe45b62010-10-28 20:33:46 +0200575 else if (strcmp(argv[0], "-u") == 0)
576 filter |= FILT_COUNT_URL_ONLY;
577 else if (strcmp(argv[0], "-uc") == 0)
578 filter |= FILT_COUNT_URL_COUNT;
579 else if (strcmp(argv[0], "-ue") == 0)
580 filter |= FILT_COUNT_URL_ERR;
581 else if (strcmp(argv[0], "-ua") == 0)
582 filter |= FILT_COUNT_URL_TAVG;
583 else if (strcmp(argv[0], "-ut") == 0)
584 filter |= FILT_COUNT_URL_TTOT;
585 else if (strcmp(argv[0], "-uao") == 0)
586 filter |= FILT_COUNT_URL_TAVGO;
587 else if (strcmp(argv[0], "-uto") == 0)
588 filter |= FILT_COUNT_URL_TTOTO;
Willy Tarreau72c28532009-01-22 18:56:50 +0100589 else if (strcmp(argv[0], "-o") == 0) {
590 if (output_file)
591 die("Fatal: output file name already specified.\n");
592 if (argc < 2)
593 die("Fatal: missing output file name.\n");
594 output_file = argv[1];
595 }
596 argc--;
597 argv++;
598 }
599
600 if (!filter)
601 die("No action specified.\n");
602
603 if (filter & FILT_ACC_COUNT && !filter_acc_count)
604 filter_acc_count=1;
605
606 if (filter & FILT_ACC_DELAY && !filter_acc_delay)
607 filter_acc_delay = 1;
608
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200609
610 /* by default, all lines are printed */
611 line_filter = filter_output_line;
612 if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY))
613 line_filter = filter_accept_holes;
614 else if (filter & (FILT_GRAPH_TIMERS|FILT_PERCENTILE))
615 line_filter = filter_graphs;
616 else if (filter & FILT_COUNT_STATUS)
617 line_filter = filter_count_status;
618 else if (filter & FILT_COUNT_TERM_CODES)
619 line_filter = filter_count_term_codes;
620 else if (filter & FILT_COUNT_SRV_STATUS)
621 line_filter = filter_count_srv_status;
622 else if (filter & FILT_COUNT_URL_ANY)
623 line_filter = filter_count_url;
624 else if (filter & FILT_COUNT_ONLY)
625 line_filter = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100626
Willy Tarreau214c2032009-02-20 11:02:32 +0100627 while ((line = fgets2(stdin)) != NULL) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100628 linenum++;
Willy Tarreau26deaf52011-07-10 19:47:48 +0200629 time_field = NULL; accept_field = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100630
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200631 test = 1;
Willy Tarreau26deaf52011-07-10 19:47:48 +0200632
633 /* for any line we process, we first ensure that there is a field
634 * looking like the accept date field (beginning with a '[').
635 */
636 accept_field = field_start(line, ACCEPT_FIELD + skip_fields);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200637 if (unlikely(*accept_field != '[')) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200638 parse_err++;
639 continue;
640 }
641
642 /* the day of month field is begin 01 and 31 */
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200643 if (accept_field[1] < '0' || accept_field[1] > '3') {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200644 parse_err++;
645 continue;
646 }
647
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200648 if (filter & FILT_HTTP_ONLY) {
Willy Tarreau70c428f2011-07-10 17:27:40 +0200649 /* only report lines with at least 4 timers */
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200650 if (!time_field) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200651 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200652 if (unlikely(!*time_field)) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200653 truncated_line(linenum, line);
654 continue;
655 }
Willy Tarreau70c428f2011-07-10 17:27:40 +0200656 }
657
Willy Tarreau758a6ea2011-07-10 18:53:44 +0200658 e = field_stop(time_field + 1);
659 /* we have field TIME_FIELD in [time_field]..[e-1] */
660 p = time_field;
Willy Tarreau70c428f2011-07-10 17:27:40 +0200661 f = 0;
Willy Tarreaudf6f0d12011-07-10 18:15:08 +0200662 while (!SEP(*p)) {
Willy Tarreau70c428f2011-07-10 17:27:40 +0200663 if (++f == 4)
664 break;
665 SKIP_CHAR(p, '/');
666 }
667 test &= (f >= 4);
668 }
669
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200670 if (filter & FILT_TIME_RESP) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200671 int tps;
672
673 /* only report lines with response times larger than filter_time_resp */
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200674 if (!time_field) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200675 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200676 if (unlikely(!*time_field)) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200677 truncated_line(linenum, line);
678 continue;
679 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200680 }
681
Willy Tarreau758a6ea2011-07-10 18:53:44 +0200682 e = field_stop(time_field + 1);
683 /* we have field TIME_FIELD in [time_field]..[e-1], let's check only the response time */
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200684
Willy Tarreau758a6ea2011-07-10 18:53:44 +0200685 p = time_field;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200686 err = 0;
Willy Tarreau24bcb4f2010-10-28 20:39:50 +0200687 f = 0;
Willy Tarreaudf6f0d12011-07-10 18:15:08 +0200688 while (!SEP(*p)) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200689 tps = str2ic(p);
690 if (tps < 0) {
691 tps = -1;
692 err = 1;
693 }
Willy Tarreau24bcb4f2010-10-28 20:39:50 +0200694 if (++f == 4)
695 break;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200696 SKIP_CHAR(p, '/');
697 }
698
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200699 if (unlikely(f < 4)) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200700 parse_err++;
701 continue;
702 }
703
704 test &= (tps >= filter_time_resp) ^ !!(filter & FILT_INVERT_TIME_RESP);
705 }
706
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200707 if (filter & (FILT_ERRORS_ONLY | FILT_HTTP_STATUS)) {
708 /* Check both error codes (-1, 5xx) and status code ranges */
Willy Tarreau26deaf52011-07-10 19:47:48 +0200709 if (time_field)
710 b = field_start(time_field, STATUS_FIELD - TIME_FIELD + 1);
711 else
712 b = field_start(accept_field, STATUS_FIELD - ACCEPT_FIELD + 1);
713
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200714 if (unlikely(!*b)) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100715 truncated_line(linenum, line);
716 continue;
717 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200718
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200719 val = str2ic(b);
720 if (filter & FILT_ERRORS_ONLY)
721 test &= (val < 0 || (val >= 500 && val <= 599)) ^ !!(filter & FILT_INVERT_ERRORS);
722
723 if (filter & FILT_HTTP_STATUS)
724 test &= (val >= filt_http_status_low && val <= filt_http_status_high) ^ !!(filter & FILT_INVERT_HTTP_STATUS);
Willy Tarreau72c28532009-01-22 18:56:50 +0100725 }
726
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200727 if (filter & FILT_TERM_CODE_NAME) {
728 /* only report corresponding termination code name */
729 if (time_field)
730 b = field_start(time_field, TERM_CODES_FIELD - TIME_FIELD + 1);
731 else
732 b = field_start(accept_field, TERM_CODES_FIELD - ACCEPT_FIELD + 1);
733
734 if (unlikely(!*b)) {
735 truncated_line(linenum, line);
736 continue;
737 }
738
739 test &= (b[0] == filter_term_code_name[0] && b[1] == filter_term_code_name[1]) ^ !!(filter & FILT_INVERT_TERM_CODE_NAME);
740 }
741
742
Willy Tarreau0f423a72010-05-03 10:50:54 +0200743 test ^= filter_invert;
744 if (!test)
745 continue;
746
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200747 /************** here we process inputs *******************/
Willy Tarreau72c28532009-01-22 18:56:50 +0100748
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200749 if (line_filter)
750 line_filter(accept_field, time_field, &t);
751 else
752 lines_out++; /* we're just counting lines */
753 }
Willy Tarreauabe45b62010-10-28 20:33:46 +0200754
Willy Tarreauabe45b62010-10-28 20:33:46 +0200755
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200756 /*****************************************************
757 * Here we've finished reading all input. Depending on the
758 * filters, we may still have some analysis to run on the
759 * collected data and to output data in a new format.
760 *************************************************** */
Willy Tarreau72c28532009-01-22 18:56:50 +0100761
762 if (t)
763 free(t);
764
765 if (filter & FILT_COUNT_ONLY) {
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200766 printf("%d\n", lines_out);
Willy Tarreau72c28532009-01-22 18:56:50 +0100767 exit(0);
768 }
769
Willy Tarreau72c28532009-01-22 18:56:50 +0100770 if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY)) {
771 /* sort and count all timers. Output will look like this :
772 * <accept_date> <delta_ms from previous one> <nb entries>
773 */
774 n = eb32_first(&timers[0]);
775
776 if (n)
777 last = n->key;
778 while (n) {
779 unsigned int d, h, m, s, ms;
780
781 t = container_of(n, struct timer, node);
782 h = n->key;
783 d = h - last;
784 last = h;
785
786 if (d >= filter_acc_delay && t->count >= filter_acc_count) {
787 ms = h % 1000; h = h / 1000;
788 s = h % 60; h = h / 60;
789 m = h % 60; h = h / 60;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200790 lines_out++;
Willy Tarreau72c28532009-01-22 18:56:50 +0100791 printf("%02d:%02d:%02d.%03d %d %d %d\n", h, m, s, ms, last, d, t->count);
792 }
793 n = eb32_next(n);
794 }
795 }
796 else if (filter & FILT_GRAPH_TIMERS) {
797 /* sort all timers */
798 for (f = 0; f < 5; f++) {
799 struct eb32_node *n;
800 int val;
801
802 val = 0;
803 n = eb32_first(&timers[f]);
804 while (n) {
805 int i;
806 double d;
807
808 t = container_of(n, struct timer, node);
809 last = n->key;
810 val = t->count;
811
812 i = (last < 0) ? -last : last;
813 i = fls_auto(i) - QBITS;
814
815 if (i > 0)
816 d = val / (double)(1 << i);
817 else
818 d = val;
819
820 if (d > 0.0) {
821 printf("%d %d %f\n", f, last, d+1.0);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200822 lines_out++;
Willy Tarreau72c28532009-01-22 18:56:50 +0100823 }
824
825 n = eb32_next(n);
826 }
Willy Tarreau214c2032009-02-20 11:02:32 +0100827 }
828 }
829 else if (filter & FILT_PERCENTILE) {
830 /* report timers by percentile :
831 * <percent> <total> <max_req_time> <max_conn_time> <max_resp_time> <max_data_time>
832 * We don't count errs.
833 */
834 struct eb32_node *n[5];
835 unsigned long cum[5];
836 double step;
837
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200838 if (!lines_out)
Willy Tarreau910ba4b2009-11-17 10:16:19 +0100839 goto empty;
840
Willy Tarreau214c2032009-02-20 11:02:32 +0100841 for (f = 1; f < 5; f++) {
842 n[f] = eb32_first(&timers[f]);
843 cum[f] = container_of(n[f], struct timer, node)->count;
844 }
845
846 for (step = 1; step <= 1000;) {
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200847 unsigned int thres = lines_out * (step / 1000.0);
Willy Tarreau214c2032009-02-20 11:02:32 +0100848
849 printf("%3.1f %d ", step/10.0, thres);
850 for (f = 1; f < 5; f++) {
851 struct eb32_node *next;
852 while (cum[f] < thres) {
853 /* need to find other keys */
854 next = eb32_next(n[f]);
855 if (!next)
856 break;
857 n[f] = next;
858 cum[f] += container_of(next, struct timer, node)->count;
859 }
860
861 /* value still within $step % of total */
862 printf("%d ", n[f]->key);
863 }
864 putchar('\n');
865 if (step >= 100 && step < 900)
866 step += 50; // jump 5% by 5% between those steps.
867 else if (step >= 20 && step < 980)
868 step += 10;
869 else
870 step += 1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100871 }
872 }
Willy Tarreau0f423a72010-05-03 10:50:54 +0200873 else if (filter & FILT_COUNT_STATUS) {
874 /* output all statuses in the form of <status> <occurrences> */
875 n = eb32_first(&timers[0]);
876 while (n) {
877 t = container_of(n, struct timer, node);
878 printf("%d %d\n", n->key, t->count);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200879 lines_out++;
Willy Tarreau0f423a72010-05-03 10:50:54 +0200880 n = eb32_next(n);
881 }
882 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200883 else if (filter & FILT_COUNT_SRV_STATUS) {
Willy Tarreaud2201062010-05-27 18:17:30 +0200884 struct ebmb_node *srv_node;
885 struct srv_st *srv;
886
887 printf("#srv_name 1xx 2xx 3xx 4xx 5xx other tot_req req_ok pct_ok avg_ct avg_rt\n");
888
889 srv_node = ebmb_first(&timers[0]);
890 while (srv_node) {
891 int tot_rq;
892
893 srv = container_of(srv_node, struct srv_st, node);
894
895 tot_rq = 0;
896 for (f = 0; f <= 5; f++)
897 tot_rq += srv->st_cnt[f];
898
899 printf("%s %d %d %d %d %d %d %d %d %.1f %d %d\n",
900 srv_node->key, srv->st_cnt[1], srv->st_cnt[2],
901 srv->st_cnt[3], srv->st_cnt[4], srv->st_cnt[5], srv->st_cnt[0],
902 tot_rq,
903 srv->nb_ok, (double)srv->nb_ok * 100.0 / (tot_rq?tot_rq:1),
904 (int)(srv->cum_ct / (srv->nb_ct?srv->nb_ct:1)), (int)(srv->cum_rt / (srv->nb_rt?srv->nb_rt:1)));
905 srv_node = ebmb_next(srv_node);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200906 lines_out++;
Willy Tarreaud2201062010-05-27 18:17:30 +0200907 }
908 }
Willy Tarreaud8fc1102010-09-12 17:56:16 +0200909 else if (filter & FILT_COUNT_TERM_CODES) {
910 /* output all statuses in the form of <code> <occurrences> */
911 n = eb32_first(&timers[0]);
912 while (n) {
913 t = container_of(n, struct timer, node);
914 printf("%c%c %d\n", (n->key >> 8), (n->key) & 255, t->count);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200915 lines_out++;
Willy Tarreaud8fc1102010-09-12 17:56:16 +0200916 n = eb32_next(n);
917 }
918 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200919 else if (filter & FILT_COUNT_URL_ANY) {
Willy Tarreauabe45b62010-10-28 20:33:46 +0200920 struct eb_node *node, *next;
921
922 if (!(filter & FILT_COUNT_URL_ONLY)) {
923 /* we have to sort on another criterion. We'll use timers[1] for the
924 * destination tree.
925 */
926
927 timers[1] = EB_ROOT; /* reconfigure to accept duplicates */
928 for (node = eb_first(&timers[0]); node; node = next) {
929 next = eb_next(node);
930 eb_delete(node);
931
932 ustat = container_of(node, struct url_stat, node.url.node);
933
934 if (filter & FILT_COUNT_URL_COUNT)
935 ustat->node.val.key = ustat->nb_req;
936 else if (filter & FILT_COUNT_URL_ERR)
937 ustat->node.val.key = ustat->nb_err;
938 else if (filter & FILT_COUNT_URL_TTOT)
939 ustat->node.val.key = ustat->total_time;
940 else if (filter & FILT_COUNT_URL_TAVG)
941 ustat->node.val.key = ustat->nb_req ? ustat->total_time / ustat->nb_req : 0;
942 else if (filter & FILT_COUNT_URL_TTOTO)
943 ustat->node.val.key = ustat->total_time_ok;
944 else if (filter & FILT_COUNT_URL_TAVGO)
945 ustat->node.val.key = (ustat->nb_req - ustat->nb_err) ? ustat->total_time_ok / (ustat->nb_req - ustat->nb_err) : 0;
946 else
947 ustat->node.val.key = 0;
948
949 eb64_insert(&timers[1], &ustat->node.val);
950 }
951 /* switch trees */
952 timers[0] = timers[1];
953 }
954
955 printf("#req err ttot tavg oktot okavg url\n");
956
957 /* scan the tree in its reverse sorting order */
958 node = eb_last(&timers[0]);
959 while (node) {
960 ustat = container_of(node, struct url_stat, node.url.node);
961 printf("%d %d %Ld %Ld %Ld %Ld %s\n",
962 ustat->nb_req,
963 ustat->nb_err,
964 ustat->total_time,
965 ustat->nb_req ? ustat->total_time / ustat->nb_req : 0,
966 ustat->total_time_ok,
967 (ustat->nb_req - ustat->nb_err) ? ustat->total_time_ok / (ustat->nb_req - ustat->nb_err) : 0,
968 ustat->url);
969
970 node = eb_prev(node);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200971 lines_out++;
Willy Tarreauabe45b62010-10-28 20:33:46 +0200972 }
973 }
Willy Tarreaud2201062010-05-27 18:17:30 +0200974
Willy Tarreau910ba4b2009-11-17 10:16:19 +0100975 empty:
Willy Tarreau72c28532009-01-22 18:56:50 +0100976 if (!(filter & FILT_QUIET))
977 fprintf(stderr, "%d lines in, %d lines out, %d parsing errors\n",
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200978 linenum, lines_out, parse_err);
Willy Tarreau72c28532009-01-22 18:56:50 +0100979 exit(0);
980}
981
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200982void filter_output_line(const char *accept_field, const char *time_field, struct timer **tptr)
983{
984 puts(line);
985 lines_out++;
986}
987
988void filter_accept_holes(const char *accept_field, const char *time_field, struct timer **tptr)
989{
990 struct timer *t2;
991 int val;
992
993 val = convert_date(accept_field);
994 if (unlikely(val < 0)) {
995 truncated_line(linenum, line);
996 return;
997 }
998
999 t2 = insert_value(&timers[0], tptr, val);
1000 t2->count++;
1001 lines_out++;
1002 return;
1003}
1004
1005void filter_count_status(const char *accept_field, const char *time_field, struct timer **tptr)
1006{
1007 struct timer *t2;
1008 const char *b;
1009 int val;
1010
1011 if (time_field)
1012 b = field_start(time_field, STATUS_FIELD - TIME_FIELD + 1);
1013 else
1014 b = field_start(accept_field, STATUS_FIELD - ACCEPT_FIELD + 1);
1015
1016 if (unlikely(!*b)) {
1017 truncated_line(linenum, line);
1018 return;
1019 }
1020
1021 val = str2ic(b);
1022
1023 t2 = insert_value(&timers[0], tptr, val);
1024 t2->count++;
1025}
1026
1027void filter_count_term_codes(const char *accept_field, const char *time_field, struct timer **tptr)
1028{
1029 struct timer *t2;
1030 const char *b;
1031 int val;
1032
1033 if (time_field)
1034 b = field_start(time_field, TERM_CODES_FIELD - TIME_FIELD + 1);
1035 else
1036 b = field_start(accept_field, TERM_CODES_FIELD - ACCEPT_FIELD + 1);
1037
1038 if (unlikely(!*b)) {
1039 truncated_line(linenum, line);
1040 return;
1041 }
1042
1043 val = 256 * b[0] + b[1];
1044
1045 t2 = insert_value(&timers[0], tptr, val);
1046 t2->count++;
1047}
1048
1049void filter_count_srv_status(const char *accept_field, const char *time_field, struct timer **tptr)
1050{
1051 const char *b, *e, *p;
1052 int f, err, array[5];
1053 struct ebmb_node *srv_node;
1054 struct srv_st *srv;
1055 int val;
1056
1057 /* the server field is before the status field, so let's
1058 * parse them in the proper order.
1059 */
1060 b = field_start(accept_field, SERVER_FIELD - ACCEPT_FIELD + 1);
1061 if (unlikely(!*b)) {
1062 truncated_line(linenum, line);
1063 return;
1064 }
1065
1066 e = field_stop(b + 1); /* we have the server name in [b]..[e-1] */
1067
1068 /* the chance that a server name already exists is extremely high,
1069 * so let's perform a normal lookup first.
1070 */
1071 srv_node = ebst_lookup_len(&timers[0], b, e - b);
1072 srv = container_of(srv_node, struct srv_st, node);
1073
1074 if (!srv_node) {
1075 /* server not yet in the tree, let's create it */
1076 srv = (void *)calloc(1, sizeof(struct srv_st) + e - b + 1);
1077 srv_node = &srv->node;
1078 memcpy(&srv_node->key, b, e - b);
1079 srv_node->key[e - b] = '\0';
1080 ebst_insert(&timers[0], srv_node);
1081 }
1082
1083 /* let's collect the connect and response times */
1084 if (!time_field) {
1085 time_field = field_start(e, TIME_FIELD - SERVER_FIELD);
1086 if (unlikely(!*time_field)) {
1087 truncated_line(linenum, line);
1088 return;
1089 }
1090 }
1091
1092 e = field_stop(time_field + 1);
1093 /* we have field TIME_FIELD in [time_field]..[e-1] */
1094
1095 p = time_field;
1096 err = 0;
1097 f = 0;
1098 while (!SEP(*p)) {
1099 array[f] = str2ic(p);
1100 if (array[f] < 0) {
1101 array[f] = -1;
1102 err = 1;
1103 }
1104 if (++f == 5)
1105 break;
1106 SKIP_CHAR(p, '/');
1107 }
1108
1109 if (unlikely(f < 5)){
1110 parse_err++;
1111 return;
1112 }
1113
1114 /* OK we have our timers in array[2,3] */
1115 if (!err)
1116 srv->nb_ok++;
1117
1118 if (array[2] >= 0) {
1119 srv->cum_ct += array[2];
1120 srv->nb_ct++;
1121 }
1122
1123 if (array[3] >= 0) {
1124 srv->cum_rt += array[3];
1125 srv->nb_rt++;
1126 }
1127
1128 /* we're interested in the 5 HTTP status classes (1xx ... 5xx), and
1129 * the invalid ones which will be reported as 0.
1130 */
1131 b = field_start(e, STATUS_FIELD - TIME_FIELD);
1132 if (unlikely(!*b)) {
1133 truncated_line(linenum, line);
1134 return;
1135 }
1136
1137 val = 0;
1138 if (*b >= '1' && *b <= '5')
1139 val = *b - '0';
1140
1141 srv->st_cnt[val]++;
1142}
1143
1144void filter_count_url(const char *accept_field, const char *time_field, struct timer **tptr)
1145{
1146 struct url_stat *ustat = NULL;
1147 struct ebpt_node *ebpt_old;
1148 const char *b, *e;
1149 int f, err, array[5];
1150
1151 /* let's collect the response time */
1152 if (!time_field) {
1153 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1); // avg 115 ns per line
1154 if (unlikely(!*time_field)) {
1155 truncated_line(linenum, line);
1156 return;
1157 }
1158 }
1159
1160 /* we have the field TIME_FIELD starting at <time_field>. We'll
1161 * parse the 5 timers to detect errors, it takes avg 55 ns per line.
1162 */
1163 e = time_field; err = 0; f = 0;
1164 while (!SEP(*e)) {
1165 array[f] = str2ic(e);
1166 if (array[f] < 0) {
1167 array[f] = -1;
1168 err = 1;
1169 }
1170 if (++f == 5)
1171 break;
1172 SKIP_CHAR(e, '/');
1173 }
1174 if (f < 5) {
1175 parse_err++;
1176 return;
1177 }
1178
1179 /* OK we have our timers in array[3], and err is >0 if at
1180 * least one -1 was seen. <e> points to the first char of
1181 * the last timer. Let's prepare a new node with that.
1182 */
1183 if (unlikely(!ustat))
1184 ustat = calloc(1, sizeof(*ustat));
1185
1186 ustat->nb_err = err;
1187 ustat->nb_req = 1;
1188
1189 /* use array[4] = total time in case of error */
1190 ustat->total_time = (array[3] >= 0) ? array[3] : array[4];
1191 ustat->total_time_ok = (array[3] >= 0) ? array[3] : 0;
1192
1193 /* the line may be truncated because of a bad request or anything like this,
1194 * without a method. Also, if it does not begin with an quote, let's skip to
1195 * the next field because it's a capture. Let's fall back to the "method" itself
1196 * if there's nothing else.
1197 */
1198 e = field_start(e, METH_FIELD - TIME_FIELD + 1); // avg 100 ns per line
Willy Tarreau61a40c72011-09-06 08:11:27 +02001199 while (*e != '"' && *e) {
1200 /* Note: some syslog servers escape quotes ! */
1201 if (*e == '\\' && e[1] == '"')
1202 break;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001203 e = field_start(e, 2);
Willy Tarreau61a40c72011-09-06 08:11:27 +02001204 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001205
1206 if (unlikely(!*e)) {
1207 truncated_line(linenum, line);
1208 return;
1209 }
1210
1211 b = field_start(e, URL_FIELD - METH_FIELD + 1); // avg 40 ns per line
1212 if (!*b)
1213 b = e;
1214
1215 /* stop at end of field or first ';' or '?', takes avg 64 ns per line */
1216 e = b;
1217 do {
Willy Tarreau14389e72011-07-10 22:11:17 +02001218 if (*e == ' ' || *e == '?' || *e == ';') {
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001219 *(char *)e = 0;
1220 break;
1221 }
1222 e++;
1223 } while (*e);
1224
1225 /* now instead of copying the URL for a simple lookup, we'll link
1226 * to it from the node we're trying to insert. If it returns a
1227 * different value, it was already there. Otherwise we just have
1228 * to dynamically realloc an entry using strdup().
1229 */
1230 ustat->node.url.key = (char *)b;
1231 ebpt_old = ebis_insert(&timers[0], &ustat->node.url);
1232
1233 if (ebpt_old != &ustat->node.url) {
1234 struct url_stat *ustat_old;
1235 /* node was already there, let's update previous one */
1236 ustat_old = container_of(ebpt_old, struct url_stat, node.url);
1237 ustat_old->nb_req ++;
1238 ustat_old->nb_err += ustat->nb_err;
1239 ustat_old->total_time += ustat->total_time;
1240 ustat_old->total_time_ok += ustat->total_time_ok;
1241 } else {
1242 ustat->url = ustat->node.url.key = strdup(ustat->node.url.key);
1243 ustat = NULL; /* node was used */
1244 }
1245}
1246
1247void filter_graphs(const char *accept_field, const char *time_field, struct timer **tptr)
1248{
1249 struct timer *t2;
1250 const char *e, *p;
1251 int f, err, array[5];
1252
1253 if (!time_field) {
1254 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1);
1255 if (unlikely(!*time_field)) {
1256 truncated_line(linenum, line);
1257 return;
1258 }
1259 }
1260
1261 e = field_stop(time_field + 1);
1262 /* we have field TIME_FIELD in [time_field]..[e-1] */
1263
1264 p = time_field;
1265 err = 0;
1266 f = 0;
1267 while (!SEP(*p)) {
1268 array[f] = str2ic(p);
1269 if (array[f] < 0) {
1270 array[f] = -1;
1271 err = 1;
1272 }
1273 if (++f == 5)
1274 break;
1275 SKIP_CHAR(p, '/');
1276 }
1277
1278 if (unlikely(f < 5)) {
1279 parse_err++;
1280 return;
1281 }
1282
1283 /* if we find at least one negative time, we count one error
1284 * with a time equal to the total session time. This will
1285 * emphasize quantum timing effects associated to known
1286 * timeouts. Note that on some buggy machines, it is possible
1287 * that the total time is negative, hence the reason to reset
1288 * it.
1289 */
1290
1291 if (filter & FILT_GRAPH_TIMERS) {
1292 if (err) {
1293 if (array[4] < 0)
1294 array[4] = -1;
1295 t2 = insert_timer(&timers[0], tptr, array[4]); // total time
1296 t2->count++;
1297 } else {
1298 int v;
1299
1300 t2 = insert_timer(&timers[1], tptr, array[0]); t2->count++; // req
1301 t2 = insert_timer(&timers[2], tptr, array[2]); t2->count++; // conn
1302 t2 = insert_timer(&timers[3], tptr, array[3]); t2->count++; // resp
1303
1304 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
1305 if (v < 0 && !(filter & FILT_QUIET))
1306 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
1307 line, array[0], array[1], array[2], array[3], array[4], v);
1308 t2 = insert_timer(&timers[4], tptr, v); t2->count++;
1309 lines_out++;
1310 }
1311 } else { /* percentile */
1312 if (err) {
1313 if (array[4] < 0)
1314 array[4] = -1;
1315 t2 = insert_value(&timers[0], tptr, array[4]); // total time
1316 t2->count++;
1317 } else {
1318 int v;
1319
1320 t2 = insert_value(&timers[1], tptr, array[0]); t2->count++; // req
1321 t2 = insert_value(&timers[2], tptr, array[2]); t2->count++; // conn
1322 t2 = insert_value(&timers[3], tptr, array[3]); t2->count++; // resp
1323
1324 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
1325 if (v < 0 && !(filter & FILT_QUIET))
1326 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
1327 line, array[0], array[1], array[2], array[3], array[4], v);
1328 t2 = insert_value(&timers[4], tptr, v); t2->count++;
1329 lines_out++;
1330 }
1331 }
1332}
1333
1334
Willy Tarreau72c28532009-01-22 18:56:50 +01001335/*
1336 * Local variables:
1337 * c-indent-level: 8
1338 * c-basic-offset: 8
1339 * End:
1340 */