lib/elastic_apm/stacktrace.rb in elastic-apm-0.6.2 vs lib/elastic_apm/stacktrace.rb in elastic-apm-0.7.0

- old
+ new

@@ -1,120 +1,16 @@ # frozen_string_literal: true -require 'elastic_apm/stacktrace/frame' -require 'elastic_apm/stacktrace/line_cache' - module ElasticAPM # @api private class Stacktrace - JAVA_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$/ - RUBY_FORMAT = /^(.+?):(\d+)(?::in `(.+?)')?$/ + attr_accessor :frames - RUBY_VERS_REGEX = %r{ruby(/gems)?[-/](\d+\.)+\d} - JRUBY_ORG_REGEX = %r{org/jruby} - - def initialize(backtrace) - @backtrace = backtrace - end - - attr_reader :frames - - def self.build(config, backtrace, type) - return nil unless backtrace - - stack = new(backtrace) - stack.build_frames(config, type) - stack - end - - def build_frames(config, type) - @frames = @backtrace.map do |line| - build_frame(config, line, type) - end - end - def length frames.length end def to_a frames.map(&:to_h) - end - - private - - def parse_line(line) - ruby_match = line.match(RUBY_FORMAT) - - if ruby_match - _, file, number, method = ruby_match.to_a - file.sub!(/\.class$/, '.rb') - module_name = nil - else - java_match = line.match(JAVA_FORMAT) - _, module_name, method, file, number = java_match.to_a - end - - [file, number, method, module_name] - end - - def library_frame?(config, abs_path) - return false unless abs_path - - if abs_path.start_with?(config.root_path) - return true if abs_path.start_with?(config.root_path + '/vendor') - return false - end - - return true if abs_path.match(RUBY_VERS_REGEX) - return true if abs_path.match(JRUBY_ORG_REGEX) - - false - end - - class << self - attr_accessor :frame_cache - end - - # rubocop:disable Metrics/AbcSize, Metrics/MethodLength - def build_frame(config, line, type) - # TODO: Eventually move this to agent 'context' - self.class.frame_cache ||= Util::LruCache.new(2048) do |cache, keys| - line, type = keys - abs_path, lineno, function, _module_name = parse_line(line) - - frame = Frame.new - frame.abs_path = abs_path - frame.filename = strip_load_path(abs_path) - frame.function = function - frame.lineno = lineno.to_i - frame.library_frame = library_frame?(config, abs_path) - - line_count = - context_lines_for(config, type, library_frame: frame.library_frame) - frame.build_context line_count - - cache[[line, type]] = frame - end - - self.class.frame_cache[[line, type]] - end - # rubocop:enable Metrics/AbcSize, Metrics/MethodLength - - def strip_load_path(path) - return nil if path.nil? - - prefix = - $LOAD_PATH - .map(&:to_s) - .select { |s| path.start_with?(s) } - .max_by(&:length) - - prefix ? path[prefix.chomp(File::SEPARATOR).length + 1..-1] : path - end - - def context_lines_for(config, type, library_frame:) - key = "source_lines_#{type}_#{library_frame ? 'library' : 'app'}_frames" - config.send(key.to_sym) end end end