scout_apm 3.0.0.pre25 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yml +49 -0
  3. data/.gitignore +1 -1
  4. data/.rubocop.yml +5 -5
  5. data/.travis.yml +19 -14
  6. data/CHANGELOG.markdown +148 -4
  7. data/Gemfile +1 -7
  8. data/README.markdown +30 -4
  9. data/Rakefile +1 -1
  10. data/ext/allocations/allocations.c +2 -0
  11. data/gems/README.md +28 -0
  12. data/gems/octoshark.gemfile +4 -0
  13. data/gems/rails3.gemfile +5 -0
  14. data/gems/rails4.gemfile +4 -0
  15. data/gems/rails5.gemfile +4 -0
  16. data/gems/rails6.gemfile +4 -0
  17. data/lib/scout_apm.rb +38 -9
  18. data/lib/scout_apm/agent.rb +29 -10
  19. data/lib/scout_apm/agent/exit_handler.rb +0 -1
  20. data/lib/scout_apm/agent_context.rb +22 -3
  21. data/lib/scout_apm/app_server_load.rb +7 -2
  22. data/lib/scout_apm/attribute_arranger.rb +0 -2
  23. data/lib/scout_apm/auto_instrument.rb +5 -0
  24. data/lib/scout_apm/auto_instrument/instruction_sequence.rb +31 -0
  25. data/lib/scout_apm/auto_instrument/layer.rb +23 -0
  26. data/lib/scout_apm/auto_instrument/parser.rb +27 -0
  27. data/lib/scout_apm/auto_instrument/rails.rb +175 -0
  28. data/lib/scout_apm/background_job_integrations/legacy_sneakers.rb +55 -0
  29. data/lib/scout_apm/background_job_integrations/que.rb +134 -0
  30. data/lib/scout_apm/background_job_integrations/resque.rb +6 -2
  31. data/lib/scout_apm/background_job_integrations/shoryuken.rb +124 -0
  32. data/lib/scout_apm/background_job_integrations/sidekiq.rb +5 -19
  33. data/lib/scout_apm/background_job_integrations/sneakers.rb +87 -0
  34. data/lib/scout_apm/config.rb +45 -8
  35. data/lib/scout_apm/detailed_trace.rb +217 -0
  36. data/lib/scout_apm/environment.rb +19 -1
  37. data/lib/scout_apm/error.rb +27 -0
  38. data/lib/scout_apm/error_service.rb +32 -0
  39. data/lib/scout_apm/error_service/error_buffer.rb +39 -0
  40. data/lib/scout_apm/error_service/error_record.rb +211 -0
  41. data/lib/scout_apm/error_service/ignored_exceptions.rb +66 -0
  42. data/lib/scout_apm/error_service/middleware.rb +32 -0
  43. data/lib/scout_apm/error_service/notifier.rb +33 -0
  44. data/lib/scout_apm/error_service/payload.rb +47 -0
  45. data/lib/scout_apm/error_service/periodic_work.rb +17 -0
  46. data/lib/scout_apm/error_service/railtie.rb +11 -0
  47. data/lib/scout_apm/error_service/sidekiq.rb +80 -0
  48. data/lib/scout_apm/extensions/transaction_callback_payload.rb +1 -1
  49. data/lib/scout_apm/fake_store.rb +3 -0
  50. data/lib/scout_apm/framework_integrations/rails_2.rb +2 -1
  51. data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +17 -6
  52. data/lib/scout_apm/git_revision.rb +6 -3
  53. data/lib/scout_apm/instant/middleware.rb +2 -1
  54. data/lib/scout_apm/instrument_manager.rb +8 -7
  55. data/lib/scout_apm/instruments/action_controller_rails_2.rb +3 -1
  56. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +56 -55
  57. data/lib/scout_apm/instruments/action_view.rb +126 -26
  58. data/lib/scout_apm/instruments/active_record.rb +70 -19
  59. data/lib/scout_apm/instruments/http.rb +48 -0
  60. data/lib/scout_apm/instruments/memcached.rb +43 -0
  61. data/lib/scout_apm/instruments/mongoid.rb +9 -4
  62. data/lib/scout_apm/instruments/net_http.rb +8 -1
  63. data/lib/scout_apm/job_record.rb +4 -2
  64. data/lib/scout_apm/layaway_file.rb +4 -0
  65. data/lib/scout_apm/layer.rb +6 -57
  66. data/lib/scout_apm/layer_children_set.rb +15 -6
  67. data/lib/scout_apm/layer_converters/converter_base.rb +15 -30
  68. data/lib/scout_apm/layer_converters/database_converter.rb +2 -15
  69. data/lib/scout_apm/layer_converters/slow_job_converter.rb +12 -2
  70. data/lib/scout_apm/layer_converters/slow_request_converter.rb +14 -4
  71. data/lib/scout_apm/layer_converters/trace_converter.rb +184 -0
  72. data/lib/scout_apm/limited_layer.rb +0 -7
  73. data/lib/scout_apm/metric_stats.rb +0 -8
  74. data/lib/scout_apm/middleware.rb +1 -1
  75. data/lib/scout_apm/periodic_work.rb +19 -0
  76. data/lib/scout_apm/remote/message.rb +4 -0
  77. data/lib/scout_apm/remote/server.rb +13 -1
  78. data/lib/scout_apm/reporter.rb +8 -3
  79. data/lib/scout_apm/reporting.rb +2 -1
  80. data/lib/scout_apm/request_histograms.rb +8 -0
  81. data/lib/scout_apm/serializers/app_server_load_serializer.rb +4 -0
  82. data/lib/scout_apm/serializers/directive_serializer.rb +4 -0
  83. data/lib/scout_apm/serializers/payload_serializer.rb +2 -2
  84. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +30 -15
  85. data/lib/scout_apm/slow_job_record.rb +5 -1
  86. data/lib/scout_apm/slow_policy/age_policy.rb +33 -0
  87. data/lib/scout_apm/slow_policy/percent_policy.rb +22 -0
  88. data/lib/scout_apm/slow_policy/percentile_policy.rb +24 -0
  89. data/lib/scout_apm/slow_policy/policy.rb +21 -0
  90. data/lib/scout_apm/slow_policy/speed_policy.rb +16 -0
  91. data/lib/scout_apm/slow_request_policy.rb +18 -77
  92. data/lib/scout_apm/slow_transaction.rb +3 -1
  93. data/lib/scout_apm/store.rb +12 -8
  94. data/lib/scout_apm/tracked_request.rb +39 -30
  95. data/lib/scout_apm/utils/active_record_metric_name.rb +16 -3
  96. data/lib/scout_apm/utils/backtrace_parser.rb +3 -0
  97. data/lib/scout_apm/utils/marshal_logging.rb +90 -0
  98. data/lib/scout_apm/utils/sql_sanitizer.rb +10 -1
  99. data/lib/scout_apm/utils/sql_sanitizer_regex.rb +8 -1
  100. data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +6 -0
  101. data/lib/scout_apm/utils/unique_id.rb +27 -0
  102. data/lib/scout_apm/version.rb +1 -1
  103. data/scout_apm.gemspec +13 -7
  104. data/test/test_helper.rb +2 -2
  105. data/test/unit/agent_context_test.rb +29 -0
  106. data/test/unit/auto_instrument/assignments-instrumented.rb +31 -0
  107. data/test/unit/auto_instrument/assignments.rb +31 -0
  108. data/test/unit/auto_instrument/controller-ast.txt +57 -0
  109. data/test/unit/auto_instrument/controller-instrumented.rb +49 -0
  110. data/test/unit/auto_instrument/controller.rb +49 -0
  111. data/test/unit/auto_instrument/rescue_from-instrumented.rb +13 -0
  112. data/test/unit/auto_instrument/rescue_from.rb +13 -0
  113. data/test/unit/auto_instrument_test.rb +54 -0
  114. data/test/unit/environment_test.rb +2 -2
  115. data/test/unit/error_service/error_buffer_test.rb +25 -0
  116. data/test/unit/error_service/ignored_exceptions_test.rb +49 -0
  117. data/test/unit/instruments/active_record_test.rb +40 -0
  118. data/test/unit/layer_children_set_test.rb +9 -0
  119. data/test/unit/request_histograms_test.rb +17 -0
  120. data/test/unit/serializers/payload_serializer_test.rb +39 -5
  121. data/test/unit/slow_request_policy_test.rb +41 -13
  122. data/test/unit/sql_sanitizer_test.rb +78 -0
  123. data/test/unit/utils/active_record_metric_name_test.rb +10 -2
  124. metadata +100 -63
  125. data/ext/stacks/extconf.rb +0 -37
  126. data/ext/stacks/scout_atomics.h +0 -86
  127. data/ext/stacks/stacks.c +0 -814
  128. data/lib/scout_apm/slow_job_policy.rb +0 -111
  129. data/lib/scout_apm/trace_compactor.rb +0 -312
  130. data/lib/scout_apm/utils/fake_stacks.rb +0 -88
  131. data/test/unit/instruments/active_record_instruments_test.rb +0 -5
  132. data/test/unit/slow_job_policy_test.rb +0 -6
  133. data/tester.rb +0 -53
