File: stacktrace.rb

package info (click to toggle)
ruby-sentry-ruby-core 5.28.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 672 kB
  • sloc: ruby: 6,118; makefile: 8; sh: 4
file content (89 lines) | stat: -rw-r--r-- 2,406 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
# frozen_string_literal: true

module Sentry
  class StacktraceInterface
    # @return [<Array[Frame]>]
    attr_reader :frames

    # @param frames [<Array[Frame]>]
    def initialize(frames:)
      @frames = frames
    end

    # @return [Hash]
    def to_hash
      { frames: @frames.map(&:to_hash) }
    end

    # @return [String]
    def inspect
      @frames.map(&:to_s)
    end

    private

    # Not actually an interface, but I want to use the same style
    class Frame < Interface
      attr_accessor :abs_path, :context_line, :function, :in_app, :filename,
                  :lineno, :module, :pre_context, :post_context, :vars

      def initialize(project_root, line, strip_backtrace_load_path = true)
        @project_root = project_root
        @strip_backtrace_load_path = strip_backtrace_load_path

        @abs_path = line.file
        @function = line.method if line.method
        @lineno = line.number
        @in_app = line.in_app
        @module = line.module_name if line.module_name
        @filename = compute_filename
      end

      def to_s
        "#{@filename}:#{@lineno}"
      end

      def compute_filename
        return if abs_path.nil?
        return abs_path unless @strip_backtrace_load_path

        prefix =
          if under_project_root? && in_app
            @project_root
          elsif under_project_root?
            longest_load_path || @project_root
          else
            longest_load_path
          end

        prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
      end

      def set_context(linecache, context_lines)
        return unless abs_path

        @pre_context, @context_line, @post_context = \
            linecache.get_file_context(abs_path, lineno, context_lines)
      end

      def to_hash(*args)
        data = super(*args)
        data.delete(:vars) unless vars && !vars.empty?
        data.delete(:pre_context) unless pre_context && !pre_context.empty?
        data.delete(:post_context) unless post_context && !post_context.empty?
        data.delete(:context_line) unless context_line && !context_line.empty?
        data
      end

      private

      def under_project_root?
        @project_root && abs_path.start_with?(@project_root)
      end

      def longest_load_path
        $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
      end
    end
  end
end