blob: 364bf90aeddc096883d2593b081bc32644ce4a13 [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>
Olivier Burgarde97b9042014-05-22 16:44:59 +020021#include <time.h>
Willy Tarreau72c28532009-01-22 18:56:50 +010022
Willy Tarreau45cb4fb2009-10-26 21:10:04 +010023#include <eb32tree.h>
Willy Tarreauabe45b62010-10-28 20:33:46 +020024#include <eb64tree.h>
25#include <ebistree.h>
Willy Tarreaud2201062010-05-27 18:17:30 +020026#include <ebsttree.h>
Willy Tarreau72c28532009-01-22 18:56:50 +010027
Willy Tarreaud2201062010-05-27 18:17:30 +020028#define SOURCE_FIELD 5
Willy Tarreau72c28532009-01-22 18:56:50 +010029#define ACCEPT_FIELD 6
Willy Tarreaud2201062010-05-27 18:17:30 +020030#define SERVER_FIELD 8
Willy Tarreau72c28532009-01-22 18:56:50 +010031#define TIME_FIELD 9
32#define STATUS_FIELD 10
Baptiste61aaad02012-09-08 23:10:03 +020033#define BYTES_SENT_FIELD 11
Willy Tarreaud8fc1102010-09-12 17:56:16 +020034#define TERM_CODES_FIELD 14
Willy Tarreau72c28532009-01-22 18:56:50 +010035#define CONN_FIELD 15
Willy Tarreau08911ff2011-10-13 13:28:36 +020036#define QUEUE_LEN_FIELD 16
Willy Tarreauabe45b62010-10-28 20:33:46 +020037#define METH_FIELD 17
38#define URL_FIELD 18
Willy Tarreau72c28532009-01-22 18:56:50 +010039#define MAXLINE 16384
40#define QBITS 4
41
Willy Tarreaudf6f0d12011-07-10 18:15:08 +020042#define SEP(c) ((unsigned char)(c) <= ' ')
43#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 +010044
45/* [0] = err/date, [1] = req, [2] = conn, [3] = resp, [4] = data */
46static struct eb_root timers[5] = {
47 EB_ROOT_UNIQUE, EB_ROOT_UNIQUE, EB_ROOT_UNIQUE,
48 EB_ROOT_UNIQUE, EB_ROOT_UNIQUE,
49};
50
51struct timer {
52 struct eb32_node node;
53 unsigned int count;
54};
55
Willy Tarreaud2201062010-05-27 18:17:30 +020056struct srv_st {
57 unsigned int st_cnt[6]; /* 0xx to 5xx */
58 unsigned int nb_ct, nb_rt, nb_ok;
59 unsigned long long cum_ct, cum_rt;
60 struct ebmb_node node;
61 /* don't put anything else here, the server name will be there */
62};
Willy Tarreau72c28532009-01-22 18:56:50 +010063
Willy Tarreauabe45b62010-10-28 20:33:46 +020064struct url_stat {
65 union {
66 struct ebpt_node url;
67 struct eb64_node val;
68 } node;
69 char *url;
70 unsigned long long total_time; /* sum(all reqs' times) */
71 unsigned long long total_time_ok; /* sum(all OK reqs' times) */
Baptiste61aaad02012-09-08 23:10:03 +020072 unsigned long long total_bytes_sent; /* sum(all bytes sent) */
Willy Tarreauabe45b62010-10-28 20:33:46 +020073 unsigned int nb_err, nb_req;
74};
75
Willy Tarreau72c28532009-01-22 18:56:50 +010076#define FILT_COUNT_ONLY 0x01
77#define FILT_INVERT 0x02
78#define FILT_QUIET 0x04
79#define FILT_ERRORS_ONLY 0x08
80#define FILT_ACC_DELAY 0x10
81#define FILT_ACC_COUNT 0x20
82#define FILT_GRAPH_TIMERS 0x40
Willy Tarreau214c2032009-02-20 11:02:32 +010083#define FILT_PERCENTILE 0x80
Willy Tarreau5bdfd962009-10-14 15:16:29 +020084#define FILT_TIME_RESP 0x100
85
86#define FILT_INVERT_ERRORS 0x200
87#define FILT_INVERT_TIME_RESP 0x400
Willy Tarreau72c28532009-01-22 18:56:50 +010088
Willy Tarreau0f423a72010-05-03 10:50:54 +020089#define FILT_COUNT_STATUS 0x800
Willy Tarreaud2201062010-05-27 18:17:30 +020090#define FILT_COUNT_SRV_STATUS 0x1000
Willy Tarreaud8fc1102010-09-12 17:56:16 +020091#define FILT_COUNT_TERM_CODES 0x2000
Willy Tarreau0f423a72010-05-03 10:50:54 +020092
Willy Tarreauabe45b62010-10-28 20:33:46 +020093#define FILT_COUNT_URL_ONLY 0x004000
94#define FILT_COUNT_URL_COUNT 0x008000
95#define FILT_COUNT_URL_ERR 0x010000
96#define FILT_COUNT_URL_TTOT 0x020000
97#define FILT_COUNT_URL_TAVG 0x040000
98#define FILT_COUNT_URL_TTOTO 0x080000
99#define FILT_COUNT_URL_TAVGO 0x100000
Willy Tarreauabe45b62010-10-28 20:33:46 +0200100
Willy Tarreau70c428f2011-07-10 17:27:40 +0200101#define FILT_HTTP_ONLY 0x200000
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200102#define FILT_TERM_CODE_NAME 0x400000
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200103#define FILT_INVERT_TERM_CODE_NAME 0x800000
Willy Tarreau70c428f2011-07-10 17:27:40 +0200104
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200105#define FILT_HTTP_STATUS 0x1000000
106#define FILT_INVERT_HTTP_STATUS 0x2000000
Willy Tarreau08911ff2011-10-13 13:28:36 +0200107#define FILT_QUEUE_ONLY 0x4000000
108#define FILT_QUEUE_SRV_ONLY 0x8000000
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200109
Baptiste61aaad02012-09-08 23:10:03 +0200110#define FILT_COUNT_URL_BAVG 0x10000000
111#define FILT_COUNT_URL_BTOT 0x20000000
112
113#define FILT_COUNT_URL_ANY (FILT_COUNT_URL_ONLY|FILT_COUNT_URL_COUNT|FILT_COUNT_URL_ERR| \
114 FILT_COUNT_URL_TTOT|FILT_COUNT_URL_TAVG|FILT_COUNT_URL_TTOTO|FILT_COUNT_URL_TAVGO| \
115 FILT_COUNT_URL_BAVG|FILT_COUNT_URL_BTOT)
116
Willy Tarreau8a09b662012-10-10 10:26:22 +0200117#define FILT_COUNT_COOK_CODES 0x40000000
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100118#define FILT_COUNT_IP_COUNT 0x80000000
Willy Tarreau8a09b662012-10-10 10:26:22 +0200119
Olivier Burgarde97b9042014-05-22 16:44:59 +0200120#define FILT2_TIMESTAMP 0x01
121
Willy Tarreau72c28532009-01-22 18:56:50 +0100122unsigned int filter = 0;
Olivier Burgarde97b9042014-05-22 16:44:59 +0200123unsigned int filter2 = 0;
Willy Tarreau72c28532009-01-22 18:56:50 +0100124unsigned int filter_invert = 0;
Willy Tarreau214c2032009-02-20 11:02:32 +0100125const char *line;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200126int linenum = 0;
127int parse_err = 0;
128int lines_out = 0;
Willy Tarreau667c9052012-10-10 16:49:28 +0200129int lines_max = -1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100130
Willy Tarreau214c2032009-02-20 11:02:32 +0100131const char *fgets2(FILE *stream);
Willy Tarreau72c28532009-01-22 18:56:50 +0100132
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200133void filter_count_url(const char *accept_field, const char *time_field, struct timer **tptr);
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100134void 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 +0200135void filter_count_srv_status(const char *accept_field, const char *time_field, struct timer **tptr);
Willy Tarreau8a09b662012-10-10 10:26:22 +0200136void filter_count_cook_codes(const char *accept_field, const char *time_field, struct timer **tptr);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200137void filter_count_term_codes(const char *accept_field, const char *time_field, struct timer **tptr);
138void filter_count_status(const char *accept_field, const char *time_field, struct timer **tptr);
139void filter_graphs(const char *accept_field, const char *time_field, struct timer **tptr);
140void filter_output_line(const char *accept_field, const char *time_field, struct timer **tptr);
141void filter_accept_holes(const char *accept_field, const char *time_field, struct timer **tptr);
142
Willy Tarreau615674c2012-01-23 08:15:51 +0100143void usage(FILE *output, const char *msg)
Willy Tarreau72c28532009-01-22 18:56:50 +0100144{
Willy Tarreau615674c2012-01-23 08:15:51 +0100145 fprintf(output,
Willy Tarreau72c28532009-01-22 18:56:50 +0100146 "%s"
Willy Tarreau615674c2012-01-23 08:15:51 +0100147 "Usage: halog [-h|--help] for long help\n"
Willy Tarreau667c9052012-10-10 16:49:28 +0200148 " halog [-q] [-c] [-m <lines>]\n"
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100149 " {-cc|-gt|-pct|-st|-tc|-srv|-u|-uc|-ue|-ua|-ut|-uao|-uto|-uba|-ubt|-ic}\n"
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200150 " [-s <skip>] [-e|-E] [-H] [-rt|-RT <time>] [-ad <delay>] [-ac <count>]\n"
Olivier Burgarde97b9042014-05-22 16:44:59 +0200151 " [-v] [-Q|-QS] [-tcn|-TCN <termcode>] [ -hs|-HS [min][:[max]] ] [ -time [min][:[max]] ] < log\n"
Willy Tarreau72c28532009-01-22 18:56:50 +0100152 "\n",
153 msg ? msg : ""
154 );
Willy Tarreau615674c2012-01-23 08:15:51 +0100155}
156
157void die(const char *msg)
158{
159 usage(stderr, msg);
Willy Tarreau72c28532009-01-22 18:56:50 +0100160 exit(1);
161}
162
Willy Tarreau615674c2012-01-23 08:15:51 +0100163void help()
164{
165 usage(stdout, NULL);
166 printf(
167 "Input filters (several filters may be combined) :\n"
168 " -H only match lines containing HTTP logs (ignore TCP)\n"
169 " -E only match lines without any error (no 5xx status)\n"
170 " -e only match lines with errors (status 5xx or negative)\n"
171 " -rt|-RT <time> only match response times larger|smaller than <time>\n"
172 " -Q|-QS only match queued requests (any queue|server queue)\n"
173 " -tcn|-TCN <code> only match requests with/without termination code <code>\n"
174 " -hs|-HS <[min][:][max]> only match requests with HTTP status codes within/not\n"
175 " within min..max. Any of them may be omitted. Exact\n"
176 " code is checked for if no ':' is specified.\n"
Olivier Burgarde97b9042014-05-22 16:44:59 +0200177 " -time <[min][:max]> only match requests recorded between timestamps.\n"
178 " Any of them may be omitted.\n"
Willy Tarreau615674c2012-01-23 08:15:51 +0100179 "Modifiers\n"
180 " -v invert the input filtering condition\n"
181 " -q don't report errors/warnings\n"
Willy Tarreau667c9052012-10-10 16:49:28 +0200182 " -m <lines> limit output to the first <lines> lines\n"
Willy Tarreau615674c2012-01-23 08:15:51 +0100183 "Output filters - only one may be used at a time\n"
184 " -c only report the number of lines that would have been printed\n"
185 " -pct output connect and response times percentiles\n"
186 " -st output number of requests per HTTP status code\n"
Willy Tarreau8a09b662012-10-10 10:26:22 +0200187 " -cc output number of requests per cookie code (2 chars)\n"
Willy Tarreau615674c2012-01-23 08:15:51 +0100188 " -tc output number of requests per termination code (2 chars)\n"
189 " -srv output statistics per server (time, requests, errors)\n"
190 " -u* output statistics per URL (time, requests, errors)\n"
191 " Additional characters indicate the output sorting key :\n"
192 " -u : by URL, -uc : request count, -ue : error count\n"
Willy Tarreau4201df72012-10-10 14:57:35 +0200193 " -ua : average response time, -ut : average total time\n"
Willy Tarreau615674c2012-01-23 08:15:51 +0100194 " -uao, -uto: average times computed on valid ('OK') requests\n"
Baptiste61aaad02012-09-08 23:10:03 +0200195 " -uba, -ubt: average bytes returned, total bytes returned\n"
Willy Tarreau615674c2012-01-23 08:15:51 +0100196 );
197 exit(0);
198}
199
Willy Tarreau72c28532009-01-22 18:56:50 +0100200
201/* return pointer to first char not part of current field starting at <p>. */
Willy Tarreauf9042062011-09-10 12:26:35 +0200202
203#if defined(__i386__)
204/* this one is always faster on 32-bits */
205static inline const char *field_stop(const char *p)
206{
207 asm(
208 /* Look for spaces */
209 "4: \n\t"
210 "inc %0 \n\t"
211 "cmpb $0x20, -1(%0) \n\t"
212 "ja 4b \n\t"
213 "jz 3f \n\t"
214
215 /* we only get there for control chars 0..31. Leave if we find '\0' */
216 "cmpb $0x0, -1(%0) \n\t"
217 "jnz 4b \n\t"
218
219 /* return %0-1 = position of the last char we checked */
220 "3: \n\t"
221 "dec %0 \n\t"
222 : "=r" (p)
223 : "0" (p)
224 );
225 return p;
226}
227#else
Willy Tarreau72c28532009-01-22 18:56:50 +0100228const char *field_stop(const char *p)
229{
230 unsigned char c;
231
232 while (1) {
233 c = *(p++);
234 if (c > ' ')
235 continue;
Willy Tarreau14389e72011-07-10 22:11:17 +0200236 if (c == ' ' || c == 0)
Willy Tarreau72c28532009-01-22 18:56:50 +0100237 break;
238 }
239 return p - 1;
240}
Willy Tarreauf9042062011-09-10 12:26:35 +0200241#endif
Willy Tarreau72c28532009-01-22 18:56:50 +0100242
243/* return field <field> (starting from 1) in string <p>. Only consider
244 * contiguous spaces (or tabs) as one delimiter. May return pointer to
245 * last char if field is not found. Equivalent to awk '{print $field}'.
246 */
247const char *field_start(const char *p, int field)
248{
Willy Tarreauf9042062011-09-10 12:26:35 +0200249#ifndef PREFER_ASM
Willy Tarreau72c28532009-01-22 18:56:50 +0100250 unsigned char c;
251 while (1) {
252 /* skip spaces */
253 while (1) {
Willy Tarreauf9042062011-09-10 12:26:35 +0200254 c = *(p++);
Willy Tarreau72c28532009-01-22 18:56:50 +0100255 if (c > ' ')
256 break;
Willy Tarreau14389e72011-07-10 22:11:17 +0200257 if (c == ' ')
Willy Tarreauf9042062011-09-10 12:26:35 +0200258 continue;
Willy Tarreau72c28532009-01-22 18:56:50 +0100259 if (!c) /* end of line */
Willy Tarreauf9042062011-09-10 12:26:35 +0200260 return p-1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100261 /* other char => new field */
262 break;
Willy Tarreau72c28532009-01-22 18:56:50 +0100263 }
264
265 /* start of field */
266 field--;
267 if (!field)
Willy Tarreauf9042062011-09-10 12:26:35 +0200268 return p-1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100269
270 /* skip this field */
271 while (1) {
272 c = *(p++);
Willy Tarreau14389e72011-07-10 22:11:17 +0200273 if (c == ' ')
Willy Tarreau72c28532009-01-22 18:56:50 +0100274 break;
Willy Tarreauf9042062011-09-10 12:26:35 +0200275 if (c > ' ')
276 continue;
Willy Tarreau72c28532009-01-22 18:56:50 +0100277 if (c == '\0')
Willy Tarreauf9042062011-09-10 12:26:35 +0200278 return p - 1;
Willy Tarreau72c28532009-01-22 18:56:50 +0100279 }
280 }
Willy Tarreauf9042062011-09-10 12:26:35 +0200281#else
282 /* This version works optimally on i386 and x86_64 but the code above
283 * shows similar performance. However, depending on the version of GCC
284 * used, inlining rules change and it may have difficulties to make
285 * efficient use of this code at other locations and could result in
286 * worse performance (eg: gcc 4.4). You may want to experience.
287 */
288 asm(
289 /* skip spaces */
290 "1: \n\t"
291 "inc %0 \n\t"
292 "cmpb $0x20, -1(%0) \n\t"
293 "ja 2f \n\t"
294 "jz 1b \n\t"
295
296 /* we only get there for control chars 0..31. Leave if we find '\0' */
297 "cmpb $0x0, -1(%0) \n\t"
298 "jz 3f \n\t"
299
300 /* start of field at [%0-1]. Check if we need to skip more fields */
301 "2: \n\t"
302 "dec %1 \n\t"
303 "jz 3f \n\t"
304
305 /* Look for spaces */
306 "4: \n\t"
307 "inc %0 \n\t"
308 "cmpb $0x20, -1(%0) \n\t"
309 "jz 1b \n\t"
310 "ja 4b \n\t"
311
312 /* we only get there for control chars 0..31. Leave if we find '\0' */
313 "cmpb $0x0, -1(%0) \n\t"
314 "jnz 4b \n\t"
315
316 /* return %0-1 = position of the last char we checked */
317 "3: \n\t"
318 "dec %0 \n\t"
319 : "=r" (p)
320 : "r" (field), "0" (p)
321 );
322 return p;
323#endif
Willy Tarreau72c28532009-01-22 18:56:50 +0100324}
325
326/* keep only the <bits> higher bits of <i> */
327static inline unsigned int quantify_u32(unsigned int i, int bits)
328{
329 int high;
330
331 if (!bits)
332 return 0;
333
334 if (i)
335 high = fls_auto(i); // 1 to 32
336 else
337 high = 0;
338
339 if (high <= bits)
340 return i;
341
342 return i & ~((1 << (high - bits)) - 1);
343}
344
345/* keep only the <bits> higher bits of the absolute value of <i>, as well as
346 * its sign. */
347static inline int quantify(int i, int bits)
348{
349 if (i >= 0)
350 return quantify_u32(i, bits);
351 else
352 return -quantify_u32(-i, bits);
353}
354
355/* Insert timer value <v> into tree <r>. A pre-allocated node must be passed
356 * in <alloc>. It may be NULL, in which case the function will allocate it
357 * itself. It will be reset to NULL once consumed. The caller is responsible
358 * for freeing the node once not used anymore. The node where the value was
359 * inserted is returned.
360 */
361struct timer *insert_timer(struct eb_root *r, struct timer **alloc, int v)
362{
363 struct timer *t = *alloc;
364 struct eb32_node *n;
365
366 if (!t) {
367 t = calloc(sizeof(*t), 1);
368 if (unlikely(!t)) {
369 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
370 exit(1);
371 }
372 }
373 t->node.key = quantify(v, QBITS); // keep only the higher QBITS bits
374
375 n = eb32i_insert(r, &t->node);
376 if (n == &t->node)
377 t = NULL; /* node inserted, will malloc next time */
378
379 *alloc = t;
380 return container_of(n, struct timer, node);
381}
382
383/* Insert value value <v> into tree <r>. A pre-allocated node must be passed
384 * in <alloc>. It may be NULL, in which case the function will allocate it
385 * itself. It will be reset to NULL once consumed. The caller is responsible
386 * for freeing the node once not used anymore. The node where the value was
387 * inserted is returned.
388 */
389struct timer *insert_value(struct eb_root *r, struct timer **alloc, int v)
390{
391 struct timer *t = *alloc;
392 struct eb32_node *n;
393
394 if (!t) {
395 t = calloc(sizeof(*t), 1);
396 if (unlikely(!t)) {
397 fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
398 exit(1);
399 }
400 }
401 t->node.key = v;
402
403 n = eb32i_insert(r, &t->node);
404 if (n == &t->node)
405 t = NULL; /* node inserted, will malloc next time */
406
407 *alloc = t;
408 return container_of(n, struct timer, node);
409}
410
411int str2ic(const char *s)
412{
413 int i = 0;
414 int j, k;
415
416 if (*s != '-') {
417 /* positive number */
418 while (1) {
419 j = (*s++) - '0';
420 k = i * 10;
421 if ((unsigned)j > 9)
422 break;
423 i = k + j;
424 }
425 } else {
426 /* negative number */
427 s++;
428 while (1) {
429 j = (*s++) - '0';
430 k = i * 10;
431 if ((unsigned)j > 9)
432 break;
433 i = k - j;
434 }
435 }
436
437 return i;
438}
439
440
441/* Equivalent to strtoul with a length. */
442static inline unsigned int __strl2ui(const char *s, int len)
443{
444 unsigned int i = 0;
445 while (len-- > 0) {
446 i = i * 10 - '0';
447 i += (unsigned char)*s++;
448 }
449 return i;
450}
451
452unsigned int strl2ui(const char *s, int len)
453{
454 return __strl2ui(s, len);
455}
456
457/* Convert "[04/Dec/2008:09:49:40.555]" to an integer equivalent to the time of
458 * the day in milliseconds. It returns -1 for all unparsable values. The parser
459 * looks ugly but gcc emits far better code that way.
460 */
461int convert_date(const char *field)
462{
463 unsigned int h, m, s, ms;
464 unsigned char c;
465 const char *b, *e;
466
467 h = m = s = ms = 0;
468 e = field;
469
470 /* skip the date */
471 while (1) {
472 c = *(e++);
473 if (c == ':')
474 break;
475 if (!c)
476 goto out_err;
477 }
478
479 /* hour + ':' */
480 b = e;
481 while (1) {
482 c = *(e++) - '0';
483 if (c > 9)
484 break;
485 h = h * 10 + c;
486 }
487 if (c == (unsigned char)(0 - '0'))
488 goto out_err;
489
490 /* minute + ':' */
491 b = e;
492 while (1) {
493 c = *(e++) - '0';
494 if (c > 9)
495 break;
496 m = m * 10 + c;
497 }
498 if (c == (unsigned char)(0 - '0'))
499 goto out_err;
500
501 /* second + '.' or ']' */
502 b = e;
503 while (1) {
504 c = *(e++) - '0';
505 if (c > 9)
506 break;
507 s = s * 10 + c;
508 }
509 if (c == (unsigned char)(0 - '0'))
510 goto out_err;
511
512 /* if there's a '.', we have milliseconds */
513 if (c == (unsigned char)('.' - '0')) {
514 /* millisecond second + ']' */
515 b = e;
516 while (1) {
517 c = *(e++) - '0';
518 if (c > 9)
519 break;
520 ms = ms * 10 + c;
521 }
522 if (c == (unsigned char)(0 - '0'))
523 goto out_err;
524 }
525 return (((h * 60) + m) * 60 + s) * 1000 + ms;
526 out_err:
527 return -1;
528}
529
Olivier Burgarde97b9042014-05-22 16:44:59 +0200530/* Convert "[04/Dec/2008:09:49:40.555]" to an unix timestamp.
531 * It returns -1 for all unparsable values. The parser
532 * looks ugly but gcc emits far better code that way.
533 */
534int convert_date_to_timestamp(const char *field)
535{
536 unsigned int d, mo, y, h, m, s;
537 unsigned char c;
538 const char *b, *e;
539 time_t rawtime;
540 struct tm * timeinfo;
541
542 d = mo = y = h = m = s = 0;
543 e = field;
544
545 c = *(e++); // remove '['
546 /* day + '/' */
547 while (1) {
548 c = *(e++) - '0';
549 if (c > 9)
550 break;
551 d = d * 10 + c;
552 if (c == (unsigned char)(0 - '0'))
553 goto out_err;
554 }
555
556 /* month + '/' */
557 c = *(e++);
558 if (c =='F') {
559 mo = 2;
560 e = e+3;
561 } else if (c =='S') {
562 mo = 9;
563 e = e+3;
564 } else if (c =='O') {
565 mo = 10;
566 e = e+3;
567 } else if (c =='N') {
568 mo = 11;
569 e = e+3;
570 } else if (c == 'D') {
571 mo = 12;
572 e = e+3;
573 } else if (c == 'A') {
574 c = *(e++);
575 if (c == 'p') {
576 mo = 4;
577 e = e+2;
578 } else if (c == 'u') {
579 mo = 8;
580 e = e+2;
581 } else
582 goto out_err;
583 } else if (c == 'J') {
584 c = *(e++);
585 if (c == 'a') {
586 mo = 1;
587 e = e+2;
588 } else if (c == 'u') {
589 c = *(e++);
590 if (c == 'n') {
591 mo = 6;
592 e = e+1;
593 } else if (c == 'l') {
594 mo = 7;
595 e++;
596 }
597 } else
598 goto out_err;
599 } else if (c == 'M') {
600 e++;
601 c = *(e++);
602 if (c == 'r') {
603 mo = 3;
604 e = e+1;
605 } else if (c == 'y') {
606 mo = 5;
607 e = e+1;
608 } else
609 goto out_err;
610 } else
611 goto out_err;
612
613 /* year + ':' */
614 while (1) {
615 c = *(e++) - '0';
616 if (c > 9)
617 break;
618 y = y * 10 + c;
619 if (c == (unsigned char)(0 - '0'))
620 goto out_err;
621 }
622
623 /* hour + ':' */
624 b = e;
625 while (1) {
626 c = *(e++) - '0';
627 if (c > 9)
628 break;
629 h = h * 10 + c;
630 }
631 if (c == (unsigned char)(0 - '0'))
632 goto out_err;
633
634 /* minute + ':' */
635 b = e;
636 while (1) {
637 c = *(e++) - '0';
638 if (c > 9)
639 break;
640 m = m * 10 + c;
641 }
642 if (c == (unsigned char)(0 - '0'))
643 goto out_err;
644
645 /* second + '.' or ']' */
646 b = e;
647 while (1) {
648 c = *(e++) - '0';
649 if (c > 9)
650 break;
651 s = s * 10 + c;
652 }
653
654 time(&rawtime);
655 timeinfo = localtime(&rawtime);
656
657 timeinfo->tm_sec = s;
658 timeinfo->tm_min = m;
659 timeinfo->tm_hour = h;
660 timeinfo->tm_mday = d;
661 timeinfo->tm_mon = mo - 1;
662 timeinfo->tm_year = y - 1900;
663
664 return mktime(timeinfo);
665 out_err:
666 return -1;
667}
668
Willy Tarreau72c28532009-01-22 18:56:50 +0100669void truncated_line(int linenum, const char *line)
670{
671 if (!(filter & FILT_QUIET))
672 fprintf(stderr, "Truncated line %d: %s\n", linenum, line);
673}
674
675int main(int argc, char **argv)
676{
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100677 const char *b, *e, *p, *time_field, *accept_field, *source_field;
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200678 const char *filter_term_code_name = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100679 const char *output_file = NULL;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200680 int f, last, err;
681 struct timer *t = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100682 struct eb32_node *n;
Willy Tarreauabe45b62010-10-28 20:33:46 +0200683 struct url_stat *ustat = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100684 int val, test;
Olivier Burgarde97b9042014-05-22 16:44:59 +0200685 uint uval;
Willy Tarreau72c28532009-01-22 18:56:50 +0100686 int filter_acc_delay = 0, filter_acc_count = 0;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200687 int filter_time_resp = 0;
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200688 int filt_http_status_low = 0, filt_http_status_high = 0;
Olivier Burgarde97b9042014-05-22 16:44:59 +0200689 int filt2_timestamp_low = 0, filt2_timestamp_high = 0;
Willy Tarreau72c28532009-01-22 18:56:50 +0100690 int skip_fields = 1;
691
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200692 void (*line_filter)(const char *accept_field, const char *time_field, struct timer **tptr) = NULL;
693
Willy Tarreau72c28532009-01-22 18:56:50 +0100694 argc--; argv++;
695 while (argc > 0) {
696 if (*argv[0] != '-')
697 break;
698
699 if (strcmp(argv[0], "-ad") == 0) {
700 if (argc < 2) die("missing option for -ad");
701 argc--; argv++;
702 filter |= FILT_ACC_DELAY;
703 filter_acc_delay = atol(*argv);
704 }
705 else if (strcmp(argv[0], "-ac") == 0) {
706 if (argc < 2) die("missing option for -ac");
707 argc--; argv++;
708 filter |= FILT_ACC_COUNT;
709 filter_acc_count = atol(*argv);
710 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200711 else if (strcmp(argv[0], "-rt") == 0) {
712 if (argc < 2) die("missing option for -rt");
713 argc--; argv++;
714 filter |= FILT_TIME_RESP;
715 filter_time_resp = atol(*argv);
716 }
717 else if (strcmp(argv[0], "-RT") == 0) {
718 if (argc < 2) die("missing option for -RT");
719 argc--; argv++;
720 filter |= FILT_TIME_RESP | FILT_INVERT_TIME_RESP;
721 filter_time_resp = atol(*argv);
722 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100723 else if (strcmp(argv[0], "-s") == 0) {
724 if (argc < 2) die("missing option for -s");
725 argc--; argv++;
726 skip_fields = atol(*argv);
727 }
Willy Tarreau667c9052012-10-10 16:49:28 +0200728 else if (strcmp(argv[0], "-m") == 0) {
729 if (argc < 2) die("missing option for -m");
730 argc--; argv++;
731 lines_max = atol(*argv);
732 }
Willy Tarreau72c28532009-01-22 18:56:50 +0100733 else if (strcmp(argv[0], "-e") == 0)
734 filter |= FILT_ERRORS_ONLY;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200735 else if (strcmp(argv[0], "-E") == 0)
736 filter |= FILT_ERRORS_ONLY | FILT_INVERT_ERRORS;
Willy Tarreau70c428f2011-07-10 17:27:40 +0200737 else if (strcmp(argv[0], "-H") == 0)
738 filter |= FILT_HTTP_ONLY;
Willy Tarreau08911ff2011-10-13 13:28:36 +0200739 else if (strcmp(argv[0], "-Q") == 0)
740 filter |= FILT_QUEUE_ONLY;
741 else if (strcmp(argv[0], "-QS") == 0)
742 filter |= FILT_QUEUE_SRV_ONLY;
Willy Tarreau72c28532009-01-22 18:56:50 +0100743 else if (strcmp(argv[0], "-c") == 0)
744 filter |= FILT_COUNT_ONLY;
745 else if (strcmp(argv[0], "-q") == 0)
746 filter |= FILT_QUIET;
747 else if (strcmp(argv[0], "-v") == 0)
748 filter_invert = !filter_invert;
749 else if (strcmp(argv[0], "-gt") == 0)
750 filter |= FILT_GRAPH_TIMERS;
Willy Tarreau214c2032009-02-20 11:02:32 +0100751 else if (strcmp(argv[0], "-pct") == 0)
752 filter |= FILT_PERCENTILE;
Willy Tarreau0f423a72010-05-03 10:50:54 +0200753 else if (strcmp(argv[0], "-st") == 0)
754 filter |= FILT_COUNT_STATUS;
Willy Tarreaud2201062010-05-27 18:17:30 +0200755 else if (strcmp(argv[0], "-srv") == 0)
756 filter |= FILT_COUNT_SRV_STATUS;
Willy Tarreau8a09b662012-10-10 10:26:22 +0200757 else if (strcmp(argv[0], "-cc") == 0)
758 filter |= FILT_COUNT_COOK_CODES;
Willy Tarreaud8fc1102010-09-12 17:56:16 +0200759 else if (strcmp(argv[0], "-tc") == 0)
760 filter |= FILT_COUNT_TERM_CODES;
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +0200761 else if (strcmp(argv[0], "-tcn") == 0) {
762 if (argc < 2) die("missing option for -tcn");
763 argc--; argv++;
764 filter |= FILT_TERM_CODE_NAME;
765 filter_term_code_name = *argv;
766 }
767 else if (strcmp(argv[0], "-TCN") == 0) {
768 if (argc < 2) die("missing option for -TCN");
769 argc--; argv++;
770 filter |= FILT_TERM_CODE_NAME | FILT_INVERT_TERM_CODE_NAME;
771 filter_term_code_name = *argv;
772 }
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200773 else if (strcmp(argv[0], "-hs") == 0 || strcmp(argv[0], "-HS") == 0) {
774 char *sep, *str;
775
776 if (argc < 2) die("missing option for -hs/-HS ([min]:[max])");
777 filter |= FILT_HTTP_STATUS;
778 if (argv[0][1] == 'H')
779 filter |= FILT_INVERT_HTTP_STATUS;
780
781 argc--; argv++;
782 str = *argv;
783 sep = strchr(str, ':'); /* [min]:[max] */
784 if (!sep)
785 sep = str; /* make max point to min */
786 else
787 *sep++ = 0;
788 filt_http_status_low = *str ? atol(str) : 0;
789 filt_http_status_high = *sep ? atol(sep) : 65535;
790 }
Olivier Burgarde97b9042014-05-22 16:44:59 +0200791 else if (strcmp(argv[0], "-time") == 0) {
792 char *sep, *str;
793
794 if (argc < 2) die("missing option for -time ([min]:[max])");
795 filter2 |= FILT2_TIMESTAMP;
796
797 argc--; argv++;
798 str = *argv;
799 sep = strchr(str, ':'); /* [min]:[max] */
800 filt2_timestamp_low = *str ? atol(str) : 0;
801 if (!sep)
802 filt2_timestamp_high = 0xFFFFFFFF;
803 else
804 filt2_timestamp_high = atol(++sep);
805 }
Willy Tarreauabe45b62010-10-28 20:33:46 +0200806 else if (strcmp(argv[0], "-u") == 0)
807 filter |= FILT_COUNT_URL_ONLY;
808 else if (strcmp(argv[0], "-uc") == 0)
809 filter |= FILT_COUNT_URL_COUNT;
810 else if (strcmp(argv[0], "-ue") == 0)
811 filter |= FILT_COUNT_URL_ERR;
812 else if (strcmp(argv[0], "-ua") == 0)
813 filter |= FILT_COUNT_URL_TAVG;
814 else if (strcmp(argv[0], "-ut") == 0)
815 filter |= FILT_COUNT_URL_TTOT;
816 else if (strcmp(argv[0], "-uao") == 0)
817 filter |= FILT_COUNT_URL_TAVGO;
818 else if (strcmp(argv[0], "-uto") == 0)
819 filter |= FILT_COUNT_URL_TTOTO;
Baptiste61aaad02012-09-08 23:10:03 +0200820 else if (strcmp(argv[0], "-uba") == 0)
821 filter |= FILT_COUNT_URL_BAVG;
822 else if (strcmp(argv[0], "-ubt") == 0)
823 filter |= FILT_COUNT_URL_BTOT;
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100824 else if (strcmp(argv[0], "-ic") == 0)
825 filter |= FILT_COUNT_IP_COUNT;
Willy Tarreau72c28532009-01-22 18:56:50 +0100826 else if (strcmp(argv[0], "-o") == 0) {
827 if (output_file)
828 die("Fatal: output file name already specified.\n");
829 if (argc < 2)
830 die("Fatal: missing output file name.\n");
831 output_file = argv[1];
832 }
Willy Tarreau615674c2012-01-23 08:15:51 +0100833 else if (strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0)
834 help();
Willy Tarreau72c28532009-01-22 18:56:50 +0100835 argc--;
836 argv++;
837 }
838
839 if (!filter)
840 die("No action specified.\n");
841
842 if (filter & FILT_ACC_COUNT && !filter_acc_count)
843 filter_acc_count=1;
844
845 if (filter & FILT_ACC_DELAY && !filter_acc_delay)
846 filter_acc_delay = 1;
847
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200848
849 /* by default, all lines are printed */
850 line_filter = filter_output_line;
851 if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY))
852 line_filter = filter_accept_holes;
853 else if (filter & (FILT_GRAPH_TIMERS|FILT_PERCENTILE))
854 line_filter = filter_graphs;
855 else if (filter & FILT_COUNT_STATUS)
856 line_filter = filter_count_status;
Willy Tarreau8a09b662012-10-10 10:26:22 +0200857 else if (filter & FILT_COUNT_COOK_CODES)
858 line_filter = filter_count_cook_codes;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200859 else if (filter & FILT_COUNT_TERM_CODES)
860 line_filter = filter_count_term_codes;
861 else if (filter & FILT_COUNT_SRV_STATUS)
862 line_filter = filter_count_srv_status;
863 else if (filter & FILT_COUNT_URL_ANY)
864 line_filter = filter_count_url;
865 else if (filter & FILT_COUNT_ONLY)
866 line_filter = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100867
Willy Tarreauf8c95d22012-06-12 09:16:56 +0200868#if defined(POSIX_FADV_SEQUENTIAL)
869 /* around 20% performance improvement is observed on Linux with this
870 * on cold-cache. Surprizingly, WILLNEED is less performant. Don't
871 * use NOREUSE as it flushes the cache and prevents easy data
872 * manipulation on logs!
873 */
874 posix_fadvise(0, 0, 0, POSIX_FADV_SEQUENTIAL);
875#endif
876
Willy Tarreaua1629a52012-11-13 20:48:15 +0100877 if (!line_filter && /* FILT_COUNT_ONLY ( see above), and no input filter (see below) */
Olivier Burgarde97b9042014-05-22 16:44:59 +0200878 !(filter & (FILT_HTTP_ONLY|FILT_TIME_RESP|FILT_ERRORS_ONLY|FILT_HTTP_STATUS|FILT_QUEUE_ONLY|FILT_QUEUE_SRV_ONLY|FILT_TERM_CODE_NAME)) &&
879 !(filter2 & (FILT2_TIMESTAMP))) {
Willy Tarreaua1629a52012-11-13 20:48:15 +0100880 /* read the whole file at once first, ignore it if inverted output */
Willy Tarreaue1a908c2012-01-03 09:23:03 +0100881 if (!filter_invert)
Willy Tarreaua1629a52012-11-13 20:48:15 +0100882 while ((lines_max < 0 || lines_out < lines_max) && fgets2(stdin) != NULL)
Willy Tarreaue1a908c2012-01-03 09:23:03 +0100883 lines_out++;
884
885 goto skip_filters;
886 }
887
Willy Tarreau214c2032009-02-20 11:02:32 +0100888 while ((line = fgets2(stdin)) != NULL) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100889 linenum++;
Willy Tarreau26deaf52011-07-10 19:47:48 +0200890 time_field = NULL; accept_field = NULL;
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100891 source_field = NULL;
Willy Tarreau72c28532009-01-22 18:56:50 +0100892
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200893 test = 1;
Willy Tarreau26deaf52011-07-10 19:47:48 +0200894
895 /* for any line we process, we first ensure that there is a field
896 * looking like the accept date field (beginning with a '[').
897 */
Willy Tarreau7cf479c2013-02-16 23:49:04 +0100898 if (filter & FILT_COUNT_IP_COUNT) {
899 /* we need the IP first */
900 source_field = field_start(line, SOURCE_FIELD + skip_fields);
901 accept_field = field_start(source_field, ACCEPT_FIELD - SOURCE_FIELD + 1);
902 }
903 else
904 accept_field = field_start(line, ACCEPT_FIELD + skip_fields);
905
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200906 if (unlikely(*accept_field != '[')) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200907 parse_err++;
908 continue;
909 }
910
911 /* the day of month field is begin 01 and 31 */
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200912 if (accept_field[1] < '0' || accept_field[1] > '3') {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200913 parse_err++;
914 continue;
915 }
916
Olivier Burgarde97b9042014-05-22 16:44:59 +0200917 if (filter2 & FILT2_TIMESTAMP) {
918 uval = convert_date_to_timestamp(accept_field);
919 test &= (uval>=filt2_timestamp_low && uval<=filt2_timestamp_high) ;
920 }
921
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200922 if (filter & FILT_HTTP_ONLY) {
Willy Tarreau70c428f2011-07-10 17:27:40 +0200923 /* only report lines with at least 4 timers */
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200924 if (!time_field) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200925 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200926 if (unlikely(!*time_field)) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200927 truncated_line(linenum, line);
928 continue;
929 }
Willy Tarreau70c428f2011-07-10 17:27:40 +0200930 }
931
Willy Tarreau758a6ea2011-07-10 18:53:44 +0200932 e = field_stop(time_field + 1);
933 /* we have field TIME_FIELD in [time_field]..[e-1] */
934 p = time_field;
Willy Tarreau70c428f2011-07-10 17:27:40 +0200935 f = 0;
Willy Tarreaudf6f0d12011-07-10 18:15:08 +0200936 while (!SEP(*p)) {
Willy Tarreau70c428f2011-07-10 17:27:40 +0200937 if (++f == 4)
938 break;
939 SKIP_CHAR(p, '/');
940 }
941 test &= (f >= 4);
942 }
943
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200944 if (filter & FILT_TIME_RESP) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200945 int tps;
946
947 /* only report lines with response times larger than filter_time_resp */
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200948 if (!time_field) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200949 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200950 if (unlikely(!*time_field)) {
Willy Tarreau26deaf52011-07-10 19:47:48 +0200951 truncated_line(linenum, line);
952 continue;
953 }
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200954 }
955
Willy Tarreau758a6ea2011-07-10 18:53:44 +0200956 e = field_stop(time_field + 1);
957 /* we have field TIME_FIELD in [time_field]..[e-1], let's check only the response time */
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200958
Willy Tarreau758a6ea2011-07-10 18:53:44 +0200959 p = time_field;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200960 err = 0;
Willy Tarreau24bcb4f2010-10-28 20:39:50 +0200961 f = 0;
Willy Tarreaudf6f0d12011-07-10 18:15:08 +0200962 while (!SEP(*p)) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200963 tps = str2ic(p);
964 if (tps < 0) {
965 tps = -1;
966 err = 1;
967 }
Willy Tarreau24bcb4f2010-10-28 20:39:50 +0200968 if (++f == 4)
969 break;
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200970 SKIP_CHAR(p, '/');
971 }
972
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200973 if (unlikely(f < 4)) {
Willy Tarreau5bdfd962009-10-14 15:16:29 +0200974 parse_err++;
975 continue;
976 }
977
978 test &= (tps >= filter_time_resp) ^ !!(filter & FILT_INVERT_TIME_RESP);
979 }
980
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200981 if (filter & (FILT_ERRORS_ONLY | FILT_HTTP_STATUS)) {
982 /* Check both error codes (-1, 5xx) and status code ranges */
Willy Tarreau26deaf52011-07-10 19:47:48 +0200983 if (time_field)
984 b = field_start(time_field, STATUS_FIELD - TIME_FIELD + 1);
985 else
986 b = field_start(accept_field, STATUS_FIELD - ACCEPT_FIELD + 1);
987
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200988 if (unlikely(!*b)) {
Willy Tarreau72c28532009-01-22 18:56:50 +0100989 truncated_line(linenum, line);
990 continue;
991 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +0200992
Willy Tarreaud3007ff2011-09-05 02:07:23 +0200993 val = str2ic(b);
994 if (filter & FILT_ERRORS_ONLY)
995 test &= (val < 0 || (val >= 500 && val <= 599)) ^ !!(filter & FILT_INVERT_ERRORS);
996
997 if (filter & FILT_HTTP_STATUS)
998 test &= (val >= filt_http_status_low && val <= filt_http_status_high) ^ !!(filter & FILT_INVERT_HTTP_STATUS);
Willy Tarreau72c28532009-01-22 18:56:50 +0100999 }
1000
Willy Tarreau08911ff2011-10-13 13:28:36 +02001001 if (filter & (FILT_QUEUE_ONLY|FILT_QUEUE_SRV_ONLY)) {
1002 /* Check if the server's queue is non-nul */
1003 if (time_field)
1004 b = field_start(time_field, QUEUE_LEN_FIELD - TIME_FIELD + 1);
1005 else
1006 b = field_start(accept_field, QUEUE_LEN_FIELD - ACCEPT_FIELD + 1);
1007
1008 if (unlikely(!*b)) {
1009 truncated_line(linenum, line);
1010 continue;
1011 }
1012
1013 if (*b == '0') {
1014 if (filter & FILT_QUEUE_SRV_ONLY) {
1015 test = 0;
1016 }
1017 else {
1018 do {
1019 b++;
1020 if (*b == '/') {
1021 b++;
1022 break;
1023 }
1024 } while (*b);
1025 test &= ((unsigned char)(*b - '1') < 9);
1026 }
1027 }
1028 }
1029
Hervé COMMOWICK927cddd2011-08-10 17:42:41 +02001030 if (filter & FILT_TERM_CODE_NAME) {
1031 /* only report corresponding termination code name */
1032 if (time_field)
1033 b = field_start(time_field, TERM_CODES_FIELD - TIME_FIELD + 1);
1034 else
1035 b = field_start(accept_field, TERM_CODES_FIELD - ACCEPT_FIELD + 1);
1036
1037 if (unlikely(!*b)) {
1038 truncated_line(linenum, line);
1039 continue;
1040 }
1041
1042 test &= (b[0] == filter_term_code_name[0] && b[1] == filter_term_code_name[1]) ^ !!(filter & FILT_INVERT_TERM_CODE_NAME);
1043 }
1044
1045
Willy Tarreau0f423a72010-05-03 10:50:54 +02001046 test ^= filter_invert;
1047 if (!test)
1048 continue;
1049
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001050 /************** here we process inputs *******************/
Willy Tarreau72c28532009-01-22 18:56:50 +01001051
Willy Tarreau7cf479c2013-02-16 23:49:04 +01001052 if (line_filter) {
1053 if (filter & FILT_COUNT_IP_COUNT)
1054 filter_count_ip(source_field, accept_field, time_field, &t);
1055 else
1056 line_filter(accept_field, time_field, &t);
1057 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001058 else
Willy Tarreaua1629a52012-11-13 20:48:15 +01001059 lines_out++; /* FILT_COUNT_ONLY was used, so we're just counting lines */
1060 if (lines_max >= 0 && lines_out >= lines_max)
Willy Tarreau667c9052012-10-10 16:49:28 +02001061 break;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001062 }
Willy Tarreauabe45b62010-10-28 20:33:46 +02001063
Willy Tarreaue1a908c2012-01-03 09:23:03 +01001064 skip_filters:
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001065 /*****************************************************
1066 * Here we've finished reading all input. Depending on the
1067 * filters, we may still have some analysis to run on the
1068 * collected data and to output data in a new format.
1069 *************************************************** */
Willy Tarreau72c28532009-01-22 18:56:50 +01001070
1071 if (t)
1072 free(t);
1073
1074 if (filter & FILT_COUNT_ONLY) {
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001075 printf("%d\n", lines_out);
Willy Tarreau72c28532009-01-22 18:56:50 +01001076 exit(0);
1077 }
1078
Willy Tarreau72c28532009-01-22 18:56:50 +01001079 if (filter & (FILT_ACC_COUNT|FILT_ACC_DELAY)) {
1080 /* sort and count all timers. Output will look like this :
1081 * <accept_date> <delta_ms from previous one> <nb entries>
1082 */
1083 n = eb32_first(&timers[0]);
1084
1085 if (n)
1086 last = n->key;
1087 while (n) {
1088 unsigned int d, h, m, s, ms;
1089
1090 t = container_of(n, struct timer, node);
1091 h = n->key;
1092 d = h - last;
1093 last = h;
1094
1095 if (d >= filter_acc_delay && t->count >= filter_acc_count) {
1096 ms = h % 1000; h = h / 1000;
1097 s = h % 60; h = h / 60;
1098 m = h % 60; h = h / 60;
Willy Tarreau72c28532009-01-22 18:56:50 +01001099 printf("%02d:%02d:%02d.%03d %d %d %d\n", h, m, s, ms, last, d, t->count);
Willy Tarreau667c9052012-10-10 16:49:28 +02001100 lines_out++;
Willy Tarreaua1629a52012-11-13 20:48:15 +01001101 if (lines_max >= 0 && lines_out >= lines_max)
Willy Tarreau667c9052012-10-10 16:49:28 +02001102 break;
Willy Tarreau72c28532009-01-22 18:56:50 +01001103 }
1104 n = eb32_next(n);
1105 }
1106 }
1107 else if (filter & FILT_GRAPH_TIMERS) {
1108 /* sort all timers */
1109 for (f = 0; f < 5; f++) {
1110 struct eb32_node *n;
1111 int val;
1112
1113 val = 0;
1114 n = eb32_first(&timers[f]);
1115 while (n) {
1116 int i;
1117 double d;
1118
1119 t = container_of(n, struct timer, node);
1120 last = n->key;
1121 val = t->count;
1122
1123 i = (last < 0) ? -last : last;
1124 i = fls_auto(i) - QBITS;
1125
1126 if (i > 0)
1127 d = val / (double)(1 << i);
1128 else
1129 d = val;
1130
Willy Tarreaua1629a52012-11-13 20:48:15 +01001131 if (d > 0.0)
Willy Tarreau72c28532009-01-22 18:56:50 +01001132 printf("%d %d %f\n", f, last, d+1.0);
Willy Tarreau72c28532009-01-22 18:56:50 +01001133
1134 n = eb32_next(n);
1135 }
Willy Tarreau214c2032009-02-20 11:02:32 +01001136 }
1137 }
1138 else if (filter & FILT_PERCENTILE) {
1139 /* report timers by percentile :
1140 * <percent> <total> <max_req_time> <max_conn_time> <max_resp_time> <max_data_time>
1141 * We don't count errs.
1142 */
1143 struct eb32_node *n[5];
1144 unsigned long cum[5];
1145 double step;
1146
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001147 if (!lines_out)
Willy Tarreau910ba4b2009-11-17 10:16:19 +01001148 goto empty;
1149
Willy Tarreau214c2032009-02-20 11:02:32 +01001150 for (f = 1; f < 5; f++) {
1151 n[f] = eb32_first(&timers[f]);
1152 cum[f] = container_of(n[f], struct timer, node)->count;
1153 }
1154
1155 for (step = 1; step <= 1000;) {
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001156 unsigned int thres = lines_out * (step / 1000.0);
Willy Tarreau214c2032009-02-20 11:02:32 +01001157
1158 printf("%3.1f %d ", step/10.0, thres);
1159 for (f = 1; f < 5; f++) {
1160 struct eb32_node *next;
1161 while (cum[f] < thres) {
1162 /* need to find other keys */
1163 next = eb32_next(n[f]);
1164 if (!next)
1165 break;
1166 n[f] = next;
1167 cum[f] += container_of(next, struct timer, node)->count;
1168 }
1169
1170 /* value still within $step % of total */
1171 printf("%d ", n[f]->key);
1172 }
1173 putchar('\n');
1174 if (step >= 100 && step < 900)
1175 step += 50; // jump 5% by 5% between those steps.
1176 else if (step >= 20 && step < 980)
1177 step += 10;
1178 else
1179 step += 1;
Willy Tarreau72c28532009-01-22 18:56:50 +01001180 }
1181 }
Willy Tarreau0f423a72010-05-03 10:50:54 +02001182 else if (filter & FILT_COUNT_STATUS) {
1183 /* output all statuses in the form of <status> <occurrences> */
1184 n = eb32_first(&timers[0]);
1185 while (n) {
1186 t = container_of(n, struct timer, node);
1187 printf("%d %d\n", n->key, t->count);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001188 lines_out++;
Willy Tarreaua1629a52012-11-13 20:48:15 +01001189 if (lines_max >= 0 && lines_out >= lines_max)
Willy Tarreau667c9052012-10-10 16:49:28 +02001190 break;
Willy Tarreau0f423a72010-05-03 10:50:54 +02001191 n = eb32_next(n);
1192 }
1193 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001194 else if (filter & FILT_COUNT_SRV_STATUS) {
Willy Tarreaud2201062010-05-27 18:17:30 +02001195 struct ebmb_node *srv_node;
1196 struct srv_st *srv;
1197
1198 printf("#srv_name 1xx 2xx 3xx 4xx 5xx other tot_req req_ok pct_ok avg_ct avg_rt\n");
1199
1200 srv_node = ebmb_first(&timers[0]);
1201 while (srv_node) {
1202 int tot_rq;
1203
1204 srv = container_of(srv_node, struct srv_st, node);
1205
1206 tot_rq = 0;
1207 for (f = 0; f <= 5; f++)
1208 tot_rq += srv->st_cnt[f];
1209
1210 printf("%s %d %d %d %d %d %d %d %d %.1f %d %d\n",
1211 srv_node->key, srv->st_cnt[1], srv->st_cnt[2],
1212 srv->st_cnt[3], srv->st_cnt[4], srv->st_cnt[5], srv->st_cnt[0],
1213 tot_rq,
1214 srv->nb_ok, (double)srv->nb_ok * 100.0 / (tot_rq?tot_rq:1),
1215 (int)(srv->cum_ct / (srv->nb_ct?srv->nb_ct:1)), (int)(srv->cum_rt / (srv->nb_rt?srv->nb_rt:1)));
1216 srv_node = ebmb_next(srv_node);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001217 lines_out++;
Willy Tarreaua1629a52012-11-13 20:48:15 +01001218 if (lines_max >= 0 && lines_out >= lines_max)
Willy Tarreau667c9052012-10-10 16:49:28 +02001219 break;
Willy Tarreaud2201062010-05-27 18:17:30 +02001220 }
1221 }
Willy Tarreau8a09b662012-10-10 10:26:22 +02001222 else if (filter & (FILT_COUNT_TERM_CODES|FILT_COUNT_COOK_CODES)) {
Willy Tarreaud8fc1102010-09-12 17:56:16 +02001223 /* output all statuses in the form of <code> <occurrences> */
1224 n = eb32_first(&timers[0]);
1225 while (n) {
1226 t = container_of(n, struct timer, node);
1227 printf("%c%c %d\n", (n->key >> 8), (n->key) & 255, t->count);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001228 lines_out++;
Willy Tarreaua1629a52012-11-13 20:48:15 +01001229 if (lines_max >= 0 && lines_out >= lines_max)
Willy Tarreau667c9052012-10-10 16:49:28 +02001230 break;
Willy Tarreaud8fc1102010-09-12 17:56:16 +02001231 n = eb32_next(n);
1232 }
1233 }
Willy Tarreau7cf479c2013-02-16 23:49:04 +01001234 else if (filter & (FILT_COUNT_URL_ANY|FILT_COUNT_IP_COUNT)) {
Willy Tarreauabe45b62010-10-28 20:33:46 +02001235 struct eb_node *node, *next;
1236
1237 if (!(filter & FILT_COUNT_URL_ONLY)) {
1238 /* we have to sort on another criterion. We'll use timers[1] for the
1239 * destination tree.
1240 */
1241
1242 timers[1] = EB_ROOT; /* reconfigure to accept duplicates */
1243 for (node = eb_first(&timers[0]); node; node = next) {
1244 next = eb_next(node);
1245 eb_delete(node);
1246
1247 ustat = container_of(node, struct url_stat, node.url.node);
1248
Willy Tarreau7cf479c2013-02-16 23:49:04 +01001249 if (filter & (FILT_COUNT_URL_COUNT|FILT_COUNT_IP_COUNT))
Willy Tarreauabe45b62010-10-28 20:33:46 +02001250 ustat->node.val.key = ustat->nb_req;
1251 else if (filter & FILT_COUNT_URL_ERR)
1252 ustat->node.val.key = ustat->nb_err;
1253 else if (filter & FILT_COUNT_URL_TTOT)
1254 ustat->node.val.key = ustat->total_time;
1255 else if (filter & FILT_COUNT_URL_TAVG)
1256 ustat->node.val.key = ustat->nb_req ? ustat->total_time / ustat->nb_req : 0;
1257 else if (filter & FILT_COUNT_URL_TTOTO)
1258 ustat->node.val.key = ustat->total_time_ok;
1259 else if (filter & FILT_COUNT_URL_TAVGO)
1260 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 +02001261 else if (filter & FILT_COUNT_URL_BAVG)
1262 ustat->node.val.key = ustat->nb_req ? ustat->total_bytes_sent / ustat->nb_req : 0;
1263 else if (filter & FILT_COUNT_URL_BTOT)
1264 ustat->node.val.key = ustat->total_bytes_sent;
Willy Tarreauabe45b62010-10-28 20:33:46 +02001265 else
1266 ustat->node.val.key = 0;
1267
1268 eb64_insert(&timers[1], &ustat->node.val);
1269 }
1270 /* switch trees */
1271 timers[0] = timers[1];
1272 }
1273
Willy Tarreau7cf479c2013-02-16 23:49:04 +01001274 if (FILT_COUNT_IP_COUNT)
1275 printf("#req err ttot tavg oktot okavg bavg btot src\n");
1276 else
1277 printf("#req err ttot tavg oktot okavg bavg btot url\n");
Willy Tarreauabe45b62010-10-28 20:33:46 +02001278
1279 /* scan the tree in its reverse sorting order */
1280 node = eb_last(&timers[0]);
1281 while (node) {
1282 ustat = container_of(node, struct url_stat, node.url.node);
Baptiste61aaad02012-09-08 23:10:03 +02001283 printf("%d %d %Ld %Ld %Ld %Ld %Ld %Ld %s\n",
Willy Tarreauabe45b62010-10-28 20:33:46 +02001284 ustat->nb_req,
1285 ustat->nb_err,
1286 ustat->total_time,
1287 ustat->nb_req ? ustat->total_time / ustat->nb_req : 0,
1288 ustat->total_time_ok,
1289 (ustat->nb_req - ustat->nb_err) ? ustat->total_time_ok / (ustat->nb_req - ustat->nb_err) : 0,
Baptiste61aaad02012-09-08 23:10:03 +02001290 ustat->nb_req ? ustat->total_bytes_sent / ustat->nb_req : 0,
1291 ustat->total_bytes_sent,
Willy Tarreauabe45b62010-10-28 20:33:46 +02001292 ustat->url);
1293
1294 node = eb_prev(node);
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001295 lines_out++;
Willy Tarreaua1629a52012-11-13 20:48:15 +01001296 if (lines_max >= 0 && lines_out >= lines_max)
Willy Tarreau667c9052012-10-10 16:49:28 +02001297 break;
Willy Tarreauabe45b62010-10-28 20:33:46 +02001298 }
1299 }
Willy Tarreaud2201062010-05-27 18:17:30 +02001300
Willy Tarreau910ba4b2009-11-17 10:16:19 +01001301 empty:
Willy Tarreau72c28532009-01-22 18:56:50 +01001302 if (!(filter & FILT_QUIET))
1303 fprintf(stderr, "%d lines in, %d lines out, %d parsing errors\n",
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001304 linenum, lines_out, parse_err);
Willy Tarreau72c28532009-01-22 18:56:50 +01001305 exit(0);
1306}
1307
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001308void filter_output_line(const char *accept_field, const char *time_field, struct timer **tptr)
1309{
1310 puts(line);
1311 lines_out++;
1312}
1313
1314void filter_accept_holes(const char *accept_field, const char *time_field, struct timer **tptr)
1315{
1316 struct timer *t2;
1317 int val;
1318
1319 val = convert_date(accept_field);
1320 if (unlikely(val < 0)) {
1321 truncated_line(linenum, line);
1322 return;
1323 }
1324
1325 t2 = insert_value(&timers[0], tptr, val);
1326 t2->count++;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001327 return;
1328}
1329
1330void filter_count_status(const char *accept_field, const char *time_field, struct timer **tptr)
1331{
1332 struct timer *t2;
1333 const char *b;
1334 int val;
1335
1336 if (time_field)
1337 b = field_start(time_field, STATUS_FIELD - TIME_FIELD + 1);
1338 else
1339 b = field_start(accept_field, STATUS_FIELD - ACCEPT_FIELD + 1);
1340
1341 if (unlikely(!*b)) {
1342 truncated_line(linenum, line);
1343 return;
1344 }
1345
1346 val = str2ic(b);
1347
1348 t2 = insert_value(&timers[0], tptr, val);
1349 t2->count++;
1350}
1351
Willy Tarreau8a09b662012-10-10 10:26:22 +02001352void filter_count_cook_codes(const char *accept_field, const char *time_field, struct timer **tptr)
1353{
1354 struct timer *t2;
1355 const char *b;
1356 int val;
1357
1358 if (time_field)
1359 b = field_start(time_field, TERM_CODES_FIELD - TIME_FIELD + 1);
1360 else
1361 b = field_start(accept_field, TERM_CODES_FIELD - ACCEPT_FIELD + 1);
1362
1363 if (unlikely(!*b)) {
1364 truncated_line(linenum, line);
1365 return;
1366 }
1367
1368 val = 256 * b[2] + b[3];
1369
1370 t2 = insert_value(&timers[0], tptr, val);
1371 t2->count++;
1372}
1373
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001374void filter_count_term_codes(const char *accept_field, const char *time_field, struct timer **tptr)
1375{
1376 struct timer *t2;
1377 const char *b;
1378 int val;
1379
1380 if (time_field)
1381 b = field_start(time_field, TERM_CODES_FIELD - TIME_FIELD + 1);
1382 else
1383 b = field_start(accept_field, TERM_CODES_FIELD - ACCEPT_FIELD + 1);
1384
1385 if (unlikely(!*b)) {
1386 truncated_line(linenum, line);
1387 return;
1388 }
1389
1390 val = 256 * b[0] + b[1];
1391
1392 t2 = insert_value(&timers[0], tptr, val);
1393 t2->count++;
1394}
1395
1396void filter_count_srv_status(const char *accept_field, const char *time_field, struct timer **tptr)
1397{
1398 const char *b, *e, *p;
1399 int f, err, array[5];
1400 struct ebmb_node *srv_node;
1401 struct srv_st *srv;
1402 int val;
1403
1404 /* the server field is before the status field, so let's
1405 * parse them in the proper order.
1406 */
1407 b = field_start(accept_field, SERVER_FIELD - ACCEPT_FIELD + 1);
1408 if (unlikely(!*b)) {
1409 truncated_line(linenum, line);
1410 return;
1411 }
1412
1413 e = field_stop(b + 1); /* we have the server name in [b]..[e-1] */
1414
1415 /* the chance that a server name already exists is extremely high,
1416 * so let's perform a normal lookup first.
1417 */
1418 srv_node = ebst_lookup_len(&timers[0], b, e - b);
1419 srv = container_of(srv_node, struct srv_st, node);
1420
1421 if (!srv_node) {
1422 /* server not yet in the tree, let's create it */
1423 srv = (void *)calloc(1, sizeof(struct srv_st) + e - b + 1);
1424 srv_node = &srv->node;
1425 memcpy(&srv_node->key, b, e - b);
1426 srv_node->key[e - b] = '\0';
1427 ebst_insert(&timers[0], srv_node);
1428 }
1429
1430 /* let's collect the connect and response times */
1431 if (!time_field) {
1432 time_field = field_start(e, TIME_FIELD - SERVER_FIELD);
1433 if (unlikely(!*time_field)) {
1434 truncated_line(linenum, line);
1435 return;
1436 }
1437 }
1438
1439 e = field_stop(time_field + 1);
1440 /* we have field TIME_FIELD in [time_field]..[e-1] */
1441
1442 p = time_field;
1443 err = 0;
1444 f = 0;
1445 while (!SEP(*p)) {
1446 array[f] = str2ic(p);
1447 if (array[f] < 0) {
1448 array[f] = -1;
1449 err = 1;
1450 }
1451 if (++f == 5)
1452 break;
1453 SKIP_CHAR(p, '/');
1454 }
1455
1456 if (unlikely(f < 5)){
1457 parse_err++;
1458 return;
1459 }
1460
1461 /* OK we have our timers in array[2,3] */
1462 if (!err)
1463 srv->nb_ok++;
1464
1465 if (array[2] >= 0) {
1466 srv->cum_ct += array[2];
1467 srv->nb_ct++;
1468 }
1469
1470 if (array[3] >= 0) {
1471 srv->cum_rt += array[3];
1472 srv->nb_rt++;
1473 }
1474
1475 /* we're interested in the 5 HTTP status classes (1xx ... 5xx), and
1476 * the invalid ones which will be reported as 0.
1477 */
1478 b = field_start(e, STATUS_FIELD - TIME_FIELD);
1479 if (unlikely(!*b)) {
1480 truncated_line(linenum, line);
1481 return;
1482 }
1483
1484 val = 0;
1485 if (*b >= '1' && *b <= '5')
1486 val = *b - '0';
1487
1488 srv->st_cnt[val]++;
1489}
1490
1491void filter_count_url(const char *accept_field, const char *time_field, struct timer **tptr)
1492{
1493 struct url_stat *ustat = NULL;
1494 struct ebpt_node *ebpt_old;
1495 const char *b, *e;
1496 int f, err, array[5];
Baptiste61aaad02012-09-08 23:10:03 +02001497 int val;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001498
1499 /* let's collect the response time */
1500 if (!time_field) {
1501 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1); // avg 115 ns per line
1502 if (unlikely(!*time_field)) {
1503 truncated_line(linenum, line);
1504 return;
1505 }
1506 }
1507
1508 /* we have the field TIME_FIELD starting at <time_field>. We'll
1509 * parse the 5 timers to detect errors, it takes avg 55 ns per line.
1510 */
1511 e = time_field; err = 0; f = 0;
1512 while (!SEP(*e)) {
1513 array[f] = str2ic(e);
1514 if (array[f] < 0) {
1515 array[f] = -1;
1516 err = 1;
1517 }
1518 if (++f == 5)
1519 break;
1520 SKIP_CHAR(e, '/');
1521 }
1522 if (f < 5) {
1523 parse_err++;
1524 return;
1525 }
1526
1527 /* OK we have our timers in array[3], and err is >0 if at
1528 * least one -1 was seen. <e> points to the first char of
1529 * the last timer. Let's prepare a new node with that.
1530 */
1531 if (unlikely(!ustat))
1532 ustat = calloc(1, sizeof(*ustat));
1533
1534 ustat->nb_err = err;
1535 ustat->nb_req = 1;
1536
1537 /* use array[4] = total time in case of error */
1538 ustat->total_time = (array[3] >= 0) ? array[3] : array[4];
1539 ustat->total_time_ok = (array[3] >= 0) ? array[3] : 0;
1540
Baptiste61aaad02012-09-08 23:10:03 +02001541 e = field_start(e, BYTES_SENT_FIELD - TIME_FIELD + 1);
1542 val = str2ic(e);
1543 ustat->total_bytes_sent = val;
1544
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001545 /* the line may be truncated because of a bad request or anything like this,
1546 * without a method. Also, if it does not begin with an quote, let's skip to
1547 * the next field because it's a capture. Let's fall back to the "method" itself
1548 * if there's nothing else.
1549 */
Baptiste61aaad02012-09-08 23:10:03 +02001550 e = field_start(e, METH_FIELD - BYTES_SENT_FIELD + 1);
Willy Tarreau61a40c72011-09-06 08:11:27 +02001551 while (*e != '"' && *e) {
1552 /* Note: some syslog servers escape quotes ! */
1553 if (*e == '\\' && e[1] == '"')
1554 break;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001555 e = field_start(e, 2);
Willy Tarreau61a40c72011-09-06 08:11:27 +02001556 }
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001557
1558 if (unlikely(!*e)) {
1559 truncated_line(linenum, line);
1560 return;
1561 }
1562
1563 b = field_start(e, URL_FIELD - METH_FIELD + 1); // avg 40 ns per line
1564 if (!*b)
1565 b = e;
1566
1567 /* stop at end of field or first ';' or '?', takes avg 64 ns per line */
1568 e = b;
1569 do {
Willy Tarreau14389e72011-07-10 22:11:17 +02001570 if (*e == ' ' || *e == '?' || *e == ';') {
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001571 *(char *)e = 0;
1572 break;
1573 }
1574 e++;
1575 } while (*e);
1576
1577 /* now instead of copying the URL for a simple lookup, we'll link
1578 * to it from the node we're trying to insert. If it returns a
1579 * different value, it was already there. Otherwise we just have
1580 * to dynamically realloc an entry using strdup().
1581 */
1582 ustat->node.url.key = (char *)b;
1583 ebpt_old = ebis_insert(&timers[0], &ustat->node.url);
1584
1585 if (ebpt_old != &ustat->node.url) {
1586 struct url_stat *ustat_old;
1587 /* node was already there, let's update previous one */
1588 ustat_old = container_of(ebpt_old, struct url_stat, node.url);
1589 ustat_old->nb_req ++;
1590 ustat_old->nb_err += ustat->nb_err;
1591 ustat_old->total_time += ustat->total_time;
1592 ustat_old->total_time_ok += ustat->total_time_ok;
Baptiste61aaad02012-09-08 23:10:03 +02001593 ustat_old->total_bytes_sent += ustat->total_bytes_sent;
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001594 } else {
1595 ustat->url = ustat->node.url.key = strdup(ustat->node.url.key);
1596 ustat = NULL; /* node was used */
1597 }
1598}
1599
Willy Tarreau7cf479c2013-02-16 23:49:04 +01001600void filter_count_ip(const char *source_field, const char *accept_field, const char *time_field, struct timer **tptr)
1601{
1602 struct url_stat *ustat = NULL;
1603 struct ebpt_node *ebpt_old;
1604 const char *b, *e;
1605 int f, err, array[5];
1606 int val;
1607
1608 /* let's collect the response time */
1609 if (!time_field) {
1610 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1); // avg 115 ns per line
1611 if (unlikely(!*time_field)) {
1612 truncated_line(linenum, line);
1613 return;
1614 }
1615 }
1616
1617 /* we have the field TIME_FIELD starting at <time_field>. We'll
1618 * parse the 5 timers to detect errors, it takes avg 55 ns per line.
1619 */
1620 e = time_field; err = 0; f = 0;
1621 while (!SEP(*e)) {
1622 if (f == 0 || f == 4) {
1623 array[f] = str2ic(e);
1624 if (array[f] < 0) {
1625 array[f] = -1;
1626 err = 1;
1627 }
1628 }
1629 if (++f == 5)
1630 break;
1631 SKIP_CHAR(e, '/');
1632 }
1633 if (f < 5) {
1634 parse_err++;
1635 return;
1636 }
1637
1638 /* OK we have our timers in array[0], and err is >0 if at
1639 * least one -1 was seen. <e> points to the first char of
1640 * the last timer. Let's prepare a new node with that.
1641 */
1642 if (unlikely(!ustat))
1643 ustat = calloc(1, sizeof(*ustat));
1644
1645 ustat->nb_err = err;
1646 ustat->nb_req = 1;
1647
1648 /* use array[4] = total time in case of error */
1649 ustat->total_time = (array[0] >= 0) ? array[0] : array[4];
1650 ustat->total_time_ok = (array[0] >= 0) ? array[0] : 0;
1651
1652 e = field_start(e, BYTES_SENT_FIELD - TIME_FIELD + 1);
1653 val = str2ic(e);
1654 ustat->total_bytes_sent = val;
1655
1656 /* the source might be IPv4 or IPv6, so we always strip the port by
1657 * removing the last colon.
1658 */
1659 b = source_field;
1660 e = field_stop(b + 1);
1661 while (e > b && e[-1] != ':')
1662 e--;
1663 *(char *)(e - 1) = '\0';
1664
1665 /* now instead of copying the src for a simple lookup, we'll link
1666 * to it from the node we're trying to insert. If it returns a
1667 * different value, it was already there. Otherwise we just have
1668 * to dynamically realloc an entry using strdup(). We're using the
1669 * <url> field of the node to store the source address.
1670 */
1671 ustat->node.url.key = (char *)b;
1672 ebpt_old = ebis_insert(&timers[0], &ustat->node.url);
1673
1674 if (ebpt_old != &ustat->node.url) {
1675 struct url_stat *ustat_old;
1676 /* node was already there, let's update previous one */
1677 ustat_old = container_of(ebpt_old, struct url_stat, node.url);
1678 ustat_old->nb_req ++;
1679 ustat_old->nb_err += ustat->nb_err;
1680 ustat_old->total_time += ustat->total_time;
1681 ustat_old->total_time_ok += ustat->total_time_ok;
1682 ustat_old->total_bytes_sent += ustat->total_bytes_sent;
1683 } else {
1684 ustat->url = ustat->node.url.key = strdup(ustat->node.url.key);
1685 ustat = NULL; /* node was used */
1686 }
1687}
1688
Willy Tarreaua2b39fb2011-07-10 21:39:35 +02001689void filter_graphs(const char *accept_field, const char *time_field, struct timer **tptr)
1690{
1691 struct timer *t2;
1692 const char *e, *p;
1693 int f, err, array[5];
1694
1695 if (!time_field) {
1696 time_field = field_start(accept_field, TIME_FIELD - ACCEPT_FIELD + 1);
1697 if (unlikely(!*time_field)) {
1698 truncated_line(linenum, line);
1699 return;
1700 }
1701 }
1702
1703 e = field_stop(time_field + 1);
1704 /* we have field TIME_FIELD in [time_field]..[e-1] */
1705
1706 p = time_field;
1707 err = 0;
1708 f = 0;
1709 while (!SEP(*p)) {
1710 array[f] = str2ic(p);
1711 if (array[f] < 0) {
1712 array[f] = -1;
1713 err = 1;
1714 }
1715 if (++f == 5)
1716 break;
1717 SKIP_CHAR(p, '/');
1718 }
1719
1720 if (unlikely(f < 5)) {
1721 parse_err++;
1722 return;
1723 }
1724
1725 /* if we find at least one negative time, we count one error
1726 * with a time equal to the total session time. This will
1727 * emphasize quantum timing effects associated to known
1728 * timeouts. Note that on some buggy machines, it is possible
1729 * that the total time is negative, hence the reason to reset
1730 * it.
1731 */
1732
1733 if (filter & FILT_GRAPH_TIMERS) {
1734 if (err) {
1735 if (array[4] < 0)
1736 array[4] = -1;
1737 t2 = insert_timer(&timers[0], tptr, array[4]); // total time
1738 t2->count++;
1739 } else {
1740 int v;
1741
1742 t2 = insert_timer(&timers[1], tptr, array[0]); t2->count++; // req
1743 t2 = insert_timer(&timers[2], tptr, array[2]); t2->count++; // conn
1744 t2 = insert_timer(&timers[3], tptr, array[3]); t2->count++; // resp
1745
1746 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
1747 if (v < 0 && !(filter & FILT_QUIET))
1748 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
1749 line, array[0], array[1], array[2], array[3], array[4], v);
1750 t2 = insert_timer(&timers[4], tptr, v); t2->count++;
1751 lines_out++;
1752 }
1753 } else { /* percentile */
1754 if (err) {
1755 if (array[4] < 0)
1756 array[4] = -1;
1757 t2 = insert_value(&timers[0], tptr, array[4]); // total time
1758 t2->count++;
1759 } else {
1760 int v;
1761
1762 t2 = insert_value(&timers[1], tptr, array[0]); t2->count++; // req
1763 t2 = insert_value(&timers[2], tptr, array[2]); t2->count++; // conn
1764 t2 = insert_value(&timers[3], tptr, array[3]); t2->count++; // resp
1765
1766 v = array[4] - array[0] - array[1] - array[2] - array[3]; // data time
1767 if (v < 0 && !(filter & FILT_QUIET))
1768 fprintf(stderr, "ERR: %s (%d %d %d %d %d => %d)\n",
1769 line, array[0], array[1], array[2], array[3], array[4], v);
1770 t2 = insert_value(&timers[4], tptr, v); t2->count++;
1771 lines_out++;
1772 }
1773 }
1774}
1775
1776
Willy Tarreau72c28532009-01-22 18:56:50 +01001777/*
1778 * Local variables:
1779 * c-indent-level: 8
1780 * c-basic-offset: 8
1781 * End:
1782 */