sentry-ruby 0.3.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +102 -14
  3. metadata +41 -54
  4. data/.craft.yml +0 -19
  5. data/.gitignore +0 -11
  6. data/.rspec +0 -3
  7. data/.travis.yml +0 -6
  8. data/CHANGELOG.md +0 -31
  9. data/CODE_OF_CONDUCT.md +0 -74
  10. data/Gemfile +0 -16
  11. data/Rakefile +0 -8
  12. data/bin/console +0 -14
  13. data/bin/setup +0 -8
  14. data/lib/sentry/backtrace.rb +0 -128
  15. data/lib/sentry/benchmarks/benchmark_transport.rb +0 -14
  16. data/lib/sentry/breadcrumb/sentry_logger.rb +0 -87
  17. data/lib/sentry/breadcrumb.rb +0 -25
  18. data/lib/sentry/breadcrumb_buffer.rb +0 -47
  19. data/lib/sentry/client.rb +0 -80
  20. data/lib/sentry/configuration.rb +0 -387
  21. data/lib/sentry/core_ext/object/deep_dup.rb +0 -57
  22. data/lib/sentry/core_ext/object/duplicable.rb +0 -153
  23. data/lib/sentry/dsn.rb +0 -48
  24. data/lib/sentry/event.rb +0 -177
  25. data/lib/sentry/hub.rb +0 -137
  26. data/lib/sentry/interface.rb +0 -22
  27. data/lib/sentry/interfaces/exception.rb +0 -11
  28. data/lib/sentry/interfaces/request.rb +0 -95
  29. data/lib/sentry/interfaces/single_exception.rb +0 -14
  30. data/lib/sentry/interfaces/stacktrace.rb +0 -57
  31. data/lib/sentry/linecache.rb +0 -44
  32. data/lib/sentry/logger.rb +0 -20
  33. data/lib/sentry/rack/capture_exception.rb +0 -45
  34. data/lib/sentry/rack/tracing.rb +0 -39
  35. data/lib/sentry/rack.rb +0 -5
  36. data/lib/sentry/scope.rb +0 -214
  37. data/lib/sentry/span.rb +0 -155
  38. data/lib/sentry/transaction.rb +0 -113
  39. data/lib/sentry/transaction_event.rb +0 -29
  40. data/lib/sentry/transport/configuration.rb +0 -21
  41. data/lib/sentry/transport/dummy_transport.rb +0 -14
  42. data/lib/sentry/transport/http_transport.rb +0 -65
  43. data/lib/sentry/transport/state.rb +0 -40
  44. data/lib/sentry/transport.rb +0 -97
  45. data/lib/sentry/utils/exception_cause_chain.rb +0 -20
  46. data/lib/sentry/utils/real_ip.rb +0 -70
  47. data/lib/sentry/utils/request_id.rb +0 -16
  48. data/lib/sentry/version.rb +0 -3
  49. data/lib/sentry-ruby.rb +0 -123
  50. data/sentry-ruby.gemspec +0 -26
