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