puma-log_stats-codeur 0.1.1 → 0.1.3

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: 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