github.com/netdata/go.d.plugin@v0.58.1/modules/squidlog/collect.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package squidlog
     4  
     5  import (
     6  	"io"
     7  	"runtime"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/netdata/go.d.plugin/pkg/logs"
    12  	"github.com/netdata/go.d.plugin/pkg/stm"
    13  
    14  	"github.com/netdata/go.d.plugin/agent/module"
    15  )
    16  
    17  func (s SquidLog) logPanicStackIfAny() {
    18  	err := recover()
    19  	if err == nil {
    20  		return
    21  	}
    22  	s.Errorf("[ERROR] %s\n", err)
    23  	for depth := 0; ; depth++ {
    24  		_, file, line, ok := runtime.Caller(depth)
    25  		if !ok {
    26  			break
    27  		}
    28  		s.Errorf("======> %d: %v:%d", depth, file, line)
    29  	}
    30  	panic(err)
    31  }
    32  
    33  func (s *SquidLog) collect() (map[string]int64, error) {
    34  	defer s.logPanicStackIfAny()
    35  	s.mx.reset()
    36  
    37  	var mx map[string]int64
    38  
    39  	n, err := s.collectLogLines()
    40  
    41  	if n > 0 || err == nil {
    42  		mx = stm.ToMap(s.mx)
    43  	}
    44  	return mx, err
    45  }
    46  
    47  func (s *SquidLog) collectLogLines() (int, error) {
    48  	var n int
    49  	for {
    50  		s.line.reset()
    51  		err := s.parser.ReadLine(s.line)
    52  		if err != nil {
    53  			if err == io.EOF {
    54  				return n, nil
    55  			}
    56  			if !logs.IsParseError(err) {
    57  				return n, err
    58  			}
    59  			n++
    60  			s.collectUnmatched()
    61  			continue
    62  		}
    63  		n++
    64  		if s.line.empty() {
    65  			s.collectUnmatched()
    66  		} else {
    67  			s.collectLogLine()
    68  		}
    69  	}
    70  }
    71  
    72  func (s *SquidLog) collectLogLine() {
    73  	s.mx.Requests.Inc()
    74  	s.collectRespTime()
    75  	s.collectClientAddress()
    76  	s.collectCacheCode()
    77  	s.collectHTTPCode()
    78  	s.collectRespSize()
    79  	s.collectReqMethod()
    80  	s.collectHierCode()
    81  	s.collectServerAddress()
    82  	s.collectMimeType()
    83  }
    84  
    85  func (s *SquidLog) collectUnmatched() {
    86  	s.mx.Requests.Inc()
    87  	s.mx.Unmatched.Inc()
    88  }
    89  
    90  func (s *SquidLog) collectRespTime() {
    91  	if !s.line.hasRespTime() {
    92  		return
    93  	}
    94  	s.mx.RespTime.Observe(float64(s.line.respTime))
    95  }
    96  
    97  func (s *SquidLog) collectClientAddress() {
    98  	if !s.line.hasClientAddress() {
    99  		return
   100  	}
   101  	s.mx.UniqueClients.Insert(s.line.clientAddr)
   102  }
   103  
   104  func (s *SquidLog) collectCacheCode() {
   105  	if !s.line.hasCacheCode() {
   106  		return
   107  	}
   108  
   109  	c, ok := s.mx.CacheCode.GetP(s.line.cacheCode)
   110  	if !ok {
   111  		s.addDimToCacheCodeChart(s.line.cacheCode)
   112  	}
   113  	c.Inc()
   114  
   115  	tags := strings.Split(s.line.cacheCode, "_")
   116  	for _, tag := range tags {
   117  		s.collectCacheCodeTag(tag)
   118  	}
   119  }
   120  
   121  func (s *SquidLog) collectHTTPCode() {
   122  	if !s.line.hasHTTPCode() {
   123  		return
   124  	}
   125  
   126  	code := s.line.httpCode
   127  	switch {
   128  	case code >= 100 && code < 300, code == 0, code == 304, code == 401:
   129  		s.mx.ReqSuccess.Inc()
   130  	case code >= 300 && code < 400:
   131  		s.mx.ReqRedirect.Inc()
   132  	case code >= 400 && code < 500:
   133  		s.mx.ReqBad.Inc()
   134  	case code >= 500 && code <= 603:
   135  		s.mx.ReqError.Inc()
   136  	}
   137  
   138  	switch code / 100 {
   139  	case 0:
   140  		s.mx.HTTPResp0xx.Inc()
   141  	case 1:
   142  		s.mx.HTTPResp1xx.Inc()
   143  	case 2:
   144  		s.mx.HTTPResp2xx.Inc()
   145  	case 3:
   146  		s.mx.HTTPResp3xx.Inc()
   147  	case 4:
   148  		s.mx.HTTPResp4xx.Inc()
   149  	case 5:
   150  		s.mx.HTTPResp5xx.Inc()
   151  	case 6:
   152  		s.mx.HTTPResp6xx.Inc()
   153  	}
   154  
   155  	codeStr := strconv.Itoa(code)
   156  	c, ok := s.mx.HTTPRespCode.GetP(codeStr)
   157  	if !ok {
   158  		s.addDimToHTTPRespCodesChart(codeStr)
   159  	}
   160  	c.Inc()
   161  }
   162  
   163  func (s *SquidLog) collectRespSize() {
   164  	if !s.line.hasRespSize() {
   165  		return
   166  	}
   167  	s.mx.BytesSent.Add(float64(s.line.respSize))
   168  }
   169  
   170  func (s *SquidLog) collectReqMethod() {
   171  	if !s.line.hasReqMethod() {
   172  		return
   173  	}
   174  	c, ok := s.mx.ReqMethod.GetP(s.line.reqMethod)
   175  	if !ok {
   176  		s.addDimToReqMethodChart(s.line.reqMethod)
   177  	}
   178  	c.Inc()
   179  }
   180  
   181  func (s *SquidLog) collectHierCode() {
   182  	if !s.line.hasHierCode() {
   183  		return
   184  	}
   185  	c, ok := s.mx.HierCode.GetP(s.line.hierCode)
   186  	if !ok {
   187  		s.addDimToHierCodeChart(s.line.hierCode)
   188  	}
   189  	c.Inc()
   190  }
   191  
   192  func (s *SquidLog) collectServerAddress() {
   193  	if !s.line.hasServerAddress() {
   194  		return
   195  	}
   196  	c, ok := s.mx.Server.GetP(s.line.serverAddr)
   197  	if !ok {
   198  		s.addDimToServerAddressChart(s.line.serverAddr)
   199  	}
   200  	c.Inc()
   201  }
   202  
   203  func (s *SquidLog) collectMimeType() {
   204  	if !s.line.hasMimeType() {
   205  		return
   206  	}
   207  	c, ok := s.mx.MimeType.GetP(s.line.mimeType)
   208  	if !ok {
   209  		s.addDimToMimeTypeChart(s.line.mimeType)
   210  	}
   211  	c.Inc()
   212  }
   213  
   214  func (s *SquidLog) collectCacheCodeTag(tag string) {
   215  	// https://wiki.squid-cache.org/SquidFaq/SquidLogs#Squid_result_codes
   216  	switch tag {
   217  	default:
   218  	case "TCP", "UDP", "NONE":
   219  		c, ok := s.mx.CacheCodeTransportTag.GetP(tag)
   220  		if !ok {
   221  			s.addDimToCacheCodeTransportTagChart(tag)
   222  		}
   223  		c.Inc()
   224  	case "CF", "CLIENT", "IMS", "ASYNC", "SWAPFAIL", "REFRESH", "SHARED", "REPLY":
   225  		c, ok := s.mx.CacheCodeHandlingTag.GetP(tag)
   226  		if !ok {
   227  			s.addDimToCacheCodeHandlingTagChart(tag)
   228  		}
   229  		c.Inc()
   230  	case "NEGATIVE", "STALE", "OFFLINE", "INVALID", "FAIL", "MODIFIED", "UNMODIFIED", "REDIRECT":
   231  		c, ok := s.mx.CacheCodeObjectTag.GetP(tag)
   232  		if !ok {
   233  			s.addDimToCacheCodeObjectTagChart(tag)
   234  		}
   235  		c.Inc()
   236  	case "HIT", "MEM", "MISS", "DENIED", "NOFETCH", "TUNNEL":
   237  		c, ok := s.mx.CacheCodeLoadSourceTag.GetP(tag)
   238  		if !ok {
   239  			s.addDimToCacheCodeLoadSourceTagChart(tag)
   240  		}
   241  		c.Inc()
   242  	case "ABORTED", "TIMEOUT", "IGNORED":
   243  		c, ok := s.mx.CacheCodeErrorTag.GetP(tag)
   244  		if !ok {
   245  			s.addDimToCacheCodeErrorTagChart(tag)
   246  		}
   247  		c.Inc()
   248  	}
   249  }
   250  
   251  func (s *SquidLog) addDimToCacheCodeChart(code string) {
   252  	chartID := cacheCodeChart.ID
   253  	dimID := pxCacheCode + code
   254  	s.addDimToChart(chartID, dimID, code)
   255  }
   256  
   257  func (s *SquidLog) addDimToCacheCodeTransportTagChart(tag string) {
   258  	chartID := cacheCodeTransportTagChart.ID
   259  	dimID := pxTransportTag + tag
   260  	s.addDimToChart(chartID, dimID, tag)
   261  }
   262  
   263  func (s *SquidLog) addDimToCacheCodeHandlingTagChart(tag string) {
   264  	chartID := cacheCodeHandlingTagChart.ID
   265  	dimID := pxHandlingTag + tag
   266  	s.addDimToChart(chartID, dimID, tag)
   267  }
   268  
   269  func (s *SquidLog) addDimToCacheCodeObjectTagChart(tag string) {
   270  	chartID := cacheCodeObjectTagChart.ID
   271  	dimID := pxObjectTag + tag
   272  	s.addDimToChart(chartID, dimID, tag)
   273  }
   274  
   275  func (s *SquidLog) addDimToCacheCodeLoadSourceTagChart(tag string) {
   276  	chartID := cacheCodeLoadSourceTagChart.ID
   277  	dimID := pxSourceTag + tag
   278  	s.addDimToChart(chartID, dimID, tag)
   279  }
   280  
   281  func (s *SquidLog) addDimToCacheCodeErrorTagChart(tag string) {
   282  	chartID := cacheCodeErrorTagChart.ID
   283  	dimID := pxErrorTag + tag
   284  	s.addDimToChart(chartID, dimID, tag)
   285  }
   286  
   287  func (s *SquidLog) addDimToHTTPRespCodesChart(tag string) {
   288  	chartID := httpRespCodesChart.ID
   289  	dimID := pxHTTPCode + tag
   290  	s.addDimToChart(chartID, dimID, tag)
   291  }
   292  
   293  func (s *SquidLog) addDimToReqMethodChart(method string) {
   294  	chartID := reqMethodChart.ID
   295  	dimID := pxReqMethod + method
   296  	s.addDimToChart(chartID, dimID, method)
   297  }
   298  
   299  func (s *SquidLog) addDimToHierCodeChart(code string) {
   300  	chartID := hierCodeChart.ID
   301  	dimID := pxHierCode + code
   302  	dimName := code[5:] // remove "HIER_"
   303  	s.addDimToChart(chartID, dimID, dimName)
   304  }
   305  
   306  func (s *SquidLog) addDimToServerAddressChart(address string) {
   307  	chartID := serverAddrChart.ID
   308  	dimID := pxSrvAddr + address
   309  	s.addDimToChartOrCreateIfNotExist(chartID, dimID, address)
   310  }
   311  
   312  func (s *SquidLog) addDimToMimeTypeChart(mimeType string) {
   313  	chartID := mimeTypeChart.ID
   314  	dimID := pxMimeType + mimeType
   315  	s.addDimToChartOrCreateIfNotExist(chartID, dimID, mimeType)
   316  }
   317  
   318  func (s *SquidLog) addDimToChart(chartID, dimID, dimName string) {
   319  	chart := s.Charts().Get(chartID)
   320  	if chart == nil {
   321  		s.Warningf("add '%s' dim: couldn't find '%s' chart in charts", dimID, chartID)
   322  		return
   323  	}
   324  
   325  	dim := &Dim{ID: dimID, Name: dimName, Algo: module.Incremental}
   326  
   327  	if err := chart.AddDim(dim); err != nil {
   328  		s.Warningf("add '%s' dim: %v", dimID, err)
   329  		return
   330  	}
   331  	chart.MarkNotCreated()
   332  }
   333  
   334  func (s *SquidLog) addDimToChartOrCreateIfNotExist(chartID, dimID, dimName string) {
   335  	if s.Charts().Has(chartID) {
   336  		s.addDimToChart(chartID, dimID, dimName)
   337  		return
   338  	}
   339  
   340  	chart := newChartByID(chartID)
   341  	if chart == nil {
   342  		s.Warningf("add '%s' dim: couldn't create '%s' chart", dimID, chartID)
   343  		return
   344  	}
   345  	if err := s.Charts().Add(chart); err != nil {
   346  		s.Warning(err)
   347  		return
   348  	}
   349  	s.addDimToChart(chartID, dimID, dimName)
   350  }
   351  
   352  func newChartByID(chartID string) *Chart {
   353  	switch chartID {
   354  	case serverAddrChart.ID:
   355  		return serverAddrChart.Copy()
   356  	case mimeTypeChart.ID:
   357  		return mimeTypeChart.Copy()
   358  	}
   359  	return nil
   360  }