// Save metric and index into db. func (d *Detector) save(m *models.Metric, idx *models.Index) error { // Save index. if err := d.db.Index.Put(idx); err != nil { return err } // Save metric. m.LinkTo(idx) // Important if err := d.db.Metric.Put(m); err != nil { return err } return nil }
// analyze given metric with 3sigma, returns the new index. // Steps: // 1. Get index. // 2. Get history values. // 3. Do 3sigma calculation. // 4. Move the index next. func (d *Detector) analyze(idx *models.Index, m *models.Metric, rules []*models.Rule) *models.Index { fz := idx != nil && d.shouldFill0(m, rules) if idx != nil { m.LinkTo(idx) } vals, err := d.values(m, fz) if err != nil { return nil } d.div3Sigma(m, vals) return d.nextIdx(idx, m, d.pickTrendingFactor(rules)) }
// div3Sigma sets given metric score and average via 3-sigma. // states that nearly all values (99.7%) lie within the 3 standard deviations // of the mean in a normal distribution. func (d *Detector) div3Sigma(m *models.Metric, vals []float64) { if len(vals) == 0 { m.Score = 0 m.Average = m.Value return } // Values average and standard deviation. avg := mathutil.Average(vals) std := mathutil.StdDev(vals, avg) // Set metric average m.Average = avg // Set metric score if len(vals) <= int(d.cfg.Detector.LeastCount) { // Number of values not enough m.Score = 0 return } last := m.Value if std == 0 { // Eadger switch { case last == avg: m.Score = 0 case last > avg: m.Score = 1 case last < avg: m.Score = -1 } return } m.Score = (last - avg) / (3 * std) // 3-sigma }
// Test metric and index with rules. // The following function will fill the m.TestedRules. func (d *Detector) test(m *models.Metric, idx *models.Index, rules []*models.Rule) { for _, rule := range rules { if rule.Test(m, idx, d.cfg) { // Add tested ok rules. m.TestedRules = append(m.TestedRules, rule) } } }
// decodeKey decodes db key into metric, this will fill metric name and metric // stamp. func decodeKey(key []byte, m *models.Metric) error { s := string(key) if len(s) <= stampLen { return ErrCorrupted } // First substring is Name. idx := len(s) - stampLen m.Name = s[:idx] // Last substring is Stamp. str := s[idx:] n, err := strconv.ParseUint(str, convBase, 32) if err != nil { return err } m.Stamp = horizon + uint32(n) return nil }