puma-log_stats-codeur 0.1.1 → 0.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2449156394b5c86c2bee695565450e4c49a0b2bb87b15d3e7cb03e21ac47b322
4
- data.tar.gz: 91887bd1d2f23c594889504063268d5dfeadcebd2a1539bbd4300aec14625eb4
3
+ metadata.gz: 7dccacaaaed068382710999238bf1563335653ba81ac0afe70373eb53fc6c4ec
4
+ data.tar.gz: 3183d3640deb3b060873061e10087c06e429aa0be8d60ba0b8924adf3c379890
5
5
  SHA512:
6
- metadata.gz: 3084f6e89321b110e7bec943ff66435dbc3446a84257929e937a073bc80e8ca21209661050dbc9c4b2bf7a6d3f14dd291b31221eeafe937b8e737974c1bda630
7
- data.tar.gz: 6b6f9f8f4fe2925f4dccdd89b503516d350932e0875fb3d50e42eb2c21f4b8f5ad10c53108564fe8ec721a47a9d1add5bc464076ac4affc8a873f08692002b59
6
+ metadata.gz: 060ed7df03b49ccfb212d4d18f117bff2f4fc054fc6dcece408d11efbd8bbacf4f958c9e7e46251fb3da1703318afb60df7bad77723038677febf1197cc5d7bf
7
+ data.tar.gz: fbf036c802861ff041d4a3c777af0767b6c8e0d73cebb489c2049dea6183036f16bd6964cd8b8c54312a244502454271ead82259e2cb4abcee93f82811919a6c
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # PumaLogStats
2
2
 
3
- Puma plugin to log server stats whenever the number of concurrent requests exceeds a configured threshold.
3
+ Puma plugin to log server stats to puma.log. It logs changes only and can raise Sentry issues when a threshold is reached.
4
4
 
5
5
  ## Installation
6
6
 
@@ -26,7 +26,10 @@ This plugin is loaded using Puma's plugin API. To enable, add a `plugin :log_sta
26
26
  # config/puma.rb
27
27
 
28
28
  plugin :log_stats
