/atalaya

Watchtower Ruby rack application

Primary LanguageRubyMIT LicenseMIT

Atalaya

A watchtower Ruby rack application. This gem provides a Rack-application that offers a snapshot insight into the state of the mother-application Ruby process.

Visors

Atalaya features several visors:

  • VM
  • Process
  • Rails
  • Puma

Example output

{
     "process" : {
      "process_id" : 12108,
      "threads" : {
         "backtraces" : [
            "/app/shared/bundle/ruby/2.6.0/gems/puma-3.12.0/lib/puma/cluster.rb:306:in `join'",
            "/app/shared/bundle/ruby/2.6.0/gems/puma-3.12.0/lib/puma/cluster.rb:254:in `select'",
            "/app/shared/bundle/ruby/2.6.0/gems/activerecord-5.2.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:301:in `sleep'",
            "/app/shared/bundle/ruby/2.6.0/gems/puma-3.12.0/lib/puma/cluster.rb:291:in `sleep'",
            "/app/shared/bundle/ruby/2.6.0/gems/puma-3.12.0/lib/puma/thread_pool.rb:119:in `sleep'",
            "/app/shared/bundle/ruby/2.6.0/gems/puma-3.12.0/lib/puma/thread_pool.rb:119:in `sleep'",
            "/app/shared/bundle/ruby/2.6.0/gems/puma-3.12.0/lib/puma/thread_pool.rb:119:in `sleep'",
            "/app/shared/bundle/ruby/2.6.0/bundler/gems/atalaya-0eab2b5556fa/lib/atalaya/visors/process.rb:25:in `backtrace'",
            "/app/shared/bundle/ruby/2.6.0/gems/puma-3.12.0/lib/puma/reactor.rb:126:in `select'",
            "/app/shared/bundle/ruby/2.6.0/gems/puma-3.12.0/lib/puma/thread_pool.rb:283:in `sleep'",
            "/app/shared/bundle/ruby/2.6.0/gems/puma-3.12.0/lib/puma/thread_pool.rb:254:in `sleep'",
            "/app/shared/bundle/ruby/2.6.0/gems/puma-3.12.0/lib/puma/server.rb:384:in `select'",
            "/app/shared/bundle/ruby/2.6.0/gems/newrelic_rpm-6.0.0.351/lib/new_relic/agent/event_loop.rb:118:in `select'"
         ],
         "statuses" : [
            "sleep",
            "sleep",
            "sleep",
            "sleep",
            "sleep",
            "sleep",
            "sleep",
            "run",
            "sleep",
            "sleep",
            "sleep",
            "sleep",
            "sleep"
         ],
         "count" : 13
      }
   },
   "puma" : {
      "server" : {
         "mode" : "cluster",
         "workers" : 2
      }
   },
   "rails" : {
      "action_mailer" : null,
      "action_controller" : null,
      "action_view" : null,
      "active_record" : {
         "prepared_statements_count" : [
            4
         ],
         "pool_size" : 5,
         "prepared_statements" : [
            {
               "\"$user\", public-SELECT  \"specifications\".* FROM \"specifications\" WHERE \"specifications\".\"id\" = $1 LIMIT $2" : "a4",
               "\"$user\", public-SELECT  \"makes\".* FROM \"makes\" WHERE \"makes\".\"id\" = $1 LIMIT $2" : "a2",
               "\"$user\", public-SELECT  \"models\".* FROM \"models\" WHERE \"models\".\"id\" = $1 LIMIT $2" : "a3",
               "\"$user\", public-SELECT  \"motorcycles\".* FROM \"motorcycles\" WHERE \"motorcycles\".\"fake_id\" = $1 LIMIT $2" : "a1"
            }
         ],
         "active_connections_count" : 1
      },
      "cache": null
   },
   "vm" : {
      "object_space" : {
         "T_STRING" : 78621,
         "T_RATIONAL" : 790,
         "T_COMPLEX" : 1,
         "T_DATA" : 5608,
         "T_STRUCT" : 904,
         "T_CLASS" : 7468,
         "T_OBJECT" : 7477,
         "T_MATCH" : 60,
         "TOTAL" : 287656,
         "T_SYMBOL" : 665,
         "T_IMEMO" : 86961,
         "T_MODULE" : 1066,
         "T_REGEXP" : 1898,
         "T_ICLASS" : 1845,
         "T_HASH" : 4109,
         "T_FLOAT" : 2282,
         "T_FILE" : 27,
         "T_ARRAY" : 13057,
         "T_BIGNUM" : 731,
         "FREE" : 74086
      },
      "gc_stat" : {
         "heap_tomb_pages" : 0,
         "heap_available_slots" : 287656,
         "old_objects_limit" : 405568,
         "remembered_wb_unprotected_objects_limit" : 5810,
         "total_allocated_objects" : 2033068,
         "heap_allocatable_pages" : 0,
         "total_freed_pages" : 0,
         "malloc_increase_bytes_limit" : 16777216,
         "total_allocated_pages" : 352,
         "heap_marked_slots" : 208589,
         "total_freed_objects" : 1819498,
         "malloc_increase_bytes" : 197020,
         "heap_eden_pages" : 352,
         "heap_free_slots" : 74086,
         "heap_allocated_pages" : 352,
         "heap_final_slots" : 0,
         "heap_live_slots" : 213570,
         "old_objects" : 202784,
         "heap_sorted_length" : 352,
         "oldmalloc_increase_bytes_limit" : 16777216,
         "remembered_wb_unprotected_objects" : 2905,
         "major_gc_count" : 11,
         "oldmalloc_increase_bytes" : 3257268,
         "count" : 63,
         "minor_gc_count" : 52
      }
   }
}

Installation

You'll need to add atalaya to your Gemfile

# Use most recent version
gem 'atalaya', git: 'https://github.com/irvingwashington/atalaya.git'

# Or use the stable version from RubyGems
gem 'atalaya'

Run bundle install.

In your Rails app, add the rack application entry.

Rails.application.routes.draw do
  # ...
  match '/atalaya.json', to: Atalaya::App.new, via: :get
  # ...
end

However, serving this information publicly is not a good idea. You should wrap the app with some form of authentication. You can use a custom routing constraint class if your app doesn't have any other form of authentication.

# lib/constraints/authenticated_call.rb
module Constraints
  class AuthenticatedCall
    def self.matches?(request)
      if ActionController::HttpAuthentication::Basic.has_basic_credentials?(request)
        credentials = ActionController::HttpAuthentication::Basic.decode_credentials(request)
        login, password = credentials.split(':')

        login == 'foo' && password == 'bar'
    end
    end
  end
end

# config/routes.rb
require Rails.root.join('lib', 'constraints', 'authenticated_call')

Rails.applcation.routes.draw do
  constraints Constraints::AuthenticatedCall do
    match '/atalaya.json', to: Atalaya::App.new, via: :get
  end
end