github.com/netdata/go.d.plugin@v0.58.1/modules/weblog/weblog_test.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package weblog
     4  
     5  import (
     6  	"bytes"
     7  	"fmt"
     8  	"os"
     9  	"reflect"
    10  	"strconv"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/netdata/go.d.plugin/pkg/logs"
    15  	"github.com/netdata/go.d.plugin/pkg/metrics"
    16  
    17  	"github.com/netdata/go.d.plugin/agent/module"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  var (
    23  	testCommonLog, _          = os.ReadFile("testdata/common.log")
    24  	testFullLog, _            = os.ReadFile("testdata/full.log")
    25  	testCustomLog, _          = os.ReadFile("testdata/custom.log")
    26  	testCustomTimeFieldLog, _ = os.ReadFile("testdata/custom_time_fields.log")
    27  	testIISLog, _             = os.ReadFile("testdata/u_ex221107.log")
    28  )
    29  
    30  func Test_readTestData(t *testing.T) {
    31  	assert.NotNil(t, testFullLog)
    32  	assert.NotNil(t, testCommonLog)
    33  	assert.NotNil(t, testCustomLog)
    34  	assert.NotNil(t, testCustomTimeFieldLog)
    35  	assert.NotNil(t, testIISLog)
    36  }
    37  
    38  func TestNew(t *testing.T) {
    39  	assert.Implements(t, (*module.Module)(nil), New())
    40  }
    41  
    42  func TestWebLog_Init(t *testing.T) {
    43  	weblog := New()
    44  
    45  	assert.True(t, weblog.Init())
    46  }
    47  
    48  func TestWebLog_Init_ErrorOnCreatingURLPatterns(t *testing.T) {
    49  	weblog := New()
    50  	weblog.URLPatterns = []userPattern{{Match: "* !*"}}
    51  
    52  	assert.False(t, weblog.Init())
    53  }
    54  
    55  func TestWebLog_Init_ErrorOnCreatingCustomFields(t *testing.T) {
    56  	weblog := New()
    57  	weblog.CustomFields = []customField{{Patterns: []userPattern{{Name: "p1", Match: "* !*"}}}}
    58  
    59  	assert.False(t, weblog.Init())
    60  }
    61  
    62  func TestWebLog_Check(t *testing.T) {
    63  	weblog := New()
    64  	defer weblog.Cleanup()
    65  	weblog.Path = "testdata/common.log"
    66  	require.True(t, weblog.Init())
    67  
    68  	assert.True(t, weblog.Check())
    69  }
    70  
    71  func TestWebLog_Check_ErrorOnCreatingLogReaderNoLogFile(t *testing.T) {
    72  	weblog := New()
    73  	defer weblog.Cleanup()
    74  	weblog.Path = "testdata/not_exists.log"
    75  	require.True(t, weblog.Init())
    76  
    77  	assert.False(t, weblog.Check())
    78  }
    79  
    80  func TestWebLog_Check_ErrorOnCreatingParserUnknownFormat(t *testing.T) {
    81  	weblog := New()
    82  	defer weblog.Cleanup()
    83  	weblog.Path = "testdata/custom.log"
    84  	require.True(t, weblog.Init())
    85  
    86  	assert.False(t, weblog.Check())
    87  }
    88  
    89  func TestWebLog_Check_ErrorOnCreatingParserEmptyLine(t *testing.T) {
    90  	weblog := New()
    91  	defer weblog.Cleanup()
    92  	weblog.Path = "testdata/custom.log"
    93  	weblog.Parser.LogType = logs.TypeCSV
    94  	weblog.Parser.CSV.Format = "$one $two"
    95  	require.True(t, weblog.Init())
    96  
    97  	assert.False(t, weblog.Check())
    98  }
    99  
   100  func TestWebLog_Charts(t *testing.T) {
   101  	weblog := New()
   102  	defer weblog.Cleanup()
   103  	weblog.Path = "testdata/common.log"
   104  	require.True(t, weblog.Init())
   105  	require.True(t, weblog.Check())
   106  
   107  	assert.NotNil(t, weblog.Charts())
   108  }
   109  
   110  func TestWebLog_Cleanup(t *testing.T) {
   111  	New().Cleanup()
   112  }
   113  
   114  func TestWebLog_Collect(t *testing.T) {
   115  	weblog := prepareWebLogCollectFull(t)
   116  
   117  	//m := weblog.Collect()
   118  	//l := make([]string, 0)
   119  	//for k := range m {
   120  	//	l = append(l, k)
   121  	//}
   122  	//sort.Strings(l)
   123  	//for _, value := range l {
   124  	//	fmt.Println(fmt.Sprintf("\"%s\": %d,", value, m[value]))
   125  	//}
   126  
   127  	expected := map[string]int64{
   128  		"bytes_received":                                          1374096,
   129  		"bytes_sent":                                              1373185,
   130  		"custom_field_drink_beer":                                 221,
   131  		"custom_field_drink_wine":                                 231,
   132  		"custom_field_side_dark":                                  231,
   133  		"custom_field_side_light":                                 221,
   134  		"req_http_scheme":                                         218,
   135  		"req_https_scheme":                                        234,
   136  		"req_ipv4":                                                275,
   137  		"req_ipv6":                                                177,
   138  		"req_method_GET":                                          156,
   139  		"req_method_HEAD":                                         150,
   140  		"req_method_POST":                                         146,
   141  		"req_port_80":                                             96,
   142  		"req_port_81":                                             100,
   143  		"req_port_82":                                             84,
   144  		"req_port_83":                                             85,
   145  		"req_port_84":                                             87,
   146  		"req_proc_time_avg":                                       247,
   147  		"req_proc_time_count":                                     452,
   148  		"req_proc_time_hist_bucket_1":                             452,
   149  		"req_proc_time_hist_bucket_10":                            452,
   150  		"req_proc_time_hist_bucket_11":                            452,
   151  		"req_proc_time_hist_bucket_2":                             452,
   152  		"req_proc_time_hist_bucket_3":                             452,
   153  		"req_proc_time_hist_bucket_4":                             452,
   154  		"req_proc_time_hist_bucket_5":                             452,
   155  		"req_proc_time_hist_bucket_6":                             452,
   156  		"req_proc_time_hist_bucket_7":                             452,
   157  		"req_proc_time_hist_bucket_8":                             452,
   158  		"req_proc_time_hist_bucket_9":                             452,
   159  		"req_proc_time_hist_count":                                452,
   160  		"req_proc_time_hist_sum":                                  111927,
   161  		"req_proc_time_max":                                       499,
   162  		"req_proc_time_min":                                       2,
   163  		"req_proc_time_sum":                                       111927,
   164  		"req_ssl_cipher_suite_AES256-SHA":                         101,
   165  		"req_ssl_cipher_suite_DHE-RSA-AES256-SHA":                 111,
   166  		"req_ssl_cipher_suite_ECDHE-RSA-AES256-SHA":               127,
   167  		"req_ssl_cipher_suite_PSK-RC4-SHA":                        113,
   168  		"req_ssl_proto_SSLv2":                                     74,
   169  		"req_ssl_proto_SSLv3":                                     57,
   170  		"req_ssl_proto_TLSv1":                                     76,
   171  		"req_ssl_proto_TLSv1.1":                                   87,
   172  		"req_ssl_proto_TLSv1.2":                                   73,
   173  		"req_ssl_proto_TLSv1.3":                                   85,
   174  		"req_type_bad":                                            49,
   175  		"req_type_error":                                          0,
   176  		"req_type_redirect":                                       119,
   177  		"req_type_success":                                        284,
   178  		"req_unmatched":                                           48,
   179  		"req_url_ptn_com":                                         120,
   180  		"req_url_ptn_net":                                         116,
   181  		"req_url_ptn_not_match":                                   0,
   182  		"req_url_ptn_org":                                         113,
   183  		"req_version_1.1":                                         168,
   184  		"req_version_2":                                           143,
   185  		"req_version_2.0":                                         141,
   186  		"req_vhost_198.51.100.1":                                  81,
   187  		"req_vhost_2001:db8:1ce::1":                               100,
   188  		"req_vhost_localhost":                                     102,
   189  		"req_vhost_test.example.com":                              87,
   190  		"req_vhost_test.example.org":                              82,
   191  		"requests":                                                500,
   192  		"resp_1xx":                                                110,
   193  		"resp_2xx":                                                128,
   194  		"resp_3xx":                                                119,
   195  		"resp_4xx":                                                95,
   196  		"resp_5xx":                                                0,
   197  		"resp_code_100":                                           60,
   198  		"resp_code_101":                                           50,
   199  		"resp_code_200":                                           58,
   200  		"resp_code_201":                                           70,
   201  		"resp_code_300":                                           58,
   202  		"resp_code_301":                                           61,
   203  		"resp_code_400":                                           49,
   204  		"resp_code_401":                                           46,
   205  		"uniq_ipv4":                                               3,
   206  		"uniq_ipv6":                                               2,
   207  		"upstream_resp_time_avg":                                  255,
   208  		"upstream_resp_time_count":                                452,
   209  		"upstream_resp_time_hist_bucket_1":                        452,
   210  		"upstream_resp_time_hist_bucket_10":                       452,
   211  		"upstream_resp_time_hist_bucket_11":                       452,
   212  		"upstream_resp_time_hist_bucket_2":                        452,
   213  		"upstream_resp_time_hist_bucket_3":                        452,
   214  		"upstream_resp_time_hist_bucket_4":                        452,
   215  		"upstream_resp_time_hist_bucket_5":                        452,
   216  		"upstream_resp_time_hist_bucket_6":                        452,
   217  		"upstream_resp_time_hist_bucket_7":                        452,
   218  		"upstream_resp_time_hist_bucket_8":                        452,
   219  		"upstream_resp_time_hist_bucket_9":                        452,
   220  		"upstream_resp_time_hist_count":                           452,
   221  		"upstream_resp_time_hist_sum":                             115615,
   222  		"upstream_resp_time_max":                                  497,
   223  		"upstream_resp_time_min":                                  7,
   224  		"upstream_resp_time_sum":                                  115615,
   225  		"url_ptn_com_bytes_received":                              379864,
   226  		"url_ptn_com_bytes_sent":                                  372669,
   227  		"url_ptn_com_req_method_GET":                              38,
   228  		"url_ptn_com_req_method_HEAD":                             39,
   229  		"url_ptn_com_req_method_POST":                             43,
   230  		"url_ptn_com_req_proc_time_avg":                           212,
   231  		"url_ptn_com_req_proc_time_count":                         120,
   232  		"url_ptn_com_req_proc_time_max":                           495,
   233  		"url_ptn_com_req_proc_time_min":                           5,
   234  		"url_ptn_com_req_proc_time_sum":                           25544,
   235  		"url_ptn_com_resp_code_100":                               12,
   236  		"url_ptn_com_resp_code_101":                               15,
   237  		"url_ptn_com_resp_code_200":                               13,
   238  		"url_ptn_com_resp_code_201":                               26,
   239  		"url_ptn_com_resp_code_300":                               16,
   240  		"url_ptn_com_resp_code_301":                               12,
   241  		"url_ptn_com_resp_code_400":                               13,
   242  		"url_ptn_com_resp_code_401":                               13,
   243  		"url_ptn_net_bytes_received":                              349988,
   244  		"url_ptn_net_bytes_sent":                                  339867,
   245  		"url_ptn_net_req_method_GET":                              51,
   246  		"url_ptn_net_req_method_HEAD":                             33,
   247  		"url_ptn_net_req_method_POST":                             32,
   248  		"url_ptn_net_req_proc_time_avg":                           260,
   249  		"url_ptn_net_req_proc_time_count":                         116,
   250  		"url_ptn_net_req_proc_time_max":                           499,
   251  		"url_ptn_net_req_proc_time_min":                           10,
   252  		"url_ptn_net_req_proc_time_sum":                           30221,
   253  		"url_ptn_net_resp_code_100":                               16,
   254  		"url_ptn_net_resp_code_101":                               12,
   255  		"url_ptn_net_resp_code_200":                               16,
   256  		"url_ptn_net_resp_code_201":                               14,
   257  		"url_ptn_net_resp_code_300":                               14,
   258  		"url_ptn_net_resp_code_301":                               17,
   259  		"url_ptn_net_resp_code_400":                               14,
   260  		"url_ptn_net_resp_code_401":                               13,
   261  		"url_ptn_not_match_bytes_received":                        0,
   262  		"url_ptn_not_match_bytes_sent":                            0,
   263  		"url_ptn_not_match_req_proc_time_avg":                     0,
   264  		"url_ptn_not_match_req_proc_time_count":                   0,
   265  		"url_ptn_not_match_req_proc_time_max":                     0,
   266  		"url_ptn_not_match_req_proc_time_min":                     0,
   267  		"url_ptn_not_match_req_proc_time_sum":                     0,
   268  		"url_ptn_org_bytes_received":                              331836,
   269  		"url_ptn_org_bytes_sent":                                  340095,
   270  		"url_ptn_org_req_method_GET":                              29,
   271  		"url_ptn_org_req_method_HEAD":                             46,
   272  		"url_ptn_org_req_method_POST":                             38,
   273  		"url_ptn_org_req_proc_time_avg":                           263,
   274  		"url_ptn_org_req_proc_time_count":                         113,
   275  		"url_ptn_org_req_proc_time_max":                           497,
   276  		"url_ptn_org_req_proc_time_min":                           2,
   277  		"url_ptn_org_req_proc_time_sum":                           29796,
   278  		"url_ptn_org_resp_code_100":                               15,
   279  		"url_ptn_org_resp_code_101":                               11,
   280  		"url_ptn_org_resp_code_200":                               20,
   281  		"url_ptn_org_resp_code_201":                               16,
   282  		"url_ptn_org_resp_code_300":                               10,
   283  		"url_ptn_org_resp_code_301":                               19,
   284  		"url_ptn_org_resp_code_400":                               13,
   285  		"url_ptn_org_resp_code_401":                               9,
   286  		"custom_time_field_random_time_field_time_avg":            230,
   287  		"custom_time_field_random_time_field_time_count":          452,
   288  		"custom_time_field_random_time_field_time_hist_bucket_1":  452,
   289  		"custom_time_field_random_time_field_time_hist_bucket_10": 452,
   290  		"custom_time_field_random_time_field_time_hist_bucket_11": 452,
   291  		"custom_time_field_random_time_field_time_hist_bucket_2":  452,
   292  		"custom_time_field_random_time_field_time_hist_bucket_3":  452,
   293  		"custom_time_field_random_time_field_time_hist_bucket_4":  452,
   294  		"custom_time_field_random_time_field_time_hist_bucket_5":  452,
   295  		"custom_time_field_random_time_field_time_hist_bucket_6":  452,
   296  		"custom_time_field_random_time_field_time_hist_bucket_7":  452,
   297  		"custom_time_field_random_time_field_time_hist_bucket_8":  452,
   298  		"custom_time_field_random_time_field_time_hist_bucket_9":  452,
   299  		"custom_time_field_random_time_field_time_hist_count":     452,
   300  		"custom_time_field_random_time_field_time_hist_sum":       103960,
   301  		"custom_time_field_random_time_field_time_max":            230,
   302  		"custom_time_field_random_time_field_time_min":            230,
   303  		"custom_time_field_random_time_field_time_sum":            103960,
   304  	}
   305  
   306  	mx := weblog.Collect()
   307  	assert.Equal(t, expected, mx)
   308  	testCharts(t, weblog, mx)
   309  }
   310  
   311  func TestWebLog_Collect_CommonLogFormat(t *testing.T) {
   312  	weblog := prepareWebLogCollectCommon(t)
   313  
   314  	expected := map[string]int64{
   315  		"bytes_received":                    0,
   316  		"bytes_sent":                        1388056,
   317  		"req_http_scheme":                   0,
   318  		"req_https_scheme":                  0,
   319  		"req_ipv4":                          283,
   320  		"req_ipv6":                          173,
   321  		"req_method_GET":                    159,
   322  		"req_method_HEAD":                   143,
   323  		"req_method_POST":                   154,
   324  		"req_proc_time_avg":                 0,
   325  		"req_proc_time_count":               0,
   326  		"req_proc_time_hist_bucket_1":       0,
   327  		"req_proc_time_hist_bucket_10":      0,
   328  		"req_proc_time_hist_bucket_11":      0,
   329  		"req_proc_time_hist_bucket_2":       0,
   330  		"req_proc_time_hist_bucket_3":       0,
   331  		"req_proc_time_hist_bucket_4":       0,
   332  		"req_proc_time_hist_bucket_5":       0,
   333  		"req_proc_time_hist_bucket_6":       0,
   334  		"req_proc_time_hist_bucket_7":       0,
   335  		"req_proc_time_hist_bucket_8":       0,
   336  		"req_proc_time_hist_bucket_9":       0,
   337  		"req_proc_time_hist_count":          0,
   338  		"req_proc_time_hist_sum":            0,
   339  		"req_proc_time_max":                 0,
   340  		"req_proc_time_min":                 0,
   341  		"req_proc_time_sum":                 0,
   342  		"req_type_bad":                      54,
   343  		"req_type_error":                    0,
   344  		"req_type_redirect":                 122,
   345  		"req_type_success":                  280,
   346  		"req_unmatched":                     44,
   347  		"req_version_1.1":                   155,
   348  		"req_version_2":                     147,
   349  		"req_version_2.0":                   154,
   350  		"requests":                          500,
   351  		"resp_1xx":                          130,
   352  		"resp_2xx":                          100,
   353  		"resp_3xx":                          122,
   354  		"resp_4xx":                          104,
   355  		"resp_5xx":                          0,
   356  		"resp_code_100":                     80,
   357  		"resp_code_101":                     50,
   358  		"resp_code_200":                     43,
   359  		"resp_code_201":                     57,
   360  		"resp_code_300":                     70,
   361  		"resp_code_301":                     52,
   362  		"resp_code_400":                     54,
   363  		"resp_code_401":                     50,
   364  		"uniq_ipv4":                         3,
   365  		"uniq_ipv6":                         2,
   366  		"upstream_resp_time_avg":            0,
   367  		"upstream_resp_time_count":          0,
   368  		"upstream_resp_time_hist_bucket_1":  0,
   369  		"upstream_resp_time_hist_bucket_10": 0,
   370  		"upstream_resp_time_hist_bucket_11": 0,
   371  		"upstream_resp_time_hist_bucket_2":  0,
   372  		"upstream_resp_time_hist_bucket_3":  0,
   373  		"upstream_resp_time_hist_bucket_4":  0,
   374  		"upstream_resp_time_hist_bucket_5":  0,
   375  		"upstream_resp_time_hist_bucket_6":  0,
   376  		"upstream_resp_time_hist_bucket_7":  0,
   377  		"upstream_resp_time_hist_bucket_8":  0,
   378  		"upstream_resp_time_hist_bucket_9":  0,
   379  		"upstream_resp_time_hist_count":     0,
   380  		"upstream_resp_time_hist_sum":       0,
   381  		"upstream_resp_time_max":            0,
   382  		"upstream_resp_time_min":            0,
   383  		"upstream_resp_time_sum":            0,
   384  	}
   385  
   386  	mx := weblog.Collect()
   387  	assert.Equal(t, expected, mx)
   388  	testCharts(t, weblog, mx)
   389  }
   390  
   391  func TestWebLog_Collect_CustomLogs(t *testing.T) {
   392  	weblog := prepareWebLogCollectCustom(t)
   393  
   394  	expected := map[string]int64{
   395  		"bytes_received":                    0,
   396  		"bytes_sent":                        0,
   397  		"custom_field_drink_beer":           52,
   398  		"custom_field_drink_wine":           40,
   399  		"custom_field_side_dark":            46,
   400  		"custom_field_side_light":           46,
   401  		"req_http_scheme":                   0,
   402  		"req_https_scheme":                  0,
   403  		"req_ipv4":                          0,
   404  		"req_ipv6":                          0,
   405  		"req_proc_time_avg":                 0,
   406  		"req_proc_time_count":               0,
   407  		"req_proc_time_hist_bucket_1":       0,
   408  		"req_proc_time_hist_bucket_10":      0,
   409  		"req_proc_time_hist_bucket_11":      0,
   410  		"req_proc_time_hist_bucket_2":       0,
   411  		"req_proc_time_hist_bucket_3":       0,
   412  		"req_proc_time_hist_bucket_4":       0,
   413  		"req_proc_time_hist_bucket_5":       0,
   414  		"req_proc_time_hist_bucket_6":       0,
   415  		"req_proc_time_hist_bucket_7":       0,
   416  		"req_proc_time_hist_bucket_8":       0,
   417  		"req_proc_time_hist_bucket_9":       0,
   418  		"req_proc_time_hist_count":          0,
   419  		"req_proc_time_hist_sum":            0,
   420  		"req_proc_time_max":                 0,
   421  		"req_proc_time_min":                 0,
   422  		"req_proc_time_sum":                 0,
   423  		"req_type_bad":                      0,
   424  		"req_type_error":                    0,
   425  		"req_type_redirect":                 0,
   426  		"req_type_success":                  0,
   427  		"req_unmatched":                     8,
   428  		"requests":                          100,
   429  		"resp_1xx":                          0,
   430  		"resp_2xx":                          0,
   431  		"resp_3xx":                          0,
   432  		"resp_4xx":                          0,
   433  		"resp_5xx":                          0,
   434  		"uniq_ipv4":                         0,
   435  		"uniq_ipv6":                         0,
   436  		"upstream_resp_time_avg":            0,
   437  		"upstream_resp_time_count":          0,
   438  		"upstream_resp_time_hist_bucket_1":  0,
   439  		"upstream_resp_time_hist_bucket_10": 0,
   440  		"upstream_resp_time_hist_bucket_11": 0,
   441  		"upstream_resp_time_hist_bucket_2":  0,
   442  		"upstream_resp_time_hist_bucket_3":  0,
   443  		"upstream_resp_time_hist_bucket_4":  0,
   444  		"upstream_resp_time_hist_bucket_5":  0,
   445  		"upstream_resp_time_hist_bucket_6":  0,
   446  		"upstream_resp_time_hist_bucket_7":  0,
   447  		"upstream_resp_time_hist_bucket_8":  0,
   448  		"upstream_resp_time_hist_bucket_9":  0,
   449  		"upstream_resp_time_hist_count":     0,
   450  		"upstream_resp_time_hist_sum":       0,
   451  		"upstream_resp_time_max":            0,
   452  		"upstream_resp_time_min":            0,
   453  		"upstream_resp_time_sum":            0,
   454  	}
   455  
   456  	mx := weblog.Collect()
   457  	assert.Equal(t, expected, mx)
   458  	testCharts(t, weblog, mx)
   459  }
   460  
   461  func TestWebLog_Collect_CustomTimeFieldsLogs(t *testing.T) {
   462  	weblog := prepareWebLogCollectCustomTimeFields(t)
   463  
   464  	expected := map[string]int64{
   465  		"bytes_received":                              0,
   466  		"bytes_sent":                                  0,
   467  		"custom_time_field_time1_time_avg":            224,
   468  		"custom_time_field_time1_time_count":          72,
   469  		"custom_time_field_time1_time_hist_bucket_1":  72,
   470  		"custom_time_field_time1_time_hist_bucket_10": 72,
   471  		"custom_time_field_time1_time_hist_bucket_11": 72,
   472  		"custom_time_field_time1_time_hist_bucket_2":  72,
   473  		"custom_time_field_time1_time_hist_bucket_3":  72,
   474  		"custom_time_field_time1_time_hist_bucket_4":  72,
   475  		"custom_time_field_time1_time_hist_bucket_5":  72,
   476  		"custom_time_field_time1_time_hist_bucket_6":  72,
   477  		"custom_time_field_time1_time_hist_bucket_7":  72,
   478  		"custom_time_field_time1_time_hist_bucket_8":  72,
   479  		"custom_time_field_time1_time_hist_bucket_9":  72,
   480  		"custom_time_field_time1_time_hist_count":     72,
   481  		"custom_time_field_time1_time_hist_sum":       16152,
   482  		"custom_time_field_time1_time_max":            431,
   483  		"custom_time_field_time1_time_min":            121,
   484  		"custom_time_field_time1_time_sum":            16152,
   485  		"custom_time_field_time2_time_avg":            255,
   486  		"custom_time_field_time2_time_count":          72,
   487  		"custom_time_field_time2_time_hist_bucket_1":  72,
   488  		"custom_time_field_time2_time_hist_bucket_10": 72,
   489  		"custom_time_field_time2_time_hist_bucket_11": 72,
   490  		"custom_time_field_time2_time_hist_bucket_2":  72,
   491  		"custom_time_field_time2_time_hist_bucket_3":  72,
   492  		"custom_time_field_time2_time_hist_bucket_4":  72,
   493  		"custom_time_field_time2_time_hist_bucket_5":  72,
   494  		"custom_time_field_time2_time_hist_bucket_6":  72,
   495  		"custom_time_field_time2_time_hist_bucket_7":  72,
   496  		"custom_time_field_time2_time_hist_bucket_8":  72,
   497  		"custom_time_field_time2_time_hist_bucket_9":  72,
   498  		"custom_time_field_time2_time_hist_count":     72,
   499  		"custom_time_field_time2_time_hist_sum":       18360,
   500  		"custom_time_field_time2_time_max":            321,
   501  		"custom_time_field_time2_time_min":            123,
   502  		"custom_time_field_time2_time_sum":            18360,
   503  		"req_http_scheme":                             0,
   504  		"req_https_scheme":                            0,
   505  		"req_ipv4":                                    0,
   506  		"req_ipv6":                                    0,
   507  		"req_proc_time_avg":                           0,
   508  		"req_proc_time_count":                         0,
   509  		"req_proc_time_hist_bucket_1":                 0,
   510  		"req_proc_time_hist_bucket_10":                0,
   511  		"req_proc_time_hist_bucket_11":                0,
   512  		"req_proc_time_hist_bucket_2":                 0,
   513  		"req_proc_time_hist_bucket_3":                 0,
   514  		"req_proc_time_hist_bucket_4":                 0,
   515  		"req_proc_time_hist_bucket_5":                 0,
   516  		"req_proc_time_hist_bucket_6":                 0,
   517  		"req_proc_time_hist_bucket_7":                 0,
   518  		"req_proc_time_hist_bucket_8":                 0,
   519  		"req_proc_time_hist_bucket_9":                 0,
   520  		"req_proc_time_hist_count":                    0,
   521  		"req_proc_time_hist_sum":                      0,
   522  		"req_proc_time_max":                           0,
   523  		"req_proc_time_min":                           0,
   524  		"req_proc_time_sum":                           0,
   525  		"req_type_bad":                                0,
   526  		"req_type_error":                              0,
   527  		"req_type_redirect":                           0,
   528  		"req_type_success":                            0,
   529  		"req_unmatched":                               0,
   530  		"requests":                                    72,
   531  		"resp_1xx":                                    0,
   532  		"resp_2xx":                                    0,
   533  		"resp_3xx":                                    0,
   534  		"resp_4xx":                                    0,
   535  		"resp_5xx":                                    0,
   536  		"uniq_ipv4":                                   0,
   537  		"uniq_ipv6":                                   0,
   538  		"upstream_resp_time_avg":                      0,
   539  		"upstream_resp_time_count":                    0,
   540  		"upstream_resp_time_hist_bucket_1":            0,
   541  		"upstream_resp_time_hist_bucket_10":           0,
   542  		"upstream_resp_time_hist_bucket_11":           0,
   543  		"upstream_resp_time_hist_bucket_2":            0,
   544  		"upstream_resp_time_hist_bucket_3":            0,
   545  		"upstream_resp_time_hist_bucket_4":            0,
   546  		"upstream_resp_time_hist_bucket_5":            0,
   547  		"upstream_resp_time_hist_bucket_6":            0,
   548  		"upstream_resp_time_hist_bucket_7":            0,
   549  		"upstream_resp_time_hist_bucket_8":            0,
   550  		"upstream_resp_time_hist_bucket_9":            0,
   551  		"upstream_resp_time_hist_count":               0,
   552  		"upstream_resp_time_hist_sum":                 0,
   553  		"upstream_resp_time_max":                      0,
   554  		"upstream_resp_time_min":                      0,
   555  		"upstream_resp_time_sum":                      0,
   556  	}
   557  
   558  	mx := weblog.Collect()
   559  	assert.Equal(t, expected, mx)
   560  	testCharts(t, weblog, mx)
   561  }
   562  
   563  func TestWebLog_Collect_CustomNumericFieldsLogs(t *testing.T) {
   564  	weblog := prepareWebLogCollectCustomNumericFields(t)
   565  
   566  	expected := map[string]int64{
   567  		"bytes_received": 0,
   568  		"bytes_sent":     0,
   569  		"custom_numeric_field_numeric1_summary_avg":   224,
   570  		"custom_numeric_field_numeric1_summary_count": 72,
   571  		"custom_numeric_field_numeric1_summary_max":   431,
   572  		"custom_numeric_field_numeric1_summary_min":   121,
   573  		"custom_numeric_field_numeric1_summary_sum":   16152,
   574  		"custom_numeric_field_numeric2_summary_avg":   255,
   575  		"custom_numeric_field_numeric2_summary_count": 72,
   576  		"custom_numeric_field_numeric2_summary_max":   321,
   577  		"custom_numeric_field_numeric2_summary_min":   123,
   578  		"custom_numeric_field_numeric2_summary_sum":   18360,
   579  		"req_http_scheme":                   0,
   580  		"req_https_scheme":                  0,
   581  		"req_ipv4":                          0,
   582  		"req_ipv6":                          0,
   583  		"req_proc_time_avg":                 0,
   584  		"req_proc_time_count":               0,
   585  		"req_proc_time_hist_bucket_1":       0,
   586  		"req_proc_time_hist_bucket_10":      0,
   587  		"req_proc_time_hist_bucket_11":      0,
   588  		"req_proc_time_hist_bucket_2":       0,
   589  		"req_proc_time_hist_bucket_3":       0,
   590  		"req_proc_time_hist_bucket_4":       0,
   591  		"req_proc_time_hist_bucket_5":       0,
   592  		"req_proc_time_hist_bucket_6":       0,
   593  		"req_proc_time_hist_bucket_7":       0,
   594  		"req_proc_time_hist_bucket_8":       0,
   595  		"req_proc_time_hist_bucket_9":       0,
   596  		"req_proc_time_hist_count":          0,
   597  		"req_proc_time_hist_sum":            0,
   598  		"req_proc_time_max":                 0,
   599  		"req_proc_time_min":                 0,
   600  		"req_proc_time_sum":                 0,
   601  		"req_type_bad":                      0,
   602  		"req_type_error":                    0,
   603  		"req_type_redirect":                 0,
   604  		"req_type_success":                  0,
   605  		"req_unmatched":                     0,
   606  		"requests":                          72,
   607  		"resp_1xx":                          0,
   608  		"resp_2xx":                          0,
   609  		"resp_3xx":                          0,
   610  		"resp_4xx":                          0,
   611  		"resp_5xx":                          0,
   612  		"uniq_ipv4":                         0,
   613  		"uniq_ipv6":                         0,
   614  		"upstream_resp_time_avg":            0,
   615  		"upstream_resp_time_count":          0,
   616  		"upstream_resp_time_hist_bucket_1":  0,
   617  		"upstream_resp_time_hist_bucket_10": 0,
   618  		"upstream_resp_time_hist_bucket_11": 0,
   619  		"upstream_resp_time_hist_bucket_2":  0,
   620  		"upstream_resp_time_hist_bucket_3":  0,
   621  		"upstream_resp_time_hist_bucket_4":  0,
   622  		"upstream_resp_time_hist_bucket_5":  0,
   623  		"upstream_resp_time_hist_bucket_6":  0,
   624  		"upstream_resp_time_hist_bucket_7":  0,
   625  		"upstream_resp_time_hist_bucket_8":  0,
   626  		"upstream_resp_time_hist_bucket_9":  0,
   627  		"upstream_resp_time_hist_count":     0,
   628  		"upstream_resp_time_hist_sum":       0,
   629  		"upstream_resp_time_max":            0,
   630  		"upstream_resp_time_min":            0,
   631  		"upstream_resp_time_sum":            0,
   632  	}
   633  
   634  	mx := weblog.Collect()
   635  
   636  	assert.Equal(t, expected, mx)
   637  	testCharts(t, weblog, mx)
   638  }
   639  
   640  func TestWebLog_IISLogs(t *testing.T) {
   641  	weblog := prepareWebLogCollectIISFields(t)
   642  
   643  	expected := map[string]int64{
   644  		"bytes_received":                    0,
   645  		"bytes_sent":                        0,
   646  		"req_http_scheme":                   0,
   647  		"req_https_scheme":                  0,
   648  		"req_ipv4":                          38,
   649  		"req_ipv6":                          114,
   650  		"req_method_GET":                    152,
   651  		"req_port_80":                       152,
   652  		"req_proc_time_avg":                 5,
   653  		"req_proc_time_count":               152,
   654  		"req_proc_time_hist_bucket_1":       133,
   655  		"req_proc_time_hist_bucket_10":      145,
   656  		"req_proc_time_hist_bucket_11":      146,
   657  		"req_proc_time_hist_bucket_2":       133,
   658  		"req_proc_time_hist_bucket_3":       133,
   659  		"req_proc_time_hist_bucket_4":       133,
   660  		"req_proc_time_hist_bucket_5":       133,
   661  		"req_proc_time_hist_bucket_6":       133,
   662  		"req_proc_time_hist_bucket_7":       133,
   663  		"req_proc_time_hist_bucket_8":       138,
   664  		"req_proc_time_hist_bucket_9":       143,
   665  		"req_proc_time_hist_count":          152,
   666  		"req_proc_time_hist_sum":            799,
   667  		"req_proc_time_max":                 256,
   668  		"req_proc_time_min":                 0,
   669  		"req_proc_time_sum":                 799,
   670  		"req_type_bad":                      42,
   671  		"req_type_error":                    0,
   672  		"req_type_redirect":                 0,
   673  		"req_type_success":                  110,
   674  		"req_unmatched":                     16,
   675  		"req_vhost_127.0.0.1":               38,
   676  		"req_vhost_::1":                     114,
   677  		"requests":                          168,
   678  		"resp_1xx":                          0,
   679  		"resp_2xx":                          99,
   680  		"resp_3xx":                          11,
   681  		"resp_4xx":                          42,
   682  		"resp_5xx":                          0,
   683  		"resp_code_200":                     99,
   684  		"resp_code_304":                     11,
   685  		"resp_code_404":                     42,
   686  		"uniq_ipv4":                         1,
   687  		"uniq_ipv6":                         1,
   688  		"upstream_resp_time_avg":            0,
   689  		"upstream_resp_time_count":          0,
   690  		"upstream_resp_time_hist_bucket_1":  0,
   691  		"upstream_resp_time_hist_bucket_10": 0,
   692  		"upstream_resp_time_hist_bucket_11": 0,
   693  		"upstream_resp_time_hist_bucket_2":  0,
   694  		"upstream_resp_time_hist_bucket_3":  0,
   695  		"upstream_resp_time_hist_bucket_4":  0,
   696  		"upstream_resp_time_hist_bucket_5":  0,
   697  		"upstream_resp_time_hist_bucket_6":  0,
   698  		"upstream_resp_time_hist_bucket_7":  0,
   699  		"upstream_resp_time_hist_bucket_8":  0,
   700  		"upstream_resp_time_hist_bucket_9":  0,
   701  		"upstream_resp_time_hist_count":     0,
   702  		"upstream_resp_time_hist_sum":       0,
   703  		"upstream_resp_time_max":            0,
   704  		"upstream_resp_time_min":            0,
   705  		"upstream_resp_time_sum":            0,
   706  	}
   707  
   708  	mx := weblog.Collect()
   709  	assert.Equal(t, expected, mx)
   710  }
   711  
   712  func testCharts(t *testing.T, w *WebLog, mx map[string]int64) {
   713  	testVhostChart(t, w)
   714  	testPortChart(t, w)
   715  	testSchemeChart(t, w)
   716  	testClientCharts(t, w)
   717  	testHTTPMethodChart(t, w)
   718  	testURLPatternChart(t, w)
   719  	testHTTPVersionChart(t, w)
   720  	testRespCodeCharts(t, w)
   721  	testBandwidthChart(t, w)
   722  	testReqProcTimeCharts(t, w)
   723  	testUpsRespTimeCharts(t, w)
   724  	testSSLProtoChart(t, w)
   725  	testSSLCipherSuiteChart(t, w)
   726  	testURLPatternStatsCharts(t, w)
   727  	testCustomFieldCharts(t, w)
   728  	testCustomTimeFieldCharts(t, w)
   729  	testCustomNumericFieldCharts(t, w)
   730  
   731  	testChartsDimIDs(t, w, mx)
   732  }
   733  
   734  func testChartsDimIDs(t *testing.T, w *WebLog, mx map[string]int64) {
   735  	for _, chart := range *w.Charts() {
   736  		for _, dim := range chart.Dims {
   737  			_, ok := mx[dim.ID]
   738  			assert.Truef(t, ok, "collected metrics has no data for dim '%s' chart '%s'", dim.ID, chart.ID)
   739  		}
   740  	}
   741  }
   742  
   743  func testVhostChart(t *testing.T, w *WebLog) {
   744  	if len(w.mx.ReqVhost) == 0 {
   745  		assert.Falsef(t, w.Charts().Has(reqByVhost.ID), "chart '%s' is created", reqByVhost.ID)
   746  		return
   747  	}
   748  
   749  	chart := w.Charts().Get(reqByVhost.ID)
   750  	assert.NotNilf(t, chart, "chart '%s' is not created", reqByVhost.ID)
   751  	if chart == nil {
   752  		return
   753  	}
   754  	for v := range w.mx.ReqVhost {
   755  		id := "req_vhost_" + v
   756  		assert.Truef(t, chart.HasDim(id), "chart '%s' has no dim for '%s' vhost, expected '%s'", chart.ID, v, id)
   757  	}
   758  }
   759  
   760  func testPortChart(t *testing.T, w *WebLog) {
   761  	if len(w.mx.ReqPort) == 0 {
   762  		assert.Falsef(t, w.Charts().Has(reqByPort.ID), "chart '%s' is created", reqByPort.ID)
   763  		return
   764  	}
   765  
   766  	chart := w.Charts().Get(reqByPort.ID)
   767  	assert.NotNilf(t, chart, "chart '%s' is not created", reqByPort.ID)
   768  	if chart == nil {
   769  		return
   770  	}
   771  	for v := range w.mx.ReqPort {
   772  		id := "req_port_" + v
   773  		assert.Truef(t, chart.HasDim(id), "chart '%s' has no dim for '%s' port, expected '%s'", chart.ID, v, id)
   774  	}
   775  }
   776  
   777  func testSchemeChart(t *testing.T, w *WebLog) {
   778  	if w.mx.ReqHTTPScheme.Value() == 0 && w.mx.ReqHTTPSScheme.Value() == 0 {
   779  		assert.Falsef(t, w.Charts().Has(reqByScheme.ID), "chart '%s' is created", reqByScheme.ID)
   780  	} else {
   781  		assert.Truef(t, w.Charts().Has(reqByScheme.ID), "chart '%s' is not created", reqByScheme.ID)
   782  	}
   783  }
   784  
   785  func testClientCharts(t *testing.T, w *WebLog) {
   786  	if w.mx.ReqIPv4.Value() == 0 && w.mx.ReqIPv6.Value() == 0 {
   787  		assert.Falsef(t, w.Charts().Has(reqByIPProto.ID), "chart '%s' is created", reqByIPProto.ID)
   788  	} else {
   789  		assert.Truef(t, w.Charts().Has(reqByIPProto.ID), "chart '%s' is not created", reqByIPProto.ID)
   790  	}
   791  
   792  	if w.mx.UniqueIPv4.Value() == 0 && w.mx.UniqueIPv6.Value() == 0 {
   793  		assert.Falsef(t, w.Charts().Has(uniqIPsCurPoll.ID), "chart '%s' is created", uniqIPsCurPoll.ID)
   794  	} else {
   795  		assert.Truef(t, w.Charts().Has(uniqIPsCurPoll.ID), "chart '%s' is not created", uniqIPsCurPoll.ID)
   796  	}
   797  }
   798  
   799  func testHTTPMethodChart(t *testing.T, w *WebLog) {
   800  	if len(w.mx.ReqMethod) == 0 {
   801  		assert.Falsef(t, w.Charts().Has(reqByMethod.ID), "chart '%s' is created", reqByMethod.ID)
   802  		return
   803  	}
   804  
   805  	chart := w.Charts().Get(reqByMethod.ID)
   806  	assert.NotNilf(t, chart, "chart '%s' is not created", reqByMethod.ID)
   807  	if chart == nil {
   808  		return
   809  	}
   810  	for v := range w.mx.ReqMethod {
   811  		id := "req_method_" + v
   812  		assert.Truef(t, chart.HasDim(id), "chart '%s' has no dim for '%s' method, expected '%s'", chart.ID, v, id)
   813  	}
   814  }
   815  
   816  func testURLPatternChart(t *testing.T, w *WebLog) {
   817  	if isEmptyCounterVec(w.mx.ReqURLPattern) {
   818  		assert.Falsef(t, w.Charts().Has(reqByURLPattern.ID), "chart '%s' is created", reqByURLPattern.ID)
   819  		return
   820  	}
   821  
   822  	chart := w.Charts().Get(reqByURLPattern.ID)
   823  	assert.NotNilf(t, chart, "chart '%s' is not created", reqByURLPattern.ID)
   824  	if chart == nil {
   825  		return
   826  	}
   827  	for v := range w.mx.ReqURLPattern {
   828  		id := "req_url_ptn_" + v
   829  		assert.True(t, chart.HasDim(id), "chart '%s' has no dim for '%s' pattern, expected '%s'", chart.ID, v, id)
   830  	}
   831  }
   832  
   833  func testHTTPVersionChart(t *testing.T, w *WebLog) {
   834  	if len(w.mx.ReqVersion) == 0 {
   835  		assert.Falsef(t, w.Charts().Has(reqByVersion.ID), "chart '%s' is created", reqByVersion.ID)
   836  		return
   837  	}
   838  
   839  	chart := w.Charts().Get(reqByVersion.ID)
   840  	assert.NotNilf(t, chart, "chart '%s' is not created", reqByVersion.ID)
   841  	if chart == nil {
   842  		return
   843  	}
   844  	for v := range w.mx.ReqVersion {
   845  		id := "req_version_" + v
   846  		assert.Truef(t, chart.HasDim(id), "chart '%s' has no dim for '%s' version, expected '%s'", chart.ID, v, id)
   847  	}
   848  }
   849  
   850  func testRespCodeCharts(t *testing.T, w *WebLog) {
   851  	if isEmptyCounterVec(w.mx.RespCode) {
   852  		for _, id := range []string{
   853  			respCodes.ID,
   854  			respCodes1xx.ID,
   855  			respCodes2xx.ID,
   856  			respCodes3xx.ID,
   857  			respCodes4xx.ID,
   858  			respCodes5xx.ID,
   859  		} {
   860  			assert.Falsef(t, w.Charts().Has(id), "chart '%s' is created", id)
   861  		}
   862  		return
   863  	}
   864  
   865  	if !w.GroupRespCodes {
   866  		chart := w.Charts().Get(respCodes.ID)
   867  		assert.NotNilf(t, chart, "chart '%s' is not created", respCodes.ID)
   868  		if chart == nil {
   869  			return
   870  		}
   871  		for v := range w.mx.RespCode {
   872  			id := "resp_code_" + v
   873  			assert.Truef(t, chart.HasDim(id), "chart '%s' has no dim for '%s' code, expected '%s'", chart.ID, v, id)
   874  		}
   875  		return
   876  	}
   877  
   878  	findCodes := func(class string) (codes []string) {
   879  		for v := range w.mx.RespCode {
   880  			if v[:1] == class {
   881  				codes = append(codes, v)
   882  			}
   883  		}
   884  		return codes
   885  	}
   886  
   887  	var n int
   888  	ids := []string{
   889  		respCodes1xx.ID,
   890  		respCodes2xx.ID,
   891  		respCodes3xx.ID,
   892  		respCodes4xx.ID,
   893  		respCodes5xx.ID,
   894  	}
   895  	for i, chartID := range ids {
   896  		class := strconv.Itoa(i + 1)
   897  		codes := findCodes(class)
   898  		n += len(codes)
   899  		chart := w.Charts().Get(chartID)
   900  		assert.NotNilf(t, chart, "chart '%s' is not created", chartID)
   901  		if chart == nil {
   902  			return
   903  		}
   904  		for _, v := range codes {
   905  			id := "resp_code_" + v
   906  			assert.Truef(t, chart.HasDim(id), "chart '%s' has no dim for '%s' code, expected '%s'", chartID, v, id)
   907  		}
   908  	}
   909  	assert.Equal(t, len(w.mx.RespCode), n)
   910  }
   911  
   912  func testBandwidthChart(t *testing.T, w *WebLog) {
   913  	if w.mx.BytesSent.Value() == 0 && w.mx.BytesReceived.Value() == 0 {
   914  		assert.Falsef(t, w.Charts().Has(bandwidth.ID), "chart '%s' is created", bandwidth.ID)
   915  	} else {
   916  		assert.Truef(t, w.Charts().Has(bandwidth.ID), "chart '%s' is not created", bandwidth.ID)
   917  	}
   918  }
   919  
   920  func testReqProcTimeCharts(t *testing.T, w *WebLog) {
   921  	if isEmptySummary(w.mx.ReqProcTime) {
   922  		assert.Falsef(t, w.Charts().Has(reqProcTime.ID), "chart '%s' is created", reqProcTime.ID)
   923  	} else {
   924  		assert.Truef(t, w.Charts().Has(reqProcTime.ID), "chart '%s' is not created", reqProcTime.ID)
   925  	}
   926  
   927  	if isEmptyHistogram(w.mx.ReqProcTimeHist) {
   928  		assert.Falsef(t, w.Charts().Has(reqProcTimeHist.ID), "chart '%s' is created", reqProcTimeHist.ID)
   929  	} else {
   930  		assert.Truef(t, w.Charts().Has(reqProcTimeHist.ID), "chart '%s' is not created", reqProcTimeHist.ID)
   931  	}
   932  }
   933  
   934  func testUpsRespTimeCharts(t *testing.T, w *WebLog) {
   935  	if isEmptySummary(w.mx.UpsRespTime) {
   936  		assert.Falsef(t, w.Charts().Has(upsRespTime.ID), "chart '%s' is created", upsRespTime.ID)
   937  	} else {
   938  		assert.Truef(t, w.Charts().Has(upsRespTime.ID), "chart '%s' is not created", upsRespTime.ID)
   939  	}
   940  
   941  	if isEmptyHistogram(w.mx.UpsRespTimeHist) {
   942  		assert.Falsef(t, w.Charts().Has(upsRespTimeHist.ID), "chart '%s' is created", upsRespTimeHist.ID)
   943  	} else {
   944  		assert.Truef(t, w.Charts().Has(upsRespTimeHist.ID), "chart '%s' is not created", upsRespTimeHist.ID)
   945  	}
   946  }
   947  
   948  func testSSLProtoChart(t *testing.T, w *WebLog) {
   949  	if len(w.mx.ReqSSLProto) == 0 {
   950  		assert.Falsef(t, w.Charts().Has(reqBySSLProto.ID), "chart '%s' is created", reqBySSLProto.ID)
   951  		return
   952  	}
   953  
   954  	chart := w.Charts().Get(reqBySSLProto.ID)
   955  	assert.NotNilf(t, chart, "chart '%s' is not created", reqBySSLProto.ID)
   956  	if chart == nil {
   957  		return
   958  	}
   959  	for v := range w.mx.ReqSSLProto {
   960  		id := "req_ssl_proto_" + v
   961  		assert.Truef(t, chart.HasDim(id), "chart '%s' has no dim for '%s' ssl proto, expected '%s'", chart.ID, v, id)
   962  	}
   963  }
   964  
   965  func testSSLCipherSuiteChart(t *testing.T, w *WebLog) {
   966  	if len(w.mx.ReqSSLCipherSuite) == 0 {
   967  		assert.Falsef(t, w.Charts().Has(reqBySSLCipherSuite.ID), "chart '%s' is created", reqBySSLCipherSuite.ID)
   968  		return
   969  	}
   970  
   971  	chart := w.Charts().Get(reqBySSLCipherSuite.ID)
   972  	assert.NotNilf(t, chart, "chart '%s' is not created", reqBySSLCipherSuite.ID)
   973  	if chart == nil {
   974  		return
   975  	}
   976  	for v := range w.mx.ReqSSLCipherSuite {
   977  		id := "req_ssl_cipher_suite_" + v
   978  		assert.Truef(t, chart.HasDim(id), "chart '%s' has no dim for '%s' ssl cipher suite, expected '%s'", chart.ID, v, id)
   979  	}
   980  }
   981  
   982  func testURLPatternStatsCharts(t *testing.T, w *WebLog) {
   983  	for _, p := range w.URLPatterns {
   984  		chartID := fmt.Sprintf(urlPatternRespCodes.ID, p.Name)
   985  
   986  		if isEmptyCounterVec(w.mx.RespCode) {
   987  			assert.Falsef(t, w.Charts().Has(chartID), "chart '%s' is created", chartID)
   988  			continue
   989  		}
   990  
   991  		chart := w.Charts().Get(chartID)
   992  		assert.NotNilf(t, chart, "chart '%s' is not created", chartID)
   993  		if chart == nil {
   994  			continue
   995  		}
   996  
   997  		stats, ok := w.mx.URLPatternStats[p.Name]
   998  		assert.Truef(t, ok, "url pattern '%s' has no metric in w.mx.URLPatternStats", p.Name)
   999  		if !ok {
  1000  			continue
  1001  		}
  1002  		for v := range stats.RespCode {
  1003  			id := fmt.Sprintf("url_ptn_%s_resp_code_%s", p.Name, v)
  1004  			assert.Truef(t, chart.HasDim(id), "chart '%s' has no dim for '%s' code, expected '%s'", chartID, v, id)
  1005  		}
  1006  	}
  1007  
  1008  	for _, p := range w.URLPatterns {
  1009  		id := fmt.Sprintf(urlPatternReqMethods.ID, p.Name)
  1010  		if isEmptyCounterVec(w.mx.ReqMethod) {
  1011  			assert.Falsef(t, w.Charts().Has(id), "chart '%s' is created", id)
  1012  			continue
  1013  		}
  1014  
  1015  		chart := w.Charts().Get(id)
  1016  		assert.NotNilf(t, chart, "chart '%s' is not created", id)
  1017  		if chart == nil {
  1018  			continue
  1019  		}
  1020  
  1021  		stats, ok := w.mx.URLPatternStats[p.Name]
  1022  		assert.Truef(t, ok, "url pattern '%s' has no metric in w.mx.URLPatternStats", p.Name)
  1023  		if !ok {
  1024  			continue
  1025  		}
  1026  		for v := range stats.ReqMethod {
  1027  			dimID := fmt.Sprintf("url_ptn_%s_req_method_%s", p.Name, v)
  1028  			assert.Truef(t, chart.HasDim(dimID), "chart '%s' has no dim for '%s' method, expected '%s'", id, v, dimID)
  1029  		}
  1030  	}
  1031  
  1032  	for _, p := range w.URLPatterns {
  1033  		id := fmt.Sprintf(urlPatternBandwidth.ID, p.Name)
  1034  		if w.mx.BytesSent.Value() == 0 && w.mx.BytesReceived.Value() == 0 {
  1035  			assert.Falsef(t, w.Charts().Has(id), "chart '%s' is created", id)
  1036  		} else {
  1037  			assert.Truef(t, w.Charts().Has(id), "chart '%s' is not created", id)
  1038  		}
  1039  	}
  1040  
  1041  	for _, p := range w.URLPatterns {
  1042  		id := fmt.Sprintf(urlPatternReqProcTime.ID, p.Name)
  1043  		if isEmptySummary(w.mx.ReqProcTime) {
  1044  			assert.Falsef(t, w.Charts().Has(id), "chart '%s' is created", id)
  1045  		} else {
  1046  			assert.Truef(t, w.Charts().Has(id), "chart '%s' is not created", id)
  1047  		}
  1048  	}
  1049  }
  1050  
  1051  func testCustomFieldCharts(t *testing.T, w *WebLog) {
  1052  	for _, cf := range w.CustomFields {
  1053  		id := fmt.Sprintf(reqByCustomFieldPattern.ID, cf.Name)
  1054  		chart := w.Charts().Get(id)
  1055  		assert.NotNilf(t, chart, "chart '%s' is not created", id)
  1056  		if chart == nil {
  1057  			continue
  1058  		}
  1059  
  1060  		for _, p := range cf.Patterns {
  1061  			id := fmt.Sprintf("custom_field_%s_%s", cf.Name, p.Name)
  1062  			assert.True(t, chart.HasDim(id), "chart '%s' has no dim for '%s' pattern, expected '%s'", chart.ID, p, id)
  1063  		}
  1064  	}
  1065  }
  1066  
  1067  func testCustomTimeFieldCharts(t *testing.T, w *WebLog) {
  1068  	for _, cf := range w.CustomTimeFields {
  1069  		id := fmt.Sprintf(reqByCustomTimeField.ID, cf.Name)
  1070  		chart := w.Charts().Get(id)
  1071  		assert.NotNilf(t, chart, "chart '%s' is not created", id)
  1072  		if chart == nil {
  1073  			continue
  1074  		}
  1075  		dimMinID := fmt.Sprintf("custom_time_field_%s_time_min", cf.Name)
  1076  		assert.True(t, chart.HasDim(dimMinID), "chart '%s' has no dim for '%s' name, expected '%s'", chart.ID, cf.Name, dimMinID)
  1077  
  1078  		dimMaxID := fmt.Sprintf("custom_time_field_%s_time_min", cf.Name)
  1079  		assert.True(t, chart.HasDim(dimMaxID), "chart '%s' has no dim for '%s' name, expected '%s'", chart.ID, cf.Name, dimMaxID)
  1080  
  1081  		dimAveID := fmt.Sprintf("custom_time_field_%s_time_min", cf.Name)
  1082  		assert.True(t, chart.HasDim(dimAveID), "chart '%s' has no dim for '%s' name, expected '%s'", chart.ID, cf.Name, dimAveID)
  1083  	}
  1084  }
  1085  
  1086  func testCustomNumericFieldCharts(t *testing.T, w *WebLog) {
  1087  	for _, cf := range w.CustomNumericFields {
  1088  		id := fmt.Sprintf(customNumericFieldSummaryChartTmpl.ID, cf.Name)
  1089  		chart := w.Charts().Get(id)
  1090  		assert.NotNilf(t, chart, "chart '%s' is not created", id)
  1091  		if chart == nil {
  1092  			continue
  1093  		}
  1094  		dimMinID := fmt.Sprintf("custom_numeric_field_%s_summary_min", cf.Name)
  1095  		assert.True(t, chart.HasDim(dimMinID), "chart '%s' has no dim for '%s' name, expected '%s'", chart.ID, cf.Name, dimMinID)
  1096  
  1097  		dimMaxID := fmt.Sprintf("custom_numeric_field_%s_summary_min", cf.Name)
  1098  		assert.True(t, chart.HasDim(dimMaxID), "chart '%s' has no dim for '%s' name, expected '%s'", chart.ID, cf.Name, dimMaxID)
  1099  
  1100  		dimAveID := fmt.Sprintf("custom_numeric_field_%s_summary_min", cf.Name)
  1101  		assert.True(t, chart.HasDim(dimAveID), "chart '%s' has no dim for '%s' name, expected '%s'", chart.ID, cf.Name, dimAveID)
  1102  	}
  1103  }
  1104  
  1105  var (
  1106  	emptySummary   = newWebLogSummary()
  1107  	emptyHistogram = metrics.NewHistogram(metrics.DefBuckets)
  1108  )
  1109  
  1110  func isEmptySummary(s metrics.Summary) bool     { return reflect.DeepEqual(s, emptySummary) }
  1111  func isEmptyHistogram(h metrics.Histogram) bool { return reflect.DeepEqual(h, emptyHistogram) }
  1112  
  1113  func isEmptyCounterVec(cv metrics.CounterVec) bool {
  1114  	for _, c := range cv {
  1115  		if c.Value() > 0 {
  1116  			return false
  1117  		}
  1118  	}
  1119  	return true
  1120  }
  1121  
  1122  func prepareWebLogCollectFull(t *testing.T) *WebLog {
  1123  	t.Helper()
  1124  	format := strings.Join([]string{
  1125  		"$host:$server_port",
  1126  		"$remote_addr",
  1127  		"-",
  1128  		"-",
  1129  		"$time_local",
  1130  		`"$request"`,
  1131  		"$status",
  1132  		"$body_bytes_sent",
  1133  		"$request_length",
  1134  		"$request_time",
  1135  		"$upstream_response_time",
  1136  		"$scheme",
  1137  		"$ssl_protocol",
  1138  		"$ssl_cipher",
  1139  		"$side",
  1140  		"$drink",
  1141  		"$random_time_field",
  1142  	}, " ")
  1143  
  1144  	cfg := Config{
  1145  		Parser: logs.ParserConfig{
  1146  			LogType: logs.TypeCSV,
  1147  			CSV: logs.CSVConfig{
  1148  				FieldsPerRecord:  -1,
  1149  				Delimiter:        " ",
  1150  				TrimLeadingSpace: false,
  1151  				Format:           format,
  1152  				CheckField:       checkCSVFormatField,
  1153  			},
  1154  		},
  1155  		Path:        "testdata/full.log",
  1156  		ExcludePath: "",
  1157  		URLPatterns: []userPattern{
  1158  			{Name: "com", Match: "~ com$"},
  1159  			{Name: "org", Match: "~ org$"},
  1160  			{Name: "net", Match: "~ net$"},
  1161  			{Name: "not_match", Match: "* !*"},
  1162  		},
  1163  		CustomFields: []customField{
  1164  			{
  1165  				Name: "side",
  1166  				Patterns: []userPattern{
  1167  					{Name: "dark", Match: "= dark"},
  1168  					{Name: "light", Match: "= light"},
  1169  				},
  1170  			},
  1171  			{
  1172  				Name: "drink",
  1173  				Patterns: []userPattern{
  1174  					{Name: "beer", Match: "= beer"},
  1175  					{Name: "wine", Match: "= wine"},
  1176  				},
  1177  			},
  1178  		},
  1179  		CustomTimeFields: []customTimeField{
  1180  			{
  1181  				Name:      "random_time_field",
  1182  				Histogram: metrics.DefBuckets,
  1183  			},
  1184  		},
  1185  		Histogram:      metrics.DefBuckets,
  1186  		GroupRespCodes: true,
  1187  	}
  1188  	weblog := New()
  1189  	weblog.Config = cfg
  1190  	require.True(t, weblog.Init())
  1191  	require.True(t, weblog.Check())
  1192  	defer weblog.Cleanup()
  1193  
  1194  	p, err := logs.NewCSVParser(weblog.Parser.CSV, bytes.NewReader(testFullLog))
  1195  	require.NoError(t, err)
  1196  	weblog.parser = p
  1197  	return weblog
  1198  }
  1199  
  1200  func prepareWebLogCollectCommon(t *testing.T) *WebLog {
  1201  	t.Helper()
  1202  	format := strings.Join([]string{
  1203  		"$remote_addr",
  1204  		"-",
  1205  		"-",
  1206  		"$time_local",
  1207  		`"$request"`,
  1208  		"$status",
  1209  		"$body_bytes_sent",
  1210  	}, " ")
  1211  
  1212  	cfg := Config{
  1213  		Parser: logs.ParserConfig{
  1214  			LogType: logs.TypeCSV,
  1215  			CSV: logs.CSVConfig{
  1216  				FieldsPerRecord:  -1,
  1217  				Delimiter:        " ",
  1218  				TrimLeadingSpace: false,
  1219  				Format:           format,
  1220  				CheckField:       checkCSVFormatField,
  1221  			},
  1222  		},
  1223  		Path:           "testdata/common.log",
  1224  		ExcludePath:    "",
  1225  		URLPatterns:    nil,
  1226  		CustomFields:   nil,
  1227  		Histogram:      nil,
  1228  		GroupRespCodes: false,
  1229  	}
  1230  
  1231  	weblog := New()
  1232  	weblog.Config = cfg
  1233  	require.True(t, weblog.Init())
  1234  	require.True(t, weblog.Check())
  1235  	defer weblog.Cleanup()
  1236  
  1237  	p, err := logs.NewCSVParser(weblog.Parser.CSV, bytes.NewReader(testCommonLog))
  1238  	require.NoError(t, err)
  1239  	weblog.parser = p
  1240  	return weblog
  1241  }
  1242  
  1243  func prepareWebLogCollectCustom(t *testing.T) *WebLog {
  1244  	t.Helper()
  1245  	format := strings.Join([]string{
  1246  		"$side",
  1247  		"$drink",
  1248  	}, " ")
  1249  
  1250  	cfg := Config{
  1251  		Parser: logs.ParserConfig{
  1252  			LogType: logs.TypeCSV,
  1253  			CSV: logs.CSVConfig{
  1254  				FieldsPerRecord:  2,
  1255  				Delimiter:        " ",
  1256  				TrimLeadingSpace: false,
  1257  				Format:           format,
  1258  				CheckField:       checkCSVFormatField,
  1259  			},
  1260  		},
  1261  		CustomFields: []customField{
  1262  			{
  1263  				Name: "side",
  1264  				Patterns: []userPattern{
  1265  					{Name: "dark", Match: "= dark"},
  1266  					{Name: "light", Match: "= light"},
  1267  				},
  1268  			},
  1269  			{
  1270  				Name: "drink",
  1271  				Patterns: []userPattern{
  1272  					{Name: "beer", Match: "= beer"},
  1273  					{Name: "wine", Match: "= wine"},
  1274  				},
  1275  			},
  1276  		},
  1277  		Path:           "testdata/custom.log",
  1278  		ExcludePath:    "",
  1279  		URLPatterns:    nil,
  1280  		Histogram:      nil,
  1281  		GroupRespCodes: false,
  1282  	}
  1283  	weblog := New()
  1284  	weblog.Config = cfg
  1285  	require.True(t, weblog.Init())
  1286  	require.True(t, weblog.Check())
  1287  	defer weblog.Cleanup()
  1288  
  1289  	p, err := logs.NewCSVParser(weblog.Parser.CSV, bytes.NewReader(testCustomLog))
  1290  	require.NoError(t, err)
  1291  	weblog.parser = p
  1292  	return weblog
  1293  }
  1294  
  1295  func prepareWebLogCollectCustomTimeFields(t *testing.T) *WebLog {
  1296  	t.Helper()
  1297  	format := strings.Join([]string{
  1298  		"$time1",
  1299  		"$time2",
  1300  	}, " ")
  1301  
  1302  	cfg := Config{
  1303  		Parser: logs.ParserConfig{
  1304  			LogType: logs.TypeCSV,
  1305  			CSV: logs.CSVConfig{
  1306  				FieldsPerRecord:  2,
  1307  				Delimiter:        " ",
  1308  				TrimLeadingSpace: false,
  1309  				Format:           format,
  1310  				CheckField:       checkCSVFormatField,
  1311  			},
  1312  		},
  1313  		CustomTimeFields: []customTimeField{
  1314  			{
  1315  				Name:      "time1",
  1316  				Histogram: metrics.DefBuckets,
  1317  			},
  1318  			{
  1319  				Name:      "time2",
  1320  				Histogram: metrics.DefBuckets,
  1321  			},
  1322  		},
  1323  		Path:           "testdata/custom_time_fields.log",
  1324  		ExcludePath:    "",
  1325  		URLPatterns:    nil,
  1326  		Histogram:      nil,
  1327  		GroupRespCodes: false,
  1328  	}
  1329  	weblog := New()
  1330  	weblog.Config = cfg
  1331  	require.True(t, weblog.Init())
  1332  	require.True(t, weblog.Check())
  1333  	defer weblog.Cleanup()
  1334  
  1335  	p, err := logs.NewCSVParser(weblog.Parser.CSV, bytes.NewReader(testCustomTimeFieldLog))
  1336  	require.NoError(t, err)
  1337  	weblog.parser = p
  1338  	return weblog
  1339  }
  1340  
  1341  func prepareWebLogCollectCustomNumericFields(t *testing.T) *WebLog {
  1342  	t.Helper()
  1343  	format := strings.Join([]string{
  1344  		"$numeric1",
  1345  		"$numeric2",
  1346  	}, " ")
  1347  
  1348  	cfg := Config{
  1349  		Parser: logs.ParserConfig{
  1350  			LogType: logs.TypeCSV,
  1351  			CSV: logs.CSVConfig{
  1352  				FieldsPerRecord:  2,
  1353  				Delimiter:        " ",
  1354  				TrimLeadingSpace: false,
  1355  				Format:           format,
  1356  				CheckField:       checkCSVFormatField,
  1357  			},
  1358  		},
  1359  		CustomNumericFields: []customNumericField{
  1360  			{
  1361  				Name:  "numeric1",
  1362  				Units: "bytes",
  1363  			},
  1364  			{
  1365  				Name:  "numeric2",
  1366  				Units: "requests",
  1367  			},
  1368  		},
  1369  		Path:           "testdata/custom_time_fields.log",
  1370  		ExcludePath:    "",
  1371  		URLPatterns:    nil,
  1372  		Histogram:      nil,
  1373  		GroupRespCodes: false,
  1374  	}
  1375  	weblog := New()
  1376  	weblog.Config = cfg
  1377  	require.True(t, weblog.Init())
  1378  	require.True(t, weblog.Check())
  1379  	defer weblog.Cleanup()
  1380  
  1381  	p, err := logs.NewCSVParser(weblog.Parser.CSV, bytes.NewReader(testCustomTimeFieldLog))
  1382  	require.NoError(t, err)
  1383  	weblog.parser = p
  1384  	return weblog
  1385  }
  1386  
  1387  func prepareWebLogCollectIISFields(t *testing.T) *WebLog {
  1388  	t.Helper()
  1389  	format := strings.Join([]string{
  1390  		"-",               // date
  1391  		"-",               // time
  1392  		"$host",           // s-ip
  1393  		"$request_method", // cs-method
  1394  		"$request_uri",    // cs-uri-stem
  1395  		"-",               // cs-uri-query
  1396  		"$server_port",    // s-port
  1397  		"-",               // cs-username
  1398  		"$remote_addr",    // c-ip
  1399  		"-",               // cs(User-Agent)
  1400  		"-",               // cs(Referer)
  1401  		"$status",         // sc-status
  1402  		"-",               // sc-substatus
  1403  		"-",               // sc-win32-status
  1404  		"$request_time",   // time-taken
  1405  	}, " ")
  1406  	cfg := Config{
  1407  		Parser: logs.ParserConfig{
  1408  			LogType: logs.TypeCSV,
  1409  			CSV: logs.CSVConfig{
  1410  				// Users can define number of fields
  1411  				FieldsPerRecord:  -1,
  1412  				Delimiter:        " ",
  1413  				TrimLeadingSpace: false,
  1414  				Format:           format,
  1415  				CheckField:       checkCSVFormatField,
  1416  			},
  1417  		},
  1418  		Path:           "testdata/u_ex221107.log",
  1419  		ExcludePath:    "",
  1420  		URLPatterns:    nil,
  1421  		Histogram:      nil,
  1422  		GroupRespCodes: false,
  1423  	}
  1424  
  1425  	weblog := New()
  1426  	weblog.Config = cfg
  1427  	require.True(t, weblog.Init())
  1428  	require.True(t, weblog.Check())
  1429  	defer weblog.Cleanup()
  1430  
  1431  	p, err := logs.NewCSVParser(weblog.Parser.CSV, bytes.NewReader(testIISLog))
  1432  	require.NoError(t, err)
  1433  	weblog.parser = p
  1434  	return weblog
  1435  }
  1436  
  1437  // generateLogs is used to populate 'testdata/full.log'
  1438  //func generateLogs(w io.Writer, num int) error {
  1439  //	var (
  1440  //		vhost     = []string{"localhost", "test.example.com", "test.example.org", "198.51.100.1", "2001:db8:1ce::1"}
  1441  //		scheme    = []string{"http", "https"}
  1442  //		client    = []string{"localhost", "203.0.113.1", "203.0.113.2", "2001:db8:2ce:1", "2001:db8:2ce:2"}
  1443  //		method    = []string{"GET", "HEAD", "POST"}
  1444  //		url       = []string{"example.other", "example.com", "example.org", "example.net"}
  1445  //		version   = []string{"1.1", "2", "2.0"}
  1446  //		status    = []int{100, 101, 200, 201, 300, 301, 400, 401} // no 5xx on purpose
  1447  //		sslProto  = []string{"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3", "SSLv2", "SSLv3"}
  1448  //		sslCipher = []string{"ECDHE-RSA-AES256-SHA", "DHE-RSA-AES256-SHA", "AES256-SHA", "PSK-RC4-SHA"}
  1449  //
  1450  //		customField1 = []string{"dark", "light"}
  1451  //		customField2 = []string{"beer", "wine"}
  1452  //	)
  1453  //
  1454  //	var line string
  1455  //	for i := 0; i < num; i++ {
  1456  //		unmatched := randInt(1, 100) > 90
  1457  //		if unmatched {
  1458  //			line = "Unmatched! The rat the cat the dog chased killed ate the malt!\n"
  1459  //		} else {
  1460  //			// test.example.com:80 203.0.113.1 - - "GET / HTTP/1.1" 200 1674 2674 3674 4674 http TLSv1 AES256-SHA dark beer
  1461  //			line = fmt.Sprintf(
  1462  //				"%s:%d %s - - [22/Mar/2009:09:30:31 +0100] \"%s /%s HTTP/%s\" %d %d %d %d %d %s %s %s %s %s\n",
  1463  //				randFromString(vhost),
  1464  //				randInt(80, 85),
  1465  //				randFromString(client),
  1466  //				randFromString(method),
  1467  //				randFromString(url),
  1468  //				randFromString(version),
  1469  //				randFromInt(status),
  1470  //				randInt(1000, 5000),
  1471  //				randInt(1000, 5000),
  1472  //				randInt(1, 500),
  1473  //				randInt(1, 500),
  1474  //				randFromString(scheme),
  1475  //				randFromString(sslProto),
  1476  //				randFromString(sslCipher),
  1477  //				randFromString(customField1),
  1478  //				randFromString(customField2),
  1479  //			)
  1480  //		}
  1481  //		_, err := fmt.Fprint(w, line)
  1482  //		if err != nil {
  1483  //			return err
  1484  //		}
  1485  //	}
  1486  //	return nil
  1487  //}
  1488  //
  1489  //var r = rand.New(rand.NewSource(time.Now().UnixNano()))
  1490  //
  1491  //func randFromString(s []string) string { return s[r.Intn(len(s))] }
  1492  //func randFromInt(s []int) int          { return s[r.Intn(len(s))] }
  1493  //func randInt(min, max int) int         { return r.Intn(max-min) + min }