blob: 95529983a84130aea57bd48e16dfbdb0899cacc3 [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 Tarreau8a09b662012-10-10 10:26:22 +02004 * Copyright 2000-2012 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
Baptiste61aaad02012-09-08 23:10:03 +020032#define BYTES_SENT_FIELD 11
Willy Tarreaud8fc1102010-09-12 17:56:16 +020033#define TERM_CODES_FIELD 14
Willy Tarreau72c28532009-01-22 18:56:50 +010034#define CONN_FIELD 15
Willy Tarreau08911ff2011-10-13 13:28:36 +020035#define QUEUE_LEN_FIELD 16
Willy Tarreauabe45b62010-10-28 20:33:46 +020036#define METH_FIELD 17
37#define URL_FIELD 18
Willy Tarreau72c28532009-01-22 18:56:50 +010038#define MAXLINE 16384
39#define QBITS 4
40
Willy Tarreaudf6f0d12011-07-10 18:15:08 +020041#define SEP(c) ((unsigned char)(c) <= ' ')
42#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 +010043
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
Willy Tarreauabe45b62010-10-28 20:33:46 +020063struct url_stat {
64 union {
65 struct ebpt_node url;
66 struct eb64_node val;
67 } node;
68 char *url;
69 unsigned long long total_time; /* sum(all reqs' times) */
70 unsigned long long total_time_ok; /* sum(all OK reqs' times) */
Baptiste61aaad02012-09-08 23:10:03 +020071 unsigned long long total_bytes_sent; /* sum(all bytes sent) */
Willy Tarreauabe45b62010-10-28 20:33:46 +020072 unsigned int nb_err, nb_req;
73};
74
Willy Tarreau72c28532009-01-22 18:56:50 +010075#define FILT_COUNT_ONLY 0x01
76#define FILT_INVERT 0x02
77#define FILT_QUIET 0x04
78#define FILT_ERRORS_ONLY 0x08
79#define FILT_ACC_DELAY 0x10
80#define FILT_ACC_COUNT 0x20
81#define FILT_GRAPH_TIMERS 0x40
Willy Tarreau214c2032009-02-20 11:02:32 +010082#define FILT_PERCENTILE 0x80
Willy Tarreau5bdfd962009-10-14 15:16:29 +020083#define FILT_TIME_RESP 0x100
84
85#define FILT_INVERT_ERRORS 0x200
86#define FILT_INVERT_TIME_RESP 0x400
Willy Tarreau72c28532009-01-22 18:56:50 +010087
Willy Tarreau0f423a72010-05-03 10:50:54 +020088#define FILT_COUNT_STATUS 0x800
Willy Tarreaud2201062010-05-27 18:17:30 +020089#define FILT_COUNT_SRV_STATUS 0x1000
Willy Tarreaud8fc1102010-09-12 17:56:16 +020090#define FILT_COUNT_TERM_CODES 0x2000
Willy Tarreau0f423a72010-05-03 10:50:54 +020091
Willy Tarreauabe45b62010-10-28 20:33:46 +020092#define FILT_COUNT_URL_ONLY 0x004000
93#define FILT_COUNT_URL_COUNT 0x008000
94#define FILT_COUNT_URL_ERR 0x010000
95#define FILT_COUNT_URL_TTOT 0x020000
96#define FILT_COUNT_URL_TAVG 0x040000
97#define FILT_COUNT_URL_TTOTO 0x080000
98#define FILT_COUNT_URL_TAVGO 0x100000
Willy Tarreauabe45b62010-10-28 20:33:46 +020099
Willy Tarreau70c428f2011-07-10 17:27:40 +0200100#define FILT_HTTP_ONLY 0x200000
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200101#define FILT_TERM_CODE_NAME 0x400000
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200102#define FILT_INVERT_TERM_CODE_NAME 0x800000
Willy Tarreau70c428f2011-07-10 17:27:40 +0200103
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200104#define FILT_HTTP_STATUS 0x1000000
105#define FILT_INVERT_HTTP_STATUS 0x2000000
Willy Tarreau08911ff2011-10-13 13:28:36 +0200106#define FILT_QUEUE_ONLY 0x4000000
107#define FILT_QUEUE_SRV_ONLY 0x8000000
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200108
Baptiste61aaad02012-09-08 23:10:03 +0200109#define FILT_COUNT_URL_BAVG 0x10000000
110#define FILT_COUNT_URL_BTOT 0x20000000
111
112#define FILT_COUNT_URL_ANY (FILT_COUNT_URL_ONLY|FILT_COUNT_URL_COUNT|FILT_COUNT_URL_ERR| \
113 FILT_COUNT_URL_TTOT|FILT_COUNT_URL_TAVG|FILT_COUNT_URL_TTOTO|FILT_COUNT_URL_TAVGO| \
114 FILT_COUNT_URL_BAVG|FILT_COUNT_URL_BTOT)
115
Willy Tarreau8a09b662012-10-10 10:26:22 +0200116#define FILT_COUNT_COOK_CODES 0x40000000
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100117#define FILT_COUNT_IP_COUNT 0x80000000
Willy Tarreau8a09b662012-10-10 10:26:22 +0200118
Willy Tarreau72c28532009-01-22 18:56:50 +0100119unsigned int filter = 0;
120unsigned int filter_invert = 0;
Willy Tarreau214c2032009-02-20 11:02:32 +0100121const char *line;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200122int linenum = 0;
123int parse_err = 0;
124int lines_out = 0;
Willy Tarreau667c9052012-10-10 16:49:28 +0200125int lines_max = -1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100126
Willy Tarreau214c2032009-02-20 11:02:32 +0100127const char *fgets2(FILE *stream);
Willy Tarreau72c28532009-01-22 18:56:50 +0100128
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200129void filter_count_url(const char *accept_field, const char *time_field, struct timer **tptr);
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100130void filter_count_ip(const char *source_field, const char *accept_field, const char *time_field, struct timer **tptr);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200131void filter_count_srv_status(const char *accept_field, const char *time_field, struct timer **tptr);
Willy Tarreau8a09b662012-10-10 10:26:22 +0200132void filter_count_cook_codes(const char *accept_field, const char *time_field, struct timer **tptr);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200133void filter_count_term_codes(const char *accept_field, const char *time_field, struct timer **tptr);
134void filter_count_status(const char *accept_field, const char *time_field, struct timer **tptr);
135void filter_graphs(const char *accept_field, const char *time_field, struct timer **tptr);
136void filter_output_line(const char *accept_field, const char *time_field, struct timer **tptr);
137void filter_accept_holes(const char *accept_field, const char *time_field, struct timer **tptr);
138
Willy Tarreau615674c2012-01-23 08:15:51 +0100139void usage(FILE *output, const char *msg)
Willy Tarreau72c28532009-01-22 18:56:50 +0100140{
Willy Tarreau615674c2012-01-23 08:15:51 +0100141 fprintf(output,
Willy Tarreau72c28532009-01-22 18:56:50 +0100142 "%s"
Willy Tarreau615674c2012-01-23 08:15:51 +0100143 "Usage: halog [-h|--help] for long help\n"
Willy Tarreau667c9052012-10-10 16:49:28 +0200144 " halog [-q] [-c] [-m <lines>]\n"
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100145 " {-cc|-gt|-pct|-st|-tc|-srv|-u|-uc|-ue|-ua|-ut|-uao|-uto|-uba|-ubt|-ic}\n"
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200146 " [-s <skip>] [-e|-E] [-H] [-rt|-RT <time>] [-ad <delay>] [-ac <count>]\n"
Baptiste61aaad02012-09-08 23:10:03 +0200147 " [-v] [-Q|-QS] [-tcn|-TCN <termcode>] [ -hs|-HS [min][:[max]] ] < log\n"
Willy Tarreau72c28532009-01-22 18:56:50 +0100148 "\n",
149 msg ? msg : ""
150 );
Willy Tarreau615674c2012-01-23 08:15:51 +0100151}
152
153void die(const char *msg)
154{
155 usage(stderr, msg);
Willy Tarreau72c28532009-01-22 18:56:50 +0100156 exit(1);
157}
158
Willy Tarreau615674c2012-01-23 08:15:51 +0100159void help()
160{
161 usage(stdout, NULL);
162 printf(
163 "Input filters (several filters may be combined) :\n"
164 " -H only match lines containing HTTP logs (ignore TCP)\n"
165 " -E only match lines without any error (no 5xx status)\n"
166 " -e only match lines with errors (status 5xx or negative)\n"
167 " -rt|-RT <time> only match response times larger|smaller than <time>\n"
168 " -Q|-QS only match queued requests (any queue|server queue)\n"
169 " -tcn|-TCN <code> only match requests with/without termination code <code>\n"
170 " -hs|-HS <[min][:][max]> only match requests with HTTP status codes within/not\n"
171 " within min..max. Any of them may be omitted. Exact\n"
172 " code is checked for if no ':' is specified.\n"
173 "Modifiers\n"
174 " -v invert the input filtering condition\n"
175 " -q don't report errors/warnings\n"
Willy Tarreau667c9052012-10-10 16:49:28 +0200176 " -m <lines> limit output to the first <lines> lines\n"
Willy Tarreau615674c2012-01-23 08:15:51 +0100177 "Output filters - only one may be used at a time\n"
178 " -c only report the number of lines that would have been printed\n"
179 " -pct output connect and response times percentiles\n"
180 " -st output number of requests per HTTP status code\n"
Willy Tarreau8a09b662012-10-10 10:26:22 +0200181 " -cc output number of requests per cookie code (2 chars)\n"
Willy Tarreau615674c2012-01-23 08:15:51 +0100182 " -tc output number of requests per termination code (2 chars)\n"
183 " -srv output statistics per server (time, requests, errors)\n"
184 " -u* output statistics per URL (time, requests, errors)\n"
185 " Additional characters indicate the output sorting key :\n"
186 " -u : by URL, -uc : request count, -ue : error count\n"
Willy Tarreau4201df72012-10-10 14:57:35 +0200187 " -ua : average response time, -ut : average total time\n"
Willy Tarreau615674c2012-01-23 08:15:51 +0100188 " -uao, -uto: average times computed on valid ('OK') requests\n"
Baptiste61aaad02012-09-08 23:10:03 +0200189 " -uba, -ubt: average bytes returned, total bytes returned\n"
Willy Tarreau615674c2012-01-23 08:15:51 +0100190 );
191 exit(0);
192}
193
Willy Tarreau72c28532009-01-22 18:56:50 +0100194
195/* return pointer to first char not part of current field starting at <p>. */
Willy Tarreauf9042062011-09-10 12:26:35 +0200196
197#if defined(__i386__)
198/* this one is always faster on 32-bits */
199static inline const char *field_stop(const char *p)
200{
201 asm(
202 /* Look for spaces */
203 "4: \n\t"
204 "inc %0 \n\t"
205 "cmpb $0x20, -1(%0) \n\t"
206 "ja 4b \n\t"
207 "jz 3f \n\t"
208
209 /* we only get there for control chars 0..31. Leave if we find '\0' */
210 "cmpb $0x0, -1(%0) \n\t"
211 "jnz 4b \n\t"
212
213 /* return %0-1 = position of the last char we checked */
214 "3: \n\t"
215 "dec %0 \n\t"
216 : "=r" (p)
217 : "0" (p)
218 );
219 return p;
220}
221#else
Willy Tarreau72c28532009-01-22 18:56:50 +0100222const char *field_stop(const char *p)
223{
224 unsigned char c;
225
226 while (1) {
227 c = *(p++);
228 if (c > ' ')
229 continue;
Willy Tarreau14389e72011-07-10 22:11:17 +0200230 if (c == ' ' || c == 0)
Willy Tarreau72c28532009-01-22 18:56:50 +0100231 break;
232 }
233 return p - 1;
234}
Willy Tarreauf9042062011-09-10 12:26:35 +0200235#endif
Willy Tarreau72c28532009-01-22 18:56:50 +0100236
237/* return field <field> (starting from 1) in string <p>. Only consider
238 * contiguous spaces (or tabs) as one delimiter. May return pointer to
239 * last char if field is not found. Equivalent to awk '{print $field}'.
240 */
241const char *field_start(const char *p, int field)
242{
Willy Tarreauf9042062011-09-10 12:26:35 +0200243#ifndef PREFER_ASM
Willy Tarreau72c28532009-01-22 18:56:50 +0100244 unsigned char c;
245 while (1) {
246 /* skip spaces */
247 while (1) {
Willy Tarreauf9042062011-09-10 12:26:35 +0200248 c = *(p++);
Willy Tarreau72c28532009-01-22 18:56:50 +0100249 if (c > ' ')
250 break;
Willy Tarreau14389e72011-07-10 22:11:17 +0200251 if (c == ' ')
Willy Tarreauf9042062011-09-10 12:26:35 +0200252 continue;
Willy Tarreau72c28532009-01-22 18:56:50 +0100253 if (!c) /* end of line */
Willy Tarreauf9042062011-09-10 12:26:35 +0200254 return p-1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100255 /* other char => new field */
256 break;
Willy Tarreau72c28532009-01-22 18:56:50 +0100257 }
258
259 /* start of field */
260 field--;
261 if (!field)
Willy Tarreauf9042062011-09-10 12:26:35 +0200262 return p-1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100263
264 /* skip this field */
265 while (1) {
266 c = *(p++);
Willy Tarreau14389e72011-07-10 22:11:17 +0200267 if (c == ' ')
Willy Tarreau72c28532009-01-22 18:56:50 +0100268 break;
Willy Tarreauf9042062011-09-10 12:26:35 +0200269 if (c > ' ')
270 continue;
Willy Tarreau72c28532009-01-22 18:56:50 +0100271 if (c == '\0')
Willy Tarreauf9042062011-09-10 12:26:35 +0200272 return p - 1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100273 }
274 }
Willy Tarreauf9042062011-09-10 12:26:35 +0200275#else
276 /* This version works optimally on i386 and x86_64 but the code above
277 * shows similar performance. However, depending on the version of GCC
278 * used, inlining rules change and it may have difficulties to make
279 * efficient use of this code at other locations and could result in
280 * worse performance (eg: gcc 4.4). You may want to experience.
281 */
282 asm(
283 /* skip spaces */
284 "1: \n\t"
285 "inc %0 \n\t"
286 "cmpb $0x20, -1(%0) \n\t"
287 "ja 2f \n\t"
288 "jz 1b \n\t"
289
290 /* we only get there for control chars 0..31. Leave if we find '\0' */
291 "cmpb $0x0, -1(%0) \n\t"
292 "jz 3f \n\t"
293
294 /* start of field at [%0-1]. Check if we need to skip more fields */
295 "2: \n\t"
296 "dec %1 \n\t"
297 "jz 3f \n\t"
298
299 /* Look for spaces */
300 "4: \n\t"
301 "inc %0 \n\t"
302 "cmpb $0x20, -1(%0) \n\t"
303 "jz 1b \n\t"
304 "ja 4b \n\t"
305
306 /* we only get there for control chars 0..31. Leave if we find '\0' */
307 "cmpb $0x0, -1(%0) \n\t"
308 "jnz 4b \n\t"
309
310 /* return %0-1 = position of the last char we checked */
311 "3: \n\t"
312 "dec %0 \n\t"
313 : "=r" (p)
314 : "r" (field), "0" (p)
315 );
316 return p;
317#endif
Willy Tarreau72c28532009-01-22 18:56:50 +0100318}
319
320/* keep only the <bits> higher bits of <i> */
321static inline unsigned int quantify_u32(unsigned int i, int bits)
322{
323 int high;
324
325 if (!bits)
326 return 0;
327
328 if (i)
329 high = fls_auto(i); // 1 to 32
330 else
331 high = 0;
332
333 if (high <= bits)
334 return i;
335
336 return i & ~((1 << (high - bits)) - 1);
337}
338
339/* keep only the <bits> higher bits of the absolute value of <i>, as well as
340 * its sign. */
341static inline int quantify(int i, int bits)
342{
343 if (i >= 0)
344 return quantify_u32(i, bits);
345 else
346 return -quantify_u32(-i, bits);
347}
348
349/* Insert timer value <v> into tree <r>. A pre-allocated node must be passed
350 * in <alloc>. It may be NULL, in which case the function will allocate it
351 * itself. It will be reset to NULL once consumed. The caller is responsible
352 * for freeing the node once not used anymore. The node where the value was
353 * inserted is returned.
354 */
355struct timer *insert_timer(struct eb_root *r, struct timer **alloc, int v)
356{
357 struct timer *t = *alloc;
358 struct eb32_node *n;
359
360 if (!t) {
361 t = calloc(sizeof(*t), 1);
362 if (unlikely(!t)) {
363 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
364 exit(1);
365 }
366 }
367 t->node.key = quantify(v, QBITS); // keep only the higher QBITS bits
368
369 n = eb32i_insert(r, &t->node);
370 if (n == &t->node)
371 t = NULL; /* node inserted, will malloc next time */
372
373 *alloc = t;
374 return container_of(n, struct timer, node);
375}
376
377/* Insert value value <v> into tree <r>. A pre-allocated node must be passed
378 * in <alloc>. It may be NULL, in which case the function will allocate it
379 * itself. It will be reset to NULL once consumed. The caller is responsible
380 * for freeing the node once not used anymore. The node where the value was
381 * inserted is returned.
382 */
383struct timer *insert_value(struct eb_root *r, struct timer **alloc, int v)
384{
385 struct timer *t = *alloc;
386 struct eb32_node *n;
387
388 if (!t) {
389 t = calloc(sizeof(*t), 1);
390 if (unlikely(!t)) {
391 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
392 exit(1);
393 }
394 }
395 t->node.key = v;
396
397 n = eb32i_insert(r, &t->node);
398 if (n == &t->node)
399 t = NULL; /* node inserted, will malloc next time */
400
401 *alloc = t;
402 return container_of(n, struct timer, node);
403}
404
405int str2ic(const char *s)
406{
407 int i = 0;
408 int j, k;
409
410 if (*s != '-') {
411 /* positive number */
412 while (1) {
413 j = (*s++) - '0';
414 k = i * 10;
415 if ((unsigned)j > 9)
416 break;
417 i = k + j;
418 }
419 } else {
420 /* negative number */
421 s++;
422 while (1) {
423 j = (*s++) - '0';
424 k = i * 10;
425 if ((unsigned)j > 9)
426 break;
427 i = k - j;
428 }
429 }
430
431 return i;
432}
433
434
435/* Equivalent to strtoul with a length. */
436static inline unsigned int __strl2ui(const char *s, int len)
437{
438 unsigned int i = 0;
439 while (len-- > 0) {
440 i = i * 10 - '0';
441 i += (unsigned char)*s++;
442 }
443 return i;
444}
445
446unsigned int strl2ui(const char *s, int len)
447{
448 return __strl2ui(s, len);
449}
450
451/* Convert "[04/Dec/2008:09:49:40.555]" to an integer equivalent to the time of
452 * the day in milliseconds. It returns -1 for all unparsable values. The parser
453 * looks ugly but gcc emits far better code that way.
454 */
455int convert_date(const char *field)
456{
457 unsigned int h, m, s, ms;
458 unsigned char c;
459 const char *b, *e;
460
461 h = m = s = ms = 0;
462 e = field;
463
464 /* skip the date */
465 while (1) {
466 c = *(e++);
467 if (c == ':')
468 break;
469 if (!c)
470 goto out_err;
471 }
472
473 /* hour + ':' */
474 b = e;
475 while (1) {
476 c = *(e++) - '0';
477 if (c > 9)
478 break;
479 h = h * 10 + c;
480 }
481 if (c == (unsigned char)(0 - '0'))
482 goto out_err;
483
484 /* minute + ':' */
485 b = e;
486 while (1) {
487 c = *(e++) - '0';
488 if (c > 9)
489 break;
490 m = m * 10 + c;
491 }
492 if (c == (unsigned char)(0 - '0'))
493 goto out_err;
494
495 /* second + '.' or ']' */
496 b = e;
497 while (1) {
498 c = *(e++) - '0';
499 if (c > 9)
500 break;
501 s = s * 10 + c;
502 }
503 if (c == (unsigned char)(0 - '0'))
504 goto out_err;
505
506 /* if there's a '.', we have milliseconds */
507 if (c == (unsigned char)('.' - '0')) {
508 /* millisecond second + ']' */
509 b = e;
510 while (1) {
511 c = *(e++) - '0';
512 if (c > 9)
513 break;
514 ms = ms * 10 + c;
515 }
516 if (c == (unsigned char)(0 - '0'))
517 goto out_err;
518 }
519 return (((h * 60) + m) * 60 + s) * 1000 + ms;
520 out_err:
521 return -1;
522}
523
524void truncated_line(int linenum, const char *line)
525{
526 if (!(filter & FILT_QUIET))
527 fprintf(stderr, "Truncated line %d: %s\n", linenum, line);
528}
529
530int main(int argc, char **argv)
531{
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100532 const char *b, *e, *p, *time_field, *accept_field, *source_field;
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200533 const char *filter_term_code_name = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100534 const char *output_file = NULL;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200535 int f, last, err;
536 struct timer *t = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100537 struct eb32_node *n;
Willy Tarreauabe45b62010-10-28 20:33:46 +0200538 struct url_stat *ustat = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100539 int val, test;
Willy Tarreau72c28532009-01-22 18:56:50 +0100540 int filter_acc_delay = 0, filter_acc_count = 0;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200541 int filter_time_resp = 0;
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200542 int filt_http_status_low = 0, filt_http_status_high = 0;
Willy Tarreau72c28532009-01-22 18:56:50 +0100543 int skip_fields = 1;
544
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200545 void (*line_filter)(const char *accept_field, const char *time_field, struct timer **tptr) = NULL;
546
Willy Tarreau72c28532009-01-22 18:56:50 +0100547 argc--; argv++;
548 while (argc > 0) {
549 if (*argv[0] != '-')
550 break;
551
552 if (strcmp(argv[0], "-ad") == 0) {
553 if (argc < 2) die("missing option for -ad");
554 argc--; argv++;
555 filter |= FILT_ACC_DELAY;
556 filter_acc_delay = atol(*argv);
557 }
558 else if (strcmp(argv[0], "-ac") == 0) {
559 if (argc < 2) die("missing option for -ac");
560 argc--; argv++;
561 filter |= FILT_ACC_COUNT;
562 filter_acc_count = atol(*argv);
563 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200564 else if (strcmp(argv[0], "-rt") == 0) {
565 if (argc < 2) die("missing option for -rt");
566 argc--; argv++;
567 filter |= FILT_TIME_RESP;
568 filter_time_resp = atol(*argv);
569 }
570 else if (strcmp(argv[0], "-RT") == 0) {
571 if (argc < 2) die("missing option for -RT");
572 argc--; argv++;
573 filter |= FILT_TIME_RESP | FILT_INVERT_TIME_RESP;
574 filter_time_resp = atol(*argv);
575 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100576 else if (strcmp(argv[0], "-s") == 0) {
577 if (argc < 2) die("missing option for -s");
578 argc--; argv++;
579 skip_fields = atol(*argv);
580 }
Willy Tarreau667c9052012-10-10 16:49:28 +0200581 else if (strcmp(argv[0], "-m") == 0) {
582 if (argc < 2) die("missing option for -m");
583 argc--; argv++;
584 lines_max = atol(*argv);
585 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100586 else if (strcmp(argv[0], "-e") == 0)
587 filter |= FILT_ERRORS_ONLY;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200588 else if (strcmp(argv[0], "-E") == 0)
589 filter |= FILT_ERRORS_ONLY | FILT_INVERT_ERRORS;
Willy Tarreau70c428f2011-07-10 17:27:40 +0200590 else if (strcmp(argv[0], "-H") == 0)
591 filter |= FILT_HTTP_ONLY;
Willy Tarreau08911ff2011-10-13 13:28:36 +0200592 else if (strcmp(argv[0], "-Q") == 0)
593 filter |= FILT_QUEUE_ONLY;
594 else if (strcmp(argv[0], "-QS") == 0)
595 filter |= FILT_QUEUE_SRV_ONLY;
Willy Tarreau72c28532009-01-22 18:56:50 +0100596 else if (strcmp(argv[0], "-c") == 0)
597 filter |= FILT_COUNT_ONLY;
598 else if (strcmp(argv[0], "-q") == 0)
599 filter |= FILT_QUIET;
600 else if (strcmp(argv[0], "-v") == 0)
601 filter_invert = !filter_invert;
602 else if (strcmp(argv[0], "-gt") == 0)
603 filter |= FILT_GRAPH_TIMERS;
Willy Tarreau214c2032009-02-20 11:02:32 +0100604 else if (strcmp(argv[0], "-pct") == 0)
605 filter |= FILT_PERCENTILE;
Willy Tarreau0f423a72010-05-03 10:50:54 +0200606 else if (strcmp(argv[0], "-st") == 0)
607 filter |= FILT_COUNT_STATUS;
Willy Tarreaud2201062010-05-27 18:17:30 +0200608 else if (strcmp(argv[0], "-srv") == 0)
609 filter |= FILT_COUNT_SRV_STATUS;
Willy Tarreau8a09b662012-10-10 10:26:22 +0200610 else if (strcmp(argv[0], "-cc") == 0)
611 filter |= FILT_COUNT_COOK_CODES;
Willy Tarreaud8fc1102010-09-12 17:56:16 +0200612 else if (strcmp(argv[0], "-tc") == 0)
613 filter |= FILT_COUNT_TERM_CODES;
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200614 else if (strcmp(argv[0], "-tcn") == 0) {
615 if (argc < 2) die("missing option for -tcn");
616 argc--; argv++;
617 filter |= FILT_TERM_CODE_NAME;
618 filter_term_code_name = *argv;
619 }
620 else if (strcmp(argv[0], "-TCN") == 0) {
621 if (argc < 2) die("missing option for -TCN");
622 argc--; argv++;
623 filter |= FILT_TERM_CODE_NAME | FILT_INVERT_TERM_CODE_NAME;
624 filter_term_code_name = *argv;
625 }
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200626 else if (strcmp(argv[0], "-hs") == 0 || strcmp(argv[0], "-HS") == 0) {
627 char *sep, *str;
628
629 if (argc < 2) die("missing option for -hs/-HS ([min]:[max])");
630 filter |= FILT_HTTP_STATUS;
631 if (argv[0][1] == 'H')
632 filter |= FILT_INVERT_HTTP_STATUS;
633
634 argc--; argv++;
635 str = *argv;
636 sep = strchr(str, ':'); /* [min]:[max] */
637 if (!sep)
638 sep = str; /* make max point to min */
639 else
640 *sep++ = 0;
641 filt_http_status_low = *str ? atol(str) : 0;
642 filt_http_status_high = *sep ? atol(sep) : 65535;
643 }
Willy Tarreauabe45b62010-10-28 20:33:46 +0200644 else if (strcmp(argv[0], "-u") == 0)
645 filter |= FILT_COUNT_URL_ONLY;
646 else if (strcmp(argv[0], "-uc") == 0)
647 filter |= FILT_COUNT_URL_COUNT;
648 else if (strcmp(argv[0], "-ue") == 0)
649 filter |= FILT_COUNT_URL_ERR;
650 else if (strcmp(argv[0], "-ua") == 0)
651 filter |= FILT_COUNT_URL_TAVG;
652 else if (strcmp(argv[0], "-ut") == 0)
653 filter |= FILT_COUNT_URL_TTOT;
654 else if (strcmp(argv[0], "-uao") == 0)
655 filter |= FILT_COUNT_URL_TAVGO;
656 else if (strcmp(argv[0], "-uto") == 0)
657 filter |= FILT_COUNT_URL_TTOTO;
Baptiste61aaad02012-09-08 23:10:03 +0200658 else if (strcmp(argv[0], "-uba") == 0)
659 filter |= FILT_COUNT_URL_BAVG;
660 else if (strcmp(argv[0], "-ubt") == 0)
661 filter |= FILT_COUNT_URL_BTOT;
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100662 else if (strcmp(argv[0], "-ic") == 0)
663 filter |= FILT_COUNT_IP_COUNT;
Willy Tarreau72c28532009-01-22 18:56:50 +0100664 else if (strcmp(argv[0], "-o") == 0) {
665 if (output_file)
666 die("Fatal: output file name already specified.\n");
667 if (argc < 2)
668 die("Fatal: missing output file name.\n");
669 output_file = argv[1];
670 }
Willy Tarreau615674c2012-01-23 08:15:51 +0100671 else if (strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0)
672 help();
Willy Tarreau72c28532009-01-22 18:56:50 +0100673 argc--;
674 argv++;
675 }
676
677 if (!filter)
678 die("No action specified.\n");
679
680 if (filter & FILT_ACC_COUNT && !filter_acc_count)
681 filter_acc_count=1;
682
683 if (filter & FILT_ACC_DELAY && !filter_acc_delay)
684 filter_acc_delay = 1;
685
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200686
687 /* by default, all lines are printed */
688 line_filter = filter_output_line;
689 if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY))
690 line_filter = filter_accept_holes;
691 else if (filter & (FILT_GRAPH_TIMERS|FILT_PERCENTILE))
692 line_filter = filter_graphs;
693 else if (filter & FILT_COUNT_STATUS)
694 line_filter = filter_count_status;
Willy Tarreau8a09b662012-10-10 10:26:22 +0200695 else if (filter & FILT_COUNT_COOK_CODES)
696 line_filter = filter_count_cook_codes;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200697 else if (filter & FILT_COUNT_TERM_CODES)
698 line_filter = filter_count_term_codes;
699 else if (filter & FILT_COUNT_SRV_STATUS)
700 line_filter = filter_count_srv_status;
701 else if (filter & FILT_COUNT_URL_ANY)
702 line_filter = filter_count_url;
703 else if (filter & FILT_COUNT_ONLY)
704 line_filter = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100705
Willy Tarreauf8c95d22012-06-12 09:16:56 +0200706#if defined(POSIX_FADV_SEQUENTIAL)
707 /* around 20% performance improvement is observed on Linux with this
708 * on cold-cache. Surprizingly, WILLNEED is less performant. Don't
709 * use NOREUSE as it flushes the cache and prevents easy data
710 * manipulation on logs!
711 */
712 posix_fadvise(0, 0, 0, POSIX_FADV_SEQUENTIAL);
713#endif
714
Willy Tarreaua1629a52012-11-13 20:48:15 +0100715 if (!line_filter && /* FILT_COUNT_ONLY ( see above), and no input filter (see below) */
Willy Tarreaue1a908c2012-01-03 09:23:03 +0100716 !(filter & (FILT_HTTP_ONLY|FILT_TIME_RESP|FILT_ERRORS_ONLY|FILT_HTTP_STATUS|FILT_QUEUE_ONLY|FILT_QUEUE_SRV_ONLY|FILT_TERM_CODE_NAME))) {
Willy Tarreaua1629a52012-11-13 20:48:15 +0100717 /* read the whole file at once first, ignore it if inverted output */
Willy Tarreaue1a908c2012-01-03 09:23:03 +0100718 if (!filter_invert)
Willy Tarreaua1629a52012-11-13 20:48:15 +0100719 while ((lines_max < 0 || lines_out < lines_max) && fgets2(stdin) != NULL)
Willy Tarreaue1a908c2012-01-03 09:23:03 +0100720 lines_out++;
721
722 goto skip_filters;
723 }
724
Willy Tarreau214c2032009-02-20 11:02:32 +0100725 while ((line = fgets2(stdin)) != NULL) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100726 linenum++;
Willy Tarreau26deaf52011-07-10 19:47:48 +0200727 time_field = NULL; accept_field = NULL;
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100728 source_field = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100729
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200730 test = 1;
Willy Tarreau26deaf52011-07-10 19:47:48 +0200731
732 /* for any line we process, we first ensure that there is a field
733 * looking like the accept date field (beginning with a '[').
734 */
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100735 if (filter & FILT_COUNT_IP_COUNT) {
736 /* we need the IP first */
737 source_field = field_start(line, SOURCE_FIELD + skip_fields);
738 accept_field = field_start(source_field, ACCEPT_FIELD - SOURCE_FIELD + 1);
739 }
740 else
741 accept_field = field_start(line, ACCEPT_FIELD + skip_fields);
742
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200743 if (unlikely(*accept_field != '[')) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200744 parse_err++;
745 continue;
746 }
747
748 /* the day of month field is begin 01 and 31 */
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200749 if (accept_field[1] < '0' || accept_field[1] > '3') {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200750 parse_err++;
751 continue;
752 }
753
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200754 if (filter & FILT_HTTP_ONLY) {
Willy Tarreau70c428f2011-07-10 17:27:40 +0200755 /* only report lines with at least 4 timers */
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200756 if (!time_field) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200757 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200758 if (unlikely(!*time_field)) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200759 truncated_line(linenum, line);
760 continue;
761 }
Willy Tarreau70c428f2011-07-10 17:27:40 +0200762 }
763
Willy Tarreau758a6ea2011-07-10 18:53:44 +0200764 e = field_stop(time_field + 1);
765 /* we have field TIME_FIELD in [time_field]..[e-1] */
766 p = time_field;
Willy Tarreau70c428f2011-07-10 17:27:40 +0200767 f = 0;
Willy Tarreaudf6f0d12011-07-10 18:15:08 +0200768 while (!SEP(*p)) {
Willy Tarreau70c428f2011-07-10 17:27:40 +0200769 if (++f == 4)
770 break;
771 SKIP_CHAR(p, '/');
772 }
773 test &= (f >= 4);
774 }
775
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200776 if (filter & FILT_TIME_RESP) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200777 int tps;
778
779 /* only report lines with response times larger than filter_time_resp */
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200780 if (!time_field) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200781 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200782 if (unlikely(!*time_field)) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200783 truncated_line(linenum, line);
784 continue;
785 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200786 }
787
Willy Tarreau758a6ea2011-07-10 18:53:44 +0200788 e = field_stop(time_field + 1);
789 /* we have field TIME_FIELD in [time_field]..[e-1], let's check only the response time */
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200790
Willy Tarreau758a6ea2011-07-10 18:53:44 +0200791 p = time_field;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200792 err = 0;
Willy Tarreau24bcb4f2010-10-28 20:39:50 +0200793 f = 0;
Willy Tarreaudf6f0d12011-07-10 18:15:08 +0200794 while (!SEP(*p)) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200795 tps = str2ic(p);
796 if (tps < 0) {
797 tps = -1;
798 err = 1;
799 }
Willy Tarreau24bcb4f2010-10-28 20:39:50 +0200800 if (++f == 4)
801 break;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200802 SKIP_CHAR(p, '/');
803 }
804
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200805 if (unlikely(f < 4)) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200806 parse_err++;
807 continue;
808 }
809
810 test &= (tps >= filter_time_resp) ^ !!(filter & FILT_INVERT_TIME_RESP);
811 }
812
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200813 if (filter & (FILT_ERRORS_ONLY | FILT_HTTP_STATUS)) {
814 /* Check both error codes (-1, 5xx) and status code ranges */
Willy Tarreau26deaf52011-07-10 19:47:48 +0200815 if (time_field)
816 b = field_start(time_field, STATUS_FIELD - TIME_FIELD + 1);
817 else
818 b = field_start(accept_field, STATUS_FIELD - ACCEPT_FIELD + 1);
819
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200820 if (unlikely(!*b)) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100821 truncated_line(linenum, line);
822 continue;
823 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200824
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200825 val = str2ic(b);
826 if (filter & FILT_ERRORS_ONLY)
827 test &= (val < 0 || (val >= 500 && val <= 599)) ^ !!(filter & FILT_INVERT_ERRORS);
828
829 if (filter & FILT_HTTP_STATUS)
830 test &= (val >= filt_http_status_low && val <= filt_http_status_high) ^ !!(filter & FILT_INVERT_HTTP_STATUS);
Willy Tarreau72c28532009-01-22 18:56:50 +0100831 }
832
Willy Tarreau08911ff2011-10-13 13:28:36 +0200833 if (filter & (FILT_QUEUE_ONLY|FILT_QUEUE_SRV_ONLY)) {
834 /* Check if the server's queue is non-nul */
835 if (time_field)
836 b = field_start(time_field, QUEUE_LEN_FIELD - TIME_FIELD + 1);
837 else
838 b = field_start(accept_field, QUEUE_LEN_FIELD - ACCEPT_FIELD + 1);
839
840 if (unlikely(!*b)) {
841 truncated_line(linenum, line);
842 continue;
843 }
844
845 if (*b == '0') {
846 if (filter & FILT_QUEUE_SRV_ONLY) {
847 test = 0;
848 }
849 else {
850 do {
851 b++;
852 if (*b == '/') {
853 b++;
854 break;
855 }
856 } while (*b);
857 test &= ((unsigned char)(*b - '1') < 9);
858 }
859 }
860 }
861
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200862 if (filter & FILT_TERM_CODE_NAME) {
863 /* only report corresponding termination code name */
864 if (time_field)
865 b = field_start(time_field, TERM_CODES_FIELD - TIME_FIELD + 1);
866 else
867 b = field_start(accept_field, TERM_CODES_FIELD - ACCEPT_FIELD + 1);
868
869 if (unlikely(!*b)) {
870 truncated_line(linenum, line);
871 continue;
872 }
873
874 test &= (b[0] == filter_term_code_name[0] && b[1] == filter_term_code_name[1]) ^ !!(filter & FILT_INVERT_TERM_CODE_NAME);
875 }
876
877
Willy Tarreau0f423a72010-05-03 10:50:54 +0200878 test ^= filter_invert;
879 if (!test)
880 continue;
881
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200882 /************** here we process inputs *******************/
Willy Tarreau72c28532009-01-22 18:56:50 +0100883
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100884 if (line_filter) {
885 if (filter & FILT_COUNT_IP_COUNT)
886 filter_count_ip(source_field, accept_field, time_field, &t);
887 else
888 line_filter(accept_field, time_field, &t);
889 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200890 else
Willy Tarreaua1629a52012-11-13 20:48:15 +0100891 lines_out++; /* FILT_COUNT_ONLY was used, so we're just counting lines */
892 if (lines_max >= 0 && lines_out >= lines_max)
Willy Tarreau667c9052012-10-10 16:49:28 +0200893 break;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200894 }
Willy Tarreauabe45b62010-10-28 20:33:46 +0200895
Willy Tarreaue1a908c2012-01-03 09:23:03 +0100896 skip_filters:
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200897 /*****************************************************
898 * Here we've finished reading all input. Depending on the
899 * filters, we may still have some analysis to run on the
900 * collected data and to output data in a new format.
901 *************************************************** */
Willy Tarreau72c28532009-01-22 18:56:50 +0100902
903 if (t)
904 free(t);
905
906 if (filter & FILT_COUNT_ONLY) {
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200907 printf("%d\n", lines_out);
Willy Tarreau72c28532009-01-22 18:56:50 +0100908 exit(0);
909 }
910
Willy Tarreau72c28532009-01-22 18:56:50 +0100911 if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY)) {
912 /* sort and count all timers. Output will look like this :
913 * <accept_date> <delta_ms from previous one> <nb entries>
914 */
915 n = eb32_first(&timers[0]);
916
917 if (n)
918 last = n->key;
919 while (n) {
920 unsigned int d, h, m, s, ms;
921
922 t = container_of(n, struct timer, node);
923 h = n->key;
924 d = h - last;
925 last = h;
926
927 if (d >= filter_acc_delay && t->count >= filter_acc_count) {
928 ms = h % 1000; h = h / 1000;
929 s = h % 60; h = h / 60;
930 m = h % 60; h = h / 60;
Willy Tarreau72c28532009-01-22 18:56:50 +0100931 printf("%02d:%02d:%02d.%03d %d %d %d\n", h, m, s, ms, last, d, t->count);
Willy Tarreau667c9052012-10-10 16:49:28 +0200932 lines_out++;
Willy Tarreaua1629a52012-11-13 20:48:15 +0100933 if (lines_max >= 0 && lines_out >= lines_max)
Willy Tarreau667c9052012-10-10 16:49:28 +0200934 break;
Willy Tarreau72c28532009-01-22 18:56:50 +0100935 }
936 n = eb32_next(n);
937 }
938 }
939 else if (filter & FILT_GRAPH_TIMERS) {
940 /* sort all timers */
941 for (f = 0; f < 5; f++) {
942 struct eb32_node *n;
943 int val;
944
945 val = 0;
946 n = eb32_first(&timers[f]);
947 while (n) {
948 int i;
949 double d;
950
951 t = container_of(n, struct timer, node);
952 last = n->key;
953 val = t->count;
954
955 i = (last < 0) ? -last : last;
956 i = fls_auto(i) - QBITS;
957
958 if (i > 0)
959 d = val / (double)(1 << i);
960 else
961 d = val;
962
Willy Tarreaua1629a52012-11-13 20:48:15 +0100963 if (d > 0.0)
Willy Tarreau72c28532009-01-22 18:56:50 +0100964 printf("%d %d %f\n", f, last, d+1.0);
Willy Tarreau72c28532009-01-22 18:56:50 +0100965
966 n = eb32_next(n);
967 }
Willy Tarreau214c2032009-02-20 11:02:32 +0100968 }
969 }
970 else if (filter & FILT_PERCENTILE) {
971 /* report timers by percentile :
972 * <percent> <total> <max_req_time> <max_conn_time> <max_resp_time> <max_data_time>
973 * We don't count errs.
974 */
975 struct eb32_node *n[5];
976 unsigned long cum[5];
977 double step;
978
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200979 if (!lines_out)
Willy Tarreau910ba4b2009-11-17 10:16:19 +0100980 goto empty;
981
Willy Tarreau214c2032009-02-20 11:02:32 +0100982 for (f = 1; f < 5; f++) {
983 n[f] = eb32_first(&timers[f]);
984 cum[f] = container_of(n[f], struct timer, node)->count;
985 }
986
987 for (step = 1; step <= 1000;) {
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200988 unsigned int thres = lines_out * (step / 1000.0);
Willy Tarreau214c2032009-02-20 11:02:32 +0100989
990 printf("%3.1f %d ", step/10.0, thres);
991 for (f = 1; f < 5; f++) {
992 struct eb32_node *next;
993 while (cum[f] < thres) {
994 /* need to find other keys */
995 next = eb32_next(n[f]);
996 if (!next)
997 break;
998 n[f] = next;
999 cum[f] += container_of(next, struct timer, node)->count;
1000 }
1001
1002 /* value still within $step % of total */
1003 printf("%d ", n[f]->key);
1004 }
1005 putchar('\n');
1006 if (step >= 100 && step < 900)
1007 step += 50; // jump 5% by 5% between those steps.
1008 else if (step >= 20 && step < 980)
1009 step += 10;
1010 else
1011 step += 1;
Willy Tarreau72c28532009-01-22 18:56:50 +01001012 }
1013 }
Willy Tarreau0f423a72010-05-03 10:50:54 +02001014 else if (filter & FILT_COUNT_STATUS) {
1015 /* output all statuses in the form of <status> <occurrences> */
1016 n = eb32_first(&timers[0]);
1017 while (n) {
1018 t = container_of(n, struct timer, node);
1019 printf("%d %d\n", n->key, t->count);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001020 lines_out++;
Willy Tarreaua1629a52012-11-13 20:48:15 +01001021 if (lines_max >= 0 && lines_out >= lines_max)
Willy Tarreau667c9052012-10-10 16:49:28 +02001022 break;
Willy Tarreau0f423a72010-05-03 10:50:54 +02001023 n = eb32_next(n);
1024 }
1025 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001026 else if (filter & FILT_COUNT_SRV_STATUS) {
Willy Tarreaud2201062010-05-27 18:17:30 +02001027 struct ebmb_node *srv_node;
1028 struct srv_st *srv;
1029
1030 printf("#srv_name 1xx 2xx 3xx 4xx 5xx other tot_req req_ok pct_ok avg_ct avg_rt\n");
1031
1032 srv_node = ebmb_first(&timers[0]);
1033 while (srv_node) {
1034 int tot_rq;
1035
1036 srv = container_of(srv_node, struct srv_st, node);
1037
1038 tot_rq = 0;
1039 for (f = 0; f <= 5; f++)
1040 tot_rq += srv->st_cnt[f];
1041
1042 printf("%s %d %d %d %d %d %d %d %d %.1f %d %d\n",
1043 srv_node->key, srv->st_cnt[1], srv->st_cnt[2],
1044 srv->st_cnt[3], srv->st_cnt[4], srv->st_cnt[5], srv->st_cnt[0],
1045 tot_rq,
1046 srv->nb_ok, (double)srv->nb_ok * 100.0 / (tot_rq?tot_rq:1),
1047 (int)(srv->cum_ct / (srv->nb_ct?srv->nb_ct:1)), (int)(srv->cum_rt / (srv->nb_rt?srv->nb_rt:1)));
1048 srv_node = ebmb_next(srv_node);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001049 lines_out++;
Willy Tarreaua1629a52012-11-13 20:48:15 +01001050 if (lines_max >= 0 && lines_out >= lines_max)
Willy Tarreau667c9052012-10-10 16:49:28 +02001051 break;
Willy Tarreaud2201062010-05-27 18:17:30 +02001052 }
1053 }
Willy Tarreau8a09b662012-10-10 10:26:22 +02001054 else if (filter & (FILT_COUNT_TERM_CODES|FILT_COUNT_COOK_CODES)) {
Willy Tarreaud8fc1102010-09-12 17:56:16 +02001055 /* output all statuses in the form of <code> <occurrences> */
1056 n = eb32_first(&timers[0]);
1057 while (n) {
1058 t = container_of(n, struct timer, node);
1059 printf("%c%c %d\n", (n->key >> 8), (n->key) & 255, t->count);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001060 lines_out++;
Willy Tarreaua1629a52012-11-13 20:48:15 +01001061 if (lines_max >= 0 && lines_out >= lines_max)
Willy Tarreau667c9052012-10-10 16:49:28 +02001062 break;
Willy Tarreaud8fc1102010-09-12 17:56:16 +02001063 n = eb32_next(n);
1064 }
1065 }
Willy Tarreau7cf479c2013-02-16 23:49:04 +01001066 else if (filter & (FILT_COUNT_URL_ANY|FILT_COUNT_IP_COUNT)) {
Willy Tarreauabe45b62010-10-28 20:33:46 +02001067 struct eb_node *node, *next;
1068
1069 if (!(filter & FILT_COUNT_URL_ONLY)) {
1070 /* we have to sort on another criterion. We'll use timers[1] for the
1071 * destination tree.
1072 */
1073
1074 timers[1] = EB_ROOT; /* reconfigure to accept duplicates */
1075 for (node = eb_first(&timers[0]); node; node = next) {
1076 next = eb_next(node);
1077 eb_delete(node);
1078
1079 ustat = container_of(node, struct url_stat, node.url.node);
1080
Willy Tarreau7cf479c2013-02-16 23:49:04 +01001081 if (filter & (FILT_COUNT_URL_COUNT|FILT_COUNT_IP_COUNT))
Willy Tarreauabe45b62010-10-28 20:33:46 +02001082 ustat->node.val.key = ustat->nb_req;
1083 else if (filter & FILT_COUNT_URL_ERR)
1084 ustat->node.val.key = ustat->nb_err;
1085 else if (filter & FILT_COUNT_URL_TTOT)
1086 ustat->node.val.key = ustat->total_time;
1087 else if (filter & FILT_COUNT_URL_TAVG)
1088 ustat->node.val.key = ustat->nb_req ? ustat->total_time / ustat->nb_req : 0;
1089 else if (filter & FILT_COUNT_URL_TTOTO)
1090 ustat->node.val.key = ustat->total_time_ok;
1091 else if (filter & FILT_COUNT_URL_TAVGO)
1092 ustat->node.val.key = (ustat->nb_req - ustat->nb_err) ? ustat->total_time_ok / (ustat->nb_req - ustat->nb_err) : 0;
Baptiste61aaad02012-09-08 23:10:03 +02001093 else if (filter & FILT_COUNT_URL_BAVG)
1094 ustat->node.val.key = ustat->nb_req ? ustat->total_bytes_sent / ustat->nb_req : 0;
1095 else if (filter & FILT_COUNT_URL_BTOT)
1096 ustat->node.val.key = ustat->total_bytes_sent;
Willy Tarreauabe45b62010-10-28 20:33:46 +02001097 else
1098 ustat->node.val.key = 0;
1099
1100 eb64_insert(&timers[1], &ustat->node.val);
1101 }
1102 /* switch trees */
1103 timers[0] = timers[1];
1104 }
1105
Willy Tarreau7cf479c2013-02-16 23:49:04 +01001106 if (FILT_COUNT_IP_COUNT)
1107 printf("#req err ttot tavg oktot okavg bavg btot src\n");
1108 else
1109 printf("#req err ttot tavg oktot okavg bavg btot url\n");
Willy Tarreauabe45b62010-10-28 20:33:46 +02001110
1111 /* scan the tree in its reverse sorting order */
1112 node = eb_last(&timers[0]);
1113 while (node) {
1114 ustat = container_of(node, struct url_stat, node.url.node);
Baptiste61aaad02012-09-08 23:10:03 +02001115 printf("%d %d %Ld %Ld %Ld %Ld %Ld %Ld %s\n",
Willy Tarreauabe45b62010-10-28 20:33:46 +02001116 ustat->nb_req,
1117 ustat->nb_err,
1118 ustat->total_time,
1119 ustat->nb_req ? ustat->total_time / ustat->nb_req : 0,
1120 ustat->total_time_ok,
1121 (ustat->nb_req - ustat->nb_err) ? ustat->total_time_ok / (ustat->nb_req - ustat->nb_err) : 0,
Baptiste61aaad02012-09-08 23:10:03 +02001122 ustat->nb_req ? ustat->total_bytes_sent / ustat->nb_req : 0,
1123 ustat->total_bytes_sent,
Willy Tarreauabe45b62010-10-28 20:33:46 +02001124 ustat->url);
1125
1126 node = eb_prev(node);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001127 lines_out++;
Willy Tarreaua1629a52012-11-13 20:48:15 +01001128 if (lines_max >= 0 && lines_out >= lines_max)
Willy Tarreau667c9052012-10-10 16:49:28 +02001129 break;
Willy Tarreauabe45b62010-10-28 20:33:46 +02001130 }
1131 }
Willy Tarreaud2201062010-05-27 18:17:30 +02001132
Willy Tarreau910ba4b2009-11-17 10:16:19 +01001133 empty:
Willy Tarreau72c28532009-01-22 18:56:50 +01001134 if (!(filter & FILT_QUIET))
1135 fprintf(stderr, "%d lines in, %d lines out, %d parsing errors\n",
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001136 linenum, lines_out, parse_err);
Willy Tarreau72c28532009-01-22 18:56:50 +01001137 exit(0);
1138}
1139
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001140void filter_output_line(const char *accept_field, const char *time_field, struct timer **tptr)
1141{
1142 puts(line);
1143 lines_out++;
1144}
1145
1146void filter_accept_holes(const char *accept_field, const char *time_field, struct timer **tptr)
1147{
1148 struct timer *t2;
1149 int val;
1150
1151 val = convert_date(accept_field);
1152 if (unlikely(val < 0)) {
1153 truncated_line(linenum, line);
1154 return;
1155 }
1156
1157 t2 = insert_value(&timers[0], tptr, val);
1158 t2->count++;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001159 return;
1160}
1161
1162void filter_count_status(const char *accept_field, const char *time_field, struct timer **tptr)
1163{
1164 struct timer *t2;
1165 const char *b;
1166 int val;
1167
1168 if (time_field)
1169 b = field_start(time_field, STATUS_FIELD - TIME_FIELD + 1);
1170 else
1171 b = field_start(accept_field, STATUS_FIELD - ACCEPT_FIELD + 1);
1172
1173 if (unlikely(!*b)) {
1174 truncated_line(linenum, line);
1175 return;
1176 }
1177
1178 val = str2ic(b);
1179
1180 t2 = insert_value(&timers[0], tptr, val);
1181 t2->count++;
1182}
1183
Willy Tarreau8a09b662012-10-10 10:26:22 +02001184void filter_count_cook_codes(const char *accept_field, const char *time_field, struct timer **tptr)
1185{
1186 struct timer *t2;
1187 const char *b;
1188 int val;
1189
1190 if (time_field)
1191 b = field_start(time_field, TERM_CODES_FIELD - TIME_FIELD + 1);
1192 else
1193 b = field_start(accept_field, TERM_CODES_FIELD - ACCEPT_FIELD + 1);
1194
1195 if (unlikely(!*b)) {
1196 truncated_line(linenum, line);
1197 return;
1198 }
1199
1200 val = 256 * b[2] + b[3];
1201
1202 t2 = insert_value(&timers[0], tptr, val);
1203 t2->count++;
1204}
1205
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001206void filter_count_term_codes(const char *accept_field, const char *time_field, struct timer **tptr)
1207{
1208 struct timer *t2;
1209 const char *b;
1210 int val;
1211
1212 if (time_field)
1213 b = field_start(time_field, TERM_CODES_FIELD - TIME_FIELD + 1);
1214 else
1215 b = field_start(accept_field, TERM_CODES_FIELD - ACCEPT_FIELD + 1);
1216
1217 if (unlikely(!*b)) {
1218 truncated_line(linenum, line);
1219 return;
1220 }
1221
1222 val = 256 * b[0] + b[1];
1223
1224 t2 = insert_value(&timers[0], tptr, val);
1225 t2->count++;
1226}
1227
1228void filter_count_srv_status(const char *accept_field, const char *time_field, struct timer **tptr)
1229{
1230 const char *b, *e, *p;
1231 int f, err, array[5];
1232 struct ebmb_node *srv_node;
1233 struct srv_st *srv;
1234 int val;
1235
1236 /* the server field is before the status field, so let's
1237 * parse them in the proper order.
1238 */
1239 b = field_start(accept_field, SERVER_FIELD - ACCEPT_FIELD + 1);
1240 if (unlikely(!*b)) {
1241 truncated_line(linenum, line);
1242 return;
1243 }
1244
1245 e = field_stop(b + 1); /* we have the server name in [b]..[e-1] */
1246
1247 /* the chance that a server name already exists is extremely high,
1248 * so let's perform a normal lookup first.
1249 */
1250 srv_node = ebst_lookup_len(&timers[0], b, e - b);
1251 srv = container_of(srv_node, struct srv_st, node);
1252
1253 if (!srv_node) {
1254 /* server not yet in the tree, let's create it */
1255 srv = (void *)calloc(1, sizeof(struct srv_st) + e - b + 1);
1256 srv_node = &srv->node;
1257 memcpy(&srv_node->key, b, e - b);
1258 srv_node->key[e - b] = '\0';
1259 ebst_insert(&timers[0], srv_node);
1260 }
1261
1262 /* let's collect the connect and response times */
1263 if (!time_field) {
1264 time_field = field_start(e, TIME_FIELD - SERVER_FIELD);
1265 if (unlikely(!*time_field)) {
1266 truncated_line(linenum, line);
1267 return;
1268 }
1269 }
1270
1271 e = field_stop(time_field + 1);
1272 /* we have field TIME_FIELD in [time_field]..[e-1] */
1273
1274 p = time_field;
1275 err = 0;
1276 f = 0;
1277 while (!SEP(*p)) {
1278 array[f] = str2ic(p);
1279 if (array[f] < 0) {
1280 array[f] = -1;
1281 err = 1;
1282 }
1283 if (++f == 5)
1284 break;
1285 SKIP_CHAR(p, '/');
1286 }
1287
1288 if (unlikely(f < 5)){
1289 parse_err++;
1290 return;
1291 }
1292
1293 /* OK we have our timers in array[2,3] */
1294 if (!err)
1295 srv->nb_ok++;
1296
1297 if (array[2] >= 0) {
1298 srv->cum_ct += array[2];
1299 srv->nb_ct++;
1300 }
1301
1302 if (array[3] >= 0) {
1303 srv->cum_rt += array[3];
1304 srv->nb_rt++;
1305 }
1306
1307 /* we're interested in the 5 HTTP status classes (1xx ... 5xx), and
1308 * the invalid ones which will be reported as 0.
1309 */
1310 b = field_start(e, STATUS_FIELD - TIME_FIELD);
1311 if (unlikely(!*b)) {
1312 truncated_line(linenum, line);
1313 return;
1314 }
1315
1316 val = 0;
1317 if (*b >= '1' && *b <= '5')
1318 val = *b - '0';
1319
1320 srv->st_cnt[val]++;
1321}
1322
1323void filter_count_url(const char *accept_field, const char *time_field, struct timer **tptr)
1324{
1325 struct url_stat *ustat = NULL;
1326 struct ebpt_node *ebpt_old;
1327 const char *b, *e;
1328 int f, err, array[5];
Baptiste61aaad02012-09-08 23:10:03 +02001329 int val;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001330
1331 /* let's collect the response time */
1332 if (!time_field) {
1333 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1); // avg 115 ns per line
1334 if (unlikely(!*time_field)) {
1335 truncated_line(linenum, line);
1336 return;
1337 }
1338 }
1339
1340 /* we have the field TIME_FIELD starting at <time_field>. We'll
1341 * parse the 5 timers to detect errors, it takes avg 55 ns per line.
1342 */
1343 e = time_field; err = 0; f = 0;
1344 while (!SEP(*e)) {
1345 array[f] = str2ic(e);
1346 if (array[f] < 0) {
1347 array[f] = -1;
1348 err = 1;
1349 }
1350 if (++f == 5)
1351 break;
1352 SKIP_CHAR(e, '/');
1353 }
1354 if (f < 5) {
1355 parse_err++;
1356 return;
1357 }
1358
1359 /* OK we have our timers in array[3], and err is >0 if at
1360 * least one -1 was seen. <e> points to the first char of
1361 * the last timer. Let's prepare a new node with that.
1362 */
1363 if (unlikely(!ustat))
1364 ustat = calloc(1, sizeof(*ustat));
1365
1366 ustat->nb_err = err;
1367 ustat->nb_req = 1;
1368
1369 /* use array[4] = total time in case of error */
1370 ustat->total_time = (array[3] >= 0) ? array[3] : array[4];
1371 ustat->total_time_ok = (array[3] >= 0) ? array[3] : 0;
1372
Baptiste61aaad02012-09-08 23:10:03 +02001373 e = field_start(e, BYTES_SENT_FIELD - TIME_FIELD + 1);
1374 val = str2ic(e);
1375 ustat->total_bytes_sent = val;
1376
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001377 /* the line may be truncated because of a bad request or anything like this,
1378 * without a method. Also, if it does not begin with an quote, let's skip to
1379 * the next field because it's a capture. Let's fall back to the "method" itself
1380 * if there's nothing else.
1381 */
Baptiste61aaad02012-09-08 23:10:03 +02001382 e = field_start(e, METH_FIELD - BYTES_SENT_FIELD + 1);
Willy Tarreau61a40c72011-09-06 08:11:27 +02001383 while (*e != '"' && *e) {
1384 /* Note: some syslog servers escape quotes ! */
1385 if (*e == '\\' && e[1] == '"')
1386 break;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001387 e = field_start(e, 2);
Willy Tarreau61a40c72011-09-06 08:11:27 +02001388 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001389
1390 if (unlikely(!*e)) {
1391 truncated_line(linenum, line);
1392 return;
1393 }
1394
1395 b = field_start(e, URL_FIELD - METH_FIELD + 1); // avg 40 ns per line
1396 if (!*b)
1397 b = e;
1398
1399 /* stop at end of field or first ';' or '?', takes avg 64 ns per line */
1400 e = b;
1401 do {
Willy Tarreau14389e72011-07-10 22:11:17 +02001402 if (*e == ' ' || *e == '?' || *e == ';') {
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001403 *(char *)e = 0;
1404 break;
1405 }
1406 e++;
1407 } while (*e);
1408
1409 /* now instead of copying the URL for a simple lookup, we'll link
1410 * to it from the node we're trying to insert. If it returns a
1411 * different value, it was already there. Otherwise we just have
1412 * to dynamically realloc an entry using strdup().
1413 */
1414 ustat->node.url.key = (char *)b;
1415 ebpt_old = ebis_insert(&timers[0], &ustat->node.url);
1416
1417 if (ebpt_old != &ustat->node.url) {
1418 struct url_stat *ustat_old;
1419 /* node was already there, let's update previous one */
1420 ustat_old = container_of(ebpt_old, struct url_stat, node.url);
1421 ustat_old->nb_req ++;
1422 ustat_old->nb_err += ustat->nb_err;
1423 ustat_old->total_time += ustat->total_time;
1424 ustat_old->total_time_ok += ustat->total_time_ok;
Baptiste61aaad02012-09-08 23:10:03 +02001425 ustat_old->total_bytes_sent += ustat->total_bytes_sent;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001426 } else {
1427 ustat->url = ustat->node.url.key = strdup(ustat->node.url.key);
1428 ustat = NULL; /* node was used */
1429 }
1430}
1431
Willy Tarreau7cf479c2013-02-16 23:49:04 +01001432void filter_count_ip(const char *source_field, const char *accept_field, const char *time_field, struct timer **tptr)
1433{
1434 struct url_stat *ustat = NULL;
1435 struct ebpt_node *ebpt_old;
1436 const char *b, *e;
1437 int f, err, array[5];
1438 int val;
1439
1440 /* let's collect the response time */
1441 if (!time_field) {
1442 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1); // avg 115 ns per line
1443 if (unlikely(!*time_field)) {
1444 truncated_line(linenum, line);
1445 return;
1446 }
1447 }
1448
1449 /* we have the field TIME_FIELD starting at <time_field>. We'll
1450 * parse the 5 timers to detect errors, it takes avg 55 ns per line.
1451 */
1452 e = time_field; err = 0; f = 0;
1453 while (!SEP(*e)) {
1454 if (f == 0 || f == 4) {
1455 array[f] = str2ic(e);
1456 if (array[f] < 0) {
1457 array[f] = -1;
1458 err = 1;
1459 }
1460 }
1461 if (++f == 5)
1462 break;
1463 SKIP_CHAR(e, '/');
1464 }
1465 if (f < 5) {
1466 parse_err++;
1467 return;
1468 }
1469
1470 /* OK we have our timers in array[0], and err is >0 if at
1471 * least one -1 was seen. <e> points to the first char of
1472 * the last timer. Let's prepare a new node with that.
1473 */
1474 if (unlikely(!ustat))
1475 ustat = calloc(1, sizeof(*ustat));
1476
1477 ustat->nb_err = err;
1478 ustat->nb_req = 1;
1479
1480 /* use array[4] = total time in case of error */
1481 ustat->total_time = (array[0] >= 0) ? array[0] : array[4];
1482 ustat->total_time_ok = (array[0] >= 0) ? array[0] : 0;
1483
1484 e = field_start(e, BYTES_SENT_FIELD - TIME_FIELD + 1);
1485 val = str2ic(e);
1486 ustat->total_bytes_sent = val;
1487
1488 /* the source might be IPv4 or IPv6, so we always strip the port by
1489 * removing the last colon.
1490 */
1491 b = source_field;
1492 e = field_stop(b + 1);
1493 while (e > b && e[-1] != ':')
1494 e--;
1495 *(char *)(e - 1) = '\0';
1496
1497 /* now instead of copying the src for a simple lookup, we'll link
1498 * to it from the node we're trying to insert. If it returns a
1499 * different value, it was already there. Otherwise we just have
1500 * to dynamically realloc an entry using strdup(). We're using the
1501 * <url> field of the node to store the source address.
1502 */
1503 ustat->node.url.key = (char *)b;
1504 ebpt_old = ebis_insert(&timers[0], &ustat->node.url);
1505
1506 if (ebpt_old != &ustat->node.url) {
1507 struct url_stat *ustat_old;
1508 /* node was already there, let's update previous one */
1509 ustat_old = container_of(ebpt_old, struct url_stat, node.url);
1510 ustat_old->nb_req ++;
1511 ustat_old->nb_err += ustat->nb_err;
1512 ustat_old->total_time += ustat->total_time;
1513 ustat_old->total_time_ok += ustat->total_time_ok;
1514 ustat_old->total_bytes_sent += ustat->total_bytes_sent;
1515 } else {
1516 ustat->url = ustat->node.url.key = strdup(ustat->node.url.key);
1517 ustat = NULL; /* node was used */
1518 }
1519}
1520
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001521void filter_graphs(const char *accept_field, const char *time_field, struct timer **tptr)
1522{
1523 struct timer *t2;
1524 const char *e, *p;
1525 int f, err, array[5];
1526
1527 if (!time_field) {
1528 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1);
1529 if (unlikely(!*time_field)) {
1530 truncated_line(linenum, line);
1531 return;
1532 }
1533 }
1534
1535 e = field_stop(time_field + 1);
1536 /* we have field TIME_FIELD in [time_field]..[e-1] */
1537
1538 p = time_field;
1539 err = 0;
1540 f = 0;
1541 while (!SEP(*p)) {
1542 array[f] = str2ic(p);
1543 if (array[f] < 0) {
1544 array[f] = -1;
1545 err = 1;
1546 }
1547 if (++f == 5)
1548 break;
1549 SKIP_CHAR(p, '/');
1550 }
1551
1552 if (unlikely(f < 5)) {
1553 parse_err++;
1554 return;
1555 }
1556
1557 /* if we find at least one negative time, we count one error
1558 * with a time equal to the total session time. This will
1559 * emphasize quantum timing effects associated to known
1560 * timeouts. Note that on some buggy machines, it is possible
1561 * that the total time is negative, hence the reason to reset
1562 * it.
1563 */
1564
1565 if (filter & FILT_GRAPH_TIMERS) {
1566 if (err) {
1567 if (array[4] < 0)
1568 array[4] = -1;
1569 t2 = insert_timer(&timers[0], tptr, array[4]); // total time
1570 t2->count++;
1571 } else {
1572 int v;
1573
1574 t2 = insert_timer(&timers[1], tptr, array[0]); t2->count++; // req
1575 t2 = insert_timer(&timers[2], tptr, array[2]); t2->count++; // conn
1576 t2 = insert_timer(&timers[3], tptr, array[3]); t2->count++; // resp
1577
1578 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
1579 if (v < 0 && !(filter & FILT_QUIET))
1580 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
1581 line, array[0], array[1], array[2], array[3], array[4], v);
1582 t2 = insert_timer(&timers[4], tptr, v); t2->count++;
1583 lines_out++;
1584 }
1585 } else { /* percentile */
1586 if (err) {
1587 if (array[4] < 0)
1588 array[4] = -1;
1589 t2 = insert_value(&timers[0], tptr, array[4]); // total time
1590 t2->count++;
1591 } else {
1592 int v;
1593
1594 t2 = insert_value(&timers[1], tptr, array[0]); t2->count++; // req
1595 t2 = insert_value(&timers[2], tptr, array[2]); t2->count++; // conn
1596 t2 = insert_value(&timers[3], tptr, array[3]); t2->count++; // resp
1597
1598 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
1599 if (v < 0 && !(filter & FILT_QUIET))
1600 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
1601 line, array[0], array[1], array[2], array[3], array[4], v);
1602 t2 = insert_value(&timers[4], tptr, v); t2->count++;
1603 lines_out++;
1604 }
1605 }
1606}
1607
1608
Willy Tarreau72c28532009-01-22 18:56:50 +01001609/*
1610 * Local variables:
1611 * c-indent-level: 8
1612 * c-basic-offset: 8
1613 * End:
1614 */