module LogStats

Puma plugin to log server stats whenever the number of concurrent requests exceeds a configured threshold.

Constants

STAT_METHODS

Attributes

backtrace_filter[RW]

Proc to filter backtraces.

interval[RW]

Interval between logging attempts in seconds.

threshold[RW]

Minimum concurrent requests per process that will trigger logging server stats, or nil to disable logging. Default is the max number of threads in the server's thread pool. If this attribute is a Proc, it will be re-evaluated each interval.

Public Instance Methods

log(str) click to toggle source
# File lib/puma/plugin/log_stats.rb, line 63
def log(str)
  launcher.events.log str
end
server() click to toggle source

Save reference to Server object from the thread-local key.

# File lib/puma/plugin/log_stats.rb, line 68
def server
  @server ||= Thread.list.map { |t| t[Puma::Server::ThreadLocalKey] }.compact.first
end
server_stats() click to toggle source
# File lib/puma/plugin/log_stats.rb, line 73
def server_stats
  STAT_METHODS.select(&server.method(:respond_to?))
              .map { |name| [name, server.send(name) || 0] }.to_h
end
should_log?() click to toggle source

True if current server load meets configured threshold.

# File lib/puma/plugin/log_stats.rb, line 79
def should_log?
  threshold = LogStats.threshold
  threshold = threshold.call if threshold.is_a?(Proc)
  threshold = server.max_threads if threshold == :max
  threshold && (server.max_threads - server.pool_capacity) >= threshold
end
start(launcher) click to toggle source
# File lib/puma/plugin/log_stats.rb, line 30
def start(launcher)
  @launcher = launcher
  launcher.events.register(:state) do |state|
    @state = state
    stats_logger_thread if state == :running
  end
end
stats_logger_loop() click to toggle source
# File lib/puma/plugin/log_stats.rb, line 49
def stats_logger_loop
  sleep LogStats.interval
  return unless server

  if should_log?
    stats = server_stats
    stats[:threads] = thread_backtraces
    stats[:gc] = GC.stat
    log stats.to_json
  end
rescue StandardError => e
  log "LogStats failed: #{e}\n  #{e.backtrace.join("\n    ")}"
end
stats_logger_thread() click to toggle source
# File lib/puma/plugin/log_stats.rb, line 40
def stats_logger_thread
  Thread.new do
    if Thread.current.respond_to?(:name=)
      Thread.current.name = 'puma stats logger'
    end
    stats_logger_loop while @state == :running
  end
end
thread_backtraces() click to toggle source
# File lib/puma/plugin/log_stats.rb, line 86
def thread_backtraces
  worker_threads.map do |t|
    name = t.respond_to?(:name) ? t.name : thread.object_id.to_s(36)
    [name, LogStats.backtrace_filter.call(t.backtrace)]
  end.sort.to_h
end
worker_threads() click to toggle source

List all non-idle worker threads in the thread pool.

# File lib/puma/plugin/log_stats.rb, line 94
def worker_threads
  server.instance_variable_get(:@thread_pool)
        .instance_variable_get(:@workers)
        .reject { |t| t.backtrace.first.match?(/thread_pool\.rb.*sleep/) }
end