@@ -1,57 +0,0 @@
1
- module Sentry
2
- class StacktraceInterface < Interface
3
- attr_accessor :frames
4
-
5
- def to_hash
6
- data = super
7
- data[:frames] = data[:frames].map(&:to_hash)
8
- data
9
- end
10
-
11
- # Not actually an interface, but I want to use the same style
12
- class Frame < Interface
13
- attr_accessor :abs_path, :context_line, :function, :in_app,
14
- :lineno, :module, :pre_context, :post_context, :vars
15
-
16
- def initialize(project_root)
17
- @project_root = project_root
18
- end
19
-
20
- def filename
21
- return if abs_path.nil?
22
- return @filename if instance_variable_defined?(:@filename)
23
-
24
- prefix =
25
- if under_project_root? && in_app
26
- @project_root
27
- elsif under_project_root?
28
- longest_load_path || @project_root
29
- else
30
- longest_load_path
31
- end
32
-
33
- @filename = prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
34
- end
35
-
36
- def to_hash(*args)
37
- data = super(*args)
38
- data[:filename] = filename
39
- data.delete(:vars) unless vars && !vars.empty?
40
- data.delete(:pre_context) unless pre_context && !pre_context.empty?
41
- data.delete(:post_context) unless post_context && !post_context.empty?
42
- data.delete(:context_line) unless context_line && !context_line.empty?
43
- data
44
- end
45
-
46
- private
47
-
48
- def under_project_root?
49
- @project_root && abs_path.start_with?(@project_root)
50
- end
51
-
52
- def longest_load_path
53
- $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
54
- end
55
- end
56
- end
57
- end
@@ -1,44 +0,0 @@
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
data/lib/sentry/logger.rb DELETED
@@ -1,20 +0,0 @@
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
@@ -1,45 +0,0 @@
1
- module Sentry
2
- module Rack
3
- class CaptureException
4
- def initialize(app)
5
- @app = app
6
- end
7
-
8
- def call(env)
9
- # this call clones the main (global) hub
10
- # and assigns it to the current thread's Sentry#get_current_hub
11
- # it's essential for multi-thread servers (e.g. puma)
12
- Sentry.clone_hub_to_current_thread unless Sentry.get_current_hub
13
- # this call creates an isolated scope for every request
14
- # it's essential for multi-process servers (e.g. unicorn)
15
- Sentry.with_scope do |scope|
16
- # there could be some breadcrumbs already stored in the top-level scope
17
- # and for request information, we don't need those breadcrumbs
18
- scope.clear_breadcrumbs
19
- env['sentry.client'] = Sentry.get_current_client
20
-
21
- scope.set_transaction_name(env["PATH_INFO"]) if env["PATH_INFO"]
22
- scope.set_rack_env(env)
23
-
24
- begin
25
- response = @app.call(env)
26
- rescue Sentry::Error
27
- raise # Don't capture Sentry errors
28
- rescue Exception => e
29
- Sentry.capture_exception(e)
30
- raise
31
- end
32
-
33
- exception = collect_exception(env)
34
- Sentry.capture_exception(exception) if exception
35
-
36
- response
37
- end
38
- end
39
-
40
- def collect_exception(env)
41
- env['rack.exception'] || env['sinatra.error']
42
- end
43
- end
44
- end
45
- end
@@ -1,39 +0,0 @@
1
- module Sentry
2
- module Rack
3
- class Tracing
4
- def initialize(app)
5
- @app = app
6
- end
7
-
8
- def call(env)
9
- Sentry.clone_hub_to_current_thread unless Sentry.get_current_hub
10
-
11
- if Sentry.configuration.traces_sample_rate.to_f == 0.0
12
- return @app.call(env)
13
- end
14
-
15
- Sentry.with_scope do |scope|
16
- scope.clear_breadcrumbs
17
- scope.set_transaction_name(env["PATH_INFO"]) if env["PATH_INFO"]
18
- span = Sentry.start_transaction(name: scope.transaction_name, op: "rack.request")
19
- scope.set_span(span)
20
-
21
- begin
22
- response = @app.call(env)
23
- rescue
24
- finish_span(span, 500)
25
- raise
26
- end
27
-
28
- finish_span(span, response[0])
29
- response
30
- end
31
- end
32
-
33
- def finish_span(span, status_code)
34
- span.set_http_status(status_code)
35
- span.finish
36
- end
37
- end
38
- end
39
- end
data/lib/sentry/rack.rb DELETED
@@ -1,5 +0,0 @@
1
- require 'time'
2
- require 'rack'
3
-
4
- require 'sentry/rack/capture_exception'
5
- require 'sentry/rack/tracing'
data/lib/sentry/scope.rb DELETED
@@ -1,214 +0,0 @@
1
- require "sentry/breadcrumb_buffer"
2
- require "etc"
3
-
4
- module Sentry
5
- class Scope
6
- ATTRIBUTES = [:transaction_names, :contexts, :extra, :tags, :user, :level, :breadcrumbs, :fingerprint, :event_processors, :rack_env, :span]
7
-
8
- attr_reader(*ATTRIBUTES)
9
-
10
- def initialize
11
- set_default_value
12
- end
13
-
14
- def clear
15
- set_default_value
16
- end
17
-
18
- def apply_to_event(event, hint = nil)
19
- event.tags = tags.merge(event.tags)
20
- event.user = user.merge(event.user)
21
- event.extra = extra.merge(event.extra)
22
- event.contexts = contexts.merge(event.contexts)
23
-
24
- if span
25
- event.contexts[:trace] = span.get_trace_context
26
- end
27
-
28
- event.fingerprint = fingerprint
29
- event.level = level
30
- event.transaction = transaction_names.last
31
- event.breadcrumbs = breadcrumbs
32
- event.rack_env = rack_env
33
-
34
- unless @event_processors.empty?
35
- @event_processors.each do |processor_block|
36
- event = processor_block.call(event, hint)
37
- end
38
- end
39
-
40
- event
41
- end
42
-
43
- def add_breadcrumb(breadcrumb)
44
- breadcrumbs.record(breadcrumb)
45
- end
46
-
47
- def clear_breadcrumbs
48
- @breadcrumbs = BreadcrumbBuffer.new
49
- end
50
-
51
- def dup
52
- copy = super
53
- copy.breadcrumbs = breadcrumbs.dup
54
- copy.contexts = contexts.deep_dup
55
- copy.extra = extra.deep_dup
56
- copy.tags = tags.deep_dup
57
- copy.user = user.deep_dup
58
- copy.transaction_names = transaction_names.deep_dup
59
- copy.fingerprint = fingerprint.deep_dup
60
- copy.span = span
61
- copy
62
- end
63
-
64
- def update_from_scope(scope)
65
- self.breadcrumbs = scope.breadcrumbs
66
- self.contexts = scope.contexts
67
- self.extra = scope.extra
68
- self.tags = scope.tags
69
- self.user = scope.user
70
- self.transaction_names = scope.transaction_names
71
- self.fingerprint = scope.fingerprint
72
- self.span = scope.span
73
- end
74
-
75
- def update_from_options(
76
- contexts: nil,
77
- extra: nil,
78
- tags: nil,
79
- user: nil,
80
- level: nil,
81
- fingerprint: nil
82
- )
83
- self.contexts.merge!(contexts) if contexts
84
- self.extra.merge!(extra) if extra
85
- self.tags.merge!(tags) if tags
86
- self.user = user if user
87
- self.level = level if level
88
- self.fingerprint = fingerprint if fingerprint
89
- end
90
-
91
- def set_rack_env(env)
92
- env = env || {}
93
- @rack_env = env
94
- end
95
-
96
- def set_span(span)
97
- check_argument_type!(span, Span)
98
- @span = span
99
- end
100
-
101
- def set_user(user_hash)
102
- check_argument_type!(user_hash, Hash)
103
- @user = user_hash
104
- end
105
-
106
- def set_extras(extras_hash)
107
- check_argument_type!(extras_hash, Hash)
108
- @extra.merge!(extras_hash)
109
- end
110
-
111
- def set_extra(key, value)
112
- @extra.merge!(key => value)
113
- end
114
-
115
- def set_tags(tags_hash)
116
- check_argument_type!(tags_hash, Hash)
117
- @tags.merge!(tags_hash)
118
- end
119
-
120
- def set_tag(key, value)
121
- @tags.merge!(key => value)
122
- end
123
-
124
- def set_contexts(contexts_hash)
125
- check_argument_type!(contexts_hash, Hash)
126
- @contexts = contexts_hash
127
- end
128
-
129
- def set_context(key, value)
130
- @contexts.merge!(key => value)
131
- end
132
-
133
- def set_level(level)
134
- @level = level
135
- end
136
-
137
- def set_transaction_name(transaction_name)
138
- @transaction_names << transaction_name
139
- end
140
-
141
- def transaction_name
142
- @transaction_names.last
143
- end
144
-
145
- def get_transaction
146
- # transaction will always be the first in the span_recorder
147
- span.span_recorder.spans.first if span
148
- end
149
-
150
- def get_span
151
- span
152
- end
153
-
154
- def set_fingerprint(fingerprint)
155
- check_argument_type!(fingerprint, Array)
156
-
157
- @fingerprint = fingerprint
158
- end
159
-
160
- def add_event_processor(&block)
161
- @event_processors << block
162
- end
163
-
164
- protected
165
-
166
- # for duplicating scopes internally
167
- attr_writer(*ATTRIBUTES)
168
-
169
- private
170
-
171
- def check_argument_type!(argument, expected_type)
172
- unless argument.is_a?(expected_type)
173
- raise ArgumentError, "expect the argument to be a #{expected_type}, got #{argument.class} (#{argument})"
174
- end
175
- end
176
-
177
- def set_default_value
178
- @breadcrumbs = BreadcrumbBuffer.new
179
- @contexts = { :os => self.class.os_context, :runtime => self.class.runtime_context }
180
- @extra = {}
181
- @tags = {}
182
- @user = {}
183
- @level = :error
184
- @fingerprint = []
185
- @transaction_names = []
186
- @event_processors = []
187
- @rack_env = {}
188
- @span = nil
189
- end
190
-
191
- class << self
192
- def os_context
193
- @os_context ||=
194
- begin
195
- uname = Etc.uname
196
- {
197
- name: uname[:sysname] || RbConfig::CONFIG["host_os"],
198
- version: uname[:version],
199
- build: uname[:release],
200
- kernel_version: uname[:version]
201
- }
202
- end
203
- end
204
-
205
- def runtime_context
206
- @runtime_context ||= {
207
- name: RbConfig::CONFIG["ruby_install_name"],
208
- version: RUBY_DESCRIPTION || Sentry.sys_command("ruby -v")
209
- }
210
- end
211
- end
212
-
213
- end
214
- end
data/lib/sentry/span.rb DELETED
@@ -1,155 +0,0 @@
1
- # frozen_string_literal: true
2
- require "securerandom"
3
-
4
- module Sentry
5
- class Span
6
- STATUS_MAP = {
7
- 400 => "invalid_argument",
8
- 401 => "unauthenticated",
9
- 403 => "permission_denied",
10
- 404 => "not_found",
11
- 409 => "already_exists",
12
- 429 => "resource_exhausted",
13
- 499 => "cancelled",
14
- 500 => "internal_error",
15
- 501 => "unimplemented",
16
- 503 => "unavailable",
17
- 504 => "deadline_exceeded"
18
- }
19
-
20
-
21
- attr_reader :trace_id, :span_id, :parent_span_id, :sampled, :start_timestamp, :timestamp, :description, :op, :status, :tags, :data
22
- attr_accessor :span_recorder
23
-
24
- def initialize(description: nil, op: nil, status: nil, trace_id: nil, parent_span_id: nil, sampled: nil, start_timestamp: nil, timestamp: nil)
25
- @trace_id = trace_id || SecureRandom.uuid.delete("-")
26
- @span_id = SecureRandom.hex(8)
27
- @parent_span_id = parent_span_id
28
- @sampled = sampled
29
- @start_timestamp = start_timestamp || Sentry.utc_now.to_f
30
- @timestamp = timestamp
31
- @description = description
32
- @op = op
33
- @status = status
34
- @data = {}
35
- @tags = {}
36
- end
37
-
38
- def set_span_recorder
39
- @span_recorder = SpanRecorder.new(1000)
40
- @span_recorder.add(self)
41
- end
42
-
43
- def finish
44
- # already finished
45
- return if @timestamp
46
-
47
- @timestamp = Sentry.utc_now.to_f
48
- self
49
- end
50
-
51
- def to_sentry_trace
52
- sampled_flag = ""
53
- sampled_flag = @sampled ? 1 : 0 unless @sampled.nil?
54
-
55
- "#{@trace_id}-#{@span_id}-#{sampled_flag}"
56
- end
57
-
58
- def to_hash
59
- {
60
- trace_id: @trace_id,
61
- span_id: @span_id,
62
- parent_span_id: @parent_span_id,
63
- start_timestamp: @start_timestamp,
64
- timestamp: @timestamp,
65
- description: @description,
66
- op: @op,
67
- status: @status,
68
- tags: @tags,
69
- data: @data
70
- }
71
- end
72
-
73
- def get_trace_context
74
- {
75
- trace_id: @trace_id,
76
- span_id: @span_id,
77
- description: @description,
78
- op: @op,
79
- status: @status
80
- }
81
- end
82
-
83
- def start_child(**options)
84
- options = options.dup.merge(trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
85
- child_span = Span.new(options)
86
- child_span.span_recorder = @span_recorder
87
-
88
- if @span_recorder && @sampled
89
- @span_recorder.add(child_span)
90
- end
91
-
92
- child_span
93
- end
94
-
95
- def with_child_span(**options, &block)
96
- child_span = start_child(**options)
97
-
98
- yield(child_span)
99
-
100
- child_span.finish
101
- end
102
-
103
- def set_op(op)
104
- @op = op
105
- end
106
-
107
- def set_description(description)
108
- @description = description
109
- end
110
-
111
- def set_status(status)
112
- @status = status
113
- end
114
-
115
- def set_timestamp(timestamp)
116
- @timestamp = timestamp
117
- end
118
-
119
- def set_http_status(status_code)
120
- status_code = status_code.to_i
121
- set_data("status_code", status_code)
122
-
123
- status =
124
- if status_code >= 200 && status_code < 299
125
- "ok"
126
- else
127
- STATUS_MAP[status_code]
128
- end
129
- set_status(status)
130
- end
131
-
132
- def set_data(key, value)
133
- @data[key] = value
134
- end
135
-
136
- def set_tag(key, value)
137
- @tags[key] = value
138
- end
139
-
140
- class SpanRecorder
141
- attr_reader :max_length, :spans
142
-
143
- def initialize(max_length)
144
- @max_length = max_length
145
- @spans = []
146
- end
147
-
148
- def add(span)
149
- if @spans.count < @max_length
150
- @spans << span
151
- end
152
- end
153
- end
154
- end
155
- end
@@ -1,113 +0,0 @@
1
- module Sentry
2
- class Transaction < Span
3
- SENTRY_TRACE_REGEXP = Regexp.new(
4
- "^[ \t]*" + # whitespace
5
- "([0-9a-f]{32})?" + # trace_id
6
- "-?([0-9a-f]{16})?" + # span_id
7
- "-?([01])?" + # sampled
8
- "[ \t]*$" # whitespace
9
- )
10
- UNLABELD_NAME = "<unlabeled transaction>".freeze
11
- MESSAGE_PREFIX = "[Tracing]"
12
-
13
- attr_reader :name, :parent_sampled
14
-
15
- def initialize(name: nil, parent_sampled: nil, **options)
16
- super(**options)
17
-
18
- @name = name
19
- @parent_sampled = parent_sampled
20
- set_span_recorder
21
- end
22
-
23
- def self.from_sentry_trace(sentry_trace, **options)
24
- return unless sentry_trace
25
-
26
- match = SENTRY_TRACE_REGEXP.match(sentry_trace)
27
- trace_id, parent_span_id, sampled_flag = match[1..3]
28
-
29
- sampled = sampled_flag != "0"
30
-
31
- new(trace_id: trace_id, parent_span_id: parent_span_id, parent_sampled: sampled, **options)
32
- end
33
-
34
- def to_hash
35
- hash = super
36
- hash.merge!(name: @name, sampled: @sampled, parent_sampled: @parent_sampled)
37
- hash
38
- end
39
-
40
- def set_initial_sample_desicion(sampling_context = {})
41
- unless Sentry.configuration.tracing_enabled?
42
- @sampled = false
43
- return
44
- end
45
-
46
- return unless @sampled.nil?
47
-
48
- transaction_description = generate_transaction_description
49
-
50
- logger = Sentry.configuration.logger
51
- sample_rate = Sentry.configuration.traces_sample_rate
52
- traces_sampler = Sentry.configuration.traces_sampler
53
-
54
- if traces_sampler.is_a?(Proc)
55
- sampling_context = sampling_context.merge(
56
- parent_sampled: @parent_sampled,
57
- transaction_context: self.to_hash
58
- )
59
-
60
- sample_rate = traces_sampler.call(sampling_context)
61
- end
62
-
63
- unless [true, false].include?(sample_rate) || (sample_rate.is_a?(Float) && sample_rate >= 0.0 && sample_rate <= 1.0)
64
- @sampled = false
65
- logger.warn("#{MESSAGE_PREFIX} Discarding #{transaction_description} because of invalid sample_rate: #{sample_rate}")
66
- return
67
- end
68
-
69
- if sample_rate == 0.0 || sample_rate == false
70
- @sampled = false
71
- logger.debug("#{MESSAGE_PREFIX} Discarding #{transaction_description} because traces_sampler returned 0 or false")
72
- return
73
- end
74
-
75
- if sample_rate == true
76
- @sampled = true
77
- else
78
- @sampled = Random.rand < sample_rate
79
- end
80
-
81
- if @sampled
82
- logger.debug("#{MESSAGE_PREFIX} Starting #{transaction_description}")
83
- else
84
- logger.debug(
85
- "#{MESSAGE_PREFIX} Discarding #{transaction_description} because it's not included in the random sample (sampling rate = #{sample_rate})"
86
- )
87
- end
88
- end
89
-
90
- def finish(hub: nil)
91
- super() # Span#finish doesn't take arguments
92
-
93
- if @name.nil?
94
- @name = UNLABELD_NAME
95
- end
96
-
97
- return unless @sampled
98
-
99
- hub ||= Sentry.get_current_hub
100
- event = hub.current_client.event_from_transaction(self)
101
- hub.capture_event(event)
102
- end
103
-
104
- private
105
-
106
- def generate_transaction_description
107
- result = op.nil? ? "" : "<#{@op}> "
108
- result += "transaction"
109
- result += " <#{@name}>" if @name
110
- result
111
- end
112
- end
113
- end