Module: Railscope

Defined in:
lib/railscope.rb,
lib/railscope/engine.rb,
lib/railscope/filter.rb,
lib/railscope/context.rb,
lib/railscope/version.rb,
lib/railscope/entry_data.rb,
lib/railscope/middleware.rb,
app/models/railscope/entry.rb,
lib/railscope/storage/base.rb,
lib/railscope/flush_service.rb,
app/jobs/railscope/purge_job.rb,
lib/railscope/storage/database.rb,
app/jobs/railscope/application_job.rb,
lib/railscope/storage/redis_buffer.rb,
app/helpers/railscope/dashboard_helper.rb,
app/models/railscope/application_record.rb,
lib/railscope/subscribers/job_subscriber.rb,
lib/railscope/subscribers/base_subscriber.rb,
lib/railscope/subscribers/view_subscriber.rb,
lib/generators/railscope/install_generator.rb,
lib/railscope/subscribers/model_subscriber.rb,
lib/railscope/subscribers/query_subscriber.rb,
app/controllers/railscope/entries_controller.rb,
lib/railscope/subscribers/command_subscriber.rb,
lib/railscope/subscribers/request_subscriber.rb,
app/controllers/railscope/dashboard_controller.rb,
lib/railscope/subscribers/exception_subscriber.rb,
app/controllers/railscope/api/entries_controller.rb,
app/controllers/railscope/application_controller.rb

Defined Under Namespace

Modules: Api, DashboardHelper, Generators, Storage, Subscribers Classes: ApplicationController, ApplicationJob, ApplicationRecord, AuthenticationError, Context, DashboardController, Engine, EntriesController, Entry, EntryData, Error, Filter, FlushService, Middleware, PurgeJob

Constant Summary collapse

DEFAULT_IGNORE_PATHS =
%w[
  /railscope
  /assets
  /packs
  /cable
].freeze
STORAGE_DATABASE =

Storage backends

:database
STORAGE_REDIS =
:redis
VERSION =
"0.1.11"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.authenticate_with ⇒ Object

Returns the value of attribute authenticate_with.



32
33
34
# File 'lib/railscope.rb', line 32

def authenticate_with
  @authenticate_with
end

.ignore_commands ⇒ Object



115
116
117
# File 'lib/railscope.rb', line 115

def ignore_commands
  @ignore_commands ||= []
end

.ignore_jobs ⇒ Object



103
104
105
# File 'lib/railscope.rb', line 103

def ignore_jobs
  @ignore_jobs ||= []
end

.ignore_models ⇒ Object

Model filtering (blocklist)



129
130
131
# File 'lib/railscope.rb', line 129

def ignore_models
  @ignore_models ||= []
end

.ignore_paths ⇒ Object



95
96
97
# File 'lib/railscope.rb', line 95

def ignore_paths
  @ignore_paths ||= DEFAULT_IGNORE_PATHS.dup
end

.redis ⇒ Object

Redis configuration



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/railscope.rb', line 75

def redis
  return @redis if defined?(@redis) && @redis

  redis_url = ENV.fetch("RAILSCOPE_REDIS_URL", nil) || ENV.fetch("REDIS_URL", nil)
  return nil unless redis_url

  require "redis"
  options = { url: redis_url }
  options[:ssl_params] = { verify_mode: OpenSSL::SSL::VERIFY_NONE } if redis_url.start_with?("rediss://")
  @redis = Redis.new(**options)
end

.retention_days ⇒ Object



44
45
46
# File 'lib/railscope.rb', line 44

def retention_days
  @retention_days ||= ENV.fetch("RAILSCOPE_RETENTION_DAYS", 7).to_i
end

.storage_backend ⇒ Symbol

Storage configuration

Returns:

  • (Symbol) —

    :database or :redis



50
51
52
53
54
55
# File 'lib/railscope.rb', line 50

def storage_backend
  return @storage_backend if defined?(@storage_backend) && @storage_backend

  backend = ENV.fetch("RAILSCOPE_STORAGE", "database").to_sym
  @storage_backend = %i[database redis].include?(backend) ? backend : STORAGE_DATABASE
end

Class Method Details

.add_ignore_commands(*commands) ⇒ Object



119
120
121
# File 'lib/railscope.rb', line 119

def add_ignore_commands(*commands)
  @ignore_commands = ignore_commands.concat(commands.flatten.map(&:to_s)).uniq
end

.add_ignore_jobs(*job_classes) ⇒ Object



107
108
109
# File 'lib/railscope.rb', line 107

def add_ignore_jobs(*job_classes)
  @ignore_jobs = ignore_jobs.concat(job_classes.flatten.map(&:to_s)).uniq
end

.add_ignore_models(*model_names) ⇒ Object



133
134
135
# File 'lib/railscope.rb', line 133

def add_ignore_models(*model_names)
  @ignore_models = ignore_models.concat(model_names.flatten.map(&:to_s)).uniq
end

.add_ignore_paths(*paths) ⇒ Object



99
100
101
# File 'lib/railscope.rb', line 99

def add_ignore_paths(*paths)
  @ignore_paths = ignore_paths.concat(paths.flatten).uniq
end

