rorvswild 1.5.12 → 1.5.15

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48093e6f93439e8216bd5b0c631b6b56386281d9792f728312d227634834ee11
4
- data.tar.gz: 9d7cc84527fc054d47e17ed8f8c736c119660f59d7f077a24b420588875e0685
3
+ metadata.gz: 29d23fa806fe2d9547169a35795b4b313bf7510264aba4d0f8f3471ae7acf047
4
+ data.tar.gz: 681d88bef1fb76ede952387ef1d8ffbdf4bfa4cbf08f990f7964624401ff71a9
5
5
  SHA512:
6
- metadata.gz: 55d90ef1666a3e5696cfbf998a13446d8dc2cddeccf05d56d5dcc6aa3855e5bd5443eb0dccd0b2d54e6af00b1e5babce359fabd2899a4a624a0fcb9f5ab1a5c4
7
- data.tar.gz: 909982650664ed49fed2103c7e031ae761ccea22eb5fd39bb82b85e2e3996fd7ec28b891b2151d41ebeadae6f2076ff3c14d895e0a552c006e444e6178d77754
6
+ metadata.gz: e54dba0143bdb8e94677519698857c7ddc62f91ecb0a876ecc9e0e2fb402e4d11055a59f03cca1c2ca5a2e73b82cf5380574d9e3e4c73d1a66e21c7ea54cfe6c
7
+ data.tar.gz: 92d4cb2e15015929ccc609b6b81837fb1a78ea12ebe4538555e0a023266fc852001d1aa17d7e620b50dd6ac3f856cbf08bba1d3cb9e4f24f10333b1faa26064d
data/README.md CHANGED
@@ -89,24 +89,22 @@ If you are using `Rack::Deflater` middleware you won't see the small button in t
89
89
  *RoRvsWild.com* makes it easy to monitor requests, background jobs and errors in your production and staging environment.
90
90
  It also comes with some extra options listed below.
91
91
 
92
- #### Measure any code
92
+ #### Measure any section of code
93
93
 
94
- You can measure any code like this (useful to monitor cronjobs):
94
+ RorVsWild measures a lot of events such as SQL queries. But it might not be enough for you. There is a solution to measure any section of code to help you find the most hidden bottlenecks.
95
95
 
96
96
  ```ruby
97
- RorVsWild.measure_code("User.all.do_something_great")
98
- ```
97
+ # Measure a code given as a string
98
+ RorVsWild.measure("bubble_sort(array)")
99
99
 
100
- Or like that:
100
+ # Measure a code given as a block
101
+ RorVsWild.measure { bubble_sort(array) }
101
102
 
102
- ```ruby
103
- RorVsWild.measure_block("A great job name") { User.all.do_something_great }
103
+ # Measure a code given as a block with an optional description
104
+ RorVsWild.measure("Optional description") { bubble_sort(array) }
104
105
  ```
105
106
 
106
- Then it will appear in the jobs page.
107
-
108
- Note that Calling `measure_code` or `measure_block` inside or a request or a job will add a section.
109
- That is convenient to profile finely parts of your code.
107
+ For each custom measure, a section is added with the file name and line number where it has been called.
110
108
 
111
109
  #### Send errors manually
112
110
 
@@ -136,6 +134,18 @@ RorVsWild.record_error(exception, {something: "important"})
136
134
  RorVsWild.catch_error(something: "important") { 1 / 0 }
137
135
  ```
138
136
 
137
+ It is also possible to pre-fill this context data at the begining of each request or job :
138
+
139
+ ```ruby
140
+ class ApplicationController < ActionController::Base
141
+ before_action :prefill_error_context
142
+
143
+ def prefill_error_context
144
+ RorVsWild.merge_error_context(something: "important")
145
+ end
146
+ end
147
+ ```
148
+
139
149
  #### Ignore requests, jobs, exceptions and plugins
140
150
 
141
151
  From the configuration file, you can tell RorVsWild to skip monitoring some requests, jobs, exceptions and plugins.
@@ -208,6 +218,30 @@ In the case you want a custom logger such as Syslog, you can only do it by initi
208
218
  RorVsWild.start(api_key: "API_KEY", logger: Logger::Syslog.new)
209
219
  ```