@@ -6,10 +6,11 @@ require 'scout_apm/environment'
6
6
  # Valid Config Options:
7
7
  #
8
8
  # This list is complete, but some are old and unused, or for developers of
9
- # scout_apm itself. See the documentation at http://help.apm.scoutapp.com for
9
+ # scout_apm itself. See the documentation at https://docs.scoutapm.com for
10
10
  # customer-focused documentation.
11
11
  #
12
12
  # application_root - override the detected directory of the application
13
+ # collect_remote_ip - automatically capture user's IP into a Trace's Context
13
14
  # compress_payload - true/false to enable gzipping of payload
14
15
  # data_file - override the default temporary storage location. Must be a location in a writable directory
15
16
  # dev_trace - true or false. Enables always-on tracing in development environmen only
@@ -30,6 +31,10 @@ require 'scout_apm/environment'
30
31
  # uri_reporting - 'path' or 'full_path' default is 'full_path', which reports URL params as well as the path.
31
32
  # remote_agent_host - Internal: What host to bind to, and also send messages to for remote. Default: 127.0.0.1.
32
33
  # remote_agent_port - What port to bind the remote webserver to
34
+ # start_resque_server_instrument - Used in special situations with certain Resque installs
35
+ # timeline_traces - true/false to enable sending of of the timeline trace format.
36
+ # auto_instruments - true/false whether to install autoinstruments. Only installed if on a supported Ruby version.
37
+ # auto_instruments_ignore - An array of file names to exclude from autoinstruments (Ex: ['application_controller']).
33
38
  #
