neeto-monitor-ruby 1.0.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|