1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
# frozen_string_literal: true
require "securerandom"
require_relative "../profiler/helpers"
require_relative "output"
require "sentry/utils/uuid"
module Sentry
module Vernier
class Profiler
EMPTY_RESULT = {}.freeze
attr_reader :started, :event_id, :result
def initialize(configuration)
@event_id = Utils.uuid
@started = false
@sampled = nil
@profiling_enabled = defined?(Vernier) && configuration.profiling_enabled?
@profiles_sample_rate = configuration.profiles_sample_rate
@project_root = configuration.project_root
@app_dirs_pattern = configuration.app_dirs_pattern
@in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}")
end
def set_initial_sample_decision(transaction_sampled)
unless @profiling_enabled
@sampled = false
return
end
unless transaction_sampled
@sampled = false
log("Discarding profile because transaction not sampled")
return
end
case @profiles_sample_rate
when 0.0
@sampled = false
log("Discarding profile because sample_rate is 0")
return
when 1.0
@sampled = true
return
else
@sampled = Random.rand < @profiles_sample_rate
end
log("Discarding profile due to sampling decision") unless @sampled
end
def start
return unless @sampled
return if @started
@started = ::Vernier.start_profile
log("Started")
@started
rescue RuntimeError => e
# TODO: once Vernier raises something more dedicated, we should catch that instead
if e.message.include?("Profile already started")
log("Not started since running elsewhere")
else
log("Failed to start: #{e.message}")
end
end
def stop
return unless @sampled
return unless @started
@result = ::Vernier.stop_profile
@started = false
log("Stopped")
rescue RuntimeError => e
if e.message.include?("Profile not started")
log("Not stopped since not started")
else
log("Failed to stop Vernier: #{e.message}")
end
end
def active_thread_id
Thread.current.object_id
end
def to_hash
unless @sampled
record_lost_event(:sample_rate)
return EMPTY_RESULT
end
return EMPTY_RESULT unless result
{ **profile_meta, profile: output.to_h }
end
private
def log(message)
Sentry.sdk_logger.debug(LOGGER_PROGNAME) { "[Profiler::Vernier] #{message}" }
end
def record_lost_event(reason)
Sentry.get_current_client&.transport&.record_lost_event(reason, "profile")
end
def profile_meta
{
event_id: @event_id,
version: "1",
platform: "ruby"
}
end
def output
@output ||= Output.new(
result,
project_root: @project_root,
app_dirs_pattern: @app_dirs_pattern,
in_app_pattern: @in_app_pattern
)
end
end
end
end
|