class RorVsWild::Agent

Attributes

client[R]
config[R]
locator[R]
queue[R]

Public Class Methods

default_config() click to toggle source
# File lib/rorvswild/agent.rb, line 7
def self.default_config
  {
    api_url: "https://www.rorvswild.com/api/v1",
    ignore_exceptions: default_ignored_exceptions,
    ignore_requests: [],
    ignore_plugins: [],
    ignore_jobs: [],
  }
end
default_ignored_exceptions() click to toggle source
# File lib/rorvswild/agent.rb, line 17
def self.default_ignored_exceptions
  if defined?(Rails)
    ActionDispatch::ExceptionWrapper.rescue_responses.keys
  else
    []
  end
end
new(config) click to toggle source
# File lib/rorvswild/agent.rb, line 27
def initialize(config)
  @config = self.class.default_config.merge(config)
  load_features
  @client = Client.new(@config)
  @queue = config[:queue] || Queue.new(client)
  @locator = RorVsWild::Locator.new
  Host.load_config(config)
  Deployment.load_config(config)

  RorVsWild.logger.debug("Start RorVsWild #{RorVsWild::VERSION}")
  setup_plugins
  cleanup_data
end

Public Instance Methods

add_section(section) click to toggle source
# File lib/rorvswild/agent.rb, line 170
def add_section(section)
  return unless current_data[:sections]
  if sibling = current_data[:sections].find { |s| s.sibling?(section) }
    sibling.merge(section)
  else
    current_data[:sections] << section
  end
end
catch_error(context = nil, &block) click to toggle source
# File lib/rorvswild/agent.rb, line 129
def catch_error(context = nil, &block)
  begin
    block.call
  rescue Exception => ex
    record_error(ex, context)
    ex
  end
end
current_data() click to toggle source
# File lib/rorvswild/agent.rb, line 166
def current_data
  Thread.current[:rorvswild_data]
end
error_context() click to toggle source
# File lib/rorvswild/agent.rb, line 154
def error_context
  current_data[:error_context] if current_data
end
error_context=(hash) click to toggle source
# File lib/rorvswild/agent.rb, line 158
def error_context=(hash)
  current_data[:error_context] = hash if current_data
end
ignored_exception?(exception) click to toggle source
# File lib/rorvswild/agent.rb, line 187
def ignored_exception?(exception)
  return false unless config[:ignore_exceptions]
  config[:ignore_exceptions].any? { |str_or_regex| str_or_regex === exception.class.to_s }
end
ignored_job?(name) click to toggle source
# File lib/rorvswild/agent.rb, line 183
def ignored_job?(name)
  config[:ignore_jobs].any? { |str_or_regex| str_or_regex === name }
end
ignored_request?(name) click to toggle source
# File lib/rorvswild/agent.rb, line 179
def ignored_request?(name)
  config[:ignore_requests].any? { |str_or_regex| str_or_regex === name }
end
load_features() click to toggle source
# File lib/rorvswild/agent.rb, line 41
def load_features
  features = config[:features] || []
  RorVsWild.logger.info("Server metrics are now monitored enabled by default") if features.include?("server_metrics")
end
measure_block(name = nil, kind = "code".freeze, &block) click to toggle source
# File lib/rorvswild/agent.rb, line 60
def measure_block(name = nil, kind = "code".freeze, &block)
  current_data ? measure_section(name, kind: kind, &block) : measure_job(name, &block)
end
measure_code(code) click to toggle source
# File lib/rorvswild/agent.rb, line 56
def measure_code(code)
  measure_block(code) { eval(code) }
end
measure_job(name, parameters: nil, &block) click to toggle source
# File lib/rorvswild/agent.rb, line 100
def measure_job(name, parameters: nil, &block)
  return measure_section(name, &block) if current_data # For recursive jobs
  return block.call if ignored_job?(name)
  initialize_data[:name] = name
  begin
    block.call
  rescue Exception => ex
    push_exception(ex, parameters: parameters, job: {name: name})
    raise
  ensure
    gc = Section.stop_gc_timing(current_data[:gc_section])
    current_data[:sections] << gc if gc.calls > 0
    current_data[:runtime] = RorVsWild.clock_milliseconds - current_data[:started_at]
    queue_job
  end
