sentry-raven 3.1.0 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/README.md +6 -0
  4. data/lib/raven/configuration.rb +10 -0
  5. data/lib/raven/instance.rb +5 -0
  6. data/lib/raven/integrations/delayed_job.rb +2 -1
  7. data/lib/raven/integrations/rack.rb +15 -2
  8. data/lib/raven/integrations/sidekiq/error_handler.rb +2 -2
  9. data/lib/raven/transports/http.rb +0 -2
  10. data/lib/raven/{integrations/sidekiq → utils}/context_filter.rb +3 -3
  11. data/lib/raven/version.rb +1 -1
  12. data/sentry-ruby/.gitignore +11 -0
  13. data/sentry-ruby/.rspec +3 -0
  14. data/sentry-ruby/.travis.yml +6 -0
  15. data/sentry-ruby/CODE_OF_CONDUCT.md +74 -0
  16. data/sentry-ruby/Gemfile +9 -0
  17. data/sentry-ruby/LICENSE.txt +21 -0
  18. data/sentry-ruby/README.md +44 -0
  19. data/sentry-ruby/Rakefile +6 -0
  20. data/sentry-ruby/bin/console +14 -0
  21. data/sentry-ruby/bin/setup +8 -0
  22. data/sentry-ruby/examples/rails-6.0/.browserslistrc +1 -0
  23. data/sentry-ruby/examples/rails-6.0/.gitignore +35 -0
  24. data/sentry-ruby/examples/rails-6.0/Gemfile +58 -0
  25. data/sentry-ruby/examples/rails-6.0/README.md +23 -0
  26. data/sentry-ruby/examples/rails-6.0/Rakefile +6 -0
  27. data/sentry-ruby/examples/rails-6.0/app/assets/config/manifest.js +2 -0
  28. data/sentry-ruby/examples/rails-6.0/app/assets/images/.keep +0 -0
  29. data/sentry-ruby/examples/rails-6.0/app/assets/stylesheets/application.css +15 -0
  30. data/sentry-ruby/examples/rails-6.0/app/channels/application_cable/channel.rb +4 -0
  31. data/sentry-ruby/examples/rails-6.0/app/channels/application_cable/connection.rb +4 -0
  32. data/sentry-ruby/examples/rails-6.0/app/controllers/application_controller.rb +2 -0
  33. data/sentry-ruby/examples/rails-6.0/app/controllers/concerns/.keep +0 -0
  34. data/sentry-ruby/examples/rails-6.0/app/controllers/welcome_controller.rb +23 -0
  35. data/sentry-ruby/examples/rails-6.0/app/helpers/application_helper.rb +2 -0
  36. data/sentry-ruby/examples/rails-6.0/app/javascript/channels/consumer.js +6 -0
  37. data/sentry-ruby/examples/rails-6.0/app/javascript/channels/index.js +5 -0
  38. data/sentry-ruby/examples/rails-6.0/app/javascript/packs/application.js +17 -0
  39. data/sentry-ruby/examples/rails-6.0/app/jobs/application_job.rb +7 -0
  40. data/sentry-ruby/examples/rails-6.0/app/mailers/application_mailer.rb +4 -0
  41. data/sentry-ruby/examples/rails-6.0/app/models/application_record.rb +3 -0
  42. data/sentry-ruby/examples/rails-6.0/app/models/concerns/.keep +0 -0
  43. data/sentry-ruby/examples/rails-6.0/app/views/layouts/application.html.erb +15 -0
  44. data/sentry-ruby/examples/rails-6.0/app/views/layouts/mailer.html.erb +13 -0
  45. data/sentry-ruby/examples/rails-6.0/app/views/layouts/mailer.text.erb +1 -0
  46. data/sentry-ruby/examples/rails-6.0/app/views/welcome/report_demo.html.erb +22 -0
  47. data/sentry-ruby/examples/rails-6.0/app/views/welcome/view_error.html.erb +1 -0
  48. data/sentry-ruby/examples/rails-6.0/app/workers/error_worker.rb +7 -0
  49. data/sentry-ruby/examples/rails-6.0/babel.config.js +72 -0
  50. data/sentry-ruby/examples/rails-6.0/bin/bundle +114 -0
  51. data/sentry-ruby/examples/rails-6.0/bin/rails +9 -0
  52. data/sentry-ruby/examples/rails-6.0/bin/rake +9 -0
  53. data/sentry-ruby/examples/rails-6.0/bin/setup +36 -0
  54. data/sentry-ruby/examples/rails-6.0/bin/spring +17 -0
  55. data/sentry-ruby/examples/rails-6.0/bin/webpack +18 -0
  56. data/sentry-ruby/examples/rails-6.0/bin/webpack-dev-server +18 -0
  57. data/sentry-ruby/examples/rails-6.0/bin/yarn +11 -0
  58. data/sentry-ruby/examples/rails-6.0/config.ru +5 -0
  59. data/sentry-ruby/examples/rails-6.0/config/application.rb +28 -0
  60. data/sentry-ruby/examples/rails-6.0/config/boot.rb +4 -0
  61. data/sentry-ruby/examples/rails-6.0/config/cable.yml +10 -0
  62. data/sentry-ruby/examples/rails-6.0/config/credentials.yml.enc +1 -0
  63. data/sentry-ruby/examples/rails-6.0/config/database.yml +25 -0
  64. data/sentry-ruby/examples/rails-6.0/config/environment.rb +5 -0
  65. data/sentry-ruby/examples/rails-6.0/config/environments/development.rb +62 -0
  66. data/sentry-ruby/examples/rails-6.0/config/environments/production.rb +112 -0
  67. data/sentry-ruby/examples/rails-6.0/config/environments/test.rb +48 -0
  68. data/sentry-ruby/examples/rails-6.0/config/initializers/application_controller_renderer.rb +8 -0
  69. data/sentry-ruby/examples/rails-6.0/config/initializers/assets.rb +14 -0
  70. data/sentry-ruby/examples/rails-6.0/config/initializers/backtrace_silencers.rb +7 -0
  71. data/sentry-ruby/examples/rails-6.0/config/initializers/content_security_policy.rb +30 -0
  72. data/sentry-ruby/examples/rails-6.0/config/initializers/cookies_serializer.rb +5 -0
  73. data/sentry-ruby/examples/rails-6.0/config/initializers/filter_parameter_logging.rb +4 -0
  74. data/sentry-ruby/examples/rails-6.0/config/initializers/inflections.rb +16 -0
  75. data/sentry-ruby/examples/rails-6.0/config/initializers/mime_types.rb +4 -0
  76. data/sentry-ruby/examples/rails-6.0/config/initializers/wrap_parameters.rb +14 -0
  77. data/sentry-ruby/examples/rails-6.0/config/locales/en.yml +33 -0
  78. data/sentry-ruby/examples/rails-6.0/config/puma.rb +38 -0
  79. data/sentry-ruby/examples/rails-6.0/config/routes.rb +10 -0
  80. data/sentry-ruby/examples/rails-6.0/config/spring.rb +6 -0
  81. data/sentry-ruby/examples/rails-6.0/config/storage.yml +34 -0
  82. data/sentry-ruby/examples/rails-6.0/config/webpack/development.js +5 -0
  83. data/sentry-ruby/examples/rails-6.0/config/webpack/environment.js +3 -0
  84. data/sentry-ruby/examples/rails-6.0/config/webpack/production.js +5 -0
  85. data/sentry-ruby/examples/rails-6.0/config/webpack/test.js +5 -0
  86. data/sentry-ruby/examples/rails-6.0/config/webpacker.yml +96 -0
  87. data/sentry-ruby/examples/rails-6.0/db/seeds.rb +7 -0
  88. data/sentry-ruby/examples/rails-6.0/lib/assets/.keep +0 -0
  89. data/sentry-ruby/examples/rails-6.0/lib/tasks/.keep +0 -0
  90. data/sentry-ruby/examples/rails-6.0/package.json +15 -0
  91. data/sentry-ruby/examples/rails-6.0/postcss.config.js +12 -0
  92. data/sentry-ruby/examples/rails-6.0/public/404.html +67 -0
  93. data/sentry-ruby/examples/rails-6.0/public/422.html +67 -0
  94. data/sentry-ruby/examples/rails-6.0/public/500.html +66 -0
  95. data/sentry-ruby/examples/rails-6.0/public/apple-touch-icon-precomposed.png +0 -0
  96. data/sentry-ruby/examples/rails-6.0/public/apple-touch-icon.png +0 -0
  97. data/sentry-ruby/examples/rails-6.0/public/favicon.ico +0 -0
  98. data/sentry-ruby/examples/rails-6.0/public/robots.txt +1 -0
  99. data/sentry-ruby/examples/rails-6.0/storage/.keep +0 -0
  100. data/sentry-ruby/examples/rails-6.0/test/application_system_test_case.rb +5 -0
  101. data/sentry-ruby/examples/rails-6.0/test/channels/application_cable/connection_test.rb +11 -0
  102. data/sentry-ruby/examples/rails-6.0/test/controllers/.keep +0 -0
  103. data/sentry-ruby/examples/rails-6.0/test/fixtures/.keep +0 -0
  104. data/sentry-ruby/examples/rails-6.0/test/fixtures/files/.keep +0 -0
  105. data/sentry-ruby/examples/rails-6.0/test/helpers/.keep +0 -0
  106. data/sentry-ruby/examples/rails-6.0/test/integration/.keep +0 -0
  107. data/sentry-ruby/examples/rails-6.0/test/mailers/.keep +0 -0
  108. data/sentry-ruby/examples/rails-6.0/test/models/.keep +0 -0
  109. data/sentry-ruby/examples/rails-6.0/test/system/.keep +0 -0
  110. data/sentry-ruby/examples/rails-6.0/test/test_helper.rb +13 -0
  111. data/sentry-ruby/examples/rails-6.0/vendor/.keep +0 -0
  112. data/sentry-ruby/examples/rails-6.0/yarn.lock +7508 -0
  113. data/sentry-ruby/lib/sentry.rb +16 -0
  114. data/sentry-ruby/lib/sentry/backtrace.rb +128 -0
  115. data/sentry-ruby/lib/sentry/client.rb +162 -0
  116. data/sentry-ruby/lib/sentry/client/state.rb +40 -0
  117. data/sentry-ruby/lib/sentry/configuration.rb +533 -0
  118. data/sentry-ruby/lib/sentry/event.rb +209 -0
  119. data/sentry-ruby/lib/sentry/interface.rb +31 -0
  120. data/sentry-ruby/lib/sentry/interfaces/exception.rb +15 -0
  121. data/sentry-ruby/lib/sentry/interfaces/http.rb +16 -0
  122. data/sentry-ruby/lib/sentry/interfaces/message.rb +18 -0
  123. data/sentry-ruby/lib/sentry/interfaces/single_exception.rb +14 -0
  124. data/sentry-ruby/lib/sentry/interfaces/stack_trace.rb +69 -0
  125. data/sentry-ruby/lib/sentry/linecache.rb +44 -0
  126. data/sentry-ruby/lib/sentry/logger.rb +20 -0
  127. data/sentry-ruby/lib/sentry/transports.rb +19 -0
  128. data/sentry-ruby/lib/sentry/transports/dummy.rb +16 -0
  129. data/sentry-ruby/lib/sentry/transports/http.rb +66 -0
  130. data/sentry-ruby/lib/sentry/transports/stdout.rb +20 -0
  131. data/sentry-ruby/lib/sentry/utils/deep_merge.rb +22 -0
  132. data/sentry-ruby/lib/sentry/utils/exception_cause_chain.rb +20 -0
  133. data/sentry-ruby/lib/sentry/version.rb +3 -0
  134. data/sentry-ruby/sentry-ruby.gemspec +26 -0
  135. data/sentry-ruby/spec/sentry/backtrace_spec.rb +38 -0
  136. data/sentry-ruby/spec/sentry/client_spec.rb +443 -0
  137. data/sentry-ruby/spec/sentry/configuration_spec.rb +400 -0
  138. data/sentry-ruby/spec/sentry/event_spec.rb +238 -0
  139. data/sentry-ruby/spec/sentry/interface_spec.rb +38 -0
  140. data/sentry-ruby/spec/sentry/interfaces/stack_trace_spec.rb +11 -0
  141. data/sentry-ruby/spec/sentry/linecache_spec.rb +40 -0
  142. data/sentry-ruby/spec/sentry/transports/http_spec.rb +57 -0
  143. data/sentry-ruby/spec/sentry/transports/stdout_spec.rb +11 -0
  144. data/sentry-ruby/spec/sentry_spec.rb +9 -0
  145. data/sentry-ruby/spec/spec_helper.rb +49 -0
  146. data/sentry-ruby/spec/support/linecache.txt +6 -0
  147. metadata +138 -3
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'socket'
4
+ require 'securerandom'
5
+ require 'sentry/interface'
6
+ require 'sentry/backtrace'
7
+ require 'sentry/utils/deep_merge'
8
+
9
+ module Sentry
10
+ class Event
11
+ # See Sentry server default limits at
12
+ # https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py
13
+ MAX_MESSAGE_SIZE_IN_BYTES = 1024 * 8
14
+ REQUIRED_OPTION_KEYS = [:configuration].freeze
15
+
16
+ SDK = { "name" => "sentry-ruby", "version" => Sentry::VERSION }.freeze
17
+
18
+ attr_accessor :id, :logger, :transaction, :server_name, :release, :modules,
19
+ :extra, :tags, :context, :configuration, :checksum,
20
+ :fingerprint, :environment, :server_os, :runtime,
21
+ :breadcrumbs, :user, :backtrace, :platform, :sdk
22
+ alias event_id id
23
+
24
+ attr_reader :level, :timestamp, :time_spent
25
+
26
+ def initialize(
27
+ configuration:,
28
+ message: nil,
29
+ user: {}, extra: {}, tags: {},
30
+ backtrace: [], level: :error, checksum: nil, fingerprint: [],
31
+ server_name: nil, release: nil, environment: nil
32
+ )
33
+ # this needs to go first because some setters rely on configuration
34
+ self.configuration = configuration
35
+
36
+ # Set some simple default values
37
+ self.id = SecureRandom.uuid.delete("-")
38
+ self.timestamp = Time.now.utc
39
+ self.level = level
40
+ self.platform = :ruby
41
+ self.sdk = SDK
42
+
43
+ # Set some attributes with empty hashes to allow merging
44
+ @interfaces = {}
45
+
46
+ self.user = user || {}
47
+ self.extra = extra || {}
48
+ self.tags = configuration.tags.merge(tags || {})
49
+
50
+ self.message = message
51
+ self.server_os = {} # TODO: contexts
52
+ self.runtime = {} # TODO: contexts
53
+
54
+ self.checksum = checksum
55
+ self.fingerprint = fingerprint
56
+
57
+ self.server_name = server_name
58
+ self.environment = environment
59
+ self.release = release
60
+
61
+ # Allow attributes to be set on the event at initialization
62
+ yield self if block_given?
63
+ # options.each_pair { |key, val| public_send("#{key}=", val) unless val.nil? }
64
+
65
+ if !backtrace.empty?
66
+ interface(:stacktrace) do |int|
67
+ int.frames = stacktrace_interface_from(backtrace)
68
+ end
69
+ end
70
+
71
+ set_core_attributes_from_configuration
72
+ end
73
+
74
+ def message
75
+ @interfaces[:logentry]&.unformatted_message
76
+ end
77
+
78
+ def message=(message)
79
+ unless message.is_a?(String)
80
+ configuration.logger.debug("You're passing a non-string message")
81
+ message = message.to_s
82
+ end
83
+
84
+ interface(:message) do |int|
85
+ int.message = message.byteslice(0...MAX_MESSAGE_SIZE_IN_BYTES) # Messages limited to 10kb
86
+ end
87
+ end
88
+
89
+ def timestamp=(time)
90
+ @timestamp = time.is_a?(Time) ? time.strftime('%Y-%m-%dT%H:%M:%S') : time
91
+ end
92
+
93
+ def time_spent=(time)
94
+ @time_spent = time.is_a?(Float) ? (time * 1000).to_i : time
95
+ end
96
+
97
+ def level=(new_level) # needed to meet the Sentry spec
98
+ @level = new_level.to_s == "warn" ? :warning : new_level
99
+ end
100
+
101
+ def interface(name, value = nil, &block)
102
+ int = Interface.registered[name]
103
+ raise(Error, "Unknown interface: #{name}") unless int
104
+
105
+ @interfaces[int.sentry_alias] = int.new(value, &block) if value || block
106
+ @interfaces[int.sentry_alias]
107
+ end
108
+
109
+ def [](key)
110
+ interface(key)
111
+ end
112
+
113
+ def []=(key, value)
114
+ interface(key, value)
115
+ end
116
+
117
+ def to_hash
118
+ data = [:checksum, :environment, :event_id, :extra, :fingerprint, :level,
119
+ :logger, :message, :modules, :platform, :release, :sdk, :server_name,
120
+ :tags, :time_spent, :timestamp, :transaction, :user].each_with_object({}) do |att, memo|
121
+ memo[att] = public_send(att) if public_send(att)
122
+ end
123
+
124
+ # TODO-v4: Fix this
125
+ # data[:breadcrumbs] = @breadcrumbs.to_hash unless @breadcrumbs.empty?
126
+
127
+ @interfaces.each_pair do |name, int_data|
128
+ data[name.to_sym] = int_data.to_hash
129
+ end
130
+ data
131
+ end
132
+
133
+ def to_json_compatible
134
+ JSON.parse(JSON.generate(to_hash))
135
+ end
136
+
137
+ def add_exception_interface(exc)
138
+ interface(:exception) do |exc_int|
139
+ exceptions = Sentry::Utils::ExceptionCauseChain.exception_to_array(exc).reverse
140
+ backtraces = Set.new
141
+ exc_int.values = exceptions.map do |e|
142
+ SingleExceptionInterface.new do |int|
143
+ int.type = e.class.to_s
144
+ int.value = e.to_s
145
+ int.module = e.class.to_s.split('::')[0...-1].join('::')
146
+
147
+ int.stacktrace =
148
+ if e.backtrace && !backtraces.include?(e.backtrace.object_id)
149
+ backtraces << e.backtrace.object_id
150
+ StacktraceInterface.new do |stacktrace|
151
+ stacktrace.frames = stacktrace_interface_from(e.backtrace)
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+ def stacktrace_interface_from(backtrace)
160
+ Backtrace.parse(backtrace, configuration: configuration).lines.reverse.each_with_object([]) do |line, memo|
161
+ frame = StacktraceInterface::Frame.new(configuration: configuration)
162
+ frame.abs_path = line.file if line.file
163
+ frame.function = line.method if line.method
164
+ frame.lineno = line.number
165
+ frame.in_app = line.in_app
166
+ frame.module = line.module_name if line.module_name
167
+
168
+ if configuration[:context_lines] && frame.abs_path
169
+ frame.pre_context, frame.context_line, frame.post_context = \
170
+ configuration.linecache.get_file_context(frame.abs_path, frame.lineno, configuration[:context_lines])
171
+ end
172
+
173
+ memo << frame if frame.filename
174
+ end
175
+ end
176
+
177
+ private
178
+
179
+ def set_core_attributes_from_configuration
180
+ self.server_name ||= configuration.server_name
181
+ self.release ||= configuration.release
182
+ self.modules = list_gem_specs if configuration.send_modules
183
+ self.environment ||= configuration.current_environment
184
+ end
185
+
186
+ def add_rack_context
187
+ interface :http do |int|
188
+ int.from_rack(context.rack_env)
189
+ end
190
+ context.user[:ip_address] = calculate_real_ip_from_rack
191
+ end
192
+
193
+ # When behind a proxy (or if the user is using a proxy), we can't use
194
+ # REMOTE_ADDR to determine the Event IP, and must use other headers instead.
195
+ def calculate_real_ip_from_rack
196
+ Utils::RealIp.new(
197
+ :remote_addr => context.rack_env["REMOTE_ADDR"],
198
+ :client_ip => context.rack_env["HTTP_CLIENT_IP"],
199
+ :real_ip => context.rack_env["HTTP_X_REAL_IP"],
200
+ :forwarded_for => context.rack_env["HTTP_X_FORWARDED_FOR"]
201
+ ).calculate_ip
202
+ end
203
+
204
+ def list_gem_specs
205
+ # Older versions of Rubygems don't support iterating over all specs
206
+ Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,31 @@
1
+ module Sentry
2
+ class Interface
3
+ def initialize(attributes = nil)
4
+ attributes&.each do |attr, value|
5
+ public_send "#{attr}=", value
6
+ end
7
+
8
+ yield self if block_given?
9
+ end
10
+
11
+ def self.inherited(klass)
12
+ name = klass.name.split("::").last.downcase.gsub("interface", "")
13
+ registered[name.to_sym] = klass
14
+ super
15
+ end
16
+
17
+ def self.registered
18
+ @@registered ||= {} # rubocop:disable Style/ClassVars
19
+ end
20
+
21
+ def to_hash
22
+ Hash[instance_variables.map { |name| [name[1..-1].to_sym, instance_variable_get(name)] }]
23
+ end
24
+ end
25
+ end
26
+
27
+ require "sentry/interfaces/exception"
28
+ require "sentry/interfaces/http"
29
+ require "sentry/interfaces/message"
30
+ require "sentry/interfaces/single_exception"
31
+ require "sentry/interfaces/stack_trace"
@@ -0,0 +1,15 @@
1
+ module Sentry
2
+ class ExceptionInterface < Interface
3
+ attr_accessor :values
4
+
5
+ def self.sentry_alias
6
+ :exception
7
+ end
8
+
9
+ def to_hash(*args)
10
+ data = super(*args)
11
+ data[:values] = data[:values].map(&:to_hash) if data[:values]
12
+ data
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module Sentry
2
+ class HttpInterface < Interface
3
+ attr_accessor :url, :method, :data, :query_string, :cookies, :headers, :env
4
+
5
+ def initialize(*arguments)
6
+ self.headers = {}
7
+ self.env = {}
8
+ self.cookies = nil
9
+ super(*arguments)
10
+ end
11
+
12
+ def self.sentry_alias
13
+ :request
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ module Sentry
2
+ class MessageInterface < Interface
3
+ attr_accessor :message, :params
4
+
5
+ def initialize(*arguments)
6
+ self.params = []
7
+ super(*arguments)
8
+ end
9
+
10
+ def unformatted_message
11
+ Array(params).empty? ? message : message % params
12
+ end
13
+
14
+ def self.sentry_alias
15
+ :logentry
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ module Sentry
2
+ class SingleExceptionInterface < Interface
3
+ attr_accessor :type
4
+ attr_accessor :value
5
+ attr_accessor :module
6
+ attr_accessor :stacktrace
7
+
8
+ def to_hash(*args)
9
+ data = super(*args)
10
+ data[:stacktrace] = data[:stacktrace].to_hash if data[:stacktrace]
11
+ data
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,69 @@
1
+ module Sentry
2
+ class StacktraceInterface < Interface
3
+ attr_accessor :frames
4
+
5
+ def initialize(*arguments)
6
+ super(*arguments)
7
+ end
8
+
9
+ def self.sentry_alias
10
+ :stacktrace
11
+ end
12
+
13
+ def to_hash(*args)
14
+ data = super(*args)
15
+ data[:frames] = data[:frames].map(&:to_hash)
16
+ data
17
+ end
18
+
19
+ # Not actually an interface, but I want to use the same style
20
+ class Frame < Interface
21
+ attr_accessor :abs_path, :context_line, :function, :in_app,
22
+ :lineno, :module, :pre_context, :post_context, :vars, :configuration
23
+
24
+ def initialize(*arguments)
25
+ super(*arguments)
26
+ end
27
+
28
+ def filename
29
+ return if abs_path.nil?
30
+ return @filename if instance_variable_defined?(:@filename)
31
+
32
+ prefix =
33
+ if under_project_root? && in_app
34
+ project_root
35
+ elsif under_project_root?
36
+ longest_load_path || project_root
37
+ else
38
+ longest_load_path
39
+ end
40
+
41
+ @filename = prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
42
+ end
43
+
44
+ def to_hash(*args)
45
+ data = super(*args)
46
+ data[:filename] = filename
47
+ data.delete(:vars) unless vars && !vars.empty?
48
+ data.delete(:pre_context) unless pre_context && !pre_context.empty?
49
+ data.delete(:post_context) unless post_context && !post_context.empty?
50
+ data.delete(:context_line) unless context_line && !context_line.empty?
51
+ data
52
+ end
53
+
54
+ private
55
+
56
+ def under_project_root?
57
+ project_root && abs_path.start_with?(project_root)
58
+ end
59
+
60
+ def project_root
61
+ @project_root ||= configuration.project_root&.to_s
62
+ end
63
+
64
+ def longest_load_path
65
+ $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,44 @@
1
+ module Sentry
2
+ class LineCache
3
+ def initialize
4
+ @cache = {}
5
+ end
6
+
7
+ # Any linecache you provide to Sentry must implement this method.
8
+ # Returns an Array of Strings representing the lines in the source
9
+ # file. The number of lines retrieved is (2 * context) + 1, the middle
10
+ # line should be the line requested by lineno. See specs for more information.
11
+ def get_file_context(filename, lineno, context)
12
+ return nil, nil, nil unless valid_path?(filename)
13
+
14
+ lines = Array.new(2 * context + 1) do |i|
15
+ getline(filename, lineno - context + i)
16
+ end
17
+ [lines[0..(context - 1)], lines[context], lines[(context + 1)..-1]]
18
+ end
19
+
20
+ private
21
+
22
+ def valid_path?(path)
23
+ lines = getlines(path)
24
+ !lines.nil?
25
+ end
26
+
27
+ def getlines(path)
28
+ @cache[path] ||= begin
29
+ IO.readlines(path)
30
+ rescue
31
+ nil
32
+ end
33
+ end
34
+
35
+ def getline(path, n)
36
+ return nil if n < 1
37
+
38
+ lines = getlines(path)
39
+ return nil if lines.nil?
40
+
41
+ lines[n - 1]
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Sentry
6
+ class Logger < ::Logger
7
+ LOG_PREFIX = "** [Sentry] "
8
+ PROGNAME = "sentry"
9
+
10
+ def initialize(*)
11
+ super
12
+ @level = ::Logger::INFO
13
+ original_formatter = ::Logger::Formatter.new
14
+ @default_formatter = proc do |severity, datetime, _progname, msg|
15
+ msg = "#{LOG_PREFIX}#{msg}"
16
+ original_formatter.call(severity, datetime, PROGNAME, msg)
17
+ end
18
+ end
19
+ end
20
+ end