Class: RubyMethodTracer::SimpleTracer

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby_method_tracer/simple_tracer.rb

Overview

SimpleTracer wraps instance methods on a target class and records execution metrics for each invocation. It measures wall-clock duration, captures success or error status, stores results in-memory, and can optionally print each trace as it happens.

Options:

  • :threshold (Float): Minimum duration in seconds to record; defaults to 0.001 (1ms).

  • :auto_output (Boolean): When true, prints each call summary; defaults to false.

  • :max_calls (Integer): Maximum number of calls to store; defaults to 1000. When exceeded, oldest calls are removed.

  • :logger (Logger): Custom logger instance; defaults to Logger.new($stdout).

Usage:

tracer = RubyMethodTracer::SimpleTracer.new(MyClass, threshold: 0.005)
tracer.trace_method(:expensive_call)
results = tracer.fetch_results

Direct Known Subclasses

EnhancedTracer

Instance Method Summary collapse

Constructor Details

#initialize(target_class, **options) ⇒ SimpleTracer

Returns a new instance of SimpleTracer.



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/ruby_method_tracer/simple_tracer.rb', line 24

def initialize(target_class, **options)
  @target_class = target_class
  @options = default_options.merge(options)
  @calls = []
  @lock  = Mutex.new # Mutex to make writes to @calls thread safe.
  @wrapped_methods = Set.new
  @logger = @options[:logger] || Logger.new($stdout)
  # Unique per instance so separate tracers don't interfere with each other.
  @tracer_key = :"__ruby_method_tracer_in_trace_#{object_id}"
  @formatter = Formatters::BaseFormatter.new
end

Instance Method Details

#clear_resultsObject



85
86
87
# File 'lib/ruby_method_tracer/simple_tracer.rb', line 85

def clear_results
  @lock.synchronize { @calls.clear }
end

#fetch_resultsObject



74
75
76
77
78
79
80
81
82
83
# File 'lib/ruby_method_tracer/simple_tracer.rb', line 74

def fetch_results
  snapshot = nil
  @lock.synchronize { snapshot = @calls.dup } # Copies under lock to prevent races while reading.

  {
    total_calls: snapshot.size,
    total_time: snapshot.sum { |call| call[:execution_time] },
    calls: snapshot
  }
end

#record_call(method_name, execution_time, status, error = nil) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/ruby_method_tracer/simple_tracer.rb', line 54

def record_call(method_name, execution_time, status, error = nil)
  return if execution_time < @options[:threshold]

  call_details = {
    method_name: "#{@target_class}##{method_name}",
    execution_time: execution_time,
    status: status,
    error: error,
    timestamp: Time.now
  }

  @lock.synchronize do
    @calls << call_details
    # Enforce max_calls limit by removing oldest entries
    @calls.shift if @calls.size > @options[:max_calls]
  end

  output_call(call_details) if @options[:auto_output]
end

#trace_method(name) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/ruby_method_tracer/simple_tracer.rb', line 36

def trace_method(name)
  method_name = name.to_sym
  visibility = method_visibility(method_name)
  return unless visibility
  return unless mark_wrapped?(method_name)

  aliased = alias_for(method_name)
  @target_class.send(:alias_method, aliased, method_name) # Aliases original implementation to our private name.

  tracer = self
  key = @tracer_key # unique per tracer instance; prevents cross-tracer interference

  # Defines a new method with the original name that delegates to our wrapper.
  @target_class.define_method(method_name, &build_wrapper(aliased, method_name, key, tracer))

  @target_class.send(visibility, method_name) # Restores the original visibility after redefine.
end