Class: BuilderApm::Methods::Instrumenter

Inherits:
Object
  • Object
show all
Defined in:
lib/builder_apm/methods/instrumenter.rb

Instance Method Summary collapse

Constructor Details

#initialize(root_path: Rails.root.to_s) ⇒ Instrumenter

Returns a new instance of Instrumenter.



4
5
6
7
8
# File 'lib/builder_apm/methods/instrumenter.rb', line 4

def initialize(root_path: Rails.root.to_s)
  @this_gem_path = File.expand_path("../../..", __dir__)
  @root_path = root_path
  @call_times = {}
end

Instance Method Details

#process_trace_point(tp) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/builder_apm/methods/instrumenter.rb', line 41

def process_trace_point(tp)
  if tp.event == :call
    method_id = "#{tp.defined_class}##{tp.method_id}"
    (@call_times[method_id]||= []) << Process.clock_gettime(Process::CLOCK_MONOTONIC)
    caller_info = caller_locations(4,1).first
    calling_file_path = caller_info.absolute_path
    calling_line_number = caller_info.lineno
  
    method_call = { 
      method: method_id, 
      method_line: "#{tp.path.gsub(@root_path, '')}:#{tp.lineno}",
      triggering_line: "#{calling_file_path.gsub(@root_path, '')}:#{calling_line_number}", 
      children: [], 
      start_time: Time.now.to_f * 1000, 
      sql_events: [] 
    }
  
    (Thread.current[:stack] ||= []).push(method_call)
  else
    method_id = "#{tp.defined_class}##{tp.method_id}"
    
    if @call_times.key?(method_id)
      elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - @call_times[method_id].pop
      elapsed_time_in_ms = (elapsed_time * 1000).round(3)
      @call_times.delete(method_id)
  
      method_call = (Thread.current[:stack] ||= []).pop
      method_call[:end_time] = Time.now.to_f * 1000
      method_call[:duration] = elapsed_time_in_ms
  
      if Thread.current[:stack]&.any?
        Thread.current[:stack].last[:children].push(method_call) 
      else 
        Thread.current[:stack].push(method_call) 
      end
    end
  end
end

#setup_traceObject



20
21
22
23
24
25
26
27
28
29
# File 'lib/builder_apm/methods/instrumenter.rb', line 20

def setup_trace
  me = self
  TracePoint.new(:call, :return, :end, :raise) do |tp|
    starttime = Time.now.to_f * 1000
    me.process_trace_point(tp) if Thread.current[:request_id] && me.valid_trace_point?(tp)
    duration = (Time.now.to_f * 1000) - starttime
    
    Thread.current[:method_tracing] = (Thread.current[:method_tracing] ||= 0) + duration
  end
end

#startObject



10
11
12
13
14
# File 'lib/builder_apm/methods/instrumenter.rb', line 10

def start
  @trace = setup_trace
  @trace.enable
  Thread.current[:trace_point] = @trace
end

#stopObject



16
17
18
# File 'lib/builder_apm/methods/instrumenter.rb', line 16

def stop
  @trace.disable unless @trace.nil?
end

#valid_trace_point?(tp) ⇒ Boolean

Returns:

  • (Boolean)


31
32
33
34
35
36
37
38
39
# File 'lib/builder_apm/methods/instrumenter.rb', line 31

def valid_trace_point?(tp)
  return false unless tp.path.start_with?(@root_path)

  start_controller = Thread.current[:stack]&.first
  start_controller && "#{tp.defined_class}##{tp.method_id}" != start_controller[:method]

  # gems_to_track = BuilderApm.configuration.gems_to_track
  # gems_to_track.any? { |gem| tp.path.include?(File::SEPARATOR + gem) }
end