neeto-monitor-ruby 1.0.42
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +288 -0
- data/Rakefile +14 -0
- data/lib/neeto-monitor-ruby.rb +7 -0
- data/lib/neeto_monitor_ruby/configuration.rb +92 -0
- data/lib/neeto_monitor_ruby/heartbeat_runner_job.rb +22 -0
- data/lib/neeto_monitor_ruby/init/common.rb +13 -0
- data/lib/neeto_monitor_ruby/init/rails.rb +27 -0
- data/lib/neeto_monitor_ruby/init/ruby.rb +10 -0
- data/lib/neeto_monitor_ruby/monitor.rb +158 -0
- data/lib/neeto_monitor_ruby/monitor_utils.rb +47 -0
- data/lib/neeto_monitor_ruby/neeto_monitor.rb +28 -0
- data/lib/neeto_monitor_ruby/plugin.rb +86 -0
- data/lib/neeto_monitor_ruby/plugins/sidekiq.rb +90 -0
- data/lib/neeto_monitor_ruby/version.rb +5 -0
- data/test/dummy/failure_worker.rb +14 -0
- data/test/dummy/monitor_disabled_worker.rb +14 -0
- data/test/dummy/success_worker.rb +13 -0
- data/test/neeto_monitor/configuration_test.rb +97 -0
- data/test/neeto_monitor/heartbeat_runner_job_test.rb +36 -0
- data/test/neeto_monitor/monitor_test.rb +164 -0
- data/test/neeto_monitor/neeto_monitor_test.rb +10 -0
- data/test/neeto_monitor/plugin_test.rb +78 -0
- data/test/neeto_monitor/plugins/sidekiq_test.rb +75 -0
- data/test/test_helper.rb +15 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: adb80970d221b0ba7554aefd49426eb7018de773f7148200ba1fe588f4ccbc20
|
4
|
+
data.tar.gz: 7b080f2c0979e732dafa40243b63d56c23c4ccf6636f21a14959bf062cd9bb62
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a9e3eafe53eb9b3a117926a0c8b1d59e83816a86156888212827adc949f463f0efe35a01496e7a3d98c61a8c93ee86daf6df44ac2318055d63d8d76f40981b00
|
7
|
+
data.tar.gz: b07f078fae394db5cad93fde00a8871b24535006dc803ac2404f8584a964be0355d099fb6f9ce586d3b07405c98aa2183630f22ab144fc5f902f00a60ccfdd31
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2022 BigBinary
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,288 @@
|
|
1
|
+
# neeto-monitor-ruby
|
2
|
+
|
3
|
+
neetoMonitor - A ruby gem for monitoring application's uptime and performance.
|
4
|
+
|
5
|
+
Installation:
|
6
|
+
|
7
|
+
##### 1. Add the gem
|
8
|
+
|
9
|
+
```bash
|
10
|
+
gem "neeto-monitor-ruby", git: "https://github.com/bigbinary/neeto-monitor-ruby.git"
|
11
|
+
```
|
12
|
+
|
13
|
+
##### 2. Install the gems
|
14
|
+
|
15
|
+
```bash
|
16
|
+
bundle install
|
17
|
+
```
|
18
|
+
|
19
|
+
##### 3. Add configuration
|
20
|
+
|
21
|
+
The neetoMonitor can be configured at multiple levels. i.e. using
|
22
|
+
`env variables`, adding a `config file` or manually passing the configuration
|
23
|
+
hash to `NeetoMonitorRuby.init!`.
|
24
|
+
|
25
|
+
###### 3.1 Setting the config via `NeetoMonitorRuby.init!`
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
NeetoMonitorRuby.init!({
|
29
|
+
api_key: your_app_api_key,
|
30
|
+
environment: production
|
31
|
+
|
32
|
+
...
|
33
|
+
|
34
|
+
})
|
35
|
+
```
|
36
|
+
|
37
|
+
###### 3.2 Using a config file
|
38
|
+
|
39
|
+
```yaml
|
40
|
+
# config/neetomonitor.yml
|
41
|
+
---
|
42
|
+
api_key: [API_KEY]
|
43
|
+
ping_timeout: 5
|
44
|
+
```
|
45
|
+
|
46
|
+
Following are the available configuration options with their default values.
|
47
|
+
|
48
|
+
```yaml
|
49
|
+
base_url: (default: https://neetomonitor.com)
|
50
|
+
# The monitor server base URL. In `development` or `staging` we can set this to local/staging URL.
|
51
|
+
|
52
|
+
api_key: (default: nil)
|
53
|
+
# A unique api key for an organization.
|
54
|
+
|
55
|
+
environment: (default: nil)
|
56
|
+
# The monitoring environment name for an application
|
57
|
+
|
58
|
+
ping_timeout: (default: 8 seconds)
|
59
|
+
# The request timeout for ping requests.
|
60
|
+
|
61
|
+
sidekiq_enabled:
|
62
|
+
# Enables sidekiq plugin to report worker/job events.
|
63
|
+
|
64
|
+
config_path: (default: "config/neetomonitor.yml")
|
65
|
+
# The config file path
|
66
|
+
|
67
|
+
key_prefix_enabled: (default: true)
|
68
|
+
# This allows to enable/disable prefix for monitor key.
|
69
|
+
|
70
|
+
key_prefix: (default: application's name e.g. NeetoKb if integrated in neetoKb)
|
71
|
+
# The prefix added to the monitor key. e.g. if the check name is ArticleWorker, the result will be NeetoKb::ArticleWorker.
|
72
|
+
|
73
|
+
monitors:
|
74
|
+
# more on this in next section
|
75
|
+
```
|
76
|
+
|
77
|
+
###### 3.3 Using ENV variables:
|
78
|
+
|
79
|
+
The `ENV` variables must start with `NEETO_MONITOR` e.g.
|
80
|
+
`NEETO_MONITOR_API_KEY`.
|
81
|
+
|
82
|
+
Similarly, other ENV variables can also be set by prefixing with
|
83
|
+
`NEETO_MONITOR_`.
|
84
|
+
|
85
|
+
The precedence of the configuration is as follows:
|
86
|
+
|
87
|
+
```
|
88
|
+
Options passed to NeetoMonitorRuby.init! > Config file > ENV variables > default values
|
89
|
+
```
|
90
|
+
|
91
|
+
##### 4. Configuring monitors
|
92
|
+
|
93
|
+
The monitors can be added in bulk using a config file. e.g.
|
94
|
+
|
95
|
+
```yaml
|
96
|
+
api_key: [API_KEY]
|
97
|
+
monitors:
|
98
|
+
checks:
|
99
|
+
- name: "neeto-monitor"
|
100
|
+
request_attributes:
|
101
|
+
endpoint: "http://app.neetomonitor.test/health_check/"
|
102
|
+
kind: "http"
|
103
|
+
verb: "get"
|
104
|
+
interval: 10
|
105
|
+
timeout: 20
|
106
|
+
assertions_attributes:
|
107
|
+
- kind: "response_code"
|
108
|
+
verb: "equal"
|
109
|
+
value: 200
|
110
|
+
jobs:
|
111
|
+
- name: "neeto-job"
|
112
|
+
assertions_attributes:
|
113
|
+
- kind: max_duration
|
114
|
+
verb: less_than
|
115
|
+
value: 10
|
116
|
+
- name: "hard-job",
|
117
|
+
schedule: "* * * * *"
|
118
|
+
heartbeats:
|
119
|
+
- name: "neeto-heartbeat"
|
120
|
+
schedule: "* * * * *"
|
121
|
+
```
|
122
|
+
|
123
|
+
The above config will create total four monitors i.e. 2 jobs and 1 check and 1
|
124
|
+
heartbeat.
|
125
|
+
|
126
|
+
###### 4.1 Checks monitors (Checks)
|
127
|
+
|
128
|
+
The checks are used for monitoring website's uptime. The website requests are made at regular intervals which is configured via `interval` attribute.
|
129
|
+
|
130
|
+
```yaml
|
131
|
+
name: # The name of check monitor e.g. "neeto-auth"
|
132
|
+
request_attributes: # This specifies configuring options for the website request.
|
133
|
+
endpoint: # The request URL endpoint
|
134
|
+
|
135
|
+
kind: # The request protocol
|
136
|
+
|
137
|
+
verb: # The request verb/method i.e. get/post/head etc.
|
138
|
+
|
139
|
+
interval: # The request frequency
|
140
|
+
|
141
|
+
timeout: # The request timeout
|
142
|
+
|
143
|
+
assertions_attributes: # This specifies the assertion to be made when a request is made.
|
144
|
+
kind:# The kind of assertion to be made
|
145
|
+
# available options => response_code, response_time, response_body, response_json, response_header, ssl_expiry, min_duration, max_duration
|
146
|
+
|
147
|
+
verb:# The operation verb for assertion
|
148
|
+
# available options => less_than, less_than_equal, equal, not_equal, greater_than, greater_than_equal, contains, not_contains
|
149
|
+
|
150
|
+
key:# The key from the response body json or headers
|
151
|
+
# e.g. if response body = { "success": true }, key can be "success"
|
152
|
+
|
153
|
+
value: # Any value to be checked for the assertion
|
154
|
+
```
|
155
|
+
|
156
|
+
###### 4.2 Heartbeat monitors (Heartbeats)
|
157
|
+
|
158
|
+
The heartbeats are used for monitoring the website/service uptime. The basic
|
159
|
+
difference between `checks` and `heartbeats` is that the heartbeats requests are
|
160
|
+
sent from the host application. Where as the checks requests are made from the
|
161
|
+
`neetoMonitor` server application.
|
162
|
+
|
163
|
+
The heartbeats can be added via the config file. When it is mentioned in the
|
164
|
+
config, the heartbeats will be created in neetoMonitor upon the app
|
165
|
+
initialization.
|
166
|
+
|
167
|
+
When `schedule` is defined for the heartbeats, the gem will send the heartbeats
|
168
|
+
using the schedule frequency and neetoMonitor can send alerts when heartbeats are not received in this frequency.
|
169
|
+
|
170
|
+
The heartbeats events are sent using the `HeartbeatRunnerJob`. This sends the ping at time intervals based on the schedule. E.g. following heartbeat sends a ping every minute.
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
monitors:
|
174
|
+
heartbeats:
|
175
|
+
- name: "neeto-heartbeat"
|
176
|
+
schedule: "* * * * *" # heartbeat frequency
|
177
|
+
```
|
178
|
+
|
179
|
+
**Trigger heartbeats from application manually**
|
180
|
+
The heartbeats can be triggered manually using a periodic job. If you are using `sidekiq`, you can use `sidekiq-cron` to trigger heartbeats at desired frequency.
|
181
|
+
|
182
|
+
Here is an example of how you can configure heartbeats manually.
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
# Worker for sending heartbeat
|
186
|
+
class HeartbeatsWorker < BaseWorker
|
187
|
+
sidekiq_options neeto_monitor_disabled: true
|
188
|
+
|
189
|
+
def perform
|
190
|
+
super
|
191
|
+
|
192
|
+
NeetoMonitor::Monitor.new("test-heartbeat").ping
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# config/schedule.yml
|
197
|
+
heartbeat:
|
198
|
+
cron: "* * * * *"
|
199
|
+
class: "HeartbeatsWorker"
|
200
|
+
```
|
201
|
+
###### 4.3 Job monitors (Jobs)
|
202
|
+
|
203
|
+
The `jobs` are used for checking reliability and performance of background
|
204
|
+
workers.
|
205
|
+
|
206
|
+
All the monitors can be added either via the web application or from the
|
207
|
+
mentioned config file.
|
208
|
+
|
209
|
+
The `jobs` can be created by other methods as well i.e. without adding `jobs` to
|
210
|
+
config section or web application UI. This is done using `Sidekiq` plugin
|
211
|
+
included in the gem. When any new worker starts execution e.g. `ArticleJob`, the
|
212
|
+
job monitor's `run` event is sent automatically. The `complete` event is sent
|
213
|
+
when the job execution is successfully completed. Similarly, `fail` event is
|
214
|
+
sent when job execution fails. This `ArticleJob` monitor is created at
|
215
|
+
`neetoMonitor` application if it is not created already. If we want `schedule`
|
216
|
+
field i.e. periodic job events to be sent to neetoMonitor application, we must
|
217
|
+
add such jobs to a config file. e.g.
|
218
|
+
|
219
|
+
```yaml
|
220
|
+
monitors:
|
221
|
+
jobs:
|
222
|
+
- name: "NeetoWorker"
|
223
|
+
schedule: "* * * * *" # frequency of periodic jobs
|
224
|
+
- name: "neeto-job"
|
225
|
+
assertions_attributes:
|
226
|
+
- kind:
|
227
|
+
# available values => max_duration, min_duration
|
228
|
+
verb:
|
229
|
+
# available values => less_than_or_equal, less_than, greater_than, greater_than_or_equal, equal
|
230
|
+
value:
|
231
|
+
# allowed values => integer/float value
|
232
|
+
```
|
233
|
+
|
234
|
+
The job events are automatically sent from the `sidekiq` plugin when
|
235
|
+
`sidekiq_enabled` is set to true. This flag is set to `true` by default. The job
|
236
|
+
events are not sent when this plugin is not enabled. The gem currently supports
|
237
|
+
only `Sidekiq` for background job monitoring. If we are using any other library
|
238
|
+
for background jobs, we can send the events manually. E.g.
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
class ArticleJob
|
242
|
+
..
|
243
|
+
|
244
|
+
...
|
245
|
+
|
246
|
+
def perform
|
247
|
+
job_monitor = NeetoMonitorRuby::Monitor.new("ArticleJob")
|
248
|
+
series_stamp = Time.now.utc.to_f
|
249
|
+
job_monitor.job_ping(state: "run", series: series_stamp)
|
250
|
+
|
251
|
+
process_article_job # job business logic
|
252
|
+
|
253
|
+
job_monitor.job_ping(state: "complete", series: series_stamp)
|
254
|
+
rescue StandardError => error
|
255
|
+
job_monitor.job_ping(state: "fail", series: series_stamp, message: error.message)
|
256
|
+
end
|
257
|
+
|
258
|
+
private
|
259
|
+
|
260
|
+
..
|
261
|
+
end
|
262
|
+
```
|
263
|
+
|
264
|
+
Sometimes we don't want to monitor some `Sidekiq` workers. We have an option to
|
265
|
+
disable the monitoring for such workers.
|
266
|
+
|
267
|
+
```ruby
|
268
|
+
class MonitorDisabledWorker
|
269
|
+
include Sidekiq::Worker
|
270
|
+
sidekiq_options neeto_monitor_disabled: true
|
271
|
+
|
272
|
+
...
|
273
|
+
|
274
|
+
end
|
275
|
+
```
|
276
|
+
|
277
|
+
The default `monitor key` for `jobs` are the class name of the `Sidekiq` worker
|
278
|
+
itself. We can change the `key` of the monitored job by setting
|
279
|
+
`neeto_monitor_key` in `sidekiq_options`.
|
280
|
+
|
281
|
+
```ruby
|
282
|
+
class CustomMonitorWorker
|
283
|
+
include Sidekiq::Worker
|
284
|
+
sidekiq_options neeto_monitor_key: "super_worker"
|
285
|
+
|
286
|
+
...
|
287
|
+
end
|
288
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "bundler/gem_tasks"
|
5
|
+
require "rake/testtask"
|
6
|
+
|
7
|
+
Rake::TestTask.new(:test) do |t|
|
8
|
+
t.libs << "test"
|
9
|
+
t.pattern = "test/**/*_test.rb"
|
10
|
+
t.verbose = false
|
11
|
+
t.warning = false
|
12
|
+
end
|
13
|
+
|
14
|
+
task default: :test
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module NeetoMonitorRuby
|
6
|
+
class ConfigurationError < StandardError; end
|
7
|
+
|
8
|
+
class Configuration
|
9
|
+
BASE_URL = "https://neetomonitor.com".freeze
|
10
|
+
DEFAULT_PING_TIMEOUT = 8
|
11
|
+
DEFAULT_CONFIG_PATH = "config/neetomonitor.yml"
|
12
|
+
|
13
|
+
class << self
|
14
|
+
attr_accessor :config
|
15
|
+
|
16
|
+
def configure!(options = {}, reset = false)
|
17
|
+
return config if !config.nil? && !reset
|
18
|
+
|
19
|
+
reset! if reset
|
20
|
+
|
21
|
+
config_options = options.dup
|
22
|
+
|
23
|
+
config_path = if options[:config_path]
|
24
|
+
options[:config_path]
|
25
|
+
else
|
26
|
+
file_path = ENV.fetch("NEETO_MONITOR_CONFIG_PATH", DEFAULT_CONFIG_PATH)
|
27
|
+
|
28
|
+
defined?(Rails) && Rails.respond_to?(:root) ? Rails.root.join(file_path) : file_path
|
29
|
+
end
|
30
|
+
|
31
|
+
config_options = load_config(config_path).merge(config_options) if config_path && load_config?(config_path)
|
32
|
+
|
33
|
+
self.config = Configuration.new(config_options)
|
34
|
+
config
|
35
|
+
end
|
36
|
+
|
37
|
+
def reset!
|
38
|
+
self.config = Configuration.new
|
39
|
+
|
40
|
+
config
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_config?(config_path)
|
44
|
+
File.exist?(config_path)
|
45
|
+
end
|
46
|
+
|
47
|
+
def load_config(file_path)
|
48
|
+
config = YAML.load(ERB.new(File.read(file_path)).result(binding))
|
49
|
+
|
50
|
+
JSON.parse(JSON[config], symbolize_names: true)
|
51
|
+
|
52
|
+
rescue Errno::ENOENT
|
53
|
+
raise ConfigurationError.new("#{file_path} not found")
|
54
|
+
rescue Psych::SyntaxError => e
|
55
|
+
raise ConfigurationError.new("#{file_path} is malformed: #{e.message}")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
attr_accessor :base_url, :api_key, :environment, :ping_timeout, :logger, :config_path, :monitors,
|
60
|
+
:sidekiq_enabled, :key_prefix_enabled, :key_prefix
|
61
|
+
|
62
|
+
attr_reader :options
|
63
|
+
|
64
|
+
def initialize(options = {})
|
65
|
+
@options = options
|
66
|
+
@base_url = fetch_or_set_value(:base_url, BASE_URL)
|
67
|
+
@api_key = fetch_or_set_value(:api_key)
|
68
|
+
@environment = fetch_or_set_value(:environment)
|
69
|
+
@ping_timeout = fetch_or_set_value(:ping_timeout, DEFAULT_PING_TIMEOUT)
|
70
|
+
@logger = Logger.new($stdout)
|
71
|
+
@sidekiq_enabled = fetch_or_set_value(:sidekiq_enabled, true)
|
72
|
+
@config_path = fetch_or_set_value(:config_path)
|
73
|
+
@monitors = options[:monitors]
|
74
|
+
@key_prefix_enabled = fetch_or_set_value(:key_prefix_enabled, true)
|
75
|
+
@key_prefix = fetch_or_set_value(:key_prefix, app_key_prefix)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def fetch_or_set_value(key, default_value = nil)
|
81
|
+
env_or_default_value = ENV.fetch("NEETO_MONITOR_#{key.to_s.upcase}", default_value)
|
82
|
+
|
83
|
+
options.fetch(key, env_or_default_value)
|
84
|
+
end
|
85
|
+
|
86
|
+
def app_key_prefix
|
87
|
+
if defined?(Rails) && Rails.respond_to?(:application)
|
88
|
+
Rails.application.class.to_s.split("::").first
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fugit"
|
4
|
+
require "active_job"
|
5
|
+
|
6
|
+
module NeetoMonitorRuby
|
7
|
+
class HeartbeatRunnerJob < ActiveJob::Base
|
8
|
+
queue_as :default
|
9
|
+
|
10
|
+
def perform(heartbeat_name, schedule)
|
11
|
+
NeetoMonitorRuby::Monitor.new(heartbeat_name).ping
|
12
|
+
|
13
|
+
fugit = Fugit.do_parse_cronish(schedule)
|
14
|
+
next_time_string = fugit.next_time(Time.now).to_s
|
15
|
+
next_time = Time.parse(next_time_string)
|
16
|
+
|
17
|
+
NeetoMonitorRuby::HeartbeatRunnerJob.set(wait_until: next_time).perform_later(heartbeat_name, schedule)
|
18
|
+
rescue Date::Error, ArgumentError => exception
|
19
|
+
NeetoMonitor.logger.error "Error while parsing fugit date time: #{error.message}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
require "json"
|
5
|
+
require "httparty"
|
6
|
+
require "socket"
|
7
|
+
require "time"
|
8
|
+
require "yaml"
|
9
|
+
|
10
|
+
require "neeto_monitor_ruby/version"
|
11
|
+
require "neeto_monitor_ruby/monitor_utils"
|
12
|
+
require "neeto_monitor_ruby/monitor"
|
13
|
+
require "neeto_monitor_ruby/neeto_monitor"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails"
|
4
|
+
|
5
|
+
require_relative "common"
|
6
|
+
|
7
|
+
module NeetoMonitorRuby
|
8
|
+
module Init
|
9
|
+
module Rails
|
10
|
+
class Railtie < ::Rails::Railtie
|
11
|
+
config.before_initialize do
|
12
|
+
NeetoMonitorRuby.init!(
|
13
|
+
{
|
14
|
+
environment: ::Rails.env,
|
15
|
+
logger: ::Rails.logger,
|
16
|
+
config_path: ::Rails.root.join(Configuration::DEFAULT_CONFIG_PATH)
|
17
|
+
})
|
18
|
+
end
|
19
|
+
|
20
|
+
config.after_initialize do
|
21
|
+
NeetoMonitorRuby.load_plugins!
|
22
|
+
NeetoMonitorRuby.load_monitors!
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "httparty"
|
4
|
+
require "sidekiq/api"
|
5
|
+
require_relative "heartbeat_runner_job"
|
6
|
+
|
7
|
+
module NeetoMonitorRuby
|
8
|
+
class Monitor
|
9
|
+
include MonitorUtils
|
10
|
+
|
11
|
+
REQUEST_HEADERS = {
|
12
|
+
'Content-Type': "application/json",
|
13
|
+
'Accept': "application/json",
|
14
|
+
'User-Agent': "neeto-monitor-ruby"
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
MAX_RETRY_COUNT = 3
|
18
|
+
BULK_CREATE_PATH = "/api/v1/clients/bulk_checks".freeze
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def load_monitors!
|
22
|
+
return unless NeetoMonitorRuby.config.api_key
|
23
|
+
|
24
|
+
config_monitors = NeetoMonitorRuby.config.monitors&.except(:heartbeats)
|
25
|
+
|
26
|
+
if !config_monitors.blank?
|
27
|
+
environment = NeetoMonitorRuby.config.environment
|
28
|
+
|
29
|
+
all_monitors = config_monitors.each_with_object([]) do |(type, monitors), result|
|
30
|
+
monitors.each { |m| result << m.merge(kind: type.to_s.chop, environment:) }
|
31
|
+
end
|
32
|
+
|
33
|
+
make_bulk_create_request({ checks: all_monitors, api_key: NeetoMonitorRuby.config.api_key })
|
34
|
+
else
|
35
|
+
NeetoMonitorRuby.logger.debug "No monitors found in config"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def make_bulk_create_request(check_params)
|
40
|
+
bulk_monitors_url = "#{NeetoMonitorRuby.config.base_url}#{BULK_CREATE_PATH}"
|
41
|
+
|
42
|
+
response = HTTParty.post(
|
43
|
+
bulk_monitors_url,
|
44
|
+
body: check_params.to_json,
|
45
|
+
headers: REQUEST_HEADERS
|
46
|
+
)
|
47
|
+
|
48
|
+
result = response.code >= 200 && response.code < 300
|
49
|
+
|
50
|
+
NeetoMonitor.logger.error "Bulk create response code: #{response.code}" unless result
|
51
|
+
|
52
|
+
load_heartbeats! if defined?(ActiveJob::Base)
|
53
|
+
|
54
|
+
result
|
55
|
+
rescue StandardError => exception
|
56
|
+
NeetoMonitor.logger.error exception.message
|
57
|
+
NeetoMonitor.logger.error exception.backtrace.join("\n")
|
58
|
+
end
|
59
|
+
|
60
|
+
def load_heartbeats!
|
61
|
+
NeetoMonitor.logger.debug "Loading heartbeats!!!"
|
62
|
+
NeetoMonitor.logger.debug "NeetoMonitor.config.monitors #{NeetoMonitor.config.monitors}"
|
63
|
+
heartbeats = NeetoMonitor.config.monitors[:heartbeats]
|
64
|
+
prefix = NeetoMonitor.config.key_prefix_enabled ? NeetoMonitor.config.key_prefix : nil
|
65
|
+
|
66
|
+
heartbeats.each do |heartbeat|
|
67
|
+
heartbeat_name = prefix ? [prefix, heartbeat[:name]].join("::") : heartbeat[:name]
|
68
|
+
|
69
|
+
delete_old_heartbeat_jobs!
|
70
|
+
|
71
|
+
HeartbeatRunnerJob.perform_later(heartbeat_name, heartbeat[:schedule])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def delete_old_heartbeat_jobs!
|
76
|
+
if ActiveJob::Base.queue_adapter.respond_to?(:enqueued_jobs)
|
77
|
+
ActiveJob::Base.queue_adapter.enqueued_jobs.reject! do |job|
|
78
|
+
job["job_class"] == "NeetoMonitorRuby::HeartbeatRunnerJob"
|
79
|
+
end
|
80
|
+
elsif ActiveJob::Base.queue_adapter.class == ActiveJob::QueueAdapters::SidekiqAdapter
|
81
|
+
Sidekiq::ScheduledSet.new.scan("NeetoMonitorRuby::HeartbeatRunnerJob").each do |job|
|
82
|
+
job.delete if job.display_class == "NeetoMonitorRuby::HeartbeatRunnerJob"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
attr_reader :api_key, :environment, :monitor_key
|
89
|
+
|
90
|
+
def initialize(monitor_key, api_key: nil, environment: nil)
|
91
|
+
@monitor_key = monitor_key
|
92
|
+
@api_key = api_key || NeetoMonitorRuby.config.api_key
|
93
|
+
@environment = environment || NeetoMonitorRuby.config.environment
|
94
|
+
end
|
95
|
+
|
96
|
+
def ping(params = {})
|
97
|
+
if api_key.nil?
|
98
|
+
log_error "No API key is provided, please pass the api_key or configure."
|
99
|
+
return false
|
100
|
+
end
|
101
|
+
|
102
|
+
retry_count = params[:retry_count] || 0
|
103
|
+
response = make_ping_request(params)
|
104
|
+
response.code >= 200 && response.code < 300
|
105
|
+
rescue StandardError => exception
|
106
|
+
return false if retry_count >= MAX_RETRY_COUNT
|
107
|
+
|
108
|
+
retry_ping(params, retry_count)
|
109
|
+
end
|
110
|
+
|
111
|
+
def job_ping(params)
|
112
|
+
ping(params.merge(kind: MONITOR_TYPES[:job]))
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def make_ping_request(params)
|
118
|
+
response = HTTParty.get(
|
119
|
+
monitor_ping_url,
|
120
|
+
query: sanitize_params(params),
|
121
|
+
headers: REQUEST_HEADERS,
|
122
|
+
timeout: NeetoMonitorRuby.config.ping_timeout
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
def retry_ping(params, retry_count)
|
127
|
+
retry_count += 1
|
128
|
+
sleep(retry_count)
|
129
|
+
ping(params.merge(retry_count:))
|
130
|
+
end
|
131
|
+
|
132
|
+
def monitor_ping_url
|
133
|
+
"#{NeetoMonitorRuby.config.base_url}/tm/#{api_key}/#{CGI.escape(monitor_key)}"
|
134
|
+
end
|
135
|
+
|
136
|
+
def sanitize_params(params)
|
137
|
+
{
|
138
|
+
state: params.fetch(:state, nil),
|
139
|
+
kind: params.fetch(:kind, MONITOR_TYPES[:heartbeat]),
|
140
|
+
message: params.fetch(:message, nil),
|
141
|
+
series: params.fetch(:series, nil),
|
142
|
+
host: params.fetch(:host, Socket.gethostname),
|
143
|
+
metrics: params.fetch(:metrics, nil),
|
144
|
+
stamp: generate_stamp,
|
145
|
+
env: params.fetch(:env, environment)
|
146
|
+
}
|
147
|
+
end
|
148
|
+
|
149
|
+
def log_exception(exception)
|
150
|
+
log_error exception.message
|
151
|
+
log_error exception.backtrace.join("\n")
|
152
|
+
end
|
153
|
+
|
154
|
+
def log_error(text)
|
155
|
+
NeetoMonitorRuby.logger.error text
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|