34
39
  # Any of these config settings can be set with an environment variable prefixed
35
40
  # by SCOUT_ and uppercasing the key: SCOUT_LOG_LEVEL for instance.
@@ -39,6 +44,7 @@ module ScoutApm
39
44
  KNOWN_CONFIG_OPTIONS = [
40
45
  'application_root',
41
46
  'async_recording',
47
+ 'collect_remote_ip',
42
48
  'compress_payload',
43
49
  'config_file',
44
50
  'data_file',
@@ -66,9 +72,21 @@ module ScoutApm
66
72
  'remote_agent_host',
67
73
  'remote_agent_port',
68
74
  'report_format',
75
+ 'revision_sha',
69
76
  'scm_subdirectory',
77
+ 'start_resque_server_instrument',
78
+ 'ssl_cert_file',
70
79
  'uri_reporting',
71
80
  'instrument_http_url_length',
81
+ 'timeline_traces',
82
+ 'auto_instruments',
83
+ 'auto_instruments_ignore',
84
+
85
+ # Error Service Related Configuration
86
+ 'errors_enabled',
87
+ 'errors_ignored_exceptions',
88
+ 'errors_filtered_params',
89
+ 'errors_host',
72
90
  ]
73
91
 
74
92
  ################################################################################
@@ -148,16 +166,25 @@ module ScoutApm
148
166
 
149
167
 
150
168
  SETTING_COERCIONS = {
151
- "async_recording" => BooleanCoercion.new,
152
- "detailed_middleware" => BooleanCoercion.new,
153
- "dev_trace" => BooleanCoercion.new,
154
- "enable_background_jobs" => BooleanCoercion.new,
155
- "ignore" => JsonCoercion.new,
156
- "max_traces" => IntegerCoercion.new,
157
- "monitor" => BooleanCoercion.new,
169
+ 'async_recording' => BooleanCoercion.new,
170
+ 'detailed_middleware' => BooleanCoercion.new,
171
+ 'dev_trace' => BooleanCoercion.new,
172
+ 'enable_background_jobs' => BooleanCoercion.new,
173
+ 'ignore' => JsonCoercion.new,
174
+ 'max_traces' => IntegerCoercion.new,
175
+ 'monitor' => BooleanCoercion.new,
176
+ 'collect_remote_ip' => BooleanCoercion.new,
177
+ 'compress_payload' => BooleanCoercion.new,
158
178
  'database_metric_limit' => IntegerCoercion.new,
159
179
  'database_metric_report_limit' => IntegerCoercion.new,
160
180
  'instrument_http_url_length' => IntegerCoercion.new,
181
+ 'start_resque_server_instrument' => BooleanCoercion.new,
182
+ 'timeline_traces' => BooleanCoercion.new,
183
+ 'auto_instruments' => BooleanCoercion.new,
184
+ 'auto_instruments_ignore' => JsonCoercion.new,
185
+ 'errors_enabled' => BooleanCoercion.new,
186
+ 'errors_ignored_exceptions' => JsonCoercion.new,
187
+ 'errors_filtered_params' => JsonCoercion.new,
161
188
  }