.add_sensitive_keys(*keys) ⇒ Object



178
179
180
# File 'lib/railscope.rb', line 178

def add_sensitive_keys(*keys)
  Filter.add_sensitive_keys(*keys)
end

.add_watch_trigger(model_name, on:) ⇒ Object



149
150
151
152
# File 'lib/railscope.rb', line 149

def add_watch_trigger(model_name, on:)
  actions = Array(on).map(&:to_s)
  @watch_triggers = watch_triggers.push({ model: model_name.to_s, on: actions }).uniq
end

.authenticate(controller) ⇒ Object



168
169
170
171
172
# File 'lib/railscope.rb', line 168

def authenticate(controller)
  return true if authenticate_with.nil?

  authenticate_with.call(controller)
end

.conditional_recording? ⇒ Boolean

Returns:

  • (Boolean)


154
155
156
# File 'lib/railscope.rb', line 154

def conditional_recording?
  watch_triggers.any?
end

.configure {|_self| ... } ⇒ Object

Yields:

  • (_self)

Yield Parameters:

  • _self (Railscope) —

    the object that the method was called on



215
216
217
# File 'lib/railscope.rb', line 215

def configure
  yield self
end

.context ⇒ Object



164
165
166
# File 'lib/railscope.rb', line 164

def context
  Context.current
end

.enabled=(value) ⇒ Object



34
35
36
# File 'lib/railscope.rb', line 34

def enabled=(value)
  @enabled = value
end

.enabled? ⇒ Boolean

Returns:

  • (Boolean)


38
39
40
41
42
# File 'lib/railscope.rb', line 38

def enabled?
  return @enabled if defined?(@enabled) && !@enabled.nil?

  %w[true 1].include?(ENV.fetch("RAILSCOPE_ENABLED", nil))
end

.filter(payload) ⇒ Object



174
175
176
# File 'lib/railscope.rb', line 174

def filter(payload)
  Filter.filter(payload)
end

.ignore_command?(command_name) ⇒ Boolean

Returns:

  • (Boolean)


123
124
125
# File 'lib/railscope.rb', line 123

def ignore_command?(command_name)
  ignore_commands.any? { |pattern| command_name.match?(pattern) }
end

.ignore_job?(job_class_name) ⇒ Boolean

Returns:

  • (Boolean)


111
112
113
# File 'lib/railscope.rb', line 111

def ignore_job?(job_class_name)
  ignore_jobs.any? { |pattern| job_class_name.match?(pattern) }
end

.ignore_path?(path) ⇒ Boolean

Returns:

  • (Boolean)


209
210
211
212
213
# File 'lib/railscope.rb', line 209

def ignore_path?(path)
  return false if path.nil?

  ignore_paths.any? { |ignored| path.start_with?(ignored) }
end

.matches_trigger?(model_name, action) ⇒ Boolean

Returns:

  • (Boolean)


158
159
160
161
162
# File 'lib/railscope.rb', line 158

def matches_trigger?(model_name, action)
  watch_triggers.any? do |trigger|
    model_name.match?(trigger[:model]) && trigger[:on].include?(action.to_s)
  end
end

.ready? ⇒ Boolean

Returns:

  • (Boolean)


190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/railscope.rb', line 190

def ready?
  return true if @ready

  return false if @checking_ready

  @checking_ready = true
  result = storage.ready?
  @ready = true if result
  @checking_ready = false
  result
rescue StandardError
  @checking_ready = false
  false
end

.redis_available? ⇒ Boolean

Returns:

  • (Boolean)


87
88
89
90
91
92
93
# File 'lib/railscope.rb', line 87

def redis_available?
  return false unless redis

  redis.ping == "PONG"
rescue StandardError
  false
end

.reset_ready! ⇒ Object



205
206
207
# File 'lib/railscope.rb', line 205

def reset_ready!
  @ready = nil
end

.reset_storage! ⇒ Object

Reset storage (useful for testing)



69
70
71
72
# File 'lib/railscope.rb', line 69

def reset_storage!
  @storage = nil
  @storage_backend = nil
end

.should_record?(path: nil) ⇒ Boolean

Returns:

  • (Boolean)


182
183
184
185
186
187
188
# File 'lib/railscope.rb', line 182

def should_record?(path: nil)
  return false unless enabled?
  return false unless ready?
  return false if path && ignore_path?(path)

  true
end

.should_track_model?(model_name) ⇒ Boolean

Returns:

  • (Boolean)


137
138
139
140
141
# File 'lib/railscope.rb', line 137

def should_track_model?(model_name)
  return false if model_name.nil?

  ignore_models.none? { |pattern| model_name.match?(pattern) }
end

.storage ⇒ Storage::Base

Get the storage adapter instance

Returns:



59
60
61
62
63
64
65
66
# File 'lib/railscope.rb', line 59

def storage
  @storage ||= case storage_backend
               when STORAGE_REDIS
                 Storage::RedisBuffer.new
               else
                 Storage::Database.new
               end
end

.watch_triggers ⇒ Object

Conditional recording: only persist a batch when a watched model event fires



145
146
147
# File 'lib/railscope.rb', line 145

def watch_triggers
  @watch_triggers ||= []
end