210
220
 
221
+ #### Server metrics monitoring
222
+
223
+ We are adding server metrics as a beta feature.
224
+ It monitors load average, CPU, memory, swap and disk space.
225
+ For now, only Linux is supported.
226
+ It has to be explicitly enabled with a feature flag :
227
+
228
+ ```yaml
229
+ # config/rorvswild.yml
230
+ production:
231
+ api_key: API_KEY
232
+ features:
233
+ - server_metrics
234
+ ```
235
+
236
+ Here is the equivalent if you prefer initialising RorVsWild manually :
237
+
238
+ ```ruby
239
+ # config/initializers/rorvswild.rb
240
+ RorVsWild.start(api_key: "API_KEY", features: ["server_metrics"])
241
+ ```
242
+
243
+ The data are available in a server tab beside requests and jobs.
244
+
211
245
  ## Contributing
212
246
 
213
247
  1. Fork it ( https://github.com/[my-github-username]/rorvswild/fork )
@@ -16,7 +16,7 @@ module RorVsWild
16
16
 
17
17
  def self.default_ignored_exceptions
18
18
  if defined?(Rails)
19
- %w[ActionController::RoutingError] + Rails.application.config.action_dispatch.rescue_responses.map { |(key,value)| key }
19
+ ActionDispatch::ExceptionWrapper.rescue_responses.keys
20
20
  else
21
21
  []
22
22
  end
@@ -26,6 +26,7 @@ module RorVsWild
26
26
 
27
27
  def initialize(config)
28
28
  @config = self.class.default_config.merge(config)
29
+ load_features
29
30
  @client = Client.new(@config)
30
31
  @queue = config[:queue] || Queue.new(client)
31
32
  @locator = RorVsWild::Locator.new
@@ -35,6 +36,12 @@ module RorVsWild
35
36
  cleanup_data
36
37
  end
37
38
 
39
+ def load_features
40
+ features = config[:features] || []
41
+ features.include?("server_metrics")
42
+ require "rorvswild/metrics" if features.include?("server_metrics")
43
+ end
44
+
38
45
  def setup_plugins
39
46
  for name in RorVsWild::Plugin.constants
40
47
  next if config[:ignore_plugins] && config[:ignore_plugins].include?(name.to_s)
@@ -49,7 +56,7 @@ module RorVsWild
49
56
  measure_block(code) { eval(code) }
50
57
  end
51
58
 
52
- def measure_block(name, kind = "code".freeze, &block)
59
+ def measure_block(name = nil, kind = "code".freeze, &block)
53
60
  current_data ? measure_section(name, kind: kind, &block) : measure_job(name, &block)
54
61
  end
55
62
 
@@ -113,6 +120,18 @@ module RorVsWild
113
120
  current_data[:error]
114
121
  end
115
122
 
123
+ def merge_error_context(hash)
124
+ self.error_context = error_context ? error_context.merge(hash) : hash
125
+ end
126
+
127
+ def error_context
128
+ current_data[:error_context] if current_data
129
+ end
130
+
131
+ def error_context=(hash)
132
+ current_data[:error_context] = hash if current_data
133
+ end
134
+
116
135
  def current_data
117
136
  Thread.current[:rorvswild_data]
118
137
  end
@@ -134,6 +153,12 @@ module RorVsWild
134
153
  config[:ignore_jobs].include?(name)
135
154
  end
136
155
 
156
+ def os_description
157
+ @os_description ||= `uname -sr`
158
+ rescue Exception => ex
159
+ @os_description = RbConfig::CONFIG["host_os"]
160
+ end
161
+
137
162
  #######################
138
163
  ### Private methods ###
139
164
  #######################
@@ -162,15 +187,16 @@ module RorVsWild
162
187
  client.post_async("/errors".freeze, error: hash)
163
188
  end
164
189
 
165
- def exception_to_hash(exception, extra_details = nil)
190
+ def exception_to_hash(exception, context = nil)
166
191
  file, line = locator.find_most_relevant_file_and_line_from_exception(exception)
192
+ context = context ? error_context.merge(context) : error_context if error_context
167
193
  {
168
194
  line: line.to_i,
169
195
  file: locator.relative_path(file),
170
196
  message: exception.message,
171
197
  backtrace: exception.backtrace || ["No backtrace"],
172
198
  exception: exception.class.to_s,
173
- extra_details: extra_details,
199
+ extra_details: context,
174
200
  environment: {
175
201
  os: os_description,
176
202
  user: Etc.getlogin,
@@ -186,11 +212,5 @@ module RorVsWild
186
212
  def ignored_exception?(exception)
187
213
  (config[:ignored_exceptions] || config[:ignore_exceptions]).include?(exception.class.to_s)
188
214
  end
189
-
190
- def os_description
191
- @os_description ||= `uname -a`
192
- rescue Exception => ex
193
- @os_description = RUBY_PLATFORM
194
- end
195
215
  end
196
216
  end
@@ -0,0 +1,31 @@
1
+ module RorVsWild
2
+ class Metrics
3
+ class Cpu
4
+ attr_reader :user, :system, :idle, :waiting, :stolen
5
+ attr_reader :load_average, :count
6
+
7
+ def update
8
+ if vmstat = execute(:vmstat)
9
+ vmstat = vmstat.split("\n").last.split
10
+ @user = vmstat[12].to_i
11
+ @system = vmstat[13].to_i
12
+ @idle = vmstat[14].to_i
13
+ @waiting = vmstat[15].to_i
14
+ @stolen = vmstat[16].to_i
15
+ end
16
+ if uptime = execute(:uptime)
17
+ @load_average = uptime.split[-3].to_f
18
+ end
19
+ if nproc = execute(:nproc)
20
+ @count = nproc.to_i
21
+ end
22
+ end
23
+
24
+ def execute(command)
25
+ `#{command}`
26
+ rescue => ex
27
+ nil
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,57 @@
1
+ module RorVsWild
2
+ class Metrics
3
+ class Memory
4
+ attr_reader :ram_total, :ram_free, :ram_available, :ram_buffers, :ram_cached
5
+ attr_reader :swap_total, :swap_free
6
+ attr_reader :storage_total, :storage_used
7
+
8
+ def ram_used
9
+ ram_total - ram_available
10
+ end
11
+
12
+ def swap_used
13
+ swap_total - swap_free
14
+ end
15
+
16
+ PROC_MEMINFO = "/proc/meminfo".freeze
17
+ MEM_TOTAL = "MemTotal" # Total usable RAM (i.e., physical RAM minus a few reserved bits and the kernel binary code).
18
+ MEM_FREE = "MemFree" # The sum of LowFree+HighFree.
19
+ MEM_AVAILABLE = "MemAvailable" # An estimate of how much memory is available for starting new applications, without swapping.
20
+ BUFFERS = "Buffers" # Relatively temporary storage for raw disk blocks that shouldn't get tremendously large (20MB or so).
21
+ CACHED = "Cached" # In-memory cache for files read from the disk (the page cache). Doesn't include SwapCached.
22
+ SWAP_TOTAL = "SwapTotal" # Total amount of swap space available.
23
+ SWAP_FREE = "SwapFree" # Amount of swap space that is currently unused.
24
+
25
+ def update
26
+ info = read_meminfo
27
+ @ram_total = convert_to_bytes(info[MEM_TOTAL])
28
+ @ram_free = convert_to_bytes(info[MEM_FREE])
29
+ @ram_available = convert_to_bytes(info[MEM_AVAILABLE])
30
+ @ram_buffers = convert_to_bytes(info[BUFFERS])
31
+ @ram_cached = convert_to_bytes(info[CACHED])
32
+ @swap_total = convert_to_bytes(info[SWAP_TOTAL])
33
+ @swap_free = convert_to_bytes(info[SWAP_FREE])
34
+ end
35
+
36
+ private
37
+
38
+ def units
39
+ @units ||= {"kb" => 1000, "mb" => 1000 * 1000, "gb" => 1000 * 1000 * 1000}.freeze
40
+ end
41
+
42
+ def read_meminfo
43
+ return unless File.readable?(PROC_MEMINFO)
44
+ File.read(PROC_MEMINFO).split("\n").reduce({}) do |hash, line|
45
+ name, value = line.split(":")
46
+ hash[name] = value.strip
47
+ hash
48
+ end
49
+ end
50
+
51
+ def convert_to_bytes(string)
52
+ value, unit = string.split
53
+ value.to_i * units[unit.downcase]
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ module RorVsWild
2
+ class Metrics
3
+ class Storage
4
+ attr_reader :used, :free
5
+
6
+ def update
7
+ array = `df -k | grep " /$"`.split
8
+ @used = array[2].to_i * 1000
9
+ @free = array[3].to_i * 1000
10
+ end
11
+
12
+ def total
13
+ used + free
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,54 @@
1
+ module RorVsWild
2
+ class Metrics
3
+ UPDATE_INTERVAL_MS = 60_000 # One metric every minute
4
+
5
+ attr_reader :cpu, :memory, :storage, :updated_at
6
+
7
+ def initialize
8
+ @cpu = RorVsWild::Metrics::Cpu.new
9
+ @memory = RorVsWild::Metrics::Memory.new
10
+ @storage = RorVsWild::Metrics::Storage.new
11
+ end
12
+
13
+ def update
14
+ if staled?
15
+ cpu.update
16
+ memory.update
17
+ storage.update
18
+ @updated_at = RorVsWild.clock_milliseconds
19
+ end
20
+ end
21
+
22
+ def staled?
23
+ !updated_at || RorVsWild.clock_milliseconds - updated_at > UPDATE_INTERVAL_MS
24
+ end
25
+
26
+ def to_h
27
+ {
28
+ hostname: Socket.gethostname,
29
+ os: RorVsWild.agent.os_description,
30
+ cpu_user: cpu.user,
31
+ cpu_system: cpu.system,
32
+ cpu_idle: cpu.idle,
33
+ cpu_waiting: cpu.waiting,
34
+ cpu_stolen: cpu.stolen,
35
+ cpu_count: cpu.count,
36
+ load_average: cpu.load_average,
37
+ ram_total: memory.ram_total,
38
+ ram_free: memory.ram_free,
39
+ ram_used: memory.ram_used,
40
+ ram_cached: memory.ram_cached,
41
+ swap_total: memory.swap_total,
42
+ swap_used: memory.swap_used,
43
+ swap_free: memory.swap_free,
44
+ storage_total: storage.total,
45
+ storage_used: storage.used,
46
+ storage_free: storage.free,
47
+ }
48
+ end
49
+ end
50
+ end
51
+
52
+ require "rorvswild/metrics/cpu"
53
+ require "rorvswild/metrics/memory"
54
+ require "rorvswild/metrics/storage"
@@ -2,7 +2,6 @@ module RorVsWild
2
2
  module Plugin
3
3
  class NetHttp
4
4
  HTTP = "http".freeze
5
- HTTPS = "https".freeze
6
5
 
7
6
  def self.setup
8
7
  return if !defined?(Net::HTTP)
@@ -21,9 +20,7 @@ module RorVsWild
21
20
 
22
21
  def request_with_rorvswild(req, body = nil, &block)
23
22
  return request_without_rorvswild(req, body, &block) if request_called_twice?
24
- scheme = use_ssl? ? HTTPS : HTTP
25
- url = "#{req.method} #{scheme}://#{address}#{req.path}"
26
- RorVsWild.agent.measure_section(url, kind: HTTP) do
23
+ RorVsWild.agent.measure_section("#{req.method} #{address}", kind: HTTP) do
27
24
  request_without_rorvswild(req, body, &block)
28
25
  end
29
26
  end
@@ -18,7 +18,7 @@ module RorVsWild
18
18
  end
19
19
 
20
20
  def self.commands_to_string(commands)
21
- commands.map { |c| c[0] == :auth ? "auth *****".freeze : c.join(" ".freeze) }.join("\n".freeze)
21
+ commands.map { |c| c[0] }.join("\n".freeze)
22
22
  end
23
23
 
24
24
  APPENDABLE_COMMANDS = [:auth, :select]
@@ -1,6 +1,6 @@
1
1
  module RorVsWild
2
2
  class Queue
3
- SLEEP_TIME = 10
3
+ SLEEP_TIME = 30
4
4
  FLUSH_TRESHOLD = 10
5
5
 
6
6
  attr_reader :mutex, :thread, :client
@@ -11,6 +11,7 @@ module RorVsWild
11
11
  @requests = []
12
12
  @client = client
13
13
  @mutex = Mutex.new
14
+ @metrics = RorVsWild::Metrics.new if defined?(Metrics)
14
15
  Kernel.at_exit { flush }
15
16
  end
16
17
 
@@ -50,6 +51,10 @@ module RorVsWild
50
51
  result
51
52
  end
52
53
 
54
+ def pull_server_metrics
55
+ @metrics && @metrics.update && @metrics.to_h
56
+ end
57
+
53
58
  def flush_indefinetely
54
59
  sleep(SLEEP_TIME) and flush while true
55
60
  rescue Exception => ex
@@ -60,6 +65,7 @@ module RorVsWild
60
65
  def flush
61
66
  data = pull_jobs and client.post("/jobs", jobs: data)
62
67
  data = pull_requests and client.post("/requests", requests: data)
68
+ data = pull_server_metrics and client.post("/metrics", metrics: data)
63
69
  end
64
70
 
65
71
  def start_thread
@@ -1,3 +1,3 @@
1
1
  module RorVsWild
2
- VERSION = "1.5.12".freeze
2
+ VERSION = "1.5.15".freeze
3
3
  end
data/lib/rorvswild.rb CHANGED
@@ -23,6 +23,10 @@ module RorVsWild
23
23
  @logger ||= initialize_logger
24
24
  end
25
25
 
26
+ def self.measure(code_or_name = nil, &block)
27
+ block ? measure_block(code_or_name, &block) : measure_code(code_or_name)
28
+ end
29
+
26
30
  def self.measure_code(code)
27
31
  agent ? agent.measure_code(code) : eval(code)
28
32
  end
@@ -39,6 +43,10 @@ module RorVsWild
39
43
  agent.record_error(exception, extra_details) if agent
40
44
  end
41
45
 
46
+ def self.merge_error_context(hash)
47
+ agent.merge_error_context(hash) if agent
48
+ end
49
+
42
50
  def self.initialize_logger(destination = nil)
43
51
  if destination.respond_to?(:info) && destination.respond_to?(:warn) && destination.respond_to?(:error)
44
52
  destination
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rorvswild
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.12
4
+ version: 1.5.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Bernard
8
8
  - Antoine Marguerie
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-05-22 00:00:00.000000000 Z
12
+ date: 2022-08-17 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Performances and errors insights for rails developers.
15
15
  email:
@@ -40,6 +40,10 @@ files:
40
40
  - lib/rorvswild/local/stylesheet/local.css
41
41
  - lib/rorvswild/local/stylesheet/vendor/prism.css
42
42
  - lib/rorvswild/locator.rb
43
+ - lib/rorvswild/metrics.rb
44
+ - lib/rorvswild/metrics/cpu.rb
45
+ - lib/rorvswild/metrics/memory.rb
46
+ - lib/rorvswild/metrics/storage.rb
43
47
  - lib/rorvswild/plugin/action_controller.rb
44
48
  - lib/rorvswild/plugin/action_mailer.rb
45
49
  - lib/rorvswild/plugin/action_view.rb
@@ -62,8 +66,10 @@ files:
62
66
  homepage: https://www.rorvswild.com
63
67
  licenses:
64
68
  - MIT
65
- metadata: {}
66
- post_install_message:
69
+ metadata:
70
+ source_code_uri: https://github.com/BaseSecrete/rorvswild
71
+ changelog_uri: https://github.com/BaseSecrete/rorvswild/blob/master/CHANGELOG.md
72
+ post_install_message:
67
73
  rdoc_options: []
68
74
  require_paths:
69
75
  - lib
@@ -78,8 +84,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
84
  - !ruby/object:Gem::Version
79
85
  version: '0'
80
86
  requirements: []
81
- rubygems_version: 3.0.3
82
- signing_key:
87
+ rubygems_version: 3.2.22
88
+ signing_key:
83
89
  specification_version: 4
84
90
  summary: Ruby on Rails applications monitoring
85
91
  test_files: []