summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/modules/squidlog
diff options
context:
space:
mode:
Diffstat (limited to '')
l---------src/go/collectors/go.d.plugin/modules/squidlog/README.md1
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/charts.go368
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/collect.go360
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/config_schema.json217
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/init.go74
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/integrations/squid_log_files.md249
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/logline.go407
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/logline_test.go477
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/metadata.yaml315
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/metrics.go93
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/squidlog.go112
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/squidlog_test.go348
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/testdata/access.log500
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/testdata/config.json27
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/testdata/config.yaml19
-rw-r--r--src/go/collectors/go.d.plugin/modules/squidlog/testdata/unknown.log1
16 files changed, 3568 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/README.md b/src/go/collectors/go.d.plugin/modules/squidlog/README.md
new file mode 120000
index 000000000..876d4b47a
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/README.md
@@ -0,0 +1 @@
+integrations/squid_log_files.md \ No newline at end of file
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/charts.go b/src/go/collectors/go.d.plugin/modules/squidlog/charts.go
new file mode 100644
index 000000000..dfac22498
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/charts.go
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package squidlog
+
+import (
+ "errors"
+
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+)
+
+type (
+ Charts = module.Charts
+ Chart = module.Chart
+ Dims = module.Dims
+ Dim = module.Dim
+)
+
+const (
+ prioReqTotal = module.Priority + iota
+ prioReqExcluded
+ prioReqType
+
+ prioHTTPRespCodesClass
+ prioHTTPRespCodes
+
+ prioUniqClients
+
+ prioBandwidth
+
+ prioRespTime
+
+ prioCacheCode
+ prioCacheTransportTag
+ prioCacheHandlingTag
+ prioCacheObjectTag
+ prioCacheLoadSourceTag
+ prioCacheErrorTag
+
+ prioReqMethod
+
+ prioHierCode
+ prioServers
+
+ prioMimeType
+)
+
+var (
+ // Requests
+ reqTotalChart = Chart{
+ ID: "requests",
+ Title: "Total Requests",
+ Units: "requests/s",
+ Fam: "requests",
+ Ctx: "squidlog.requests",
+ Priority: prioReqTotal,
+ Dims: Dims{
+ {ID: "requests", Algo: module.Incremental},
+ },
+ }
+ reqExcludedChart = Chart{
+ ID: "excluded_requests",
+ Title: "Excluded Requests",
+ Units: "requests/s",
+ Fam: "requests",
+ Ctx: "squidlog.excluded_requests",
+ Priority: prioReqExcluded,
+ Dims: Dims{
+ {ID: "unmatched", Algo: module.Incremental},
+ },
+ }
+ reqTypesChart = Chart{
+ ID: "requests_by_type",
+ Title: "Requests By Type",
+ Units: "requests/s",
+ Fam: "requests",
+ Ctx: "squidlog.type_requests",
+ Type: module.Stacked,
+ Priority: prioReqType,
+ Dims: Dims{
+ {ID: "req_type_success", Name: "success", Algo: module.Incremental},
+ {ID: "req_type_bad", Name: "bad", Algo: module.Incremental},
+ {ID: "req_type_redirect", Name: "redirect", Algo: module.Incremental},
+ {ID: "req_type_error", Name: "error", Algo: module.Incremental},
+ },
+ }
+
+ // HTTP Code
+ httpRespCodeClassChart = Chart{
+ ID: "responses_by_http_status_code_class",
+ Title: "Responses By HTTP Status Code Class",
+ Units: "responses/s",
+ Fam: "http code",
+ Ctx: "squidlog.http_status_code_class_responses",
+ Type: module.Stacked,
+ Priority: prioHTTPRespCodesClass,
+ Dims: Dims{
+ {ID: "http_resp_2xx", Name: "2xx", Algo: module.Incremental},
+ {ID: "http_resp_5xx", Name: "5xx", Algo: module.Incremental},
+ {ID: "http_resp_3xx", Name: "3xx", Algo: module.Incremental},
+ {ID: "http_resp_4xx", Name: "4xx", Algo: module.Incremental},
+ {ID: "http_resp_1xx", Name: "1xx", Algo: module.Incremental},
+ {ID: "http_resp_0xx", Name: "0xx", Algo: module.Incremental},
+ {ID: "http_resp_6xx", Name: "6xx", Algo: module.Incremental},
+ },
+ }
+ httpRespCodesChart = Chart{
+ ID: "responses_by_http_status_code",
+ Title: "Responses By HTTP Status Code",
+ Units: "responses/s",
+ Fam: "http code",
+ Ctx: "squidlog.http_status_code_responses",
+ Type: module.Stacked,
+ Priority: prioHTTPRespCodes,
+ }
+
+ // Bandwidth
+ bandwidthChart = Chart{
+ ID: "bandwidth",
+ Title: "Bandwidth",
+ Units: "kilobits/s",
+ Fam: "bandwidth",
+ Ctx: "squidlog.bandwidth",
+ Priority: prioBandwidth,
+ Dims: Dims{
+ {ID: "bytes_sent", Name: "sent", Algo: module.Incremental, Div: 1000},
+ },
+ }
+
+ // Response Time
+ respTimeChart = Chart{
+ ID: "response_time",
+ Title: "Response Time",
+ Units: "milliseconds",
+ Fam: "timings",
+ Ctx: "squidlog.response_time",
+ Priority: prioRespTime,
+ Dims: Dims{
+ {ID: "resp_time_min", Name: "min", Div: 1000},
+ {ID: "resp_time_max", Name: "max", Div: 1000},
+ {ID: "resp_time_avg", Name: "avg", Div: 1000},
+ },
+ }
+
+ // Clients
+ uniqClientsChart = Chart{
+ ID: "uniq_clients",
+ Title: "Unique Clients",
+ Units: "clients/s",
+ Fam: "clients",
+ Ctx: "squidlog.uniq_clients",
+ Priority: prioUniqClients,
+ Dims: Dims{
+ {ID: "uniq_clients", Name: "clients"},
+ },
+ }
+
+ // Cache Code Result
+ cacheCodeChart = Chart{
+ ID: "requests_by_cache_result_code",
+ Title: "Requests By Cache Result Code",
+ Units: "requests/s",
+ Fam: "cache result",
+ Ctx: "squidlog.cache_result_code_requests",
+ Priority: prioCacheCode,
+ Type: module.Stacked,
+ }
+ cacheCodeTransportTagChart = Chart{
+ ID: "requests_by_cache_result_code_transport_tag",
+ Title: "Requests By Cache Result Delivery Transport Tag",
+ Units: "requests/s",
+ Fam: "cache result",
+ Ctx: "squidlog.cache_result_code_transport_tag_requests",
+ Type: module.Stacked,
+ Priority: prioCacheTransportTag,
+ }
+ cacheCodeHandlingTagChart = Chart{
+ ID: "requests_by_cache_result_code_handling_tag",
+ Title: "Requests By Cache Result Handling Tag",
+ Units: "requests/s",
+ Fam: "cache result",
+ Ctx: "squidlog.cache_result_code_handling_tag_requests",
+ Type: module.Stacked,
+ Priority: prioCacheHandlingTag,
+ }
+ cacheCodeObjectTagChart = Chart{
+ ID: "requests_by_cache_code_object_tag",
+ Title: "Requests By Cache Result Produced Object Tag",
+ Units: "requests/s",
+ Fam: "cache result",
+ Ctx: "squidlog.cache_code_object_tag_requests",
+ Type: module.Stacked,
+ Priority: prioCacheObjectTag,
+ }
+ cacheCodeLoadSourceTagChart = Chart{
+ ID: "requests_by_cache_code_load_source_tag",
+ Title: "Requests By Cache Result Load Source Tag",
+ Units: "requests/s",
+ Fam: "cache result",
+ Ctx: "squidlog.cache_code_load_source_tag_requests",
+ Type: module.Stacked,
+ Priority: prioCacheLoadSourceTag,
+ }
+ cacheCodeErrorTagChart = Chart{
+ ID: "requests_by_cache_code_error_tag",
+ Title: "Requests By Cache Result Errors Tag",
+ Units: "requests/s",
+ Fam: "cache result",
+ Ctx: "squidlog.cache_code_error_tag_requests",
+ Type: module.Stacked,
+ Priority: prioCacheErrorTag,
+ }
+
+ // HTTP Method
+ reqMethodChart = Chart{
+ ID: "requests_by_http_method",
+ Title: "Requests By HTTP Method",
+ Units: "requests/s",
+ Fam: "http method",
+ Ctx: "squidlog.http_method_requests",
+ Type: module.Stacked,
+ Priority: prioReqMethod,
+ }
+
+ // MIME Type
+ mimeTypeChart = Chart{
+ ID: "requests_by_mime_type",
+ Title: "Requests By MIME Type",
+ Units: "requests/s",
+ Fam: "mime type",
+ Ctx: "squidlog.mime_type_requests",
+ Type: module.Stacked,
+ Priority: prioMimeType,
+ }
+
+ // Hierarchy
+ hierCodeChart = Chart{
+ ID: "requests_by_hier_code",
+ Title: "Requests By Hierarchy Code",
+ Units: "requests/s",
+ Fam: "hierarchy",
+ Ctx: "squidlog.hier_code_requests",
+ Type: module.Stacked,
+ Priority: prioHierCode,
+ }
+ serverAddrChart = Chart{
+ ID: "forwarded_requests_by_server_address",
+ Title: "Forwarded Requests By Server Address",
+ Units: "requests/s",
+ Fam: "hierarchy",
+ Ctx: "squidlog.server_address_forwarded_requests",
+ Type: module.Stacked,
+ Priority: prioServers,
+ }
+)
+
+func (s *SquidLog) createCharts(line *logLine) error {
+ if line.empty() {
+ return errors.New("empty line")
+ }
+ charts := &Charts{
+ reqTotalChart.Copy(),
+ reqExcludedChart.Copy(),
+ }
+ if line.hasRespTime() {
+ if err := addRespTimeCharts(charts); err != nil {
+ return err
+ }
+ }
+ if line.hasClientAddress() {
+ if err := addClientAddressCharts(charts); err != nil {
+ return err
+ }
+ }
+ if line.hasCacheCode() {
+ if err := addCacheCodeCharts(charts); err != nil {
+ return err
+ }
+ }
+ if line.hasHTTPCode() {
+ if err := addHTTPRespCodeCharts(charts); err != nil {
+ return err
+ }
+ }
+ if line.hasRespSize() {
+ if err := addRespSizeCharts(charts); err != nil {
+ return err
+ }
+ }
+ if line.hasReqMethod() {
+ if err := addMethodCharts(charts); err != nil {
+ return err
+ }
+ }
+ if line.hasHierCode() {
+ if err := addHierCodeCharts(charts); err != nil {
+ return err
+ }
+ }
+ if line.hasServerAddress() {
+ if err := addServerAddressCharts(charts); err != nil {
+ return err
+ }
+ }
+ if line.hasMimeType() {
+ if err := addMimeTypeCharts(charts); err != nil {
+ return err
+ }
+ }
+ s.charts = charts
+ return nil
+}
+
+func addRespTimeCharts(charts *Charts) error {
+ return charts.Add(respTimeChart.Copy())
+}
+
+func addClientAddressCharts(charts *Charts) error {
+ return charts.Add(uniqClientsChart.Copy())
+}
+
+func addCacheCodeCharts(charts *Charts) error {
+ cs := []Chart{
+ cacheCodeChart,
+ cacheCodeTransportTagChart,
+ cacheCodeHandlingTagChart,
+ cacheCodeObjectTagChart,
+ cacheCodeLoadSourceTagChart,
+ cacheCodeErrorTagChart,
+ }
+ for _, chart := range cs {
+ if err := charts.Add(chart.Copy()); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+func addHTTPRespCodeCharts(charts *Charts) error {
+ cs := []Chart{
+ reqTypesChart,
+ httpRespCodeClassChart,
+ httpRespCodesChart,
+ }
+ for _, chart := range cs {
+ if err := charts.Add(chart.Copy()); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func addRespSizeCharts(charts *Charts) error {
+ return charts.Add(bandwidthChart.Copy())
+}
+
+func addMethodCharts(charts *Charts) error {
+ return charts.Add(reqMethodChart.Copy())
+}
+
+func addHierCodeCharts(charts *Charts) error {
+ return charts.Add(hierCodeChart.Copy())
+}
+func addServerAddressCharts(charts *Charts) error {
+ return charts.Add(serverAddrChart.Copy())
+}
+
+func addMimeTypeCharts(charts *Charts) error {
+ return charts.Add(mimeTypeChart.Copy())
+}
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/collect.go b/src/go/collectors/go.d.plugin/modules/squidlog/collect.go
new file mode 100644
index 000000000..e0ebb6eb4
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/collect.go
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package squidlog
+
+import (
+ "io"
+ "runtime"
+ "strconv"
+ "strings"
+
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/logs"
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/stm"
+
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+)
+
+func (s *SquidLog) logPanicStackIfAny() {
+ err := recover()
+ if err == nil {
+ return
+ }
+ s.Errorf("[ERROR] %s\n", err)
+ for depth := 0; ; depth++ {
+ _, file, line, ok := runtime.Caller(depth)
+ if !ok {
+ break
+ }
+ s.Errorf("======> %d: %v:%d", depth, file, line)
+ }
+ panic(err)
+}
+
+func (s *SquidLog) collect() (map[string]int64, error) {
+ defer s.logPanicStackIfAny()
+ s.mx.reset()
+
+ var mx map[string]int64
+
+ n, err := s.collectLogLines()
+
+ if n > 0 || err == nil {
+ mx = stm.ToMap(s.mx)
+ }
+ return mx, err
+}
+
+func (s *SquidLog) collectLogLines() (int, error) {
+ var n int
+ for {
+ s.line.reset()
+ err := s.parser.ReadLine(s.line)
+ if err != nil {
+ if err == io.EOF {
+ return n, nil
+ }
+ if !logs.IsParseError(err) {
+ return n, err
+ }
+ n++
+ s.collectUnmatched()
+ continue
+ }
+ n++
+ if s.line.empty() {
+ s.collectUnmatched()
+ } else {
+ s.collectLogLine()
+ }
+ }
+}
+
+func (s *SquidLog) collectLogLine() {
+ s.mx.Requests.Inc()
+ s.collectRespTime()
+ s.collectClientAddress()
+ s.collectCacheCode()
+ s.collectHTTPCode()
+ s.collectRespSize()
+ s.collectReqMethod()
+ s.collectHierCode()
+ s.collectServerAddress()
+ s.collectMimeType()
+}
+
+func (s *SquidLog) collectUnmatched() {
+ s.mx.Requests.Inc()
+ s.mx.Unmatched.Inc()
+}
+
+func (s *SquidLog) collectRespTime() {
+ if !s.line.hasRespTime() {
+ return
+ }
+ s.mx.RespTime.Observe(float64(s.line.respTime))
+}
+
+func (s *SquidLog) collectClientAddress() {
+ if !s.line.hasClientAddress() {
+ return
+ }
+ s.mx.UniqueClients.Insert(s.line.clientAddr)
+}
+
+func (s *SquidLog) collectCacheCode() {
+ if !s.line.hasCacheCode() {
+ return
+ }
+
+ c, ok := s.mx.CacheCode.GetP(s.line.cacheCode)
+ if !ok {
+ s.addDimToCacheCodeChart(s.line.cacheCode)
+ }
+ c.Inc()
+
+ tags := strings.Split(s.line.cacheCode, "_")
+ for _, tag := range tags {
+ s.collectCacheCodeTag(tag)
+ }
+}
+
+func (s *SquidLog) collectHTTPCode() {
+ if !s.line.hasHTTPCode() {
+ return
+ }
+
+ code := s.line.httpCode
+ switch {
+ case code >= 100 && code < 300, code == 0, code == 304, code == 401:
+ s.mx.ReqSuccess.Inc()
+ case code >= 300 && code < 400:
+ s.mx.ReqRedirect.Inc()
+ case code >= 400 && code < 500:
+ s.mx.ReqBad.Inc()
+ case code >= 500 && code <= 603:
+ s.mx.ReqError.Inc()
+ }
+
+ switch code / 100 {
+ case 0:
+ s.mx.HTTPResp0xx.Inc()
+ case 1:
+ s.mx.HTTPResp1xx.Inc()
+ case 2:
+ s.mx.HTTPResp2xx.Inc()
+ case 3:
+ s.mx.HTTPResp3xx.Inc()
+ case 4:
+ s.mx.HTTPResp4xx.Inc()
+ case 5:
+ s.mx.HTTPResp5xx.Inc()
+ case 6:
+ s.mx.HTTPResp6xx.Inc()
+ }
+
+ codeStr := strconv.Itoa(code)
+ c, ok := s.mx.HTTPRespCode.GetP(codeStr)
+ if !ok {
+ s.addDimToHTTPRespCodesChart(codeStr)
+ }
+ c.Inc()
+}
+
+func (s *SquidLog) collectRespSize() {
+ if !s.line.hasRespSize() {
+ return
+ }
+ s.mx.BytesSent.Add(float64(s.line.respSize))
+}
+
+func (s *SquidLog) collectReqMethod() {
+ if !s.line.hasReqMethod() {
+ return
+ }
+ c, ok := s.mx.ReqMethod.GetP(s.line.reqMethod)
+ if !ok {
+ s.addDimToReqMethodChart(s.line.reqMethod)
+ }
+ c.Inc()
+}
+
+func (s *SquidLog) collectHierCode() {
+ if !s.line.hasHierCode() {
+ return
+ }
+ c, ok := s.mx.HierCode.GetP(s.line.hierCode)
+ if !ok {
+ s.addDimToHierCodeChart(s.line.hierCode)
+ }
+ c.Inc()
+}
+
+func (s *SquidLog) collectServerAddress() {
+ if !s.line.hasServerAddress() {
+ return
+ }
+ c, ok := s.mx.Server.GetP(s.line.serverAddr)
+ if !ok {
+ s.addDimToServerAddressChart(s.line.serverAddr)
+ }
+ c.Inc()
+}
+
+func (s *SquidLog) collectMimeType() {
+ if !s.line.hasMimeType() {
+ return
+ }
+ c, ok := s.mx.MimeType.GetP(s.line.mimeType)
+ if !ok {
+ s.addDimToMimeTypeChart(s.line.mimeType)
+ }
+ c.Inc()
+}
+
+func (s *SquidLog) collectCacheCodeTag(tag string) {
+ // https://wiki.squid-cache.org/SquidFaq/SquidLogs#Squid_result_codes
+ switch tag {
+ default:
+ case "TCP", "UDP", "NONE":
+ c, ok := s.mx.CacheCodeTransportTag.GetP(tag)
+ if !ok {
+ s.addDimToCacheCodeTransportTagChart(tag)
+ }
+ c.Inc()
+ case "CF", "CLIENT", "IMS", "ASYNC", "SWAPFAIL", "REFRESH", "SHARED", "REPLY":
+ c, ok := s.mx.CacheCodeHandlingTag.GetP(tag)
+ if !ok {
+ s.addDimToCacheCodeHandlingTagChart(tag)
+ }
+ c.Inc()
+ case "NEGATIVE", "STALE", "OFFLINE", "INVALID", "FAIL", "MODIFIED", "UNMODIFIED", "REDIRECT":
+ c, ok := s.mx.CacheCodeObjectTag.GetP(tag)
+ if !ok {
+ s.addDimToCacheCodeObjectTagChart(tag)
+ }
+ c.Inc()
+ case "HIT", "MEM", "MISS", "DENIED", "NOFETCH", "TUNNEL":
+ c, ok := s.mx.CacheCodeLoadSourceTag.GetP(tag)
+ if !ok {
+ s.addDimToCacheCodeLoadSourceTagChart(tag)
+ }
+ c.Inc()
+ case "ABORTED", "TIMEOUT", "IGNORED":
+ c, ok := s.mx.CacheCodeErrorTag.GetP(tag)
+ if !ok {
+ s.addDimToCacheCodeErrorTagChart(tag)
+ }
+ c.Inc()
+ }
+}
+
+func (s *SquidLog) addDimToCacheCodeChart(code string) {
+ chartID := cacheCodeChart.ID
+ dimID := pxCacheCode + code
+ s.addDimToChart(chartID, dimID, code)
+}
+
+func (s *SquidLog) addDimToCacheCodeTransportTagChart(tag string) {
+ chartID := cacheCodeTransportTagChart.ID
+ dimID := pxTransportTag + tag
+ s.addDimToChart(chartID, dimID, tag)
+}
+
+func (s *SquidLog) addDimToCacheCodeHandlingTagChart(tag string) {
+ chartID := cacheCodeHandlingTagChart.ID
+ dimID := pxHandlingTag + tag
+ s.addDimToChart(chartID, dimID, tag)
+}
+
+func (s *SquidLog) addDimToCacheCodeObjectTagChart(tag string) {
+ chartID := cacheCodeObjectTagChart.ID
+ dimID := pxObjectTag + tag
+ s.addDimToChart(chartID, dimID, tag)
+}
+
+func (s *SquidLog) addDimToCacheCodeLoadSourceTagChart(tag string) {
+ chartID := cacheCodeLoadSourceTagChart.ID
+ dimID := pxSourceTag + tag
+ s.addDimToChart(chartID, dimID, tag)
+}
+
+func (s *SquidLog) addDimToCacheCodeErrorTagChart(tag string) {
+ chartID := cacheCodeErrorTagChart.ID
+ dimID := pxErrorTag + tag
+ s.addDimToChart(chartID, dimID, tag)
+}
+
+func (s *SquidLog) addDimToHTTPRespCodesChart(tag string) {
+ chartID := httpRespCodesChart.ID
+ dimID := pxHTTPCode + tag
+ s.addDimToChart(chartID, dimID, tag)
+}
+
+func (s *SquidLog) addDimToReqMethodChart(method string) {
+ chartID := reqMethodChart.ID
+ dimID := pxReqMethod + method
+ s.addDimToChart(chartID, dimID, method)
+}
+
+func (s *SquidLog) addDimToHierCodeChart(code string) {
+ chartID := hierCodeChart.ID
+ dimID := pxHierCode + code
+ dimName := code[5:] // remove "HIER_"
+ s.addDimToChart(chartID, dimID, dimName)
+}
+
+func (s *SquidLog) addDimToServerAddressChart(address string) {
+ chartID := serverAddrChart.ID
+ dimID := pxSrvAddr + address
+ s.addDimToChartOrCreateIfNotExist(chartID, dimID, address)
+}
+
+func (s *SquidLog) addDimToMimeTypeChart(mimeType string) {
+ chartID := mimeTypeChart.ID
+ dimID := pxMimeType + mimeType
+ s.addDimToChartOrCreateIfNotExist(chartID, dimID, mimeType)
+}
+
+func (s *SquidLog) addDimToChart(chartID, dimID, dimName string) {
+ chart := s.Charts().Get(chartID)
+ if chart == nil {
+ s.Warningf("add '%s' dim: couldn't find '%s' chart in charts", dimID, chartID)
+ return
+ }
+
+ dim := &Dim{ID: dimID, Name: dimName, Algo: module.Incremental}
+
+ if err := chart.AddDim(dim); err != nil {
+ s.Warningf("add '%s' dim: %v", dimID, err)
+ return
+ }
+ chart.MarkNotCreated()
+}
+
+func (s *SquidLog) addDimToChartOrCreateIfNotExist(chartID, dimID, dimName string) {
+ if s.Charts().Has(chartID) {
+ s.addDimToChart(chartID, dimID, dimName)
+ return
+ }
+
+ chart := newChartByID(chartID)
+ if chart == nil {
+ s.Warningf("add '%s' dim: couldn't create '%s' chart", dimID, chartID)
+ return
+ }
+ if err := s.Charts().Add(chart); err != nil {
+ s.Warning(err)
+ return
+ }
+ s.addDimToChart(chartID, dimID, dimName)
+}
+
+func newChartByID(chartID string) *Chart {
+ switch chartID {
+ case serverAddrChart.ID:
+ return serverAddrChart.Copy()
+ case mimeTypeChart.ID:
+ return mimeTypeChart.Copy()
+ }
+ return nil
+}
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/config_schema.json b/src/go/collectors/go.d.plugin/modules/squidlog/config_schema.json
new file mode 100644
index 000000000..47e55b09b
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/config_schema.json
@@ -0,0 +1,217 @@
+{
+ "jsonSchema": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "properties": {
+ "update_every": {
+ "title": "Update every",
+ "description": "Data collection interval, measured in seconds.",
+ "type": "integer",
+ "minimum": 1,
+ "default": 1
+ },
+ "path": {
+ "title": "Log file",
+ "description": "The file path to the Squid server log file.",
+ "type": "string",
+ "default": "/var/log/squid/access.log",
+ "pattern": "^$|^/"
+ },
+ "exclude_path": {
+ "title": "Exclude path",
+ "description": "Pattern to exclude log files.",
+ "type": "string",
+ "default": "*.gz"
+ },
+ "log_type": {
+ "title": "Log parser",
+ "description": "Type of parser to use for parsing the Squid server log file.",
+ "type": "string",
+ "enum": [
+ "csv",
+ "regexp",
+ "json",
+ "ltsv"
+ ],
+ "default": "csv"
+ }
+ },
+ "required": [
+ "path",
+ "log_type"
+ ],
+ "additionalProperties": false,
+ "patternProperties": {
+ "^name$": {}
+ },
+ "dependencies": {
+ "log_type": {
+ "oneOf": [
+ {
+ "properties": {
+ "log_type": {
+ "const": "csv"
+ },
+ "csv_config": {
+ "title": "CSV parser configuration",
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "format": {
+ "title": "Format",
+ "description": "Log format.",
+ "type": "string",
+ "default": "$remote_addr - - [$time_local] \"$request\" $status $body_bytes_sent"
+ },
+ "delimiter": {
+ "title": "Delimiter",
+ "description": "Delimiter used to separate fields in the log file. Default: space (' ').",
+ "type": "string",
+ "default": " "
+ }
+ },
+ "required": [
+ "format",
+ "delimiter"
+ ]
+ }
+ },
+ "required": [
+ "csv_config"
+ ]
+ },
+ {
+ "properties": {
+ "log_type": {
+ "const": "regexp"
+ },
+ "regexp_config": {
+ "title": "Regular expression parser configuration",
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "pattern": {
+ "title": "Pattern with named groups",
+ "description": "Regular expression pattern with named groups. Use named groups for known fields.",
+ "type": "string",
+ "default": ""
+ }
+ },
+ "required": [
+ "pattern"
+ ]
+ }
+ },
+ "required": [
+ "regexp_config"
+ ]
+ },
+ {
+ "properties": {
+ "log_type": {
+ "const": "json"
+ },
+ "json_config": {
+ "title": "JSON parser configuration",
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "mapping": {
+ "title": "Field mapping",
+ "description": "Dictionary mapping fields in logs to known fields.",
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "properties": {
+ "log_type": {
+ "const": "ltsv"
+ },
+ "ltsv_config": {
+ "title": "LTSV parser configuration",
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "field_delimiter": {
+ "title": "Field delimiter",
+ "description": "Delimiter used to separate fields in LTSV logs. Default: tab ('\\t').",
+ "type": "string",
+ "default": "\t"
+ },
+ "value_delimiter": {
+ "title": "Value delimiter",
+ "description": "Delimiter used to separate label-value pairs in LTSV logs.",
+ "type": "string",
+ "default": ":"
+ },
+ "mapping": {
+ "title": "Field mapping",
+ "description": "Dictionary mapping fields in logs to known fields.",
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ },
+ "uiSchema": {
+ "uiOptions": {
+ "fullPage": true
+ },
+ "log_type": {
+ "ui:widget": "radio",
+ "ui:options": {
+ "inline": true
+ }
+ },
+ "ui:flavour": "tabs",
+ "ui:options": {
+ "tabs": [
+ {
+ "title": "Base",
+ "fields": [
+ "update_every",
+ "path",
+ "exclude_path"
+ ]
+ },
+ {
+ "title": "Parser",
+ "fields": [
+ "log_type",
+ "csv_config",
+ "ltsv_config",
+ "regexp_config",
+ "json_config"
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/init.go b/src/go/collectors/go.d.plugin/modules/squidlog/init.go
new file mode 100644
index 000000000..b995b3e65
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/init.go
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package squidlog
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/logs"
+)
+
+func (s *SquidLog) createLogReader() error {
+ s.Cleanup()
+ s.Debug("starting log reader creating")
+
+ reader, err := logs.Open(s.Path, s.ExcludePath, s.Logger)
+ if err != nil {
+ return fmt.Errorf("creating log reader: %v", err)
+ }
+
+ s.Debugf("created log reader, current file '%s'", reader.CurrentFilename())
+ s.file = reader
+ return nil
+}
+
+func (s *SquidLog) createParser() error {
+ s.Debug("starting parser creating")
+ lastLine, err := logs.ReadLastLine(s.file.CurrentFilename(), 0)
+ if err != nil {
+ return fmt.Errorf("read last line: %v", err)
+ }
+
+ lastLine = bytes.TrimRight(lastLine, "\n")
+ s.Debugf("last line: '%s'", string(lastLine))
+
+ s.parser, err = logs.NewParser(s.ParserConfig, s.file)
+ if err != nil {
+ return fmt.Errorf("create parser: %v", err)
+ }
+ s.Debugf("created parser: %s", s.parser.Info())
+
+ err = s.parser.Parse(lastLine, s.line)
+ if err != nil {
+ return fmt.Errorf("parse last line: %v (%s)", err, string(lastLine))
+ }
+
+ if err = s.line.verify(); err != nil {
+ return fmt.Errorf("verify last line: %v (%s)", err, string(lastLine))
+ }
+ return nil
+}
+
+func checkCSVFormatField(name string) (newName string, offset int, valid bool) {
+ name = cleanField(name)
+ if !knownField(name) {
+ return "", 0, false
+ }
+ return name, 0, true
+}
+
+func cleanField(name string) string {
+ return strings.TrimLeft(name, "$%")
+}
+
+func knownField(name string) bool {
+ switch name {
+ case fieldRespTime, fieldClientAddr, fieldCacheCode, fieldHTTPCode, fieldRespSize, fieldReqMethod:
+ fallthrough
+ case fieldHierCode, fieldServerAddr, fieldMimeType, fieldResultCode, fieldHierarchy:
+ return true
+ }
+ return false
+}
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/integrations/squid_log_files.md b/src/go/collectors/go.d.plugin/modules/squidlog/integrations/squid_log_files.md
new file mode 100644
index 000000000..0cf064b23
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/integrations/squid_log_files.md
@@ -0,0 +1,249 @@
+<!--startmeta
+custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/squidlog/README.md"
+meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/squidlog/metadata.yaml"
+sidebar_label: "Squid log files"
+learn_status: "Published"
+learn_rel_path: "Collecting Metrics/Web Servers and Web Proxies"
+most_popular: True
+message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE"
+endmeta-->
+
+# Squid log files
+
+
+<img src="https://netdata.cloud/img/squid.png" width="150"/>
+
+
+Plugin: go.d.plugin
+Module: squidlog
+
+<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" />
+
+## Overview
+
+his collector monitors Squid servers by parsing their access log files.
+
+
+It automatically detects log files of Squid severs running on localhost.
+
+
+This collector is supported on all platforms.
+
+This collector supports collecting metrics from multiple instances of this integration, including remote instances.
+
+
+### Default Behavior
+
+#### Auto-Detection
+
+This integration doesn't support auto-detection.
+
+#### Limits
+
+The default configuration for this integration does not impose any limits on data collection.
+
+#### Performance Impact
+
+The default configuration for this integration is not expected to impose a significant performance impact on the system.
+
+
+## Metrics
+
+Metrics grouped by *scope*.
+
+The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels.
+
+
+
+### Per Squid log files instance
+
+These metrics refer to the entire monitored application.
+
+This scope has no labels.
+
+Metrics:
+
+| Metric | Dimensions | Unit |
+|:------|:----------|:----|
+| squidlog.requests | requests | requests/s |
+| squidlog.excluded_requests | unmatched | requests/s |
+| squidlog.type_requests | success, bad, redirect, error | requests/s |
+| squidlog.http_status_code_class_responses | 1xx, 2xx, 3xx, 4xx, 5xx | responses/s |
+| squidlog.http_status_code_responses | a dimension per HTTP response code | responses/s |
+| squidlog.bandwidth | sent | kilobits/s |
+| squidlog.response_time | min, max, avg | milliseconds |
+| squidlog.uniq_clients | clients | clients |
+| squidlog.cache_result_code_requests | a dimension per cache result code | requests/s |
+| squidlog.cache_result_code_transport_tag_requests | a dimension per cache result delivery transport tag | requests/s |
+| squidlog.cache_result_code_handling_tag_requests | a dimension per cache result handling tag | requests/s |
+| squidlog.cache_code_object_tag_requests | a dimension per cache result produced object tag | requests/s |
+| squidlog.cache_code_load_source_tag_requests | a dimension per cache result load source tag | requests/s |
+| squidlog.cache_code_error_tag_requests | a dimension per cache result error tag | requests/s |
+| squidlog.http_method_requests | a dimension per HTTP method | requests/s |
+| squidlog.mime_type_requests | a dimension per MIME type | requests/s |
+| squidlog.hier_code_requests | a dimension per hierarchy code | requests/s |
+| squidlog.server_address_forwarded_requests | a dimension per server address | requests/s |
+
+
+
+## Alerts
+
+There are no alerts configured by default for this integration.
+
+
+## Setup
+
+### Prerequisites
+
+No action required.
+
+### Configuration
+
+#### File
+
+The configuration file name for this integration is `go.d/squidlog.conf`.
+
+
+You can edit the configuration file using the `edit-config` script from the
+Netdata [config directory](/docs/netdata-agent/configuration/README.md#the-netdata-config-directory).
+
+```bash
+cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata
+sudo ./edit-config go.d/squidlog.conf
+```
+#### Options
+
+Squid [log format codes](http://www.squid-cache.org/Doc/config/logformat/).
+
+Squidlog is aware how to parse and interpret the following codes:
+
+| field | squid format code | description |
+|----------------|-------------------|---------------------------------------------------------------|
+| resp_time | %tr | Response time (milliseconds). |
+| client_address | %>a | Client source IP address. |
+| client_address | %>A | Client FQDN. |
+| cache_code | %Ss | Squid request status (TCP_MISS etc). |
+| http_code | %>Hs | The HTTP response status code from Content Gateway to client. |
+| resp_size | %<st | Total size of reply sent to client (after adaptation). |
+| req_method | %rm | Request method (GET/POST etc). |
+| hier_code | %Sh | Squid hierarchy status (DEFAULT_PARENT etc). |
+| server_address | %<a | Server IP address of the last server or peer connection. |
+| server_address | %<A | Server FQDN or peer name. |
+| mime_type | %mt | MIME content type. |
+
+In addition, to make `Squid` [native log format](https://wiki.squid-cache.org/Features/LogFormat#Squid_native_access.log_format_in_detail) csv parsable, squidlog understands these groups of codes:
+
+| field | squid format code | description |
+|-------------|-------------------|------------------------------------|
+| result_code | %Ss/%>Hs | Cache code and http code. |
+| hierarchy | %Sh/%<a | Hierarchy code and server address. |
+
+
+<details open><summary>Config options</summary>
+
+| Name | Description | Default | Required |
+|:----|:-----------|:-------|:--------:|
+| update_every | Data collection frequency. | 1 | no |
+| autodetection_retry | Recheck interval in seconds. Zero means no recheck will be scheduled. | 0 | no |
+| path | Path to the Squid access log file. | /var/log/squid/access.log | yes |
+| exclude_path | Path to exclude. | *.gz | no |
+| parser | Log parser configuration. | | no |
+| parser.log_type | Log parser type. | auto | no |
+| parser.csv_config | CSV log parser config. | | no |
+| parser.csv_config.delimiter | CSV field delimiter. | space | no |
+| parser.csv_config.format | CSV log format. | - $resp_time $client_address $result_code $resp_size $req_method - - $hierarchy $mime_type | yes |
+| parser.ltsv_config | LTSV log parser config. | | no |
+| parser.ltsv_config.field_delimiter | LTSV field delimiter. | \t | no |
+| parser.ltsv_config.value_delimiter | LTSV value delimiter. | : | no |
+| parser.ltsv_config.mapping | LTSV fields mapping to **known fields**. | | yes |
+| parser.regexp_config | RegExp log parser config. | | no |
+| parser.regexp_config.pattern | RegExp pattern with named groups. | | yes |
+
+##### parser.log_type
+
+Weblog supports 3 different log parsers:
+
+| Parser type | Description |
+|-------------|-------------------------------------------|
+| csv | A comma-separated values |
+| ltsv | [LTSV](http://ltsv.org/) |
+| regexp | Regular expression with named groups |
+
+Syntax:
+
+```yaml
+parser:
+ log_type: csv
+```
+
+
+##### parser.csv_config.format
+
+
+
+##### parser.ltsv_config.mapping
+
+The mapping is a dictionary where the key is a field, as in logs, and the value is the corresponding **known field**.
+
+> **Note**: don't use `$` and `%` prefixes for mapped field names.
+
+```yaml
+parser:
+ log_type: ltsv
+ ltsv_config:
+ mapping:
+ label1: field1
+ label2: field2
+```
+
+
+##### parser.regexp_config.pattern
+
+Use pattern with subexpressions names. These names should be **known fields**.
+
+> **Note**: don't use `$` and `%` prefixes for mapped field names.
+
+Syntax:
+
+```yaml
+parser:
+ log_type: regexp
+ regexp_config:
+ pattern: PATTERN
+```
+
+
+</details>
+
+#### Examples
+There are no configuration examples.
+
+
+
+## Troubleshooting
+
+### Debug Mode
+
+To troubleshoot issues with the `squidlog` collector, run the `go.d.plugin` with the debug option enabled. The output
+should give you clues as to why the collector isn't working.
+
+- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on
+ your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`.
+
+ ```bash
+ cd /usr/libexec/netdata/plugins.d/
+ ```
+
+- Switch to the `netdata` user.
+
+ ```bash
+ sudo -u netdata -s
+ ```
+
+- Run the `go.d.plugin` to debug the collector:
+
+ ```bash
+ ./go.d.plugin -d -m squidlog
+ ```
+
+
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/logline.go b/src/go/collectors/go.d.plugin/modules/squidlog/logline.go
new file mode 100644
index 000000000..e3d200eaf
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/logline.go
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package squidlog
+
+import (
+ "errors"
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// https://wiki.squid-cache.org/Features/LogFormat
+// http://www.squid-cache.org/Doc/config/logformat/
+// https://wiki.squid-cache.org/SquidFaq/SquidLogs#Squid_result_codes
+// https://www.websense.com/content/support/library/web/v773/wcg_help/squid.aspx
+
+/*
+4.6.1:
+logformat squid %ts.%03tu %6tr %>a %Ss/%03>Hs %<st %rm %ru %[un %Sh/%<a %mt
+logformat common %>a %[ui %[un [%tl] "%rm %ru HTTP/%rv" %>Hs %<st %Ss:%Sh
+logformat combined %>a %[ui %[un [%tl] "%rm %ru HTTP/%rv" %>Hs %<st "%{Referer}>h" "%{User-Agent}>h" %Ss:%Sh
+logformat referrer %ts.%03tu %>a %{Referer}>h %ru
+logformat useragent %>a [%tl] "%{User-Agent}>h"
+logformat icap_squid %ts.%03tu %6icap::tr %>A %icap::to/%03icap::Hs %icap::<st %icap::rm %icap::ru %un -/%icap::<A -
+*/
+
+/*
+Valid Capture Name: [A-Za-z0-9_]+
+// TODO: namings
+
+| local | squid format code | description |
+|-------------------------|-------------------|------------------------------------------------------------------------|
+| resp_time | %tr | Response time (milliseconds).
+| client_address | %>a | Client source IP address.
+| client_address | %>A | Client FQDN.
+| cache_code | %Ss | Squid request status (TCP_MISS etc).
+| http_code | %>Hs | The HTTP response status code from Content Gateway to client.
+| resp_size | %<st | Total size of reply sent to client (after adaptation).
+| req_method | %rm | Request method (GET/POST etc).
+| hier_code | %Sh | Squid hierarchy status (DEFAULT_PARENT etc).
+| server_address | %<a | Server IP address of the last server or peer connection.
+| server_address | %<A | Server FQDN or peer name.
+| mime_type | %mt | MIME content type.
+
+// Following needed to make default log format csv parsable
+| result_code | %Ss/%03>Hs | cache code and http code.
+| hierarchy | %Sh/%<a | hierarchy code and server address.
+
+Notes:
+- %<a: older versions of Squid would put the origin server hostname here.
+*/
+
+var (
+ errEmptyLine = errors.New("empty line")
+ errBadRespTime = errors.New("bad response time")
+ errBadClientAddr = errors.New("bad client address")
+ errBadCacheCode = errors.New("bad cache code")
+ errBadHTTPCode = errors.New("bad http code")
+ errBadRespSize = errors.New("bad response size")
+ errBadReqMethod = errors.New("bad request method")
+ errBadHierCode = errors.New("bad hier code")
+ errBadServerAddr = errors.New("bad server address")
+ errBadMimeType = errors.New("bad mime type")
+ errBadResultCode = errors.New("bad result code")
+ errBadHierarchy = errors.New("bad hierarchy")
+)
+
+func newEmptyLogLine() *logLine {
+ var l logLine
+ l.reset()
+ return &l
+}
+
+type (
+ logLine struct {
+ clientAddr string
+ serverAddr string
+
+ respTime int
+ respSize int
+ httpCode int
+
+ reqMethod string
+ mimeType string
+
+ cacheCode string
+ hierCode string
+ }
+)
+
+const (
+ fieldRespTime = "resp_time"
+ fieldClientAddr = "client_address"
+ fieldCacheCode = "cache_code"
+ fieldHTTPCode = "http_code"
+ fieldRespSize = "resp_size"
+ fieldReqMethod = "req_method"
+ fieldHierCode = "hier_code"
+ fieldServerAddr = "server_address"
+ fieldMimeType = "mime_type"
+ fieldResultCode = "result_code"
+ fieldHierarchy = "hierarchy"
+)
+
+func (l *logLine) Assign(field string, value string) (err error) {
+ if value == "" {
+ return
+ }
+
+ switch field {
+ case fieldRespTime:
+ err = l.assignRespTime(value)
+ case fieldClientAddr:
+ err = l.assignClientAddress(value)
+ case fieldCacheCode:
+ err = l.assignCacheCode(value)
+ case fieldHTTPCode:
+ err = l.assignHTTPCode(value)
+ case fieldRespSize:
+ err = l.assignRespSize(value)
+ case fieldReqMethod:
+ err = l.assignReqMethod(value)
+ case fieldHierCode:
+ err = l.assignHierCode(value)
+ case fieldMimeType:
+ err = l.assignMimeType(value)
+ case fieldServerAddr:
+ err = l.assignServerAddress(value)
+ case fieldResultCode:
+ err = l.assignResultCode(value)
+ case fieldHierarchy:
+ err = l.assignHierarchy(value)
+ }
+ return err
+}
+
+const hyphen = "-"
+
+func (l *logLine) assignRespTime(time string) error {
+ if time == hyphen {
+ return fmt.Errorf("assign '%s': %w", time, errBadRespTime)
+ }
+ v, err := strconv.Atoi(time)
+ if err != nil || !isRespTimeValid(v) {
+ return fmt.Errorf("assign '%s': %w", time, errBadRespTime)
+ }
+ l.respTime = v
+ return nil
+}
+
+func (l *logLine) assignClientAddress(address string) error {
+ if address == hyphen {
+ return fmt.Errorf("assign '%s': %w", address, errBadClientAddr)
+ }
+ l.clientAddr = address
+ return nil
+}
+
+func (l *logLine) assignCacheCode(code string) error {
+ if code == hyphen || !isCacheCodeValid(code) {
+ return fmt.Errorf("assign '%s': %w", code, errBadCacheCode)
+ }
+ l.cacheCode = code
+ return nil
+}
+
+func (l *logLine) assignHTTPCode(code string) error {
+ if code == hyphen {
+ return fmt.Errorf("assign '%s': %w", code, errBadHTTPCode)
+ }
+ v, err := strconv.Atoi(code)
+ if err != nil || !isHTTPCodeValid(v) {
+ return fmt.Errorf("assign '%s': %w", code, errBadHTTPCode)
+ }
+ l.httpCode = v
+ return nil
+}
+
+func (l *logLine) assignResultCode(code string) error {
+ i := strings.IndexByte(code, '/')
+ if i <= 0 {
+ return fmt.Errorf("assign '%s': %w", code, errBadResultCode)
+ }
+ if err := l.assignCacheCode(code[:i]); err != nil {
+ return err
+ }
+ return l.assignHTTPCode(code[i+1:])
+}
+
+func (l *logLine) assignRespSize(size string) error {
+ if size == hyphen {
+ return fmt.Errorf("assign '%s': %w", size, errBadRespSize)
+ }
+ v, err := strconv.Atoi(size)
+ if err != nil || !isRespSizeValid(v) {
+ return fmt.Errorf("assign '%s': %w", size, errBadRespSize)
+ }
+ l.respSize = v
+ return nil
+}
+
+func (l *logLine) assignReqMethod(method string) error {
+ if method == hyphen || !isReqMethodValid(method) {
+ return fmt.Errorf("assign '%s': %w", method, errBadReqMethod)
+ }
+ l.reqMethod = method
+ return nil
+}
+
+func (l *logLine) assignHierCode(code string) error {
+ if code == hyphen || !isHierCodeValid(code) {
+ return fmt.Errorf("assign '%s': %w", code, errBadHierCode)
+ }
+ l.hierCode = code
+ return nil
+}
+
+func (l *logLine) assignServerAddress(address string) error {
+ // Logged as "-" if there is no hierarchy information.
+ // For TCP HIT, TCP failures, cachemgr requests and all UDP requests, there is no hierarchy information.
+ if address == hyphen {
+ return nil
+ }
+ l.serverAddr = address
+ return nil
+}
+
+func (l *logLine) assignHierarchy(hierarchy string) error {
+ i := strings.IndexByte(hierarchy, '/')
+ if i <= 0 {
+ return fmt.Errorf("assign '%s': %w", hierarchy, errBadHierarchy)
+ }
+ if err := l.assignHierCode(hierarchy[:i]); err != nil {
+ return err
+ }
+ return l.assignServerAddress(hierarchy[i+1:])
+}
+
+func (l *logLine) assignMimeType(mime string) error {
+ // ICP exchanges usually don't have any content type, and thus are logged "-".
+ //Also, some weird replies have content types ":" or even empty ones.
+ if mime == hyphen || mime == ":" {
+ return nil
+ }
+ // format: type/subtype, type/subtype;parameter=value
+ i := strings.IndexByte(mime, '/')
+ if i <= 0 || !isMimeTypeValid(mime[:i]) {
+ return fmt.Errorf("assign '%s': %w", mime, errBadMimeType)
+ }
+ l.mimeType = mime[:i] // drop subtype
+ return nil
+}
+
+func (l logLine) verify() error {
+ if l.empty() {
+ return fmt.Errorf("verify: %w", errEmptyLine)
+ }
+ if l.hasRespTime() && !l.isRespTimeValid() {
+ return fmt.Errorf("verify '%d': %w", l.respTime, errBadRespTime)
+ }
+ if l.hasClientAddress() && !l.isClientAddressValid() {
+ return fmt.Errorf("verify '%s': %w", l.clientAddr, errBadClientAddr)
+ }
+ if l.hasCacheCode() && !l.isCacheCodeValid() {
+ return fmt.Errorf("verify '%s': %w", l.cacheCode, errBadCacheCode)
+ }
+ if l.hasHTTPCode() && !l.isHTTPCodeValid() {
+ return fmt.Errorf("verify '%d': %w", l.httpCode, errBadHTTPCode)
+ }
+ if l.hasRespSize() && !l.isRespSizeValid() {
+ return fmt.Errorf("verify '%d': %w", l.respSize, errBadRespSize)
+ }
+ if l.hasReqMethod() && !l.isReqMethodValid() {
+ return fmt.Errorf("verify '%s': %w", l.reqMethod, errBadReqMethod)
+ }
+ if l.hasHierCode() && !l.isHierCodeValid() {
+ return fmt.Errorf("verify '%s': %w", l.hierCode, errBadHierCode)
+ }
+ if l.hasServerAddress() && !l.isServerAddressValid() {
+ return fmt.Errorf("verify '%s': %w", l.serverAddr, errBadServerAddr)
+ }
+ if l.hasMimeType() && !l.isMimeTypeValid() {
+ return fmt.Errorf("verify '%s': %w", l.mimeType, errBadMimeType)
+ }
+ return nil
+}
+
+func (l logLine) empty() bool { return l == emptyLogLine }
+func (l logLine) hasRespTime() bool { return !isEmptyNumber(l.respTime) }
+func (l logLine) hasClientAddress() bool { return !isEmptyString(l.clientAddr) }
+func (l logLine) hasCacheCode() bool { return !isEmptyString(l.cacheCode) }
+func (l logLine) hasHTTPCode() bool { return !isEmptyNumber(l.httpCode) }
+func (l logLine) hasRespSize() bool { return !isEmptyNumber(l.respSize) }
+func (l logLine) hasReqMethod() bool { return !isEmptyString(l.reqMethod) }
+func (l logLine) hasHierCode() bool { return !isEmptyString(l.hierCode) }
+func (l logLine) hasServerAddress() bool { return !isEmptyString(l.serverAddr) }
+func (l logLine) hasMimeType() bool { return !isEmptyString(l.mimeType) }
+func (l logLine) isRespTimeValid() bool { return isRespTimeValid(l.respTime) }
+func (l logLine) isClientAddressValid() bool { return reAddress.MatchString(l.clientAddr) }
+func (l logLine) isCacheCodeValid() bool { return isCacheCodeValid(l.cacheCode) }
+func (l logLine) isHTTPCodeValid() bool { return isHTTPCodeValid(l.httpCode) }
+func (l logLine) isRespSizeValid() bool { return isRespSizeValid(l.respSize) }
+func (l logLine) isReqMethodValid() bool { return isReqMethodValid(l.reqMethod) }
+func (l logLine) isHierCodeValid() bool { return isHierCodeValid(l.hierCode) }
+func (l logLine) isServerAddressValid() bool { return reAddress.MatchString(l.serverAddr) }
+func (l logLine) isMimeTypeValid() bool { return isMimeTypeValid(l.mimeType) }
+
+func (l *logLine) reset() {
+ l.respTime = emptyNumber
+ l.clientAddr = emptyString
+ l.cacheCode = emptyString
+ l.httpCode = emptyNumber
+ l.respSize = emptyNumber
+ l.reqMethod = emptyString
+ l.hierCode = emptyString
+ l.serverAddr = emptyString
+ l.mimeType = emptyString
+}
+
+var emptyLogLine = *newEmptyLogLine()
+
+const (
+ emptyString = "__empty_string__"
+ emptyNumber = -9999
+)
+
+var (
+ // IPv4, IPv6, FQDN.
+ reAddress = regexp.MustCompile(`^(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3}|[a-f0-9:]{3,}|[a-zA-Z0-9-.]{3,})$`)
+)
+
+func isEmptyString(s string) bool {
+ return s == emptyString || s == ""
+}
+
+func isEmptyNumber(n int) bool {
+ return n == emptyNumber
+}
+
+func isRespTimeValid(time int) bool {
+ return time >= 0
+}
+
+// isCacheCodeValid does not guarantee cache result code is valid, but it is very likely.
+func isCacheCodeValid(code string) bool {
+ // https://wiki.squid-cache.org/SquidFaq/SquidLogs#Squid_result_codes
+ if code == "NONE" {
+ return true
+ }
+ return len(code) > 5 && (code[:4] == "TCP_" || code[:4] == "UDP_")
+}
+
+func isHTTPCodeValid(code int) bool {
+ // https://wiki.squid-cache.org/SquidFaq/SquidLogs#HTTP_status_codes
+ return code == 0 || code >= 100 && code <= 603
+}
+
+func isRespSizeValid(size int) bool {
+ return size >= 0
+}
+
+func isReqMethodValid(method string) bool {
+ // https://wiki.squid-cache.org/SquidFaq/SquidLogs#Request_methods
+ switch method {
+ case "GET",
+ "HEAD",
+ "POST",
+ "PUT",
+ "PATCH",
+ "DELETE",
+ "CONNECT",
+ "OPTIONS",
+ "TRACE",
+ "ICP_QUERY",
+ "PURGE",
+ "PROPFIND",
+ "PROPATCH",
+ "MKCOL",
+ "COPY",
+ "MOVE",
+ "LOCK",
+ "UNLOCK",
+ "NONE":
+ return true
+ }
+ return false
+}
+
+// isHierCodeValid does not guarantee hierarchy code is valid, but it is very likely.
+func isHierCodeValid(code string) bool {
+ // https://wiki.squid-cache.org/SquidFaq/SquidLogs#Hierarchy_Codes
+ return len(code) > 6 && code[:5] == "HIER_"
+}
+
+// isMimeTypeValid expects only mime type part.
+func isMimeTypeValid(mimeType string) bool {
+ // https://www.iana.org/assignments/media-types/media-types.xhtml
+ if mimeType == "text" {
+ return true
+ }
+ switch mimeType {
+ case "application", "audio", "font", "image", "message", "model", "multipart", "video":
+ return true
+ }
+ return false
+}
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/logline_test.go b/src/go/collectors/go.d.plugin/modules/squidlog/logline_test.go
new file mode 100644
index 000000000..4a9069e3f
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/logline_test.go
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package squidlog
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+const emptyStr = ""
+
+func TestLogLine_Assign(t *testing.T) {
+ type subTest struct {
+ input string
+ wantLine logLine
+ wantErr error
+ }
+ type test struct {
+ name string
+ field string
+ cases []subTest
+ }
+ tests := []test{
+ {
+ name: "Response Time",
+ field: fieldRespTime,
+ cases: []subTest{
+ {input: "0", wantLine: logLine{respTime: 0}},
+ {input: "1000", wantLine: logLine{respTime: 1000}},
+ {input: emptyStr, wantLine: emptyLogLine},
+ {input: hyphen, wantLine: emptyLogLine, wantErr: errBadRespTime},
+ {input: "-1", wantLine: emptyLogLine, wantErr: errBadRespTime},
+ {input: "0.000", wantLine: emptyLogLine, wantErr: errBadRespTime},
+ },
+ },
+ {
+ name: "Client Address",
+ field: fieldClientAddr,
+ cases: []subTest{
+ {input: "127.0.0.1", wantLine: logLine{clientAddr: "127.0.0.1"}},
+ {input: "::1", wantLine: logLine{clientAddr: "::1"}},
+ {input: "kadr20.m1.netdata.lan", wantLine: logLine{clientAddr: "kadr20.m1.netdata.lan"}},
+ {input: "±!@#$%^&*()", wantLine: logLine{clientAddr: "±!@#$%^&*()"}},
+ {input: emptyStr, wantLine: emptyLogLine},
+ {input: hyphen, wantLine: emptyLogLine, wantErr: errBadClientAddr},
+ },
+ },
+ {
+ name: "Cache Code",
+ field: fieldCacheCode,
+ cases: []subTest{
+ {input: "TCP_MISS", wantLine: logLine{cacheCode: "TCP_MISS"}},
+ {input: "TCP_DENIED", wantLine: logLine{cacheCode: "TCP_DENIED"}},
+ {input: "TCP_CLIENT_REFRESH_MISS", wantLine: logLine{cacheCode: "TCP_CLIENT_REFRESH_MISS"}},
+ {input: "UDP_MISS_NOFETCH", wantLine: logLine{cacheCode: "UDP_MISS_NOFETCH"}},
+ {input: "UDP_INVALID", wantLine: logLine{cacheCode: "UDP_INVALID"}},
+ {input: "NONE", wantLine: logLine{cacheCode: "NONE"}},
+ {input: emptyStr, wantLine: emptyLogLine},
+ {input: hyphen, wantLine: emptyLogLine, wantErr: errBadCacheCode},
+ {input: "TCP", wantLine: emptyLogLine, wantErr: errBadCacheCode},
+ {input: "UDP_", wantLine: emptyLogLine, wantErr: errBadCacheCode},
+ {input: "NONE_MISS", wantLine: emptyLogLine, wantErr: errBadCacheCode},
+ },
+ },
+ {
+ name: "HTTP Code",
+ field: fieldHTTPCode,
+ cases: []subTest{
+ {input: "000", wantLine: logLine{httpCode: 0}},
+ {input: "100", wantLine: logLine{httpCode: 100}},
+ {input: "200", wantLine: logLine{httpCode: 200}},
+ {input: "300", wantLine: logLine{httpCode: 300}},
+ {input: "400", wantLine: logLine{httpCode: 400}},
+ {input: "500", wantLine: logLine{httpCode: 500}},
+ {input: "603", wantLine: logLine{httpCode: 603}},
+ {input: emptyStr, wantLine: emptyLogLine},
+ {input: hyphen, wantLine: emptyLogLine, wantErr: errBadHTTPCode},
+ {input: "1", wantLine: emptyLogLine, wantErr: errBadHTTPCode},
+ {input: "604", wantLine: emptyLogLine, wantErr: errBadHTTPCode},
+ {input: "1000", wantLine: emptyLogLine, wantErr: errBadHTTPCode},
+ {input: "TCP_MISS", wantLine: emptyLogLine, wantErr: errBadHTTPCode},
+ },
+ },
+ {
+ name: "Response Size",
+ field: fieldRespSize,
+ cases: []subTest{
+ {input: "0", wantLine: logLine{respSize: 0}},
+ {input: "1000", wantLine: logLine{respSize: 1000}},
+ {input: emptyStr, wantLine: emptyLogLine},
+ {input: hyphen, wantLine: emptyLogLine, wantErr: errBadRespSize},
+ {input: "-1", wantLine: emptyLogLine, wantErr: errBadRespSize},
+ {input: "0.000", wantLine: emptyLogLine, wantErr: errBadRespSize},
+ },
+ },
+ {
+ name: "Request Method",
+ field: fieldReqMethod,
+ cases: []subTest{
+ {input: "GET", wantLine: logLine{reqMethod: "GET"}},
+ {input: "HEAD", wantLine: logLine{reqMethod: "HEAD"}},
+ {input: "POST", wantLine: logLine{reqMethod: "POST"}},
+ {input: "PUT", wantLine: logLine{reqMethod: "PUT"}},
+ {input: "PATCH", wantLine: logLine{reqMethod: "PATCH"}},
+ {input: "DELETE", wantLine: logLine{reqMethod: "DELETE"}},
+ {input: "CONNECT", wantLine: logLine{reqMethod: "CONNECT"}},
+ {input: "OPTIONS", wantLine: logLine{reqMethod: "OPTIONS"}},
+ {input: "TRACE", wantLine: logLine{reqMethod: "TRACE"}},
+ {input: "ICP_QUERY", wantLine: logLine{reqMethod: "ICP_QUERY"}},
+ {input: "PURGE", wantLine: logLine{reqMethod: "PURGE"}},
+ {input: "PROPFIND", wantLine: logLine{reqMethod: "PROPFIND"}},
+ {input: "PROPATCH", wantLine: logLine{reqMethod: "PROPATCH"}},
+ {input: "MKCOL", wantLine: logLine{reqMethod: "MKCOL"}},
+ {input: "COPY", wantLine: logLine{reqMethod: "COPY"}},
+ {input: "MOVE", wantLine: logLine{reqMethod: "MOVE"}},
+ {input: "LOCK", wantLine: logLine{reqMethod: "LOCK"}},
+ {input: "UNLOCK", wantLine: logLine{reqMethod: "UNLOCK"}},
+ {input: "NONE", wantLine: logLine{reqMethod: "NONE"}},
+ {input: emptyStr, wantLine: emptyLogLine},
+ {input: hyphen, wantLine: emptyLogLine, wantErr: errBadReqMethod},
+ {input: "get", wantLine: emptyLogLine, wantErr: errBadReqMethod},
+ {input: "0.000", wantLine: emptyLogLine, wantErr: errBadReqMethod},
+ {input: "TCP_MISS", wantLine: emptyLogLine, wantErr: errBadReqMethod},
+ },
+ },
+ {
+ name: "Hier Code",
+ field: fieldHierCode,
+ cases: []subTest{
+ {input: "HIER_NONE", wantLine: logLine{hierCode: "HIER_NONE"}},
+ {input: "HIER_SIBLING_HIT", wantLine: logLine{hierCode: "HIER_SIBLING_HIT"}},
+ {input: "HIER_NO_CACHE_DIGEST_DIRECT", wantLine: logLine{hierCode: "HIER_NO_CACHE_DIGEST_DIRECT"}},
+ {input: emptyStr, wantLine: emptyLogLine},
+ {input: hyphen, wantLine: emptyLogLine, wantErr: errBadHierCode},
+ {input: "0.000", wantLine: emptyLogLine, wantErr: errBadHierCode},
+ {input: "TCP_MISS", wantLine: emptyLogLine, wantErr: errBadHierCode},
+ {input: "HIER", wantLine: emptyLogLine, wantErr: errBadHierCode},
+ {input: "HIER_", wantLine: emptyLogLine, wantErr: errBadHierCode},
+ {input: "NONE", wantLine: emptyLogLine, wantErr: errBadHierCode},
+ {input: "SIBLING_HIT", wantLine: emptyLogLine, wantErr: errBadHierCode},
+ {input: "NO_CACHE_DIGEST_DIRECT", wantLine: emptyLogLine, wantErr: errBadHierCode},
+ },
+ },
+ {
+ name: "Server Address",
+ field: fieldServerAddr,
+ cases: []subTest{
+ {input: "127.0.0.1", wantLine: logLine{serverAddr: "127.0.0.1"}},
+ {input: "::1", wantLine: logLine{serverAddr: "::1"}},
+ {input: "kadr20.m1.netdata.lan", wantLine: logLine{serverAddr: "kadr20.m1.netdata.lan"}},
+ {input: "±!@#$%^&*()", wantLine: logLine{serverAddr: "±!@#$%^&*()"}},
+ {input: emptyStr, wantLine: emptyLogLine},
+ {input: hyphen, wantLine: emptyLogLine},
+ },
+ },
+ {
+ name: "Mime Type",
+ field: fieldMimeType,
+ cases: []subTest{
+ {input: "application/zstd", wantLine: logLine{mimeType: "application"}},
+ {input: "audio/3gpp2", wantLine: logLine{mimeType: "audio"}},
+ {input: "font/otf", wantLine: logLine{mimeType: "font"}},
+ {input: "image/tiff", wantLine: logLine{mimeType: "image"}},
+ {input: "message/global", wantLine: logLine{mimeType: "message"}},
+ {input: "model/example", wantLine: logLine{mimeType: "model"}},
+ {input: "multipart/encrypted", wantLine: logLine{mimeType: "multipart"}},
+ {input: "text/html", wantLine: logLine{mimeType: "text"}},
+ {input: "video/3gpp", wantLine: logLine{mimeType: "video"}},
+ {input: emptyStr, wantLine: emptyLogLine},
+ {input: hyphen, wantLine: emptyLogLine},
+ {input: "example/example", wantLine: emptyLogLine, wantErr: errBadMimeType},
+ {input: "unknown/example", wantLine: emptyLogLine, wantErr: errBadMimeType},
+ {input: "audio", wantLine: emptyLogLine, wantErr: errBadMimeType},
+ {input: "/", wantLine: emptyLogLine, wantErr: errBadMimeType},
+ },
+ },
+ {
+ name: "Result Code",
+ field: fieldResultCode,
+ cases: []subTest{
+ {input: "TCP_MISS/000", wantLine: logLine{cacheCode: "TCP_MISS", httpCode: 0}},
+ {input: "TCP_DENIED/603", wantLine: logLine{cacheCode: "TCP_DENIED", httpCode: 603}},
+ {input: emptyStr, wantLine: emptyLogLine},
+ {input: hyphen, wantLine: emptyLogLine, wantErr: errBadResultCode},
+ {input: "TCP_MISS:000", wantLine: emptyLogLine, wantErr: errBadResultCode},
+ {input: "TCP_MISS 000", wantLine: emptyLogLine, wantErr: errBadResultCode},
+ {input: "/", wantLine: emptyLogLine, wantErr: errBadResultCode},
+ {input: "tcp/000", wantLine: emptyLogLine, wantErr: errBadCacheCode},
+ {input: "TCP_MISS/", wantLine: logLine{cacheCode: "TCP_MISS", httpCode: emptyNumber}, wantErr: errBadHTTPCode},
+ },
+ },
+ {
+ name: "Hierarchy",
+ field: fieldHierarchy,
+ cases: []subTest{
+ {input: "HIER_NONE/-", wantLine: logLine{hierCode: "HIER_NONE", serverAddr: emptyString}},
+ {input: "HIER_SIBLING_HIT/127.0.0.1", wantLine: logLine{hierCode: "HIER_SIBLING_HIT", serverAddr: "127.0.0.1"}},
+ {input: emptyStr, wantLine: emptyLogLine},
+ {input: hyphen, wantLine: emptyLogLine, wantErr: errBadHierarchy},
+ {input: "HIER_NONE:-", wantLine: emptyLogLine, wantErr: errBadHierarchy},
+ {input: "HIER_SIBLING_HIT 127.0.0.1", wantLine: emptyLogLine, wantErr: errBadHierarchy},
+ {input: "/", wantLine: emptyLogLine, wantErr: errBadHierarchy},
+ {input: "HIER/-", wantLine: emptyLogLine, wantErr: errBadHierCode},
+ {input: "HIER_NONE/", wantLine: logLine{hierCode: "HIER_NONE", serverAddr: emptyStr}},
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ for i, tc := range tt.cases {
+ name := fmt.Sprintf("[%s:%d]field='%s'|input='%s'", tt.name, i+1, tt.field, tc.input)
+ t.Run(name, func(t *testing.T) {
+
+ line := newEmptyLogLine()
+ err := line.Assign(tt.field, tc.input)
+
+ if tc.wantErr != nil {
+ require.Error(t, err)
+ assert.Truef(t, errors.Is(err, tc.wantErr), "expected '%v' error, got '%v'", tc.wantErr, err)
+ } else {
+ require.NoError(t, err)
+ }
+
+ expected := prepareAssignLogLine(t, tt.field, tc.wantLine)
+ assert.Equal(t, expected, *line)
+ })
+ }
+ }
+}
+
+func TestLogLine_verify(t *testing.T) {
+ type subTest struct {
+ input string
+ wantErr error
+ }
+ type test = struct {
+ name string
+ field string
+ cases []subTest
+ }
+ tests := []test{
+ {
+ name: "Response Time",
+ field: fieldRespTime,
+ cases: []subTest{
+ {input: "0"},
+ {input: "1000"},
+ {input: "-1", wantErr: errBadRespTime},
+ },
+ },
+ {
+ name: "Client Address",
+ field: fieldClientAddr,
+ cases: []subTest{
+ {input: "127.0.0.1"},
+ {input: "::1"},
+ {input: "kadr20.m1.netdata.lan"},
+ {input: emptyStr},
+ {input: "±!@#$%^&*()", wantErr: errBadClientAddr},
+ },
+ },
+ {
+ name: "Cache Code",
+ field: fieldCacheCode,
+ cases: []subTest{
+ {input: "TCP_MISS"},
+ {input: "TCP_DENIED"},
+ {input: "TCP_CLIENT_REFRESH_MISS"},
+ {input: "UDP_MISS_NOFETCH"},
+ {input: "UDP_INVALID"},
+ {input: "NONE"},
+ {input: emptyStr},
+ {input: "TCP", wantErr: errBadCacheCode},
+ {input: "UDP", wantErr: errBadCacheCode},
+ {input: "NONE_MISS", wantErr: errBadCacheCode},
+ },
+ },
+ {
+ name: "HTTP Code",
+ field: fieldHTTPCode,
+ cases: []subTest{
+ {input: "000"},
+ {input: "100"},
+ {input: "200"},
+ {input: "300"},
+ {input: "400"},
+ {input: "500"},
+ {input: "603"},
+ {input: "1", wantErr: errBadHTTPCode},
+ {input: "604", wantErr: errBadHTTPCode},
+ },
+ },
+ {
+ name: "Response Size",
+ field: fieldRespSize,
+ cases: []subTest{
+ {input: "0"},
+ {input: "1000"},
+ {input: "-1", wantErr: errBadRespSize},
+ },
+ },
+ {
+ name: "Request Method",
+ field: fieldReqMethod,
+ cases: []subTest{
+ {input: "GET"},
+ {input: "HEAD"},
+ {input: "POST"},
+ {input: "PUT"},
+ {input: "PATCH"},
+ {input: "DELETE"},
+ {input: "CONNECT"},
+ {input: "OPTIONS"},
+ {input: "TRACE"},
+ {input: "ICP_QUERY"},
+ {input: "PURGE"},
+ {input: "PROPFIND"},
+ {input: "PROPATCH"},
+ {input: "MKCOL"},
+ {input: "COPY"},
+ {input: "MOVE"},
+ {input: "LOCK"},
+ {input: "UNLOCK"},
+ {input: "NONE"},
+ {input: emptyStr},
+ {input: "get", wantErr: errBadReqMethod},
+ {input: "TCP_MISS", wantErr: errBadReqMethod},
+ },
+ },
+ {
+ name: "Hier Code",
+ field: fieldHierCode,
+ cases: []subTest{
+ {input: "HIER_NONE"},
+ {input: "HIER_SIBLING_HIT"},
+ {input: "HIER_NO_CACHE_DIGEST_DIRECT"},
+ {input: emptyStr},
+ {input: "0.000", wantErr: errBadHierCode},
+ {input: "TCP_MISS", wantErr: errBadHierCode},
+ {input: "HIER", wantErr: errBadHierCode},
+ {input: "HIER_", wantErr: errBadHierCode},
+ {input: "NONE", wantErr: errBadHierCode},
+ {input: "SIBLING_HIT", wantErr: errBadHierCode},
+ {input: "NO_CACHE_DIGEST_DIRECT", wantErr: errBadHierCode},
+ },
+ },
+ {
+ name: "Server Address",
+ field: fieldServerAddr,
+ cases: []subTest{
+ {input: "127.0.0.1"},
+ {input: "::1"},
+ {input: "kadr20.m1.netdata.lan"},
+ {input: emptyStr},
+ {input: "±!@#$%^&*()", wantErr: errBadServerAddr},
+ },
+ },
+ {
+ name: "Mime Type",
+ field: fieldMimeType,
+ cases: []subTest{
+ {input: "application"},
+ {input: "audio"},
+ {input: "font"},
+ {input: "image"},
+ {input: "message"},
+ {input: "model"},
+ {input: "multipart"},
+ {input: "text"},
+ {input: "video"},
+ {input: emptyStr},
+ {input: "example/example", wantErr: errBadMimeType},
+ {input: "unknown", wantErr: errBadMimeType},
+ {input: "/", wantErr: errBadMimeType},
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ for i, tc := range tt.cases {
+ name := fmt.Sprintf("[%s:%d]field='%s'|input='%s'", tt.name, i+1, tt.field, tc.input)
+ t.Run(name, func(t *testing.T) {
+ line := prepareVerifyLogLine(t, tt.field, tc.input)
+
+ err := line.verify()
+
+ if tc.wantErr != nil {
+ require.Error(t, err)
+ assert.Truef(t, errors.Is(err, tc.wantErr), "expected '%v' error, got '%v'", tc.wantErr, err)
+ } else {
+ require.NoError(t, err)
+ }
+ })
+ }
+ }
+}
+
+func prepareAssignLogLine(t *testing.T, field string, template logLine) logLine {
+ t.Helper()
+ if template.empty() {
+ return template
+ }
+
+ var line logLine
+ line.reset()
+
+ switch field {
+ default:
+ t.Errorf("prepareAssignLogLine unknown field: '%s'", field)
+ case fieldRespTime:
+ line.respTime = template.respTime
+ case fieldClientAddr:
+ line.clientAddr = template.clientAddr
+ case fieldCacheCode:
+ line.cacheCode = template.cacheCode
+ case fieldHTTPCode:
+ line.httpCode = template.httpCode
+ case fieldRespSize:
+ line.respSize = template.respSize
+ case fieldReqMethod:
+ line.reqMethod = template.reqMethod
+ case fieldHierCode:
+ line.hierCode = template.hierCode
+ case fieldMimeType:
+ line.mimeType = template.mimeType
+ case fieldServerAddr:
+ line.serverAddr = template.serverAddr
+ case fieldResultCode:
+ line.cacheCode = template.cacheCode
+ line.httpCode = template.httpCode
+ case fieldHierarchy:
+ line.hierCode = template.hierCode
+ line.serverAddr = template.serverAddr
+ }
+ return line
+}
+
+func prepareVerifyLogLine(t *testing.T, field string, value string) logLine {
+ t.Helper()
+ var line logLine
+ line.reset()
+
+ switch field {
+ default:
+ t.Errorf("prepareVerifyLogLine unknown field: '%s'", field)
+ case fieldRespTime:
+ v, err := strconv.Atoi(value)
+ require.NoError(t, err)
+ line.respTime = v
+ case fieldClientAddr:
+ line.clientAddr = value
+ case fieldCacheCode:
+ line.cacheCode = value
+ case fieldHTTPCode:
+ v, err := strconv.Atoi(value)
+ require.NoError(t, err)
+ line.httpCode = v
+ case fieldRespSize:
+ v, err := strconv.Atoi(value)
+ require.NoError(t, err)
+ line.respSize = v
+ case fieldReqMethod:
+ line.reqMethod = value
+ case fieldHierCode:
+ line.hierCode = value
+ case fieldMimeType:
+ line.mimeType = value
+ case fieldServerAddr:
+ line.serverAddr = value
+ }
+ return line
+}
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/metadata.yaml b/src/go/collectors/go.d.plugin/modules/squidlog/metadata.yaml
new file mode 100644
index 000000000..82712f9e5
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/metadata.yaml
@@ -0,0 +1,315 @@
+plugin_name: go.d.plugin
+modules:
+ - meta:
+ id: collector-go.d.plugin-squidlog
+ plugin_name: go.d.plugin
+ module_name: squidlog
+ monitored_instance:
+ name: Squid log files
+ link: https://www.lighttpd.net/
+ icon_filename: squid.png
+ categories:
+ - data-collection.web-servers-and-web-proxies
+ keywords:
+ - squid
+ - logs
+ related_resources:
+ integrations:
+ list: []
+ info_provided_to_referring_integrations:
+ description: ""
+ most_popular: true
+ overview:
+ data_collection:
+ metrics_description: |
+ his collector monitors Squid servers by parsing their access log files.
+ method_description: |
+ It automatically detects log files of Squid severs running on localhost.
+ supported_platforms:
+ include: []
+ exclude: []
+ multi_instance: true
+ additional_permissions:
+ description: ""
+ default_behavior:
+ auto_detection:
+ description: ""
+ limits:
+ description: ""
+ performance_impact:
+ description: ""
+ setup:
+ prerequisites:
+ list: []
+ configuration:
+ file:
+ name: go.d/squidlog.conf
+ options:
+ description: |
+ Squid [log format codes](http://www.squid-cache.org/Doc/config/logformat/).
+
+ Squidlog is aware how to parse and interpret the following codes:
+
+ | field | squid format code | description |
+ |----------------|-------------------|---------------------------------------------------------------|
+ | resp_time | %tr | Response time (milliseconds). |
+ | client_address | %>a | Client source IP address. |
+ | client_address | %>A | Client FQDN. |
+ | cache_code | %Ss | Squid request status (TCP_MISS etc). |
+ | http_code | %>Hs | The HTTP response status code from Content Gateway to client. |
+ | resp_size | %<st | Total size of reply sent to client (after adaptation). |
+ | req_method | %rm | Request method (GET/POST etc). |
+ | hier_code | %Sh | Squid hierarchy status (DEFAULT_PARENT etc). |
+ | server_address | %<a | Server IP address of the last server or peer connection. |
+ | server_address | %<A | Server FQDN or peer name. |
+ | mime_type | %mt | MIME content type. |
+
+ In addition, to make `Squid` [native log format](https://wiki.squid-cache.org/Features/LogFormat#Squid_native_access.log_format_in_detail) csv parsable, squidlog understands these groups of codes:
+
+ | field | squid format code | description |
+ |-------------|-------------------|------------------------------------|
+ | result_code | %Ss/%>Hs | Cache code and http code. |
+ | hierarchy | %Sh/%<a | Hierarchy code and server address. |
+ folding:
+ title: Config options
+ enabled: true
+ list:
+ - name: update_every
+ description: Data collection frequency.
+ default_value: 1
+ required: false
+ - name: autodetection_retry
+ description: Recheck interval in seconds. Zero means no recheck will be scheduled.
+ default_value: 0
+ required: false
+ - name: path
+ description: Path to the Squid access log file.
+ default_value: /var/log/squid/access.log
+ required: true
+ - name: exclude_path
+ description: Path to exclude.
+ default_value: "*.gz"
+ required: false
+ - name: parser
+ description: Log parser configuration.
+ default_value: ""
+ required: false
+ - name: parser.log_type
+ description: Log parser type.
+ default_value: auto
+ required: false
+ detailed_description: |
+ Weblog supports 3 different log parsers:
+
+ | Parser type | Description |
+ |-------------|-------------------------------------------|
+ | csv | A comma-separated values |
+ | ltsv | [LTSV](http://ltsv.org/) |
+ | regexp | Regular expression with named groups |
+
+ Syntax:
+
+ ```yaml
+ parser:
+ log_type: csv
+ ```
+ - name: parser.csv_config
+ description: CSV log parser config.
+ default_value: ""
+ required: false
+ - name: parser.csv_config.delimiter
+ description: CSV field delimiter.
+ default_value: space
+ required: false
+ - name: parser.csv_config.format
+ description: CSV log format.
+ default_value: "- $resp_time $client_address $result_code $resp_size $req_method - - $hierarchy $mime_type"
+ required: true
+ detailed_description: ""
+ - name: parser.ltsv_config
+ description: LTSV log parser config.
+ default_value: ""
+ required: false
+ - name: parser.ltsv_config.field_delimiter
+ description: LTSV field delimiter.
+ default_value: "\\t"
+ required: false
+ - name: parser.ltsv_config.value_delimiter
+ description: LTSV value delimiter.
+ default_value: ":"
+ required: false
+ - name: parser.ltsv_config.mapping
+ description: LTSV fields mapping to **known fields**.
+ default_value: ""
+ required: true
+ detailed_description: |
+ The mapping is a dictionary where the key is a field, as in logs, and the value is the corresponding **known field**.
+
+ > **Note**: don't use `$` and `%` prefixes for mapped field names.
+
+ ```yaml
+ parser:
+ log_type: ltsv
+ ltsv_config:
+ mapping:
+ label1: field1
+ label2: field2
+ ```
+ - name: parser.regexp_config
+ description: RegExp log parser config.
+ default_value: ""
+ required: false
+ - name: parser.regexp_config.pattern
+ description: RegExp pattern with named groups.
+ default_value: ""
+ required: true
+ detailed_description: |
+ Use pattern with subexpressions names. These names should be **known fields**.
+
+ > **Note**: don't use `$` and `%` prefixes for mapped field names.
+
+ Syntax:
+
+ ```yaml
+ parser:
+ log_type: regexp
+ regexp_config:
+ pattern: PATTERN
+ ```
+ examples:
+ folding:
+ title: Config
+ enabled: true
+ list: []
+ troubleshooting:
+ problems:
+ list: []
+ alerts: []
+ metrics:
+ folding:
+ title: Metrics
+ enabled: false
+ description: ""
+ availability: []
+ scopes:
+ - name: global
+ description: These metrics refer to the entire monitored application.
+ labels: []
+ metrics:
+ - name: squidlog.requests
+ description: Total Requests
+ unit: requests/s
+ chart_type: line
+ dimensions:
+ - name: requests
+ - name: squidlog.excluded_requests
+ description: Excluded Requests
+ unit: requests/s
+ chart_type: line
+ dimensions:
+ - name: unmatched
+ - name: squidlog.type_requests
+ description: Requests By Type
+ unit: requests/s
+ chart_type: stacked
+ dimensions:
+ - name: success
+ - name: bad
+ - name: redirect
+ - name: error
+ - name: squidlog.http_status_code_class_responses
+ description: Responses By HTTP Status Code Class
+ unit: responses/s
+ chart_type: stacked
+ dimensions:
+ - name: 1xx
+ - name: 2xx
+ - name: 3xx
+ - name: 4xx
+ - name: 5xx
+ - name: squidlog.http_status_code_responses
+ description: Responses By HTTP Status Code
+ unit: responses/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per HTTP response code
+ - name: squidlog.bandwidth
+ description: Bandwidth
+ unit: kilobits/s
+ chart_type: line
+ dimensions:
+ - name: sent
+ - name: squidlog.response_time
+ description: Response Time
+ unit: milliseconds
+ chart_type: line
+ dimensions:
+ - name: min
+ - name: max
+ - name: avg
+ - name: squidlog.uniq_clients
+ description: Unique Clients
+ unit: clients
+ chart_type: line
+ dimensions:
+ - name: clients
+ - name: squidlog.cache_result_code_requests
+ description: Requests By Cache Result Code
+ unit: requests/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per cache result code
+ - name: squidlog.cache_result_code_transport_tag_requests
+ description: Requests By Cache Result Delivery Transport Tag
+ unit: requests/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per cache result delivery transport tag
+ - name: squidlog.cache_result_code_handling_tag_requests
+ description: Requests By Cache Result Handling Tag
+ unit: requests/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per cache result handling tag
+ - name: squidlog.cache_code_object_tag_requests
+ description: Requests By Cache Result Produced Object Tag
+ unit: requests/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per cache result produced object tag
+ - name: squidlog.cache_code_load_source_tag_requests
+ description: Requests By Cache Result Load Source Tag
+ unit: requests/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per cache result load source tag
+ - name: squidlog.cache_code_error_tag_requests
+ description: Requests By Cache Result Errors Tag
+ unit: requests/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per cache result error tag
+ - name: squidlog.http_method_requests
+ description: Requests By HTTP Method
+ unit: requests/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per HTTP method
+ - name: squidlog.mime_type_requests
+ description: Requests By MIME Type
+ unit: requests/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per MIME type
+ - name: squidlog.hier_code_requests
+ description: Requests By Hierarchy Code
+ unit: requests/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per hierarchy code
+ - name: squidlog.server_address_forwarded_requests
+ description: Forwarded Requests By Server Address
+ unit: requests/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per server address
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/metrics.go b/src/go/collectors/go.d.plugin/modules/squidlog/metrics.go
new file mode 100644
index 000000000..3754e022b
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/metrics.go
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package squidlog
+
+import "github.com/netdata/netdata/go/go.d.plugin/pkg/metrics"
+
+func newSummary() metrics.Summary {
+ return &summary{metrics.NewSummary()}
+}
+
+type summary struct {
+ metrics.Summary
+}
+
+func (s summary) WriteTo(rv map[string]int64, key string, mul, div int) {
+ s.Summary.WriteTo(rv, key, mul, div)
+ if _, ok := rv[key+"_min"]; !ok {
+ rv[key+"_min"] = 0
+ rv[key+"_max"] = 0
+ rv[key+"_avg"] = 0
+ }
+}
+
+const (
+ pxHTTPCode = "http_resp_code_"
+ pxReqMethod = "req_method_"
+ pxCacheCode = "cache_result_code_"
+ pxTransportTag = "cache_transport_tag_"
+ pxHandlingTag = "cache_handling_tag_"
+ pxObjectTag = "cache_object_tag_"
+ pxSourceTag = "cache_load_source_tag_"
+ pxErrorTag = "cache_error_tag_"
+ pxHierCode = "hier_code_"
+ pxMimeType = "mime_type_"
+ pxSrvAddr = "server_address_"
+)
+
+type metricsData struct {
+ Requests metrics.Counter `stm:"requests"`
+ Unmatched metrics.Counter `stm:"unmatched"`
+
+ HTTPRespCode metrics.CounterVec `stm:"http_resp_code"`
+ HTTPResp0xx metrics.Counter `stm:"http_resp_0xx"`
+ HTTPResp1xx metrics.Counter `stm:"http_resp_1xx"`
+ HTTPResp2xx metrics.Counter `stm:"http_resp_2xx"`
+ HTTPResp3xx metrics.Counter `stm:"http_resp_3xx"`
+ HTTPResp4xx metrics.Counter `stm:"http_resp_4xx"`
+ HTTPResp5xx metrics.Counter `stm:"http_resp_5xx"`
+ HTTPResp6xx metrics.Counter `stm:"http_resp_6xx"`
+
+ ReqSuccess metrics.Counter `stm:"req_type_success"`
+ ReqRedirect metrics.Counter `stm:"req_type_redirect"`
+ ReqBad metrics.Counter `stm:"req_type_bad"`
+ ReqError metrics.Counter `stm:"req_type_error"`
+
+ BytesSent metrics.Counter `stm:"bytes_sent"`
+ RespTime metrics.Summary `stm:"resp_time,1000,1"`
+ UniqueClients metrics.UniqueCounter `stm:"uniq_clients"`
+
+ ReqMethod metrics.CounterVec `stm:"req_method"`
+ CacheCode metrics.CounterVec `stm:"cache_result_code"`
+ CacheCodeTransportTag metrics.CounterVec `stm:"cache_transport_tag"`
+ CacheCodeHandlingTag metrics.CounterVec `stm:"cache_handling_tag"`
+ CacheCodeObjectTag metrics.CounterVec `stm:"cache_object_tag"`
+ CacheCodeLoadSourceTag metrics.CounterVec `stm:"cache_load_source_tag"`
+ CacheCodeErrorTag metrics.CounterVec `stm:"cache_error_tag"`
+ HierCode metrics.CounterVec `stm:"hier_code"`
+ MimeType metrics.CounterVec `stm:"mime_type"`
+ Server metrics.CounterVec `stm:"server_address"`
+}
+
+func (m *metricsData) reset() {
+ m.RespTime.Reset()
+ m.UniqueClients.Reset()
+}
+
+func newMetricsData() *metricsData {
+ return &metricsData{
+ RespTime: newSummary(),
+ UniqueClients: metrics.NewUniqueCounter(true),
+ HTTPRespCode: metrics.NewCounterVec(),
+ ReqMethod: metrics.NewCounterVec(),
+ CacheCode: metrics.NewCounterVec(),
+ CacheCodeTransportTag: metrics.NewCounterVec(),
+ CacheCodeHandlingTag: metrics.NewCounterVec(),
+ CacheCodeObjectTag: metrics.NewCounterVec(),
+ CacheCodeLoadSourceTag: metrics.NewCounterVec(),
+ CacheCodeErrorTag: metrics.NewCounterVec(),
+ HierCode: metrics.NewCounterVec(),
+ Server: metrics.NewCounterVec(),
+ MimeType: metrics.NewCounterVec(),
+ }
+}
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/squidlog.go b/src/go/collectors/go.d.plugin/modules/squidlog/squidlog.go
new file mode 100644
index 000000000..6b5d36263
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/squidlog.go
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package squidlog
+
+import (
+ _ "embed"
+
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/logs"
+)
+
+//go:embed "config_schema.json"
+var configSchema string
+
+func init() {
+ module.Register("squidlog", module.Creator{
+ JobConfigSchema: configSchema,
+ Create: func() module.Module { return New() },
+ Config: func() any { return &Config{} },
+ })
+}
+
+func New() *SquidLog {
+ return &SquidLog{
+ Config: Config{
+ Path: "/var/log/squid/access.log",
+ ExcludePath: "*.gz",
+ ParserConfig: logs.ParserConfig{
+ LogType: logs.TypeCSV,
+ CSV: logs.CSVConfig{
+ FieldsPerRecord: -1,
+ Delimiter: " ",
+ TrimLeadingSpace: true,
+ Format: "- $resp_time $client_address $result_code $resp_size $req_method - - $hierarchy $mime_type",
+ CheckField: checkCSVFormatField,
+ },
+ },
+ },
+ }
+}
+
+type Config struct {
+ UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
+ Path string `yaml:"path" json:"path"`
+ ExcludePath string `yaml:"exclude_path,omitempty" json:"exclude_path"`
+ logs.ParserConfig `yaml:",inline" json:""`
+}
+
+type SquidLog struct {
+ module.Base
+ Config `yaml:",inline" json:""`
+
+ charts *module.Charts
+
+ file *logs.Reader
+ parser logs.Parser
+ line *logLine
+
+ mx *metricsData
+}
+
+func (s *SquidLog) Configuration() any {
+ return s.Config
+}
+
+func (s *SquidLog) Init() error {
+ s.line = newEmptyLogLine()
+ s.mx = newMetricsData()
+ return nil
+}
+
+func (s *SquidLog) Check() error {
+ // Note: these inits are here to make auto-detection retry working
+ if err := s.createLogReader(); err != nil {
+ s.Warning("check failed: ", err)
+ return err
+ }
+
+ if err := s.createParser(); err != nil {
+ s.Warning("check failed: ", err)
+ return err
+ }
+
+ if err := s.createCharts(s.line); err != nil {
+ s.Warning("check failed: ", err)
+ return err
+ }
+
+ return nil
+}
+
+func (s *SquidLog) Charts() *module.Charts {
+ return s.charts
+}
+
+func (s *SquidLog) Collect() map[string]int64 {
+ mx, err := s.collect()
+ if err != nil {
+ s.Error(err)
+ }
+
+ if len(mx) == 0 {
+ return nil
+ }
+ return mx
+}
+
+func (s *SquidLog) Cleanup() {
+ if s.file != nil {
+ _ = s.file.Close()
+ }
+}
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/squidlog_test.go b/src/go/collectors/go.d.plugin/modules/squidlog/squidlog_test.go
new file mode 100644
index 000000000..5cc8a7285
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/squidlog_test.go
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package squidlog
+
+import (
+ "bytes"
+ "os"
+ "testing"
+
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/logs"
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/metrics"
+
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+var (
+ dataConfigJSON, _ = os.ReadFile("testdata/config.json")
+ dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
+
+ dataNativeFormatAccessLog, _ = os.ReadFile("testdata/access.log")
+)
+
+func Test_testDataIsValid(t *testing.T) {
+ for name, data := range map[string][]byte{
+ "dataConfigJSON": dataConfigJSON,
+ "dataConfigYAML": dataConfigYAML,
+ "dataNativeFormatAccessLog": dataNativeFormatAccessLog,
+ } {
+ require.NotNil(t, data, name)
+ }
+}
+
+func TestSquidLog_ConfigurationSerialize(t *testing.T) {
+ module.TestConfigurationSerialize(t, &SquidLog{}, dataConfigJSON, dataConfigYAML)
+}
+
+func TestNew(t *testing.T) {
+ assert.Implements(t, (*module.Module)(nil), New())
+}
+
+func TestSquidLog_Init(t *testing.T) {
+ squidlog := New()
+
+ assert.NoError(t, squidlog.Init())
+}
+
+func TestSquidLog_Check(t *testing.T) {
+}
+
+func TestSquidLog_Check_ErrorOnCreatingLogReaderNoLogFile(t *testing.T) {
+ squid := New()
+ defer squid.Cleanup()
+ squid.Path = "testdata/not_exists.log"
+ require.NoError(t, squid.Init())
+
+ assert.Error(t, squid.Check())
+}
+
+func TestSquid_Check_ErrorOnCreatingParserUnknownFormat(t *testing.T) {
+ squid := New()
+ defer squid.Cleanup()
+ squid.Path = "testdata/unknown.log"
+ require.NoError(t, squid.Init())
+
+ assert.Error(t, squid.Check())
+}
+
+func TestSquid_Check_ErrorOnCreatingParserZeroKnownFields(t *testing.T) {
+ squid := New()
+ defer squid.Cleanup()
+ squid.Path = "testdata/access.log"
+ squid.ParserConfig.CSV.Format = "$one $two"
+ require.NoError(t, squid.Init())
+
+ assert.Error(t, squid.Check())
+}
+
+func TestSquidLog_Charts(t *testing.T) {
+ assert.Nil(t, New().Charts())
+
+ squid := prepareSquidCollect(t)
+ assert.NotNil(t, squid.Charts())
+
+}
+
+func TestSquidLog_Cleanup(t *testing.T) {
+ New().Cleanup()
+}
+
+func TestSquidLog_Collect(t *testing.T) {
+ squid := prepareSquidCollect(t)
+
+ expected := map[string]int64{
+ "bytes_sent": 6827357,
+ "cache_error_tag_ABORTED": 326,
+ "cache_handling_tag_CF": 154,
+ "cache_handling_tag_CLIENT": 172,
+ "cache_load_source_tag_MEM": 172,
+ "cache_object_tag_NEGATIVE": 308,
+ "cache_object_tag_STALE": 172,
+ "cache_result_code_NONE": 158,
+ "cache_result_code_TCP_CF_NEGATIVE_NEGATIVE_ABORTED": 154,
+ "cache_result_code_UDP_CLIENT_STALE_MEM_ABORTED": 172,
+ "cache_transport_tag_NONE": 158,
+ "cache_transport_tag_TCP": 154,
+ "cache_transport_tag_UDP": 172,
+ "hier_code_HIER_CACHE_DIGEST_HIT": 128,
+ "hier_code_HIER_NO_CACHE_DIGEST_DIRECT": 130,
+ "hier_code_HIER_PARENT_HIT": 106,
+ "hier_code_HIER_SINGLE_PARENT": 120,
+ "http_resp_0xx": 51,
+ "http_resp_1xx": 45,
+ "http_resp_2xx": 62,
+ "http_resp_3xx": 120,
+ "http_resp_4xx": 112,
+ "http_resp_5xx": 46,
+ "http_resp_6xx": 48,
+ "http_resp_code_0": 51,
+ "http_resp_code_100": 45,
+ "http_resp_code_200": 62,
+ "http_resp_code_300": 58,
+ "http_resp_code_304": 62,
+ "http_resp_code_400": 56,
+ "http_resp_code_401": 56,
+ "http_resp_code_500": 46,
+ "http_resp_code_603": 48,
+ "mime_type_application": 52,
+ "mime_type_audio": 56,
+ "mime_type_font": 44,
+ "mime_type_image": 50,
+ "mime_type_message": 44,
+ "mime_type_model": 62,
+ "mime_type_multipart": 61,
+ "mime_type_text": 61,
+ "mime_type_video": 54,
+ "req_method_COPY": 84,
+ "req_method_GET": 70,
+ "req_method_HEAD": 59,
+ "req_method_OPTIONS": 99,
+ "req_method_POST": 74,
+ "req_method_PURGE": 98,
+ "req_type_bad": 56,
+ "req_type_error": 94,
+ "req_type_redirect": 58,
+ "req_type_success": 276,
+ "requests": 500,
+ "resp_time_avg": 3015931,
+ "resp_time_count": 484,
+ "resp_time_max": 4988000,
+ "resp_time_min": 1002000,
+ "resp_time_sum": 1459711000,
+ "server_address_2001:db8:2ce:a": 79,
+ "server_address_2001:db8:2ce:b": 89,
+ "server_address_203.0.113.100": 67,
+ "server_address_203.0.113.200": 70,
+ "server_address_content-gateway": 87,
+ "uniq_clients": 5,
+ "unmatched": 16,
+ }
+
+ collected := squid.Collect()
+
+ assert.Equal(t, expected, collected)
+ testCharts(t, squid, collected)
+}
+
+func TestSquidLog_Collect_ReturnOldDataIfNothingRead(t *testing.T) {
+ squid := prepareSquidCollect(t)
+
+ expected := map[string]int64{
+ "bytes_sent": 6827357,
+ "cache_error_tag_ABORTED": 326,
+ "cache_handling_tag_CF": 154,
+ "cache_handling_tag_CLIENT": 172,
+ "cache_load_source_tag_MEM": 172,
+ "cache_object_tag_NEGATIVE": 308,
+ "cache_object_tag_STALE": 172,
+ "cache_result_code_NONE": 158,
+ "cache_result_code_TCP_CF_NEGATIVE_NEGATIVE_ABORTED": 154,
+ "cache_result_code_UDP_CLIENT_STALE_MEM_ABORTED": 172,
+ "cache_transport_tag_NONE": 158,
+ "cache_transport_tag_TCP": 154,
+ "cache_transport_tag_UDP": 172,
+ "hier_code_HIER_CACHE_DIGEST_HIT": 128,
+ "hier_code_HIER_NO_CACHE_DIGEST_DIRECT": 130,
+ "hier_code_HIER_PARENT_HIT": 106,
+ "hier_code_HIER_SINGLE_PARENT": 120,
+ "http_resp_0xx": 51,
+ "http_resp_1xx": 45,
+ "http_resp_2xx": 62,
+ "http_resp_3xx": 120,
+ "http_resp_4xx": 112,
+ "http_resp_5xx": 46,
+ "http_resp_6xx": 48,
+ "http_resp_code_0": 51,
+ "http_resp_code_100": 45,
+ "http_resp_code_200": 62,
+ "http_resp_code_300": 58,
+ "http_resp_code_304": 62,
+ "http_resp_code_400": 56,
+ "http_resp_code_401": 56,
+ "http_resp_code_500": 46,
+ "http_resp_code_603": 48,
+ "mime_type_application": 52,
+ "mime_type_audio": 56,
+ "mime_type_font": 44,
+ "mime_type_image": 50,
+ "mime_type_message": 44,
+ "mime_type_model": 62,
+ "mime_type_multipart": 61,
+ "mime_type_text": 61,
+ "mime_type_video": 54,
+ "req_method_COPY": 84,
+ "req_method_GET": 70,
+ "req_method_HEAD": 59,
+ "req_method_OPTIONS": 99,
+ "req_method_POST": 74,
+ "req_method_PURGE": 98,
+ "req_type_bad": 56,
+ "req_type_error": 94,
+ "req_type_redirect": 58,
+ "req_type_success": 276,
+ "requests": 500,
+ "resp_time_avg": 0,
+ "resp_time_count": 0,
+ "resp_time_max": 0,
+ "resp_time_min": 0,
+ "resp_time_sum": 0,
+ "server_address_2001:db8:2ce:a": 79,
+ "server_address_2001:db8:2ce:b": 89,
+ "server_address_203.0.113.100": 67,
+ "server_address_203.0.113.200": 70,
+ "server_address_content-gateway": 87,
+ "uniq_clients": 0,
+ "unmatched": 16,
+ }
+
+ _ = squid.Collect()
+ collected := squid.Collect()
+
+ assert.Equal(t, expected, collected)
+ testCharts(t, squid, collected)
+}
+
+func testCharts(t *testing.T, squidlog *SquidLog, collected map[string]int64) {
+ t.Helper()
+ ensureChartsDynamicDimsCreated(t, squidlog)
+ ensureCollectedHasAllChartsDimsVarsIDs(t, squidlog, collected)
+}
+
+func ensureChartsDynamicDimsCreated(t *testing.T, squid *SquidLog) {
+ ensureDynamicDimsCreated(t, squid, cacheCodeChart.ID, pxCacheCode, squid.mx.CacheCode)
+ ensureDynamicDimsCreated(t, squid, cacheCodeTransportTagChart.ID, pxTransportTag, squid.mx.CacheCodeTransportTag)
+ ensureDynamicDimsCreated(t, squid, cacheCodeHandlingTagChart.ID, pxHandlingTag, squid.mx.CacheCodeHandlingTag)
+ ensureDynamicDimsCreated(t, squid, cacheCodeObjectTagChart.ID, pxObjectTag, squid.mx.CacheCodeObjectTag)
+ ensureDynamicDimsCreated(t, squid, cacheCodeLoadSourceTagChart.ID, pxSourceTag, squid.mx.CacheCodeLoadSourceTag)
+ ensureDynamicDimsCreated(t, squid, cacheCodeErrorTagChart.ID, pxErrorTag, squid.mx.CacheCodeErrorTag)
+ ensureDynamicDimsCreated(t, squid, httpRespCodesChart.ID, pxHTTPCode, squid.mx.HTTPRespCode)
+ ensureDynamicDimsCreated(t, squid, reqMethodChart.ID, pxReqMethod, squid.mx.ReqMethod)
+ ensureDynamicDimsCreated(t, squid, hierCodeChart.ID, pxHierCode, squid.mx.HierCode)
+ ensureDynamicDimsCreated(t, squid, serverAddrChart.ID, pxSrvAddr, squid.mx.Server)
+ ensureDynamicDimsCreated(t, squid, mimeTypeChart.ID, pxMimeType, squid.mx.MimeType)
+}
+
+func ensureDynamicDimsCreated(t *testing.T, squid *SquidLog, chartID, dimPrefix string, data metrics.CounterVec) {
+ chart := squid.Charts().Get(chartID)
+ assert.NotNilf(t, chart, "chart '%s' is not created", chartID)
+ if chart == nil {
+ return
+ }
+ for v := range data {
+ id := dimPrefix + v
+ assert.Truef(t, chart.HasDim(id), "chart '%s' has no dim for '%s', expected '%s'", chart.ID, v, id)
+ }
+}
+
+func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, s *SquidLog, collected map[string]int64) {
+ for _, chart := range *s.Charts() {
+ for _, dim := range chart.Dims {
+ _, ok := collected[dim.ID]
+ assert.Truef(t, ok, "collected metrics has no data for dim '%s' chart '%s'", dim.ID, chart.ID)
+ }
+ for _, v := range chart.Vars {
+ _, ok := collected[v.ID]
+ assert.Truef(t, ok, "collected metrics has no data for var '%s' chart '%s'", v.ID, chart.ID)
+ }
+ }
+}
+
+func prepareSquidCollect(t *testing.T) *SquidLog {
+ t.Helper()
+ squid := New()
+ squid.Path = "testdata/access.log"
+ require.NoError(t, squid.Init())
+ require.NoError(t, squid.Check())
+ defer squid.Cleanup()
+
+ p, err := logs.NewCSVParser(squid.ParserConfig.CSV, bytes.NewReader(dataNativeFormatAccessLog))
+ require.NoError(t, err)
+ squid.parser = p
+ return squid
+}
+
+// generateLogs is used to populate 'testdata/access.log'
+//func generateLogs(w io.Writer, num int) error {
+// var (
+// client = []string{"localhost", "203.0.113.1", "203.0.113.2", "2001:db8:2ce:1", "2001:db8:2ce:2"}
+// cacheCode = []string{"TCP_CF_NEGATIVE_NEGATIVE_ABORTED", "UDP_CLIENT_STALE_MEM_ABORTED", "NONE"}
+// httpCode = []string{"000", "100", "200", "300", "304", "400", "401", "500", "603"}
+// method = []string{"GET", "HEAD", "POST", "COPY", "PURGE", "OPTIONS"}
+// hierCode = []string{"HIER_PARENT_HIT", "HIER_SINGLE_PARENT", "HIER_CACHE_DIGEST_HIT", "HIER_NO_CACHE_DIGEST_DIRECT"}
+// server = []string{"content-gateway", "203.0.113.100", "203.0.113.200", "2001:db8:2ce:a", "2001:db8:2ce:b", "-"}
+// mimeType = []string{"application", "audio", "font", "image", "message", "model", "multipart", "video", "text"}
+// )
+//
+// r := rand.New(rand.NewSource(time.Now().UnixNano()))
+// randFromString := func(s []string) string { return s[r.Intn(len(s))] }
+// randInt := func(min, max int) int { return r.Intn(max-min) + min }
+//
+// var line string
+// for i := 0; i < num; i++ {
+// unmatched := randInt(1, 100) > 95
+// if i > 0 && unmatched {
+// line = "Unmatched! The rat the cat the dog chased killed ate the malt!\n"
+// } else {
+// // 1576177221.686 0 ::1 TCP_MISS/200 1621 GET cache_object://localhost/counters - HIER_NONE/- text/plain
+// line = fmt.Sprintf(
+// "1576177221.686 %d %s %s/%s %d %s cache_object://localhost/counters - %s/%s %s/plain\n",
+// randInt(1000, 5000),
+// randFromString(client),
+// randFromString(cacheCode),
+// randFromString(httpCode),
+// randInt(9000, 19000),
+// randFromString(method),
+// randFromString(hierCode),
+// randFromString(server),
+// randFromString(mimeType),
+// )
+// }
+// _, err := fmt.Fprint(w, line)
+// if err != nil {
+// return err
+// }
+// }
+// return nil
+//}
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/testdata/access.log b/src/go/collectors/go.d.plugin/modules/squidlog/testdata/access.log
new file mode 100644
index 000000000..64a23d35b
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/testdata/access.log
@@ -0,0 +1,500 @@
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 3976 203.0.113.1 NONE/000 13564 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 model/plain
+1576177221.686 1241 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/401 10309 GET cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.200 text/plain
+1576177221.686 4052 203.0.113.2 NONE/400 17349 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- text/plain
+1576177221.686 3828 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 16025 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a image/plain
+1576177221.686 1798 203.0.113.2 NONE/000 14548 HEAD cache_object://localhost/counters - HIER_SINGLE_PARENT/- model/plain
+1576177221.686 3910 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/300 16516 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b multipart/plain
+1576177221.686 4343 2001:db8:2ce:1 NONE/401 13967 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway video/plain
+1576177221.686 4244 203.0.113.1 NONE/304 10096 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a model/plain
+1576177221.686 1686 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 10491 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 video/plain
+1576177221.686 3387 localhost UDP_CLIENT_STALE_MEM_ABORTED/000 15776 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.100 application/plain
+1576177221.686 1370 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/200 16088 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway image/plain
+1576177221.686 2023 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/500 11529 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b text/plain
+1576177221.686 2858 2001:db8:2ce:2 NONE/300 9358 POST cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a multipart/plain
+1576177221.686 4616 2001:db8:2ce:1 NONE/603 13869 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 image/plain
+1576177221.686 3764 2001:db8:2ce:1 NONE/304 12091 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- video/plain
+1576177221.686 4239 2001:db8:2ce:2 NONE/500 17583 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/- text/plain
+1576177221.686 1925 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 18889 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a application/plain
+1576177221.686 1451 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/304 12461 GET cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b font/plain
+1576177221.686 3907 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 9292 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 image/plain
+1576177221.686 1215 localhost NONE/000 16993 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- message/plain
+1576177221.686 4544 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/603 13625 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 multipart/plain
+1576177221.686 1611 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 9459 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a text/plain
+1576177221.686 1051 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/603 17581 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway audio/plain
+1576177221.686 3681 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 13021 COPY cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a multipart/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 2511 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 13955 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- multipart/plain
+1576177221.686 1296 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 13138 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 text/plain
+1576177221.686 3000 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 10871 GET cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b audio/plain
+1576177221.686 4571 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 13636 COPY cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b video/plain
+1576177221.686 3775 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 16627 GET cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b font/plain
+1576177221.686 2390 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 17552 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway text/plain
+1576177221.686 1022 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/401 18857 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 image/plain
+1576177221.686 4507 2001:db8:2ce:2 NONE/500 15436 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a video/plain
+1576177221.686 1938 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 13470 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a text/plain
+1576177221.686 3071 localhost NONE/500 12033 HEAD cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a audio/plain
+1576177221.686 3880 203.0.113.2 NONE/304 17929 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 model/plain
+1576177221.686 1077 2001:db8:2ce:2 NONE/401 16424 COPY cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a audio/plain
+1576177221.686 4478 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 11321 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b text/plain
+1576177221.686 2768 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 12640 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- image/plain
+1576177221.686 3803 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/400 16857 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/- multipart/plain
+1576177221.686 2111 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 11050 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 application/plain
+1576177221.686 2878 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/200 14757 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- video/plain
+1576177221.686 3053 2001:db8:2ce:1 NONE/500 10030 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- text/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 2423 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/200 10214 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/- font/plain
+1576177221.686 1407 localhost UDP_CLIENT_STALE_MEM_ABORTED/304 11029 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b font/plain
+1576177221.686 3327 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 15419 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a message/plain
+1576177221.686 2300 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/200 16423 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a video/plain
+1576177221.686 1094 localhost UDP_CLIENT_STALE_MEM_ABORTED/300 17171 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.100 font/plain
+1576177221.686 1800 203.0.113.2 NONE/100 13840 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- message/plain
+1576177221.686 1866 203.0.113.2 NONE/603 16746 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- multipart/plain
+1576177221.686 4130 203.0.113.1 NONE/603 11088 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b audio/plain
+1576177221.686 3022 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/500 16903 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- multipart/plain
+1576177221.686 4651 2001:db8:2ce:1 NONE/300 15830 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b text/plain
+1576177221.686 4265 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/401 10342 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 audio/plain
+1576177221.686 2189 localhost UDP_CLIENT_STALE_MEM_ABORTED/000 12576 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a application/plain
+1576177221.686 1621 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 17153 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 multipart/plain
+1576177221.686 2610 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/500 12526 GET cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway model/plain
+1576177221.686 1652 localhost UDP_CLIENT_STALE_MEM_ABORTED/400 15106 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway video/plain
+1576177221.686 1599 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/200 16609 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a multipart/plain
+1576177221.686 1954 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/401 13417 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/- text/plain
+1576177221.686 2338 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 16484 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b model/plain
+1576177221.686 2504 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 12935 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- model/plain
+1576177221.686 3482 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 10694 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway multipart/plain
+1576177221.686 4549 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 17110 HEAD cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 audio/plain
+1576177221.686 3596 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/603 9690 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 multipart/plain
+1576177221.686 4491 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 9378 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a model/plain
+1576177221.686 1336 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/401 14364 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- application/plain
+1576177221.686 1637 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/603 13319 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- text/plain
+1576177221.686 2330 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 16509 COPY cache_object://localhost/counters - HIER_PARENT_HIT/- audio/plain
+1576177221.686 4278 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 9931 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a audio/plain
+1576177221.686 2264 localhost UDP_CLIENT_STALE_MEM_ABORTED/603 16366 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 model/plain
+1576177221.686 4271 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 12708 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway text/plain
+1576177221.686 4580 203.0.113.2 NONE/500 17652 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- application/plain
+1576177221.686 2739 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/400 16253 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway video/plain
+1576177221.686 4122 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/400 10108 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- message/plain
+1576177221.686 2810 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/500 15493 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/- message/plain
+1576177221.686 1257 localhost UDP_CLIENT_STALE_MEM_ABORTED/500 13626 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 video/plain
+1576177221.686 2117 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/200 9348 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a video/plain
+1576177221.686 2467 203.0.113.2 NONE/603 13519 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 video/plain
+1576177221.686 3796 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 12236 HEAD cache_object://localhost/counters - HIER_SINGLE_PARENT/- model/plain
+1576177221.686 1218 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/304 10061 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b text/plain
+1576177221.686 4561 2001:db8:2ce:2 NONE/500 16695 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- multipart/plain
+1576177221.686 1880 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 18046 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway message/plain
+1576177221.686 3518 2001:db8:2ce:1 NONE/304 9991 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- font/plain
+1576177221.686 2092 203.0.113.1 NONE/400 12206 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 audio/plain
+1576177221.686 1483 2001:db8:2ce:1 NONE/200 11454 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 model/plain
+1576177221.686 3683 203.0.113.2 NONE/100 9002 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway model/plain
+1576177221.686 1823 localhost NONE/603 13991 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway font/plain
+1576177221.686 4948 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 18034 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a image/plain
+1576177221.686 2798 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/500 18660 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b font/plain
+1576177221.686 2004 localhost UDP_CLIENT_STALE_MEM_ABORTED/400 12089 GET cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 audio/plain
+1576177221.686 1087 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/603 14469 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway audio/plain
+1576177221.686 3055 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 11938 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/- image/plain
+1576177221.686 2908 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/300 13859 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b image/plain
+1576177221.686 3945 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/200 17255 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 model/plain
+1576177221.686 2225 203.0.113.2 NONE/304 11717 HEAD cache_object://localhost/counters - HIER_PARENT_HIT/- video/plain
+1576177221.686 3439 localhost UDP_CLIENT_STALE_MEM_ABORTED/000 14459 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 audio/plain
+1576177221.686 4939 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/300 9184 POST cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b text/plain
+1576177221.686 3629 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 18778 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a application/plain
+1576177221.686 3956 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/500 17471 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 font/plain
+1576177221.686 1258 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 15939 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b model/plain
+1576177221.686 3328 2001:db8:2ce:1 NONE/200 15416 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b message/plain
+1576177221.686 4055 203.0.113.1 NONE/100 14766 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b text/plain
+1576177221.686 2851 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 12938 HEAD cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway text/plain
+1576177221.686 1578 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 16826 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/- video/plain
+1576177221.686 3340 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/400 14833 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b model/plain
+1576177221.686 4474 2001:db8:2ce:1 NONE/401 11354 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b application/plain
+1576177221.686 4172 localhost NONE/300 9138 HEAD cache_object://localhost/counters - HIER_PARENT_HIT/- audio/plain
+1576177221.686 2732 localhost NONE/603 9105 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b model/plain
+1576177221.686 1581 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 17797 POST cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b image/plain
+1576177221.686 2029 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 15806 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a font/plain
+1576177221.686 3624 203.0.113.1 NONE/500 9549 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 application/plain
+1576177221.686 2591 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 10950 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a audio/plain
+1576177221.686 3351 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 10848 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.100 image/plain
+1576177221.686 2927 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/603 11330 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway image/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 3418 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/401 13606 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway text/plain
+1576177221.686 3542 localhost NONE/000 18143 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a text/plain
+1576177221.686 1755 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/401 11437 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/- message/plain
+1576177221.686 4189 localhost NONE/300 17965 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway multipart/plain
+1576177221.686 2069 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 17754 COPY cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a message/plain
+1576177221.686 1151 localhost UDP_CLIENT_STALE_MEM_ABORTED/603 12324 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/- multipart/plain
+1576177221.686 2695 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 11931 COPY cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway model/plain
+1576177221.686 3557 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 18705 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 message/plain
+1576177221.686 3862 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/401 17928 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 font/plain
+1576177221.686 2512 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/300 18026 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 image/plain
+1576177221.686 3725 localhost NONE/304 13496 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway text/plain
+1576177221.686 3295 203.0.113.1 NONE/400 11396 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 message/plain
+1576177221.686 1469 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 9413 COPY cache_object://localhost/counters - HIER_PARENT_HIT/- text/plain
+1576177221.686 2766 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 10738 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway application/plain
+1576177221.686 4106 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 9115 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 text/plain
+1576177221.686 2025 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/200 13876 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway video/plain
+1576177221.686 2522 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/304 13867 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b text/plain
+1576177221.686 4089 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/100 18319 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway video/plain
+1576177221.686 2728 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 9139 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 audio/plain
+1576177221.686 2658 203.0.113.2 NONE/500 17938 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 multipart/plain
+1576177221.686 2630 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 17682 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway video/plain
+1576177221.686 4063 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/300 10435 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a video/plain
+1576177221.686 2231 localhost NONE/500 12792 POST cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b image/plain
+1576177221.686 2259 2001:db8:2ce:1 NONE/400 10533 GET cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a image/plain
+1576177221.686 4155 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/500 18879 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- image/plain
+1576177221.686 2396 2001:db8:2ce:2 NONE/200 17470 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b application/plain
+1576177221.686 2350 localhost NONE/603 12025 COPY cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a text/plain
+1576177221.686 1684 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/603 12195 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b video/plain
+1576177221.686 3228 localhost NONE/400 9220 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/- image/plain
+1576177221.686 1251 localhost NONE/304 14902 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway video/plain
+1576177221.686 4987 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/401 11056 GET cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a font/plain
+1576177221.686 3477 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/500 10332 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a multipart/plain
+1576177221.686 3825 203.0.113.1 NONE/400 11344 GET cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway model/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 2301 2001:db8:2ce:2 NONE/300 14192 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b video/plain
+1576177221.686 4128 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 10167 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway model/plain
+1576177221.686 2638 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/500 11889 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a application/plain
+1576177221.686 3224 localhost UDP_CLIENT_STALE_MEM_ABORTED/400 16272 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/- video/plain
+1576177221.686 2606 203.0.113.1 NONE/304 14417 POST cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a multipart/plain
+1576177221.686 3032 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/100 15002 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a image/plain
+1576177221.686 1704 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/200 16472 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 multipart/plain
+1576177221.686 2207 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 15584 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b font/plain
+1576177221.686 1805 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 13707 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 font/plain
+1576177221.686 3957 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 11342 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 audio/plain
+1576177221.686 1436 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 16561 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 image/plain
+1576177221.686 4693 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/000 15382 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway message/plain
+1576177221.686 2814 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 16601 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b message/plain
+1576177221.686 3705 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/300 12188 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a audio/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 2920 2001:db8:2ce:1 NONE/304 12360 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b application/plain
+1576177221.686 4746 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 17802 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a multipart/plain
+1576177221.686 1734 2001:db8:2ce:2 NONE/500 9076 HEAD cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.200 model/plain
+1576177221.686 3903 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/200 15655 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b audio/plain
+1576177221.686 3627 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/304 17310 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a message/plain
+1576177221.686 2903 localhost NONE/401 13330 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- text/plain
+1576177221.686 3840 localhost UDP_CLIENT_STALE_MEM_ABORTED/000 9723 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a message/plain
+1576177221.686 4204 203.0.113.2 NONE/401 14758 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b video/plain
+1576177221.686 2531 203.0.113.2 NONE/401 16884 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b model/plain
+1576177221.686 4442 203.0.113.1 NONE/100 16154 GET cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.200 text/plain
+1576177221.686 1874 2001:db8:2ce:2 NONE/400 16960 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 image/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 3935 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/200 18310 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 image/plain
+1576177221.686 1444 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/100 14971 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 audio/plain
+1576177221.686 1598 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 11677 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 image/plain
+1576177221.686 1331 localhost UDP_CLIENT_STALE_MEM_ABORTED/500 11860 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 multipart/plain
+1576177221.686 3019 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/200 18581 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway multipart/plain
+1576177221.686 2439 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 9268 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 model/plain
+1576177221.686 4018 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 16046 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 application/plain
+1576177221.686 4852 localhost NONE/200 17419 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b application/plain
+1576177221.686 1002 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 15627 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a audio/plain
+1576177221.686 3092 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/603 10554 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a video/plain
+1576177221.686 4281 2001:db8:2ce:2 NONE/300 12359 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway message/plain
+1576177221.686 2099 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 16391 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway text/plain
+1576177221.686 2011 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/100 16159 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- model/plain
+1576177221.686 4830 2001:db8:2ce:2 NONE/200 15816 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway text/plain
+1576177221.686 4042 localhost UDP_CLIENT_STALE_MEM_ABORTED/500 12298 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway audio/plain
+1576177221.686 3197 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/500 15824 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway audio/plain
+1576177221.686 1370 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 9400 GET cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway message/plain
+1576177221.686 2845 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/603 9027 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- model/plain
+1576177221.686 1022 localhost NONE/603 10231 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a multipart/plain
+1576177221.686 1539 203.0.113.2 NONE/401 11300 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b image/plain
+1576177221.686 1106 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/400 14320 POST cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a multipart/plain
+1576177221.686 3392 203.0.113.1 NONE/100 11618 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.100 text/plain
+1576177221.686 4047 localhost UDP_CLIENT_STALE_MEM_ABORTED/401 11760 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 font/plain
+1576177221.686 2558 localhost NONE/500 16090 HEAD cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a message/plain
+1576177221.686 3852 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 12957 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/- audio/plain
+1576177221.686 4583 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 15348 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a video/plain
+1576177221.686 3861 localhost NONE/603 18438 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a video/plain
+1576177221.686 3642 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 11404 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 multipart/plain
+1576177221.686 4239 2001:db8:2ce:1 NONE/300 17424 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway multipart/plain
+1576177221.686 3559 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/100 17973 COPY cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.200 model/plain
+1576177221.686 2857 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 13890 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/- image/plain
+1576177221.686 4096 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/100 16852 POST cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b image/plain
+1576177221.686 1711 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/401 18346 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a image/plain
+1576177221.686 4833 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 13810 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 multipart/plain
+1576177221.686 1067 localhost NONE/401 11033 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 video/plain
+1576177221.686 3736 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 9198 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/- multipart/plain
+1576177221.686 4877 203.0.113.2 NONE/200 13819 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a video/plain
+1576177221.686 1994 203.0.113.2 NONE/400 13995 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 text/plain
+1576177221.686 4724 localhost UDP_CLIENT_STALE_MEM_ABORTED/500 18856 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- font/plain
+1576177221.686 3491 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 15865 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.100 application/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 3964 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 12752 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway image/plain
+1576177221.686 4215 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/304 14142 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 application/plain
+1576177221.686 3803 2001:db8:2ce:1 NONE/304 14779 POST cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a message/plain
+1576177221.686 4518 203.0.113.1 NONE/400 15824 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a model/plain
+1576177221.686 2816 2001:db8:2ce:2 NONE/304 14078 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 font/plain
+1576177221.686 1937 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/500 9563 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b model/plain
+1576177221.686 3870 2001:db8:2ce:1 NONE/400 15286 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b font/plain
+1576177221.686 4854 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 11432 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a text/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 4579 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 15670 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a image/plain
+1576177221.686 1316 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/500 13083 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway multipart/plain
+1576177221.686 2319 203.0.113.1 NONE/304 13725 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b model/plain
+1576177221.686 1640 localhost UDP_CLIENT_STALE_MEM_ABORTED/401 14085 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- application/plain
+1576177221.686 2368 203.0.113.2 NONE/400 17238 HEAD cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b video/plain
+1576177221.686 2035 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/603 13357 GET cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway audio/plain
+1576177221.686 2063 2001:db8:2ce:1 NONE/200 11460 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a text/plain
+1576177221.686 4884 203.0.113.2 NONE/200 9333 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b message/plain
+1576177221.686 2917 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/000 9114 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 font/plain
+1576177221.686 3784 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/300 12414 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/- font/plain
+1576177221.686 2514 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/200 16860 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b text/plain
+1576177221.686 1272 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 10082 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 audio/plain
+1576177221.686 4408 203.0.113.2 NONE/400 11884 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- text/plain
+1576177221.686 3444 2001:db8:2ce:2 NONE/300 15683 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/- message/plain
+1576177221.686 3471 localhost UDP_CLIENT_STALE_MEM_ABORTED/401 9915 COPY cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 message/plain
+1576177221.686 2684 2001:db8:2ce:1 NONE/401 13787 POST cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a font/plain
+1576177221.686 2711 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 14585 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway multipart/plain
+1576177221.686 4244 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 17274 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 multipart/plain
+1576177221.686 1967 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/300 11902 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway multipart/plain
+1576177221.686 2722 localhost UDP_CLIENT_STALE_MEM_ABORTED/304 13803 COPY cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b video/plain
+1576177221.686 2672 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 11989 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 font/plain
+1576177221.686 4308 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 14034 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway image/plain
+1576177221.686 4970 203.0.113.2 NONE/304 15711 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- model/plain
+1576177221.686 2801 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/603 13296 COPY cache_object://localhost/counters - HIER_PARENT_HIT/- audio/plain
+1576177221.686 1915 2001:db8:2ce:1 NONE/300 15831 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/- video/plain
+1576177221.686 4406 203.0.113.2 NONE/304 18616 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a application/plain
+1576177221.686 1881 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/300 17573 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a message/plain
+1576177221.686 3561 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/000 10073 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b application/plain
+1576177221.686 2957 203.0.113.2 NONE/400 12867 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 font/plain
+1576177221.686 2166 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 9753 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 font/plain
+1576177221.686 2905 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 18309 HEAD cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 text/plain
+1576177221.686 3528 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 16146 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway font/plain
+1576177221.686 3021 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 16082 GET cache_object://localhost/counters - HIER_PARENT_HIT/- image/plain
+1576177221.686 3228 203.0.113.1 NONE/200 17715 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b image/plain
+1576177221.686 2618 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/401 18779 HEAD cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b application/plain
+1576177221.686 2707 203.0.113.1 NONE/603 15920 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/- model/plain
+1576177221.686 2840 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 17752 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- application/plain
+1576177221.686 3352 localhost UDP_CLIENT_STALE_MEM_ABORTED/603 13179 POST cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 model/plain
+1576177221.686 3764 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 12217 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a video/plain
+1576177221.686 3903 203.0.113.1 NONE/200 15292 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 message/plain
+1576177221.686 1690 203.0.113.1 NONE/603 9206 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 multipart/plain
+1576177221.686 3432 localhost UDP_CLIENT_STALE_MEM_ABORTED/304 16707 POST cache_object://localhost/counters - HIER_PARENT_HIT/- text/plain
+1576177221.686 3239 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/000 12097 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.200 model/plain
+1576177221.686 3761 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/500 9167 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 audio/plain
+1576177221.686 3184 2001:db8:2ce:1 NONE/300 17832 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 message/plain
+1576177221.686 3226 203.0.113.2 NONE/000 16530 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 message/plain
+1576177221.686 1121 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/300 9632 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 application/plain
+1576177221.686 2454 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 13564 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b text/plain
+1576177221.686 2497 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/400 15475 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 text/plain
+1576177221.686 2433 localhost UDP_CLIENT_STALE_MEM_ABORTED/300 10124 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 application/plain
+1576177221.686 2652 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 12632 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- model/plain
+1576177221.686 4245 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/603 13060 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.100 multipart/plain
+1576177221.686 4365 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 13039 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 model/plain
+1576177221.686 1397 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/400 13462 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.200 model/plain
+1576177221.686 1958 203.0.113.1 NONE/304 14745 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b font/plain
+1576177221.686 2374 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/400 16475 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway text/plain
+1576177221.686 3926 localhost UDP_CLIENT_STALE_MEM_ABORTED/200 13928 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b model/plain
+1576177221.686 3628 203.0.113.1 NONE/401 9594 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway message/plain
+1576177221.686 2776 localhost NONE/304 17589 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b application/plain
+1576177221.686 4820 localhost UDP_CLIENT_STALE_MEM_ABORTED/401 11138 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 audio/plain
+1576177221.686 4759 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 18362 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 video/plain
+1576177221.686 4282 203.0.113.2 NONE/304 9048 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 model/plain
+1576177221.686 3308 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 15329 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway audio/plain
+1576177221.686 2067 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 17856 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 text/plain
+1576177221.686 1421 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/100 17391 GET cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a text/plain
+1576177221.686 2881 2001:db8:2ce:1 NONE/400 15805 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b application/plain
+1576177221.686 4457 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/400 18550 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b text/plain
+1576177221.686 4043 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 14399 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- model/plain
+1576177221.686 3516 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/300 9287 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b font/plain
+1576177221.686 2504 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/500 11278 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 audio/plain
+1576177221.686 1995 203.0.113.1 NONE/603 18002 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 font/plain
+1576177221.686 1661 203.0.113.2 NONE/300 18944 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway message/plain
+1576177221.686 3593 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/100 18815 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 message/plain
+1576177221.686 4296 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 10891 HEAD cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway application/plain
+1576177221.686 1392 203.0.113.2 NONE/401 16764 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 message/plain
+1576177221.686 2265 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/000 15565 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway model/plain
+1576177221.686 1936 2001:db8:2ce:2 NONE/000 16715 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b audio/plain
+1576177221.686 4612 203.0.113.2 NONE/304 16972 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b model/plain
+1576177221.686 4473 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 13787 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b audio/plain
+1576177221.686 1606 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/200 11784 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b image/plain
+1576177221.686 1155 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 14832 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b font/plain
+1576177221.686 1637 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 10566 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway font/plain
+1576177221.686 3313 localhost NONE/300 18497 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b multipart/plain
+1576177221.686 2058 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 17875 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/- message/plain
+1576177221.686 2789 203.0.113.2 NONE/401 10608 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b model/plain
+1576177221.686 3250 2001:db8:2ce:2 NONE/603 12794 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a image/plain
+1576177221.686 4962 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/500 18755 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a video/plain
+1576177221.686 3845 localhost UDP_CLIENT_STALE_MEM_ABORTED/200 13988 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- text/plain
+1576177221.686 3395 203.0.113.1 NONE/400 11117 GET cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway model/plain
+1576177221.686 4615 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 16982 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway multipart/plain
+1576177221.686 2663 localhost NONE/304 13113 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.100 audio/plain
+1576177221.686 4313 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/400 17031 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b text/plain
+1576177221.686 4051 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/400 9037 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/- font/plain
+1576177221.686 4779 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/500 17329 GET cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a font/plain
+1576177221.686 1086 localhost NONE/400 12162 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- application/plain
+1576177221.686 3314 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 10419 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway audio/plain
+1576177221.686 3505 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/200 13025 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b model/plain
+1576177221.686 3715 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/304 10068 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway multipart/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 3891 2001:db8:2ce:2 NONE/100 12361 HEAD cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.200 audio/plain
+1576177221.686 1420 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/400 15872 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- multipart/plain
+1576177221.686 4483 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 9958 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- message/plain
+1576177221.686 3689 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/400 18792 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b font/plain
+1576177221.686 4106 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 17681 COPY cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a application/plain
+1576177221.686 4988 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/304 11687 HEAD cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway audio/plain
+1576177221.686 2794 203.0.113.2 NONE/000 10568 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 message/plain
+1576177221.686 2742 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/401 9006 HEAD cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 audio/plain
+1576177221.686 4899 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 17927 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 font/plain
+1576177221.686 1505 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/400 16266 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a audio/plain
+1576177221.686 3867 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 17250 COPY cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.200 audio/plain
+1576177221.686 2744 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 16015 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway model/plain
+1576177221.686 3933 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 12507 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway application/plain
+1576177221.686 1413 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/500 9943 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- application/plain
+1576177221.686 1834 203.0.113.1 NONE/304 12716 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway image/plain
+1576177221.686 1019 2001:db8:2ce:1 NONE/100 13276 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 multipart/plain
+1576177221.686 3599 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 17836 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 application/plain
+1576177221.686 2532 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/500 9700 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b model/plain
+1576177221.686 1634 203.0.113.2 NONE/500 18644 HEAD cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 video/plain
+1576177221.686 3055 203.0.113.1 NONE/400 17369 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 text/plain
+1576177221.686 2935 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 17022 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway model/plain
+1576177221.686 4749 2001:db8:2ce:2 NONE/000 9821 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway message/plain
+1576177221.686 2284 203.0.113.2 NONE/200 10006 POST cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b application/plain
+1576177221.686 3371 localhost UDP_CLIENT_STALE_MEM_ABORTED/500 12975 GET cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway model/plain
+1576177221.686 1971 203.0.113.1 NONE/603 14557 POST cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 text/plain
+1576177221.686 2721 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 17072 HEAD cache_object://localhost/counters - HIER_SINGLE_PARENT/- model/plain
+1576177221.686 2604 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 13570 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway audio/plain
+1576177221.686 1344 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 16820 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway multipart/plain
+1576177221.686 4890 2001:db8:2ce:1 NONE/000 15095 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- audio/plain
+1576177221.686 1005 2001:db8:2ce:2 NONE/000 18911 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b application/plain
+1576177221.686 2956 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/304 10496 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a video/plain
+1576177221.686 3475 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/000 17288 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway image/plain
+1576177221.686 4601 localhost NONE/603 12287 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 image/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 1899 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 18603 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway application/plain
+1576177221.686 2613 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 13216 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway audio/plain
+1576177221.686 3209 localhost UDP_CLIENT_STALE_MEM_ABORTED/603 9944 COPY cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway font/plain
+1576177221.686 2856 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 9548 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.100 video/plain
+1576177221.686 2651 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/500 11656 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 application/plain
+1576177221.686 1297 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/401 15477 HEAD cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway model/plain
+1576177221.686 1261 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/200 17803 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/- video/plain
+1576177221.686 4251 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 11606 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a multipart/plain
+1576177221.686 3367 localhost NONE/300 14497 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway application/plain
+1576177221.686 2739 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/401 17643 HEAD cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 text/plain
+1576177221.686 1362 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 16303 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b image/plain
+1576177221.686 3661 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 18344 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b video/plain
+1576177221.686 3703 203.0.113.1 NONE/304 13318 HEAD cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a video/plain
+1576177221.686 1964 203.0.113.2 NONE/304 18000 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 message/plain
+1576177221.686 3324 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 11296 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/- model/plain
+1576177221.686 3112 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 16582 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b model/plain
+1576177221.686 3776 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/500 12386 GET cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b font/plain
+1576177221.686 3284 203.0.113.1 NONE/500 18718 POST cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b application/plain
+1576177221.686 3741 2001:db8:2ce:2 NONE/200 18218 GET cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.200 font/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 3133 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 10342 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b video/plain
+1576177221.686 2460 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 12281 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway message/plain
+1576177221.686 1684 localhost UDP_CLIENT_STALE_MEM_ABORTED/400 17194 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a text/plain
+1576177221.686 1859 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 10156 COPY cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a audio/plain
+1576177221.686 1351 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/200 16631 HEAD cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 model/plain
+1576177221.686 2007 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/304 10447 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- video/plain
+1576177221.686 4439 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/400 16940 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b video/plain
+1576177221.686 2943 203.0.113.2 NONE/100 18289 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 video/plain
+1576177221.686 4980 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/304 11876 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 image/plain
+1576177221.686 1472 203.0.113.1 NONE/100 15230 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 image/plain
+1576177221.686 4144 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/500 14558 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- text/plain
+1576177221.686 2425 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/000 14740 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- application/plain
+1576177221.686 2402 2001:db8:2ce:2 NONE/000 14386 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a image/plain
+1576177221.686 1256 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/100 12101 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway image/plain
+1576177221.686 3705 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/401 17437 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway message/plain
+1576177221.686 1983 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/401 15588 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/- model/plain
+1576177221.686 1236 203.0.113.2 NONE/304 18272 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.100 video/plain
+1576177221.686 4591 203.0.113.2 NONE/300 12960 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 message/plain
+1576177221.686 3565 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 11710 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 multipart/plain
+1576177221.686 3587 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 12506 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 model/plain
+1576177221.686 1945 203.0.113.1 NONE/200 12382 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.200 model/plain
+1576177221.686 4322 203.0.113.1 NONE/603 16150 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a font/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 3492 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/100 10572 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/- multipart/plain
+1576177221.686 4113 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/500 13848 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.100 application/plain
+1576177221.686 4035 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 13398 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b model/plain
+1576177221.686 4015 localhost NONE/200 18793 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- message/plain
+1576177221.686 2857 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 16562 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 video/plain
+1576177221.686 3459 localhost NONE/603 16567 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/content-gateway text/plain
+1576177221.686 2454 203.0.113.2 NONE/200 18504 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway application/plain
+1576177221.686 4180 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/000 13615 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/- audio/plain
+1576177221.686 1112 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/304 16484 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a video/plain
+1576177221.686 1997 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 16335 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway model/plain
+1576177221.686 3738 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 16001 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 application/plain
+1576177221.686 3299 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 18931 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/- image/plain
+1576177221.686 2029 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/401 16480 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway image/plain
+1576177221.686 4454 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 10548 POST cache_object://localhost/counters - HIER_PARENT_HIT/- application/plain
+1576177221.686 1384 203.0.113.1 NONE/300 13589 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a application/plain
+1576177221.686 4863 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 17670 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway audio/plain
+1576177221.686 3503 2001:db8:2ce:1 NONE/300 11721 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/- audio/plain
+1576177221.686 1778 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/100 11316 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 application/plain
+1576177221.686 1875 203.0.113.2 NONE/100 16222 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/- font/plain
+1576177221.686 1190 203.0.113.1 NONE/500 14110 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b text/plain
+1576177221.686 2266 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/300 10557 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 application/plain
+1576177221.686 4058 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 18050 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.100 multipart/plain
+1576177221.686 2274 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/200 17840 COPY cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.200 text/plain
+1576177221.686 2355 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 10842 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway text/plain
+1576177221.686 3761 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/500 17980 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 application/plain
+1576177221.686 3691 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 14715 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 message/plain
+1576177221.686 2211 203.0.113.2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 11506 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- audio/plain
+1576177221.686 3064 203.0.113.2 NONE/100 18827 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a application/plain
+1576177221.686 3739 203.0.113.2 NONE/200 12758 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b text/plain
+1576177221.686 2402 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/300 18878 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway image/plain
+1576177221.686 1166 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/400 10853 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b multipart/plain
+1576177221.686 4350 localhost NONE/000 10188 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 message/plain
+1576177221.686 4605 localhost NONE/200 15088 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a text/plain
+1576177221.686 1984 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/500 14555 HEAD cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.200 multipart/plain
+1576177221.686 2350 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/304 9723 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/- audio/plain
+1576177221.686 4382 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/100 17163 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 audio/plain
+1576177221.686 1611 2001:db8:2ce:2 NONE/100 16545 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b message/plain
+1576177221.686 1912 2001:db8:2ce:1 NONE/000 14480 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 application/plain
+1576177221.686 3990 203.0.113.2 NONE/304 9821 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/- image/plain
+1576177221.686 1396 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/000 9406 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway multipart/plain
+1576177221.686 4461 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/000 12499 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b video/plain
+1576177221.686 2152 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/500 18415 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b model/plain
+1576177221.686 3568 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 16702 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 image/plain
+1576177221.686 4207 localhost UDP_CLIENT_STALE_MEM_ABORTED/304 15949 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- message/plain
+1576177221.686 4903 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/200 14688 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a multipart/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 2145 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/300 10230 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- text/plain
+1576177221.686 2795 2001:db8:2ce:2 NONE/300 12164 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- image/plain
+1576177221.686 2045 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/304 18161 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a text/plain
+1576177221.686 4960 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/401 12553 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a multipart/plain
+1576177221.686 1844 2001:db8:2ce:2 NONE/304 16443 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/- multipart/plain
+1576177221.686 1398 203.0.113.1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/304 10761 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b video/plain
+1576177221.686 3877 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 18332 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:b audio/plain
+1576177221.686 1542 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/400 15785 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway text/plain
+1576177221.686 3736 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/000 13586 HEAD cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 video/plain
+1576177221.686 3822 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/000 11593 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b application/plain
+1576177221.686 4850 2001:db8:2ce:2 NONE/603 15130 OPTIONS cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.200 font/plain
+1576177221.686 2672 2001:db8:2ce:1 NONE/100 15113 GET cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway audio/plain
+1576177221.686 4189 localhost NONE/500 18364 HEAD cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway model/plain
+1576177221.686 4318 2001:db8:2ce:2 NONE/000 13752 COPY cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:b font/plain
+1576177221.686 4463 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 13991 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 multipart/plain
+1576177221.686 3605 2001:db8:2ce:2 NONE/400 10487 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- multipart/plain
+1576177221.686 4719 2001:db8:2ce:2 NONE/200 16659 POST cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.200 application/plain
+1576177221.686 1639 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/304 9976 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.100 video/plain
+1576177221.686 3542 localhost UDP_CLIENT_STALE_MEM_ABORTED/401 11698 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/- image/plain
+1576177221.686 4298 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/401 13045 GET cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 audio/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 4714 localhost NONE/200 11253 HEAD cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- audio/plain
+1576177221.686 2857 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/100 18801 POST cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- multipart/plain
+1576177221.686 1060 localhost UDP_CLIENT_STALE_MEM_ABORTED/000 9986 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b font/plain
+1576177221.686 4162 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/603 12053 PURGE cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/- multipart/plain
+1576177221.686 2210 203.0.113.2 NONE/300 14717 PURGE cache_object://localhost/counters - HIER_SINGLE_PARENT/203.0.113.100 message/plain
+1576177221.686 2985 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/304 11529 COPY cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a audio/plain
+1576177221.686 2836 2001:db8:2ce:1 NONE/300 18394 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a application/plain
+1576177221.686 3857 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/000 13056 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/content-gateway model/plain
+1576177221.686 3929 localhost UDP_CLIENT_STALE_MEM_ABORTED/304 17257 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway font/plain
+1576177221.686 2737 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/200 12718 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway video/plain
+1576177221.686 3312 2001:db8:2ce:2 NONE/200 11992 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b multipart/plain
+1576177221.686 3303 203.0.113.2 NONE/400 13606 GET cache_object://localhost/counters - HIER_PARENT_HIT/- text/plain
+1576177221.686 3666 203.0.113.1 NONE/500 13027 POST cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.200 multipart/plain
+1576177221.686 4233 203.0.113.2 UDP_CLIENT_STALE_MEM_ABORTED/200 16194 OPTIONS cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/- model/plain
+1576177221.686 1622 localhost NONE/200 18572 OPTIONS cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:b multipart/plain
+1576177221.686 3854 localhost UDP_CLIENT_STALE_MEM_ABORTED/400 9919 POST cache_object://localhost/counters - HIER_PARENT_HIT/203.0.113.100 multipart/plain
+Unmatched! The rat the cat the dog chased killed ate the malt!
+1576177221.686 3735 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/000 11979 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/2001:db8:2ce:a multipart/plain
+1576177221.686 3528 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/100 11686 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/203.0.113.100 image/plain
+1576177221.686 3447 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/100 15826 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:a video/plain
+1576177221.686 3509 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/400 17565 PURGE cache_object://localhost/counters - HIER_PARENT_HIT/2001:db8:2ce:a image/plain
+1576177221.686 3357 2001:db8:2ce:2 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/300 10714 COPY cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b video/plain
+1576177221.686 4608 localhost TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 10035 PURGE cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/content-gateway audio/plain
+1576177221.686 4717 203.0.113.1 UDP_CLIENT_STALE_MEM_ABORTED/300 12759 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/203.0.113.100 font/plain
+1576177221.686 1559 2001:db8:2ce:1 TCP_CF_NEGATIVE_NEGATIVE_ABORTED/200 17001 GET cache_object://localhost/counters - HIER_SINGLE_PARENT/- multipart/plain
+1576177221.686 4497 2001:db8:2ce:1 UDP_CLIENT_STALE_MEM_ABORTED/200 12530 OPTIONS cache_object://localhost/counters - HIER_CACHE_DIGEST_HIT/2001:db8:2ce:a text/plain
+1576177221.686 1142 2001:db8:2ce:2 UDP_CLIENT_STALE_MEM_ABORTED/304 15782 GET cache_object://localhost/counters - HIER_PARENT_HIT/content-gateway multipart/plain
+1576177221.686 2368 203.0.113.2 NONE/400 17664 HEAD cache_object://localhost/counters - HIER_NO_CACHE_DIGEST_DIRECT/2001:db8:2ce:b model/plain \ No newline at end of file
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/testdata/config.json b/src/go/collectors/go.d.plugin/modules/squidlog/testdata/config.json
new file mode 100644
index 000000000..5d563cc7e
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/testdata/config.json
@@ -0,0 +1,27 @@
+{
+ "update_every": 123,
+ "path": "ok",
+ "exclude_path": "ok",
+ "log_type": "ok",
+ "csv_config": {
+ "fields_per_record": 123,
+ "delimiter": "ok",
+ "trim_leading_space": true,
+ "format": "ok"
+ },
+ "ltsv_config": {
+ "field_delimiter": "ok",
+ "value_delimiter": "ok",
+ "mapping": {
+ "ok": "ok"
+ }
+ },
+ "regexp_config": {
+ "pattern": "ok"
+ },
+ "json_config": {
+ "mapping": {
+ "ok": "ok"
+ }
+ }
+}
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/testdata/config.yaml b/src/go/collectors/go.d.plugin/modules/squidlog/testdata/config.yaml
new file mode 100644
index 000000000..701205e23
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/testdata/config.yaml
@@ -0,0 +1,19 @@
+update_every: 123
+path: "ok"
+exclude_path: "ok"
+log_type: "ok"
+csv_config:
+ fields_per_record: 123
+ delimiter: "ok"
+ trim_leading_space: yes
+ format: "ok"
+ltsv_config:
+ field_delimiter: "ok"
+ value_delimiter: "ok"
+ mapping:
+ ok: "ok"
+regexp_config:
+ pattern: "ok"
+json_config:
+ mapping:
+ ok: "ok"
diff --git a/src/go/collectors/go.d.plugin/modules/squidlog/testdata/unknown.log b/src/go/collectors/go.d.plugin/modules/squidlog/testdata/unknown.log
new file mode 100644
index 000000000..0478a5c18
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/squidlog/testdata/unknown.log
@@ -0,0 +1 @@
+localhost - - [22/Mar/2009:09:30:31 +0100] "POST /example.com HTTP/2.0" 300 2698 \ No newline at end of file