29
- LogStats.threshold = 2
29
+ # LogStats.interval = 10
30
+ # LogStats.notify_change_with = :sentry # can be a Proc
31
+ # LogStats.warning_threshold = 0.7
32
+ # LogStats.critical_threshold = 0.85
30
33
  ```
31
34
 
32
35
  ## Development
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Puma
4
4
  module LogStats
5
- VERSION = "0.1.1"
5
+ VERSION = "0.1.3"
6
6
  end
7
7
  end
@@ -1,3 +1 @@
1
1
  # frozen_string_literal: true
2
-
3
- # require "puma/log_stats/version"
@@ -2,237 +2,140 @@
2
2
 
3
3
  require "puma/plugin"
4
4
 
5
- module LogStats
6
- class << self
7
- # Interval between logging attempts in seconds.
8
- attr_accessor :interval
9
- LogStats.interval = 10
5
+ module Puma
6
+ module LogStats
7
+ class << self
8
+ # Interval between logging attempts in seconds.
9
+ attr_accessor :interval
10
+ LogStats.interval = 10
10
11
 
11
- attr_accessor :alarm_on_sentry
12
- LogStats.alarm_on_sentry = true
12
+ attr_accessor :notify_change_with
13
+ LogStats.notify_change_with = :sentry
13
14
 
14
- attr_accessor :alarm_notification_interval
15
- LogStats.alarm_notification_interval = 60
15
+ attr_accessor :warning_threshold
16
+ LogStats.warning_threshold = 0.7
16
17
 
17
- attr_accessor :warning_threshold
18
- LogStats.warning_threshold = 0.7
18
+ attr_accessor :critical_threshold
19
+ LogStats.critical_threshold = 0.85
20
+ end
19
21
 
20
- attr_accessor :critical_threshold
21
- LogStats.critical_threshold = 0.85
22
- end
22
+ def start(launcher)
23
+ @launcher = launcher
24
+ launcher.events.register(:state) do |state|
25
+ if %i[halt restart stop].include?(state)
26
+ @running = false
27
+ end
28
+ end
23
29
 
24
- def start(launcher)
25
- @launcher = launcher
26
- launcher.events.register(:state) do |state|
27
- if %i[halt restart stop].include?(state)
28
- @running = false
30
+ in_background do
31
+ @running = true
32
+ @load_level = :normal
33
+ while @running
34
+ sleep LogStats.interval
35
+ @previous_status_message = @status_message
36
+ @stats = Puma.stats_hash
37
+ @status_message = status_message
38
+ log(@status_message) if @previous_status_message != @status_message
39
+ check_alarms
40
+ end
29
41
  end
30
42
  end
31
43
 
32
- in_background do
33
- @running = true
34
- @load_level = :normal
35
- while @running
36
- sleep LogStats.interval
37
- @stats = Puma.stats_hash
38
- log(status)
39
- check_alarms
40
- end
44
+ def check_alarms
45
+ threshold_reached(:critical, LogStats.critical_threshold) ||
46
+ threshold_reached(:warning, LogStats.warning_threshold) ||
47
+ normal_load
41
48
  end
42
- end
43
49
 
44
- def check_alarms
45
- threshold_reached(:critical, LogStats.critical_threshold) ||
46
- threshold_reached(:warning, LogStats.warning_threshold) ||
47
- normal_load
48
- end
50
+ def threshold_reached(level, threshold)
51
+ return false if threads_load < threshold
49
52
 
50
- def threshold_reached(level, threshold)
51
- return false if threads_load < threshold
53
+ change_level(level, "Puma threads load is greater than #{threshold * 100}% (#{max_threads - pool_capacity}/#{max_threads})")
54
+ true
55
+ end
52
56
 
53
- notify_alarm("#{level.to_s.upcase}: Puma threads load is more than #{threshold * 100}% (#{max_threads - pool_capacity}/#{max_threads})")
54
- @load_level = level if @load_level != level
55
- true
56
- end
57
+ def normal_load
58
+ change_level(:normal, "Puma threads load is back to normal values")
59
+ end
57
60
 
58
- def normal_load
59
- return if @load_level == :normal
61
+ def change_level(level, message)
62
+ return if @load_level == level
60
63
 
61
- log("INFO: Puma threads load is back to normal values")
62
- @load_level = :normal
63
- end
64
+ log("#{level.to_s.upcase}: #{message}")
65
+ notify(level, message)
66
+ @load_level = level
67
+ end
64
68
 
65
- def notify_alarm(message)
66
- if @notified_at.nil? || (Time.now - @notified_at) < LogStats.alarm_notification_interval
67
- log(message)
68
- Sentry.capture_message(message) if LogStats.alarm_on_sentry && defined?(Sentry)
69
- @notified_at = Time.now
69
+ def notify(level, message)
70
+ if LogStats.notify_change_with == :sentry
71
+ Sentry.capture_message(message, level: (level == :critical) ? :error : level) if defined?(Sentry) && level != :normal
72
+ elsif LogStats.notify_change_with.respond_to?(:call)
73
+ LogStats.notify_change_with.call(level: level, message: message, threads_load: threads_load)
74
+ end
70
75
  end
71
- end
72
76
 
73
- def status
74
- if clustered?
75
- "cluster: #{booted_workers}/#{workers} workers: #{running}/#{max_threads} threads, #{pool_capacity} available, #{backlog} backlog"
76
- else
77
- "single: #{running}/#{max_threads} threads, #{pool_capacity} available, #{backlog} backlog"
77
+ def status_message
78
+ if clustered?
79
+ "cluster: #{booted_workers}/#{workers} workers: #{running}/#{max_threads} threads, #{pool_capacity} available, #{backlog} backlog"
80
+ else
81
+ "single: #{running}/#{max_threads} threads, #{pool_capacity} available, #{backlog} backlog"
82
+ end
78
83
  end
79
- end
80
84
 
81
- def log(str)
82
- @launcher.log_writer.log("[#{Time.now}][puma #{Puma::Const::VERSION}] #{str}")
83
- end
85
+ def log(str)
86
+ @launcher.log_writer.log("[#{Time.now}][puma #{Puma::Const::VERSION}] #{str}")
87
+ end
84
88
 
85
- def threads_load
86
- 1.0 - pool_capacity.to_f / max_threads.to_f
87
- end
89
+ def threads_load
90
+ 1.0 - pool_capacity.to_f / max_threads.to_f
91
+ end
88
92
 
89
- def clustered?
90
- @stats.key?(:workers)
91
- end
93
+ def clustered?
94
+ @stats.key?(:workers)
95
+ end
92
96
 
93
- def workers
94
- @stats.fetch(:workers, 1)
95
- end
97
+ def workers
98
+ @stats.fetch(:workers, 1)
99
+ end
96
100
 
97
- def booted_workers
98
- @stats.fetch(:booted_workers, 1)
99
- end
101
+ def booted_workers
102
+ @stats.fetch(:booted_workers, 1)
103
+ end
100
104
 
101
- def running
102
- if clustered?
103
- @stats[:worker_status].sum { |s| s[:last_status].fetch(:running, 0) }
104
- else
105
- @stats.fetch(:running, 0)
105
+ def running
106
+ if clustered?
107
+ @stats[:worker_status].sum { |s| s[:last_status].fetch(:running, 0) }
108
+ else
109
+ @stats.fetch(:running, 0)
110
+ end
106
111
  end
107
- end
108
112
 
109
- def backlog
110
- if clustered?
111
- @stats[:worker_status].sum { |s| s[:last_status].fetch(:backlog, 0) }
112
- else
113
- @stats.fetch(:backlog, 0)
113
+ def backlog
114
+ if clustered?
115
+ @stats[:worker_status].sum { |s| s[:last_status].fetch(:backlog, 0) }
116
+ else
117
+ @stats.fetch(:backlog, 0)
118
+ end
114
119
  end
115
- end
116
120
 
117
- def pool_capacity
118
- if clustered?
119
- @stats[:worker_status].sum { |s| s[:last_status].fetch(:pool_capacity, 0) }
120
- else
121
- @stats.fetch(:pool_capacity, 0)
121
+ def pool_capacity
122
+ if clustered?
123
+ @stats[:worker_status].sum { |s| s[:last_status].fetch(:pool_capacity, 0) }
124
+ else
125
+ @stats.fetch(:pool_capacity, 0)
126
+ end
122
127
  end
123
- end
124
128
 
125
- def max_threads
126
- if clustered?
127
- @stats[:worker_status].sum { |s| s[:last_status].fetch(:max_threads, 0) }
128
- else
129
- @stats.fetch(:max_threads, 0)
129
+ def max_threads
130
+ if clustered?
131
+ @stats[:worker_status].sum { |s| s[:last_status].fetch(:max_threads, 0) }
132
+ else
133
+ @stats.fetch(:max_threads, 0)
134
+ end
130
135
  end
131
136
  end
132
137
  end
133
138
 
134
139
  Puma::Plugin.create do
135
- include LogStats
140
+ include Puma::LogStats
136
141
  end
137
-
138
- # require "puma"
139
- # require "puma/plugin"
140
- # require "json"
141
-
142
- # # Puma plugin to log server stats whenever the number of
143
- # # concurrent requests exceeds a configured threshold.
144
- # module LogStats
145
- # STAT_METHODS = %i[backlog running pool_capacity max_threads requests_count].freeze
146
-
147
- # class << self
148
- # # Minimum concurrent requests per process that will trigger logging server
149
- # # stats, or nil to disable logging.
150
- # # Default is the max number of threads in the server's thread pool.
151
- # # If this attribute is a Proc, it will be re-evaluated each interval.
152
- # attr_accessor :threshold
153
- # LogStats.threshold = :max
154
-
155
- # # Interval between logging attempts in seconds.
156
- # attr_accessor :interval
157
- # LogStats.interval = 1
158
-
159
- # # Proc to filter backtraces.
160
- # attr_accessor :backtrace_filter
161
- # LogStats.backtrace_filter = ->(bt) { bt }
162
- # end
163
-
164
- # Puma::Plugin.create do
165
- # attr_reader :launcher
166
-
167
- # def start(launcher)
168
- # @launcher = launcher
169
- # launcher.events.register(:state) do |state|
170
- # @state = state
171
- # stats_logger_thread if state == :running
172
- # end
173
-
174
- # in_background { start }
175
- # end
176
-
177
- # private
178
-
179
- # def stats_logger_thread
180
- # Thread.new do
181
- # if Thread.current.respond_to?(:name=)
182
- # Thread.current.name = "puma stats logger"
183
- # end
184
- # start while @state == :running
185
- # end
186
- # end
187
-
188
- # def start
189
- # sleep LogStats.interval
190
- # return unless server
191
-
192
- # if should_log?
193
- # stats = server_stats
194
- # stats[:threads] = thread_backtraces
195
- # stats[:gc] = GC.stat
196
- # log stats.to_json
197
- # end
198
- # rescue => e
199
- # log "LogStats failed: #{e}\n #{e.backtrace.join("\n ")}"
200
- # end
201
-
202
- # def log(str)
203
- # launcher.log_writer.log str
204
- # end
205
-
206
- # # Save reference to Server object from the thread-local key.
207
- # def server
208
- # @server ||= Thread.list.map { |t| t[Puma::Server::ThreadLocalKey] }.compact.first
209
- # end
210
-
211
- # def server_stats
212
- # STAT_METHODS.select(&server.method(:respond_to?))
213
- # .map { |name| [name, server.send(name) || 0] }.to_h
214
- # end
215
-
216
- # # True if current server load meets configured threshold.
217
- # def should_log?
218
- # threshold = LogStats.threshold
219
- # threshold = threshold.call if threshold.is_a?(Proc)
220
- # threshold = server.max_threads if threshold == :max
221
- # threshold && (server.max_threads - server.pool_capacity) >= threshold
222
- # end
223
-
224
- # def thread_backtraces
225
- # worker_threads.map do |t|
226
- # name = t.respond_to?(:name) ? t.name : thread.object_id.to_s(36)
227
- # [name, LogStats.backtrace_filter.call(t.backtrace)]
228
- # end.sort.to_h
229
- # end
230
-
231
- # # List all non-idle worker threads in the thread pool.
232
- # def worker_threads
233
- # server.instance_variable_get(:@thread_pool)
234
- # .instance_variable_get(:@workers)
235
- # .reject { |t| t.backtrace.first.match?(/thread_pool\.rb.*sleep/) }
236
- # end
237
- # end
238
- # end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma-log_stats-codeur
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Will Jordan