Class: Check::Metric

Inherits:
Hashr
  • Object
show all
Defined in:
lib/check/metric.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.defaultsObject

If you really want to overwrite the defaults completely, inherit this class and re-define self.defaults. Don’t forget to invoke the define method with the new defaults as this is Hashr’s way of defining the hash blueprint.

It would be interesting to go further with positives and categorize them as consistent (>50% matches are lower or higher) or fluctuating (50/50).



19
20
21
22
23
24
25
26
27
28
29
# File 'lib/check/metric.rb', line 19

def self.defaults
  {
    lower: 1,
    upper: 10,
    matches_for_positive: 2,
    over_seconds: 60,
    suspend_after_positives: 1,
    keep_positives: 10,
    suspend_for_seconds: 1800
  }
end

.defaults=(params = {}) ⇒ Object

In this example, we are overwriting the defaults so that all new configs will consider 5 matches over a period of 60 seconds to be a positive. The metric check will be suspended for 1h after 3 positives. The lower and upper bounds are also adjusted.

Check::Metric.defaults = {
  lower: 10,
  upper: 100,
  matches_for_positive: 5,
  over_seconds: 60,
  suspend_after_positives: 3,
  suspend_for_seconds: 3600
}


46
47
48
# File 'lib/check/metric.rb', line 46

def self.defaults=(params={})
  define self.defaults.merge(params)
end

.delete_all(name) ⇒ Object



50
51
52
# File 'lib/check/metric.rb', line 50

def self.delete_all(name)
  Redis.current.del(name)
end

.find(params = {}) ⇒ Object



54
55
56
# File 'lib/check/metric.rb', line 54

def self.find(params={})
  Metric.new(params)
end

Instance Method Details

#check(params) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/check/metric.rb', line 176

def check(params)
  timestamp = params.fetch(:timestamp) { Time.now.utc }.to_i
  value = params.fetch(:value)

  similar.each do |metric|
    next if metric.suspended?(timestamp) or metric.disabled?

    unless value.between?(metric.fetch(:lower), metric.fetch(:upper))
      metric.matches.push({
        value: value,
        timestamp: timestamp
      })
    end

    if metric.trigger_positive?
      new_positive = {
        timestamp: metric.matches.last.fetch(:timestamp),
        matches: metric.matches.values
      }
      metric.positives.push(new_positive)
      notify(new_positive.merge(metric))
      metric.clear_matches
    end
  end
end

#deleteObject



82
83
84
85
# File 'lib/check/metric.rb', line 82

def delete
  delete_associated
  set.delete(packed)
end

#delete_associatedObject



127
128
129
# File 'lib/check/metric.rb', line 127

def delete_associated
  Redis.current.del([matches_key, positives_key, disable_key])
end

#delete_matchesObject Also known as: clear_matches



111
112
113
# File 'lib/check/metric.rb', line 111

def delete_matches
  Redis.current.del(matches_key)
end

#delete_positivesObject Also known as: clear_positives



121
122
123
# File 'lib/check/metric.rb', line 121

def delete_positives
  Redis.current.del(positives_key)
end

#disableObject



141
142
143
# File 'lib/check/metric.rb', line 141

def disable
  Redis::Value.new(disable_key, marshal: true)
end

#disable!Object



145
146
147
# File 'lib/check/metric.rb', line 145

def disable!
  disable.value = true
end

#disable_keyObject



103
104
105
# File 'lib/check/metric.rb', line 103

def disable_key
  namespace("disable")
end

#disabled?Boolean

Returns:

  • (Boolean)


154
155
156
# File 'lib/check/metric.rb', line 154

def disabled?
  disable.exists?
end

#enable!Object Also known as: delete_disable



149
150
151
# File 'lib/check/metric.rb', line 149

def enable!
  disable.delete
end

#errorsObject



202
203
204
205
# File 'lib/check/metric.rb', line 202

def errors
  return @errors if @errors
  @errors = {}
end

#idObject



87
88
89
# File 'lib/check/metric.rb', line 87

def id
  hash.abs
end

#matchesObject



107
108
109
# File 'lib/check/metric.rb', line 107

def matches
  Redis::List.new(matches_key, maxlength: self.fetch(:matches_for_positive), marshal: true)
end

#matches_keyObject



95
96
97
# File 'lib/check/metric.rb', line 95

def matches_key
  namespace("matches")
end

#namespace(string) ⇒ Object



91
92
93
# File 'lib/check/metric.rb', line 91

def namespace(string)
  "#{self.fetch(:name)}:#{string}:#{self.id}"
end

#notify(message) ⇒ Object



172
173
174
# File 'lib/check/metric.rb', line 172

def notify(message)
  Redis.current.publish(REDIS_NOTIFICATIONS, message.to_msgpack) if notify?
end

#notify?Boolean

Returns:

  • (Boolean)


168
169
170
# File 'lib/check/metric.rb', line 168

def notify?
  REDIS_NOTIFICATIONS.strip != ""
end

#packObject Also known as: packed



68
69
70
# File 'lib/check/metric.rb', line 68

def pack
  self.to_hash.to_msgpack
end

#persisted?Boolean

Returns:

  • (Boolean)


221
222
223
# File 'lib/check/metric.rb', line 221

def persisted?
  set.include?(packed)
end

#positivesObject



117
118
119
# File 'lib/check/metric.rb', line 117

def positives
  Redis::List.new(positives_key, maxlength: self.fetch(:keep_positives), marshal: true)
end

#positives_keyObject



99
100
101
# File 'lib/check/metric.rb', line 99

def positives_key
  namespace("positives")
end

#saveObject



77
78
79
80
# File 'lib/check/metric.rb', line 77

def save
  set.add(packed) if valid?
  self
end

#setObject



58
59
60
# File 'lib/check/metric.rb', line 58

def set
  Redis::Set.new(self.fetch(:name)) if valid_name?
end

#similarObject



62
63
64
65
66
# File 'lib/check/metric.rb', line 62

def similar
  set.members.map do |member|
    Metric.new(unpack(member))
  end
end

#suspended?(timestamp = Time.now.utc.to_i) ⇒ Boolean

Returns:

  • (Boolean)


131
132
133
134
135
136
137
138
139
# File 'lib/check/metric.rb', line 131

def suspended?(timestamp=Time.now.utc.to_i)
  last_positive = positives.last

  if last_positive
    timestamp - last_positive.fetch(:timestamp) <= self.fetch(:suspend_for_seconds)
  else
    false
  end
end

#trigger_positive?Boolean

Returns:

  • (Boolean)


158
159
160
161
162
163
164
165
166
# File 'lib/check/metric.rb', line 158

def trigger_positive?
  first_match = matches.first
  last_match = matches.last

  if first_match and last_match
    matches.length == self.fetch(:matches_for_positive) and
    last_match.fetch(:timestamp) - first_match.fetch(:timestamp) <= self.fetch(:over_seconds)
  end
end

#unpack(value) ⇒ Object



73
74
75
# File 'lib/check/metric.rb', line 73

def unpack(value)
  MessagePack.unpack(value)
end

#valid?Boolean

Returns:

  • (Boolean)


217
218
219
# File 'lib/check/metric.rb', line 217

def valid?
  valid_name?
end

#valid_name?Boolean

Returns:

  • (Boolean)


207
208
209
210
211
212
213
214
215
# File 'lib/check/metric.rb', line 207

def valid_name?
  if (self.fetch(:name) { "" }.strip != "")
    errors.delete(:name)
    return true
  else
    errors[:name] = "can't be blank"
    return false
  end
end