162
189
 
163
190
 
@@ -263,6 +290,16 @@ module ScoutApm
263
290
  'database_metric_limit' => 5000, # The hard limit on db metrics
264
291
  'database_metric_report_limit' => 1000,
265
292
  'instrument_http_url_length' => 300,
293
+ 'start_resque_server_instrument' => true, # still only starts if Resque is detected
294
+ 'collect_remote_ip' => true,
295
+ 'timeline_traces' => true,
296
+ 'auto_instruments' => false,
297
+ 'auto_instruments_ignore' => [],
298
+ 'ssl_cert_file' => File.join( File.dirname(__FILE__), *%w[.. .. data cacert.pem] ),
299
+ 'errors_enabled' => false,
300
+ 'errors_ignored_exceptions' => %w(ActiveRecord::RecordNotFound ActionController::RoutingError),
301
+ 'errors_filtered_params' => %w(password s3-key),
302
+ 'errors_host' => 'https://errors.scoutapm.com',
266
303
  }.freeze
267
304
 
268
305
  def value(key)
@@ -0,0 +1,217 @@
1
+ # DetailedTrace contains all details about a certain transaction, spans with
2
+ # start & stop times, tags, etc.
3
+
4
+ # {
5
+ # "version": 1,
6
+ # "identity": {
7
+ # "transaction_id": "req-....",
8
+ # "revision": "abcdef",
9
+ # "start_instant": "01-01-01T00:00:00.0000Z",
10
+ # "stop_instant": "01-01-01T00:00:01.0000Z",
11
+ # "type": "Web",
12
+ # "naming": {
13
+ # "path": "/users",
14
+ # "code": "UsersController#index",
15
+ # },
16
+ # "score": {
17
+ # "total": 10.5,
18
+ # "percentile": 4.5,
19
+ # "age": 2.0,
20
+ # "memory_delta": 3,
21
+ # "allocations": 1
22
+ # }
23
+ # },
24
+ #
25
+ # "tags": {
26
+ # "allocations": 1000
27
+ # },
28
+ #
29
+ # "spans": [
30
+ # ...
31
+ # ]
32
+
33
+ class DetailedTrace
34
+ attr_reader :spans
35
+ attr_reader :tags
36
+
37
+ attr_reader :transaction_id
38
+ attr_reader :revision
39
+ attr_reader :start_instant
40
+ attr_reader :stop_instant
41
+ attr_reader :duration
42
+ attr_reader :type # "Web" or "Job"
43
+ attr_reader :host
44
+
45
+ attr_reader :path # /users/1
46
+ attr_reader :code # UsersController#show or similar
47
+
48
+ attr_reader :total_score
49
+ attr_reader :percentile_score
50
+ attr_reader :age_score
51
+ attr_reader :memory_delta_score
52
+ attr_reader :memory_allocations_score
53
+
54
+ VERSION = 1
55
+
56
+ def initialize(transaction_id, revision, host, start_instant, stop_instant, type, path, code, spans, tags)
57
+ @spans = spans
58
+ @tags = DetailedTraceTags(tags)
59
+
60
+ @transaction_id = transaction_id
61
+ @revision = revision
62
+ @host = host
63
+ @start_instant = start_instant
64
+ @stop_instant = stop_instant
65
+ @type = type
66
+
67
+ @path = path
68
+ @code = code
69
+
70
+ @total_score = 0
71
+ @percentile_score = 0
72
+ @age_score = 0
73
+ @memory_delta_score = 0
74
+ @memory_allocations_score = 0
75
+
76
+ end
77
+
78
+ def as_json(*)
79
+ {
80
+ :version => VERSION,
81
+ :identity => {
82
+ :transaction_id => transaction_id,
83
+ :revision => revision,
84
+ :host => host,
85
+ :start_instant => start_instant.iso8601(6),
86
+ :stop_instant => stop_instant.iso8601(6),
87
+ :type => type,
88
+ :naming => {
89
+ :path => path,
90
+ :code => code,
91
+ },
92
+ :score => {
93
+ :total => total_score,
94
+ :percentile => percentile_score,
95
+ :age => age_score,
96
+ :memory_delta => memory_delta_score,
97
+ :allocations => memory_allocations_score,
98
+ }
99
+ },
100
+ :tags => tags.as_json,
101
+ :spans => spans.map{|span| span.as_json},
102
+ }
103
+ end
104
+
105
+ ########################
106
+ # Scorable interface
107
+ #
108
+ # Needed so we can merge ScoredItemSet instances
109
+ def call
110
+ self
111
+ end
112
+
113
+ def name
114
+ code
115
+ end
116
+
117
+ def score
118
+ @total_score
119
+ end
120
+
121
+ end
122
+
123
+ ##########
124
+ # SPAN #
125
+ ##########
126
+
127
+ #
128
+ # {
129
+ # "type": "Standard",
130
+ # "identity": {
131
+ # "id": "....",
132
+ # "parent_id": "....",
133
+ # "start_time": "01-01-01T00:00:00.0000Z",
134
+ # "stop_time": "01-01-01T00:00:00.0001Z",
135
+ # "operation": "SQL/User/find"
136
+ # },
137
+ # "tags": {
138
+ # "allocations": 1000,
139
+ # "db.statement": "SELECT * FROM users where id = 1",
140
+ # "db.rows": 1,
141
+ # "backtrace": [ {
142
+ # "file": "app/controllers/users_controller.rb",
143
+ # "line": 10,
144
+ # "function": "index"
145
+ # } ]
146
+ # }
147
+ class DetailedTraceSpan
148
+ attr_reader :tags
149
+
150
+ attr_reader :span_type
151
+ attr_reader :span_id, :parent_id
152
+ attr_reader :start_instant, :stop_instant
153
+
154
+ # What is the "name" of this span.
155
+ #
156
+ # Examples:
157
+ # SQL/User/find
158
+ # Controller/Users/index
159
+ # HTTP/GET/example.com
160
+ attr_reader :operation
161
+
162
+ def initialize(span_id, parent_id, start_instant, stop_instant, operation, tags)
163
+ # This will be dynamic when we implement limited spans
164
+ @span_type = "Standard"
165
+
166
+ @span_id = span_id
167
+ @parent_id = parent_id
168
+
169
+ @start_instant = start_instant
170
+ @stop_instant = stop_instant
171
+ @operation = operation
172
+ @tags = DetailedTraceTags(tags)
173
+ end
174
+
175
+ def as_json(*)
176
+ {
177
+ :type => @span_type,
178
+ :identity => {
179
+ :id => span_id,
180
+ :parent_id => parent_id,
181
+ :start_instant => start_instant.iso8601(6),
182
+ :stop_instant => stop_instant.iso8601(6),
183
+ :operation => operation,
184
+ },
185
+ :tags => @tags.as_json,
186
+ }
187
+ end
188
+ end
189
+
190
+
191
+ #############
192
+ # content #
193
+ #############
194
+
195
+ # Tags for either a request, or a span
196
+ class DetailedTraceTags
197
+ attr_reader :tags
198
+
199
+ def initialize(hash)
200
+ @tags = hash
201
+ end
202
+
203
+ # @tags is already a hash, so no conversion needed
204
+ def as_json(*)
205
+ @tags
206
+ end
207
+ end
208
+
209
+ # Converter function to turn an input into a DetailedTraceTags object
210
+ def DetailedTraceTags(arg)
211
+ if DetailedTraceTags === arg
212
+ arg
213
+ elsif Hash === arg
214
+ DetailedTraceTags.new(arg)
215
+ end
216
+ end
217
+
@@ -26,7 +26,10 @@ module ScoutApm
26
26
  BACKGROUND_JOB_INTEGRATIONS = [
27
27
  ScoutApm::BackgroundJobIntegrations::Resque.new,
28
28
  ScoutApm::BackgroundJobIntegrations::Sidekiq.new,
29
+ ScoutApm::BackgroundJobIntegrations::Shoryuken.new,
30
+ ScoutApm::BackgroundJobIntegrations::Sneakers.new,
29
31
  ScoutApm::BackgroundJobIntegrations::DelayedJob.new,
32
+ ScoutApm::BackgroundJobIntegrations::Que.new,
30
33
  ]