end
measure_method(method) click to toggle source
# File lib/rorvswild/agent.rb, line 64
def measure_method(method)
  return if method.name.end_with?("_measured_by_rorvswild")
  if method.is_a?(Method)
    method_full_name = [method.receiver, method.name].join(".") # Method => class method
  else
    method_full_name = [method.owner, method.name].join("#") # UnboundMethod => instance method
  end
  method_alias = :"#{method.name}_measured_by_rorvswild"
  return if method.owner.method_defined?(method_alias)
  method.owner.alias_method(method_alias, method.name)
  method_file, method_line = method.source_location
  method_file = locator.relative_path(File.expand_path(method_file))
  method.owner.define_method(method.name) do |*args|
    section = Section.start
    section.file = method_file
    section.line = method_line
    section.commands << method_full_name
    result = send(method_alias, *args)
    Section.stop
    result
  end
end
measure_section(name, kind: "code", &block) click to toggle source
# File lib/rorvswild/agent.rb, line 87
def measure_section(name, kind: "code", &block)
  return block.call unless current_data
  begin
    RorVsWild::Section.start do |section|
      section.commands << name
      section.kind = kind
    end
    block.call
  ensure
    RorVsWild::Section.stop
  end
end
merge_error_context(hash) click to toggle source
# File lib/rorvswild/agent.rb, line 150
def merge_error_context(hash)
  self.error_context = error_context ? error_context.merge(hash) : hash
end
push_exception(exception, options = nil) click to toggle source
# File lib/rorvswild/agent.rb, line 142
def push_exception(exception, options = nil)
  return if ignored_exception?(exception)
  return unless current_data
  current_data[:error] = exception_to_hash(exception)
  current_data[:error].merge!(options) if options
  current_data[:error]
end
record_error(exception, context = nil) click to toggle source
# File lib/rorvswild/agent.rb, line 138
def record_error(exception, context = nil)
  queue_error(exception_to_hash(exception, context)) if !ignored_exception?(exception)
end
send_server_timing=(boolean) click to toggle source
# File lib/rorvswild/agent.rb, line 162
def send_server_timing=(boolean)
  current_data[:send_server_timing] = boolean if current_data
end
setup_plugins() click to toggle source
# File lib/rorvswild/agent.rb, line 46
def setup_plugins
  for name in RorVsWild::Plugin.constants
    next if config[:ignore_plugins] && config[:ignore_plugins].include?(name.to_s)
    if (plugin = RorVsWild::Plugin.const_get(name)).respond_to?(:setup)
      RorVsWild.logger.debug("Setup RorVsWild::Plugin::#{name}")
      plugin.setup
    end
  end
end
start_request() click to toggle source
# File lib/rorvswild/agent.rb, line 117
def start_request
  current_data || initialize_data
end
stop_request() click to toggle source
# File lib/rorvswild/agent.rb, line 121
def stop_request
  return unless data = current_data
  gc = Section.stop_gc_timing(data[:gc_section])
  data[:sections] << gc if gc.calls > 0 && gc.total_ms > 0
  data[:runtime] = RorVsWild.clock_milliseconds - current_data[:started_at]
  queue_request
end

Private Instance Methods

cleanup_data() click to toggle source
# File lib/rorvswild/agent.rb, line 208
def cleanup_data
  result = Thread.current[:rorvswild_data]
  Thread.current[:rorvswild_data] = nil
  result
end
exception_to_hash(exception, context = nil) click to toggle source
# File lib/rorvswild/agent.rb, line 227
def exception_to_hash(exception, context = nil)
  file, line = locator.find_most_relevant_file_and_line_from_exception(exception)
  context = context ? error_context.merge(context) : error_context if error_context
  {
    line: line.to_i,
    file: locator.relative_path(file),
    message: exception.message[0,1_000_000],
    backtrace: exception.backtrace || ["No backtrace"],
    exception: exception.class.to_s,
    context: context,
    environment: Host.to_h,
  }
end
initialize_data() click to toggle source

Private methods ###

# File lib/rorvswild/agent.rb, line 198
def initialize_data
  Thread.current[:rorvswild_data] = {
    started_at: RorVsWild.clock_milliseconds,
    gc_section: Section.start_gc_timing,
    environment: Host.to_h,
    section_stack: [],
    sections: [],
  }
end
queue_error(hash) click to toggle source
# File lib/rorvswild/agent.rb, line 223
def queue_error(hash)
  queue.push_error(hash)
end
queue_job() click to toggle source
# File lib/rorvswild/agent.rb, line 219
def queue_job
  queue.push_job(cleanup_data)
end
queue_request() click to toggle source
# File lib/rorvswild/agent.rb, line 214
def queue_request
  (data = cleanup_data) && data[:name] && queue.push_request(data)
  data
end