~ubuntu-branches/debian/stretch/haproxy/stretch

« back to all changes in this revision

Viewing changes to contrib/halog/halog.c

  • Committer: Bazaar Package Importer
  • Author(s): Christo Buschek
  • Date: 2011-03-11 12:41:59 UTC
  • mfrom: (1.1.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20110311124159-9foyp4juf1ilqipo
Tags: 1.4.13-1
* New maintainer upload (Closes: #615246)
* New upstream release
* Standards-version goes 3.9.1 (no change)
* Added patch bashism (Closes: #581109)
* Added a README.source file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 * haproxy log time reporter
 
2
 * haproxy log statistics reporter
3
3
 *
4
4
 * Copyright 2000-2010 Willy Tarreau <w@1wt.eu>
5
5
 *
10
10
 *
11
11
 */
12
12
 
13
 
/*
14
 
 * gcc -O2 -o halog2 halog2.c -Iinclude src/ebtree.c src/eb32tree.c fgets2.c
15
 
 *
16
 
 * Usage:
17
 
 *    $0 [ min_delay [ min_count [ field_shift ]]] < haproxy.log
18
 
 *    Note: if min_delay < 0, it only outputs lines with status codes 5xx.
19
 
 */
20
 
 
21
13
#include <errno.h>
22
14
#include <fcntl.h>
23
15
#include <stdio.h>
28
20
#include <ctype.h>
29
21
 
30
22
#include <eb32tree.h>
 
23
#include <eb64tree.h>
 
24
#include <ebistree.h>
31
25
#include <ebsttree.h>
32
26
 
33
27
#define SOURCE_FIELD 5
35
29
#define SERVER_FIELD 8
36
30
#define TIME_FIELD 9
37
31
#define STATUS_FIELD 10
 
32
#define TERM_CODES_FIELD 14
38
33
#define CONN_FIELD 15
 
34
#define METH_FIELD 17
 
35
#define URL_FIELD 18
39
36
#define MAXLINE 16384
40
37
#define QBITS 4
41
38
 
60
57
        /* don't put anything else here, the server name will be there */
61
58
};
62
59
 
 
60
struct url_stat {
 
61
        union {
 
62
                struct ebpt_node url;
 
63
                struct eb64_node val;
 
64
        } node;
 
65
        char *url;
 
66
        unsigned long long total_time;    /* sum(all reqs' times) */
 
67
        unsigned long long total_time_ok; /* sum(all OK reqs' times) */
 
68
        unsigned int nb_err, nb_req;
 
69
};
 
70
 
63
71
#define FILT_COUNT_ONLY         0x01
64
72
#define FILT_INVERT             0x02
65
73
#define FILT_QUIET              0x04
75
83
 
76
84
#define FILT_COUNT_STATUS      0x800
77
85
#define FILT_COUNT_SRV_STATUS 0x1000
 
86
#define FILT_COUNT_TERM_CODES 0x2000
 
87
 
 
88
#define FILT_COUNT_URL_ONLY  0x004000
 
89
#define FILT_COUNT_URL_COUNT 0x008000
 
90
#define FILT_COUNT_URL_ERR   0x010000
 
91
#define FILT_COUNT_URL_TTOT  0x020000
 
92
#define FILT_COUNT_URL_TAVG  0x040000
 
93
#define FILT_COUNT_URL_TTOTO 0x080000
 
94
#define FILT_COUNT_URL_TAVGO 0x100000
 
95
#define FILT_COUNT_URL_ANY   (FILT_COUNT_URL_ONLY|FILT_COUNT_URL_COUNT|FILT_COUNT_URL_ERR| \
 
96
                              FILT_COUNT_URL_TTOT|FILT_COUNT_URL_TAVG|FILT_COUNT_URL_TTOTO|FILT_COUNT_URL_TAVGO)
78
97
 
79
98
unsigned int filter = 0;
80
99
unsigned int filter_invert = 0;
86
105
{
87
106
        fprintf(stderr,
88
107
                "%s"
89
 
                "Usage: halog [-q] [-c] [-v] [-gt] [-pct] [-st] [-srv] [-s <skip>] [-e|-E] [-rt|-RT <time>] [-ad <delay>] [-ac <count>] < file.log\n"
 
108
                "Usage: halog [-q] [-c] [-v] {-gt|-pct|-st|-tc|-srv|-u|-uc|-ue|-ua|-ut|-uao|-uto}\n"
 
109
                "       [-s <skip>] [-e|-E] [-rt|-RT <time>] [-ad <delay>] [-ac <count>] < file.log\n"
90
110
                "\n",
91
111
                msg ? msg : ""
92
112
                );
367
387
        int f, tot, last, linenum, err, parse_err;
368
388
        struct timer *t = NULL, *t2;
369
389
        struct eb32_node *n;
 
390
        struct url_stat *ustat = NULL;
 
391
        struct ebpt_node *ebpt_old;
370
392
        int val, test;
371
393
        int array[5];
372
394
        int filter_acc_delay = 0, filter_acc_count = 0;
425
447
                        filter |= FILT_COUNT_STATUS;
426
448
                else if (strcmp(argv[0], "-srv") == 0)
427
449
                        filter |= FILT_COUNT_SRV_STATUS;
 
450
                else if (strcmp(argv[0], "-tc") == 0)
 
451
                        filter |= FILT_COUNT_TERM_CODES;
 
452
                else if (strcmp(argv[0], "-u") == 0)
 
453
                        filter |= FILT_COUNT_URL_ONLY;
 
454
                else if (strcmp(argv[0], "-uc") == 0)
 
455
                        filter |= FILT_COUNT_URL_COUNT;
 
456
                else if (strcmp(argv[0], "-ue") == 0)
 
457
                        filter |= FILT_COUNT_URL_ERR;
 
458
                else if (strcmp(argv[0], "-ua") == 0)
 
459
                        filter |= FILT_COUNT_URL_TAVG;
 
460
                else if (strcmp(argv[0], "-ut") == 0)
 
461
                        filter |= FILT_COUNT_URL_TTOT;
 
462
                else if (strcmp(argv[0], "-uao") == 0)
 
463
                        filter |= FILT_COUNT_URL_TAVGO;
 
464
                else if (strcmp(argv[0], "-uto") == 0)
 
465
                        filter |= FILT_COUNT_URL_TTOTO;
428
466
                else if (strcmp(argv[0], "-o") == 0) {
429
467
                        if (output_file)
430
468
                                die("Fatal: output file name already specified.\n");
464
502
                        }
465
503
 
466
504
                        e = field_stop(b + 1);
467
 
                        /* we have field TIME_FIELD in [b]..[e-1] */
 
505
                        /* we have field TIME_FIELD in [b]..[e-1], let's check only the response time */
468
506
 
469
507
                        p = b;
470
508
                        err = 0;
471
 
                        for (f = 0; f < 4 && *p; f++) {
 
509
                        f = 0;
 
510
                        while (*p) {
472
511
                                tps = str2ic(p);
473
512
                                if (tps < 0) {
474
513
                                        tps = -1;
475
514
                                        err = 1;
476
515
                                }
477
 
 
 
516
                                if (++f == 4)
 
517
                                        break;
478
518
                                SKIP_CHAR(p, '/');
479
519
                        }
480
520
 
539
579
 
540
580
                        p = b;
541
581
                        err = 0;
542
 
                        for (f = 0; f < 5 && *p; f++) {
 
582
                        f = 0;
 
583
                        while (*p) {
543
584
                                array[f] = str2ic(p);
544
585
                                if (array[f] < 0) {
545
586
                                        array[f] = -1;
546
587
                                        err = 1;
547
588
                                }
548
 
 
 
589
                                if (++f == 5)
 
590
                                        break;
549
591
                                SKIP_CHAR(p, '/');
550
592
                        }
551
593
 
607
649
                }
608
650
 
609
651
                if (unlikely(filter & FILT_COUNT_STATUS)) {
610
 
                        b = field_start(line, STATUS_FIELD + skip_fields);
 
652
                        /* first, let's ensure that the line is a traffic line (beginning
 
653
                         * with an IP address)
 
654
                         */
 
655
                        b = field_start(line, SOURCE_FIELD + skip_fields);
 
656
                        if (*b < '0' || *b > '9') {
 
657
                                parse_err++;
 
658
                                continue;
 
659
                        }
 
660
 
 
661
                        b = field_start(b, STATUS_FIELD - SOURCE_FIELD + 1);
611
662
                        if (!*b) {
612
663
                                truncated_line(linenum, line);
613
664
                                continue;
619
670
                        continue;
620
671
                }
621
672
 
 
673
                if (unlikely(filter & FILT_COUNT_TERM_CODES)) {
 
674
                        /* first, let's ensure that the line is a traffic line (beginning
 
675
                         * with an IP address)
 
676
                         */
 
677
                        b = field_start(line, SOURCE_FIELD + skip_fields);
 
678
                        if (*b < '0' || *b > '9') {
 
679
                                parse_err++;
 
680
                                continue;
 
681
                        }
 
682
 
 
683
                        b = field_start(b, TERM_CODES_FIELD - SOURCE_FIELD + 1);
 
684
                        if (!*b) {
 
685
                                truncated_line(linenum, line);
 
686
                                continue;
 
687
                        }
 
688
                        val = 256 * b[0] + b[1];
 
689
 
 
690
                        t2 = insert_value(&timers[0], &t, val);
 
691
                        t2->count++;
 
692
                        continue;
 
693
                }
 
694
 
622
695
                if (unlikely(filter & FILT_COUNT_SRV_STATUS)) {
623
696
                        char *srv_name;
624
697
                        struct ebmb_node *srv_node;
671
744
 
672
745
                        p = b;
673
746
                        err = 0;
674
 
                        for (f = 0; f < 5 && *p; f++) {
 
747
                        f = 0;
 
748
                        while (*p) {
675
749
                                array[f] = str2ic(p);
676
750
                                if (array[f] < 0) {
677
751
                                        array[f] = -1;
678
752
                                        err = 1;
679
753
                                }
680
 
 
 
754
                                if (++f == 5)
 
755
                                        break;
681
756
                                SKIP_CHAR(p, '/');
682
757
                        }
683
758
 
717
792
                        continue;
718
793
                }
719
794
 
 
795
                if (unlikely(filter & FILT_COUNT_URL_ANY)) {
 
796
                        /* first, let's ensure that the line is a traffic line (beginning
 
797
                         * with an IP address)
 
798
                         */
 
799
                        b = field_start(line, SOURCE_FIELD + skip_fields); // avg 95 ns per line
 
800
                        if (*b < '0' || *b > '9') {
 
801
                                parse_err++;
 
802
                                continue;
 
803
                        }
 
804
 
 
805
                        /* let's collect the response time */
 
806
                        b = field_start(field_stop(b + 1), TIME_FIELD - SOURCE_FIELD);  // avg 115 ns per line
 
807
                        if (!*b) {
 
808
                                truncated_line(linenum, line);
 
809
                                continue;
 
810
                        }
 
811
 
 
812
                        /* we have the field TIME_FIELD starting at <b>. We'll
 
813
                         * parse the 5 timers to detect errors, it takes avg 55 ns per line.
 
814
                         */
 
815
                        e = b; err = 0; f = 0;
 
816
                        while (*e) {
 
817
                                array[f] = str2ic(e);
 
818
                                if (array[f] < 0) {
 
819
                                        array[f] = -1;
 
820
                                        err = 1;
 
821
                                }
 
822
                                if (++f == 5)
 
823
                                        break;
 
824
                                SKIP_CHAR(e, '/');
 
825
                        }
 
826
                        if (f < 5) {
 
827
                                parse_err++;
 
828
                                continue;
 
829
                        }
 
830
 
 
831
                        /* OK we have our timers in array[3], and err is >0 if at
 
832
                         * least one -1 was seen. <e> points to the first char of
 
833
                         * the last timer. Let's prepare a new node with that.
 
834
                         */
 
835
                        if (unlikely(!ustat))
 
836
                                ustat = calloc(1, sizeof(*ustat));
 
837
 
 
838
                        ustat->nb_err = err;
 
839
                        ustat->nb_req = 1;
 
840
 
 
841
                        /* use array[4] = total time in case of error */
 
842
                        ustat->total_time = (array[3] >= 0) ? array[3] : array[4];
 
843
                        ustat->total_time_ok = (array[3] >= 0) ? array[3] : 0;
 
844
 
 
845
                        /* the line may be truncated because of a bad request or anything like this,
 
846
                         * without a method. Also, if it does not begin with an quote, let's skip to
 
847
                         * the next field because it's a capture. Let's fall back to the "method" itself
 
848
                         * if there's nothing else.
 
849
                         */
 
850
                        e = field_start(e, METH_FIELD - TIME_FIELD + 1); // avg 100 ns per line
 
851
                        while (*e != '"' && *e)
 
852
                                e = field_start(e, 2);
 
853
 
 
854
                        if (!*e) {
 
855
                                truncated_line(linenum, line);
 
856
                                continue;
 
857
                        }
 
858
 
 
859
                        b = field_start(e, URL_FIELD - METH_FIELD + 1); // avg 40 ns per line
 
860
                        if (!*b)
 
861
                                b = e;
 
862
 
 
863
                        /* stop at end of field or first ';' or '?', takes avg 64 ns per line */
 
864
                        e = b;
 
865
                        do {
 
866
                                if (*e == ' ' || *e == '?' || *e == ';' || *e == '\t') {
 
867
                                        *(char *)e = 0;
 
868
                                        break;
 
869
                                }
 
870
                                e++;
 
871
                        } while (*e);
 
872
 
 
873
                        /* now instead of copying the URL for a simple lookup, we'll link
 
874
                         * to it from the node we're trying to insert. If it returns a
 
875
                         * different value, it was already there. Otherwise we just have
 
876
                         * to dynamically realloc an entry using strdup().
 
877
                         */
 
878
                        ustat->node.url.key = (char *)b;
 
879
                        ebpt_old = ebis_insert(&timers[0], &ustat->node.url);
 
880
 
 
881
                        if (ebpt_old != &ustat->node.url) {
 
882
                                struct url_stat *ustat_old;
 
883
                                /* node was already there, let's update previous one */
 
884
                                ustat_old = container_of(ebpt_old, struct url_stat, node.url);
 
885
                                ustat_old->nb_req ++;
 
886
                                ustat_old->nb_err += ustat->nb_err;
 
887
                                ustat_old->total_time += ustat->total_time;
 
888
                                ustat_old->total_time_ok += ustat->total_time_ok;
 
889
                        } else {
 
890
                                ustat->url = ustat->node.url.key = strdup(ustat->node.url.key);
 
891
                                ustat = NULL; /* node was used */
 
892
                        }
 
893
 
 
894
                        continue;
 
895
                }
 
896
 
720
897
                /* all other cases mean we just want to count lines */
721
898
                tot++;
722
899
                if (unlikely(!(filter & FILT_COUNT_ONLY)))
870
1047
                        tot++;
871
1048
                }
872
1049
        }
 
1050
        else if (filter & FILT_COUNT_TERM_CODES) {
 
1051
                /* output all statuses in the form of <code> <occurrences> */
 
1052
                n = eb32_first(&timers[0]);
 
1053
                while (n) {
 
1054
                        t = container_of(n, struct timer, node);
 
1055
                        printf("%c%c %d\n", (n->key >> 8), (n->key) & 255, t->count);
 
1056
                        n = eb32_next(n);
 
1057
                }
 
1058
        }
 
1059
        else if (unlikely(filter & FILT_COUNT_URL_ANY)) {
 
1060
                char *srv_name;
 
1061
                struct eb_node *node, *next;
 
1062
 
 
1063
                if (!(filter & FILT_COUNT_URL_ONLY)) {
 
1064
                        /* we have to sort on another criterion. We'll use timers[1] for the
 
1065
                         * destination tree.
 
1066
                         */
 
1067
 
 
1068
                        timers[1] = EB_ROOT; /* reconfigure to accept duplicates */
 
1069
                        for (node = eb_first(&timers[0]); node; node = next) {
 
1070
                                next = eb_next(node);
 
1071
                                eb_delete(node);
 
1072
 
 
1073
                                ustat = container_of(node, struct url_stat, node.url.node);
 
1074
 
 
1075
                                if (filter & FILT_COUNT_URL_COUNT)
 
1076
                                        ustat->node.val.key = ustat->nb_req;
 
1077
                                else if (filter & FILT_COUNT_URL_ERR)
 
1078
                                        ustat->node.val.key = ustat->nb_err;
 
1079
                                else if (filter & FILT_COUNT_URL_TTOT)
 
1080
                                        ustat->node.val.key = ustat->total_time;
 
1081
                                else if (filter & FILT_COUNT_URL_TAVG)
 
1082
                                        ustat->node.val.key = ustat->nb_req ? ustat->total_time / ustat->nb_req : 0;
 
1083
                                else if (filter & FILT_COUNT_URL_TTOTO)
 
1084
                                        ustat->node.val.key = ustat->total_time_ok;
 
1085
                                else if (filter & FILT_COUNT_URL_TAVGO)
 
1086
                                        ustat->node.val.key = (ustat->nb_req - ustat->nb_err) ? ustat->total_time_ok / (ustat->nb_req - ustat->nb_err) : 0;
 
1087
                                else
 
1088
                                        ustat->node.val.key = 0;
 
1089
 
 
1090
                                eb64_insert(&timers[1], &ustat->node.val);
 
1091
                        }
 
1092
                        /* switch trees */
 
1093
                        timers[0] = timers[1];
 
1094
                }
 
1095
 
 
1096
                printf("#req err ttot tavg oktot okavg url\n");
 
1097
 
 
1098
                /* scan the tree in its reverse sorting order */
 
1099
                node = eb_last(&timers[0]);
 
1100
                while (node) {
 
1101
                        ustat = container_of(node, struct url_stat, node.url.node);
 
1102
                        printf("%d %d %Ld %Ld %Ld %Ld %s\n",
 
1103
                               ustat->nb_req,
 
1104
                               ustat->nb_err,
 
1105
                               ustat->total_time,
 
1106
                               ustat->nb_req ? ustat->total_time / ustat->nb_req : 0,
 
1107
                               ustat->total_time_ok,
 
1108
                               (ustat->nb_req - ustat->nb_err) ? ustat->total_time_ok / (ustat->nb_req - ustat->nb_err) : 0,
 
1109
                               ustat->url);
 
1110
 
 
1111
                        node = eb_prev(node);
 
1112
                        tot++;
 
1113
                }
 
1114
        }
873
1115
 
874
1116
 empty:
875
1117
        if (!(filter & FILT_QUIET))