31
34
 
32
35
  FRAMEWORK_INTEGRATIONS = [
@@ -179,9 +182,24 @@ module ScoutApm
179
182
  @ruby_2 = defined?(RUBY_VERSION) && RUBY_VERSION.match(/^2/)
180
183
  end
181
184
 
185
+ def ruby_3?
186
+ return @ruby_3 if defined?(@ruby_3)
187
+ @ruby_3 = defined?(RUBY_VERSION) && RUBY_VERSION.match(/^3/)
188
+ end
189
+
190
+ def ruby_minor
191
+ return @ruby_minor if defined?(@ruby_minor)
192
+ @ruby_minor = defined?(RUBY_VERSION) && RUBY_VERSION.split(".")[1].to_i
193
+ end
194
+
182
195
  # Returns true if this Ruby version supports Module#prepend.
183
196
  def supports_module_prepend?
184
- ruby_2?
197
+ ruby_2? || ruby_3?
198
+ end
199
+
200
+ # Returns true if this Ruby version supports Module#prepend.
201
+ def supports_kwarg_delegation?
202
+ ruby_3? || (ruby_2? && ruby_minor >= 7)
185
203
  end
186
204
 
187
205
  # Returns a string representation of the OS (ex: darwin, linux)
@@ -0,0 +1,27 @@
1
+ # Public API for the Scout Error Monitoring service
2
+ #
3
+ # See-Also ScoutApm::Transaction and ScoutApm::Tracing for APM related APIs
4
+ module ScoutApm
5
+ module Error
6
+ # Capture an exception, optionally with an environment hash. This may be a
7
+ # Rack environment, but is not required.
8
+ def self.capture(exception, env={})
9
+ context = ScoutApm::Agent.instance.context
10
+
11
+ # Skip if error monitoring isn't enabled at all
12
+ if ! context.config.value("errors_enabled")
13
+ return false
14
+ end
15
+
16
+ # Skip if this one error is ignored
17
+ if context.ignored_exceptions.ignored?(exception)
18
+ return false
19
+ end
20
+
21
+ # Capture the error for further processing and shipping
22
+ context.error_buffer.capture(exception, env)
23
+
24
+ return true
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ require "net/http"
2
+ require "net/https"
3
+ require "uri"
4
+
5
+ module ScoutApm
6
+ module ErrorService
7
+ API_VERSION = "1"
8
+
9
+ HEADERS = {
10
+ "Content-type" => "application/json",
11
+ "Accept" => "application/json"
12
+ }
13
+
14
+ # Public API to force a given exception to be captured.
15
+ # Still obeys the ignore list
16
+ # Used internally by SidekiqException
17
+ def self.capture(exception, params = {})
18
+ return if disabled?
19
+ return if ScoutApm::Agent.instance.context.ignored_exceptions.ignore?(exception)
20
+
21
+ context.errors_buffer.capture(exception, env)
22
+ end
23
+
24
+ def self.enabled?
25
+ ScoutApm::Agent.instance.context.config.value("errors_enabled")
26
+ end
27
+
28
+ def self.disabled?
29
+ !enabled?
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,39 @@
1
+ # Holds onto exceptions, and moves them forward to shipping when appropriate
2
+ module ScoutApm
3
+ module ErrorService
4
+ class ErrorBuffer
5
+ include Enumerable
6
+
7
+ attr_reader :agent_context
8
+
9
+ def initialize(agent_context)
10
+ @agent_context = agent_context
11
+ @error_records = []
12
+ @mutex = Monitor.new
13
+ end
14
+
15
+ def capture(exception, env)
16
+ context = ScoutApm::Context.current
17
+
18
+ @mutex.synchronize {
19
+ @error_records << ErrorRecord.new(agent_context, exception, env, context)
20
+ }
21
+ end
22
+
23
+ def get_and_reset_error_records
24
+ @mutex.synchronize {
25
+ ret = @error_records
26
+ @error_records = []
27
+ ret
28
+ }
29
+ end
30
+
31
+ # Enables enumerable - for count and each and similar methods
32
+ def each
33
+ @error_records.each do |error_record|
34
+ yield error_record
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end