Module: ScoutRails::Instruments::ActiveRecordInstruments

Defined in:
lib/scout_rails/instruments/active_record_instruments.rb

Overview

Contains ActiveRecord instrument, aliasing ActiveRecord::ConnectionAdapters::AbstractAdapter#log calls to trace calls to the database.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(instrumented_class) ⇒ Object



5
6
7
8
9
10
11
12
13
14
# File 'lib/scout_rails/instruments/active_record_instruments.rb', line 5

def self.included(instrumented_class)
  ScoutRails::Agent.instance.logger.debug "Instrumenting #{instrumented_class.inspect}"
  instrumented_class.class_eval do
    unless instrumented_class.method_defined?(:log_without_scout_instruments)
      alias_method :log_without_scout_instruments, :log
      alias_method :log, :log_with_scout_instruments
      protected :log
    end
  end
end

Instance Method Details

#log_with_scout_instruments(*args, &block) ⇒ Object

self.included



16
17
18
19
20
21
# File 'lib/scout_rails/instruments/active_record_instruments.rb', line 16

def log_with_scout_instruments(*args, &block)
  sql, name = args
  self.class.instrument(scout_ar_metric_name(sql,name), :desc => scout_sanitize_sql(sql)) do
    log_without_scout_instruments(sql, name, &block)
  end
end

#scout_ar_metric_name(sql, name) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/scout_rails/instruments/active_record_instruments.rb', line 23

def scout_ar_metric_name(sql,name)
  # sql: SELECT "places".* FROM "places"  ORDER BY "places"."position" ASC
  # name: Place Load
  if name && (parts = name.split " ") && parts.size == 2
    model = parts.first
    operation = parts.last.downcase
    metric_name = case operation
                  when 'load' then 'find'
                  when 'indexes', 'columns' then nil # not under developer control
                  when 'destroy', 'find', 'save', 'create', 'exists' then operation
                  when 'update' then 'save'
                  else
                    if model == 'Join'
                      operation
                    end
                  end
    metric = "ActiveRecord/#{model}/#{metric_name}" if metric_name
    metric = "ActiveRecord/SQL/other" if metric.nil?
  else
    metric = "ActiveRecord/SQL/Unknown"
  end
  metric
end

#scout_sanitize_sql(sql) ⇒ Object

Removes actual values from SQL. Used to both obfuscate the SQL and group similar queries in the UI.



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/scout_rails/instruments/active_record_instruments.rb', line 49

def scout_sanitize_sql(sql)
  return nil if sql.length > 1000 # safeguard - don't sanitize large SQL statements
  sql = sql.dup
  sql.gsub!(/\\"/, '') # removing escaping double quotes
  sql.gsub!(/\\'/, '') # removing escaping single quotes
  sql.gsub!(/'(?:[^']|'')*'/, '?') # removing strings (single quote)
  sql.gsub!(/"(?:[^"]|"")*"/, '?') # removing strings (double quote)
  sql.gsub!(/\b\d+\b/, '?') # removing integers
  sql.gsub!(/\?(,\?)+/,'?') # replace multiple ? w/a single ?
  sql
end