1
0
Fork 0
mirror of https://github.com/puma/puma.git synced 2022-11-09 13:48:40 -05:00
puma--puma/lib/puma/app/status.rb
Daniel Colson 39d16fadaf Add pumactl command to print thread backtraces (#2054)
* Add pumactl command to print thread backtraces

Completes 1 of 2 items from #1964

This commit adds an endpoint to the status app to print thread
backtraces, and control cli command to call that endpoint.

I tried this locally by starting a server with:

```sh
bundle exec bin/puma test/rackup/hello.ru \
  --control-url="unix://test.sock" \
  --control-token="token"
```

and then printing the backtraces with:

```sh
bundle exec bin/pumactl thread-backtraces \
  --control-url="unix://test.sock" \
  --control-token="token"
```

* Log threads as JSON in control app

With this commit the status app sends the thread backtraces as an array
of objects with `name` and `backtrace` keys, rather than as a string
matching the SIGINFO output.

While working on this I noticed that we logged the thread TID twice.
This commit simplifies that so we only log the thread TID once, with
both the label (I don't know when the label would get set) and name if
they are available.
2019-11-11 13:08:41 +08:00

88 lines
2.2 KiB
Ruby

# frozen_string_literal: true
require 'json'
module Puma
module App
# Check out {#call}'s source code to see what actions this web application
# can respond to.
class Status
OK_STATUS = '{ "status": "ok" }'.freeze
def initialize(cli, token = nil)
@cli = cli
@auth_token = token
end
def call(env)
unless authenticate(env)
return rack_response(403, 'Invalid auth token', 'text/plain')
end
case env['PATH_INFO']
when /\/stop$/
@cli.stop
rack_response(200, OK_STATUS)
when /\/halt$/
@cli.halt
rack_response(200, OK_STATUS)
when /\/restart$/
@cli.restart
rack_response(200, OK_STATUS)
when /\/phased-restart$/
if !@cli.phased_restart
rack_response(404, '{ "error": "phased restart not available" }')
else
rack_response(200, OK_STATUS)
end
when /\/reload-worker-directory$/
if !@cli.send(:reload_worker_directory)
rack_response(404, '{ "error": "reload_worker_directory not available" }')
else
rack_response(200, OK_STATUS)
end
when /\/gc$/
GC.start
rack_response(200, OK_STATUS)
when /\/gc-stats$/
rack_response(200, GC.stat.to_json)
when /\/stats$/
rack_response(200, @cli.stats)
when /\/thread-backtraces$/
backtraces = []
@cli.thread_status do |name, backtrace|
backtraces << { name: name, backtrace: backtrace }
end
rack_response(200, backtraces.to_json)
else
rack_response 404, "Unsupported action", 'text/plain'
end
end
private
def authenticate(env)
return true unless @auth_token
env['QUERY_STRING'].to_s.split(/&;/).include?("token=#{@auth_token}")
end
def rack_response(status, body, content_type='application/json')
headers = {
'Content-Type' => content_type,
'Content-Length' => body.bytesize.to_s
}
[status, headers, [body]]
end
end
end
end