File: backtrace.rb

package info (click to toggle)
ruby-sentry-ruby 6.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 644 kB
  • sloc: ruby: 5,771; makefile: 8; sh: 4
file content (95 lines) | stat: -rw-r--r-- 2,413 bytes parent folder | download | duplicates (2)
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
# frozen_string_literal: true

require "rubygems"
require "concurrent/map"
require "sentry/backtrace/line"

module Sentry
  # @api private
  class Backtrace
    # holder for an Array of Backtrace::Line instances
    attr_reader :lines

    def self.parse(backtrace, project_root, app_dirs_pattern, &backtrace_cleanup_callback)
      ruby_lines = backtrace.is_a?(Array) ? backtrace : backtrace.split(/\n\s*/)

      ruby_lines = backtrace_cleanup_callback.call(ruby_lines) if backtrace_cleanup_callback

      in_app_pattern ||= begin
        Regexp.new("^(#{project_root}/)?#{app_dirs_pattern}")
      end

      lines = ruby_lines.to_a.map do |unparsed_line|
        Line.parse(unparsed_line, in_app_pattern)
      end

      new(lines)
    end

    # Thread.each_caller_location is an API added in Ruby 3.2 that doesn't always collect
    # the entire stack like Kernel#caller or #caller_locations do.
    #
    # @see https://github.com/rails/rails/pull/49095 for more context.
    if Thread.respond_to?(:each_caller_location)
      def self.source_location(&backtrace_cleaner)
        Thread.each_caller_location do |location|
          frame_key = [location.absolute_path, location.lineno]
          cached_value = line_cache[frame_key]

          next if cached_value == :skip

          if cached_value
            return cached_value
          else
            if cleaned_frame = backtrace_cleaner.(location)
              line = Line.from_source_location(location)
              line_cache[frame_key] = line

              return line
            else
              line_cache[frame_key] = :skip

              next
            end
          end
        end
      end

      def self.line_cache
        @line_cache ||= Concurrent::Map.new
      end
    else
      # Since Sentry is mostly used in production, we don't want to fallback
      # to the slower implementation and adds potentially big overhead to the
      # application.
      def self.source_location(*)
        nil
      end
    end


    def initialize(lines)
      @lines = lines
    end

    def inspect
      "<Backtrace: " + lines.map(&:inspect).join(", ") + ">"
    end

    def to_s
      content = []
      lines.each do |line|
        content << line
      end
      content.join("\n")
    end

    def ==(other)
      if other.respond_to?(:lines)
        lines == other.lines
      else
        false
      end
    end
  end
end