Skip to content

Commit 29c919b

Browse files
committed
Notify/log on changes only
1 parent d6e52b7 commit 29c919b

5 files changed

Lines changed: 27 additions & 125 deletions

File tree

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
puma-log_stats-codeur (0.1.1)
4+
puma-log_stats-codeur (0.1.2)
55
puma (~> 6.0)
66

77
GEM

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# PumaLogStats
22

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.
44

55
## Installation
66

@@ -26,7 +26,10 @@ This plugin is loaded using Puma's plugin API. To enable, add a `plugin :log_sta
2626
# config/puma.rb
2727

2828
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
3033
```
3134

3235
## Development

lib/puma/log_stats.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
11
# frozen_string_literal: true
2-
3-
# require "puma/log_stats/version"

lib/puma/log_stats/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
module Puma
44
module LogStats
5-
VERSION = "0.1.1"
5+
VERSION = "0.1.2"
66
end
77
end

lib/puma/plugin/log_stats.rb

Lines changed: 20 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,8 @@ class << self
88
attr_accessor :interval
99
LogStats.interval = 10
1010

11-
attr_accessor :alarm_on_sentry
12-
LogStats.alarm_on_sentry = true
13-
14-
attr_accessor :alarm_notification_interval
15-
LogStats.alarm_notification_interval = 60
11+
attr_accessor :notify_change_with
12+
LogStats.notify_change_with = :sentry
1613

1714
attr_accessor :warning_threshold
1815
LogStats.warning_threshold = 0.7
@@ -34,8 +31,10 @@ def start(launcher)
3431
@load_level = :normal
3532
while @running
3633
sleep LogStats.interval
34+
@previous_status_message = @status_message
3735
@stats = Puma.stats_hash
38-
log(status)
36+
@status_message = status_message
37+
log(@status_message) if @previous_status_message != @status_message
3938
check_alarms
4039
end
4140
end
@@ -50,27 +49,31 @@ def check_alarms
5049
def threshold_reached(level, threshold)
5150
return false if threads_load < threshold
5251

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
52+
change_level(level, "Puma threads load is greater than #{threshold * 100}% (#{max_threads - pool_capacity}/#{max_threads})")
5553
true
5654
end
5755

5856
def normal_load
59-
return if @load_level == :normal
57+
change_level(:normal, "Puma threads load is back to normal values")
58+
end
6059

61-
log("INFO: Puma threads load is back to normal values")
62-
@load_level = :normal
60+
def change_level(level, message)
61+
return if @load_level == level
62+
63+
log("#{level.to_s.upcase}: #{message}")
64+
notify(level, message)
65+
@load_level = level
6366
end
6467

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
68+
def notify(level, message)
69+
if LogStats.notify_change_with == :sentry
70+
Sentry.capture_message(message, level: level == :critical ? :error : level) if defined?(Sentry) && level != :normal
71+
elsif LogStats.notify_change_with.respond_to?(:call)
72+
LogStats.notify_change_with.call(level: level, message: message, threads_load: threads_load)
7073
end
7174
end
7275

73-
def status
76+
def status_message
7477
if clustered?
7578
"cluster: #{booted_workers}/#{workers} workers: #{running}/#{max_threads} threads, #{pool_capacity} available, #{backlog} backlog"
7679
else
@@ -134,105 +137,3 @@ def max_threads
134137
Puma::Plugin.create do
135138
include LogStats
136139
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

0 commit comments

Comments
 (0)