appsignal 2.8.4.beta.1 → 2.9.18.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
  3. data/.github/ISSUE_TEMPLATE/chore.md +14 -0
  4. data/.gitignore +2 -3
  5. data/.rubocop.yml +3 -0
  6. data/.rubocop_todo.yml +7 -16
  7. data/.travis.yml +28 -27
  8. data/CHANGELOG.md +657 -533
  9. data/README.md +31 -3
  10. data/Rakefile +128 -129
  11. data/SUPPORT.md +16 -0
  12. data/appsignal.gemspec +17 -4
  13. data/build_matrix.yml +21 -9
  14. data/ext/Rakefile +23 -17
  15. data/ext/agent.yml +40 -37
  16. data/ext/base.rb +116 -31
  17. data/ext/extconf.rb +34 -28
  18. data/gemfiles/capistrano2.gemfile +5 -0
  19. data/gemfiles/capistrano3.gemfile +5 -0
  20. data/gemfiles/grape.gemfile +5 -0
  21. data/gemfiles/no_dependencies.gemfile +5 -0
  22. data/gemfiles/padrino.gemfile +5 -0
  23. data/gemfiles/que.gemfile +5 -0
  24. data/gemfiles/que_beta.gemfile +10 -0
  25. data/gemfiles/rails-3.2.gemfile +5 -0
  26. data/gemfiles/rails-4.0.gemfile +5 -0
  27. data/gemfiles/rails-4.1.gemfile +5 -0
  28. data/gemfiles/rails-4.2.gemfile +5 -0
  29. data/gemfiles/rails-6.0.gemfile +5 -0
  30. data/gemfiles/resque.gemfile +5 -0
  31. data/lib/appsignal.rb +14 -492
  32. data/lib/appsignal/cli/demo.rb +5 -2
  33. data/lib/appsignal/cli/diagnose.rb +84 -4
  34. data/lib/appsignal/cli/diagnose/paths.rb +0 -5
  35. data/lib/appsignal/cli/diagnose/utils.rb +19 -0
  36. data/lib/appsignal/cli/helpers.rb +6 -0
  37. data/lib/appsignal/cli/install.rb +45 -15
  38. data/lib/appsignal/cli/notify_of_deploy.rb +10 -0
  39. data/lib/appsignal/config.rb +1 -2
  40. data/lib/appsignal/event_formatter.rb +4 -5
  41. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +10 -8
  42. data/lib/appsignal/event_formatter/moped/query_formatter.rb +60 -59
  43. data/lib/appsignal/extension.rb +2 -2
  44. data/lib/appsignal/helpers/instrumentation.rb +494 -0
  45. data/lib/appsignal/helpers/metrics.rb +54 -0
  46. data/lib/appsignal/hooks.rb +11 -8
  47. data/lib/appsignal/hooks/active_support_notifications.rb +2 -5
  48. data/lib/appsignal/hooks/puma.rb +74 -11
  49. data/lib/appsignal/hooks/sequel.rb +1 -1
  50. data/lib/appsignal/hooks/sidekiq.rb +115 -0
  51. data/lib/appsignal/integrations/mongo_ruby_driver.rb +7 -0
  52. data/lib/appsignal/integrations/que.rb +9 -8
  53. data/lib/appsignal/integrations/railtie.rb +2 -1
  54. data/lib/appsignal/marker.rb +2 -3
  55. data/lib/appsignal/minutely.rb +188 -19
  56. data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -1
  57. data/lib/appsignal/system.rb +16 -18
  58. data/lib/appsignal/transaction.rb +8 -0
  59. data/lib/appsignal/utils/rails_helper.rb +20 -0
  60. data/lib/appsignal/version.rb +1 -1
  61. data/lib/puma/plugin/appsignal.rb +26 -0
  62. data/spec/lib/appsignal/cli/diagnose/utils_spec.rb +40 -0
  63. data/spec/lib/appsignal/cli/diagnose_spec.rb +129 -22
  64. data/spec/lib/appsignal/cli/install_spec.rb +57 -8
  65. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +10 -0
  66. data/spec/lib/appsignal/config_spec.rb +13 -11
  67. data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +38 -28
  68. data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +6 -0
  69. data/spec/lib/appsignal/event_formatter_spec.rb +168 -69
  70. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +104 -25
  71. data/spec/lib/appsignal/hooks/puma_spec.rb +251 -34
  72. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +209 -0
  73. data/spec/lib/appsignal/hooks_spec.rb +4 -0
  74. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +24 -1
  75. data/spec/lib/appsignal/minutely_spec.rb +318 -26
  76. data/spec/lib/appsignal/system_spec.rb +0 -35
  77. data/spec/lib/appsignal/transaction_spec.rb +68 -10
  78. data/spec/lib/appsignal/utils/hash_sanitizer_spec.rb +39 -31
  79. data/spec/lib/appsignal/utils/json_spec.rb +7 -3
  80. data/spec/lib/appsignal_spec.rb +98 -22
  81. data/spec/lib/puma/appsignal_spec.rb +91 -0
  82. data/spec/spec_helper.rb +13 -0
  83. data/spec/support/{project_fixture → fixtures/projects/valid}/config/application.rb +0 -0
  84. data/spec/support/{project_fixture → fixtures/projects/valid}/config/appsignal.yml +1 -0
  85. data/spec/support/{project_fixture → fixtures/projects/valid}/config/environments/development.rb +0 -0
  86. data/spec/support/{project_fixture → fixtures/projects/valid}/config/environments/production.rb +0 -0
  87. data/spec/support/{project_fixture → fixtures/projects/valid}/config/environments/test.rb +0 -0
  88. data/spec/support/{project_fixture → fixtures/projects/valid}/log/.gitkeep +0 -0
  89. data/spec/support/helpers/config_helpers.rb +1 -1
  90. data/spec/support/helpers/log_helpers.rb +6 -0
  91. data/spec/support/helpers/wait_for_helper.rb +28 -0
  92. data/spec/support/mocks/mock_probe.rb +11 -0
  93. data/spec/support/stubs/sidekiq/api.rb +4 -0
  94. metadata +43 -31
  95. data/spec/support/fixtures/containers/cgroups/docker +0 -14
  96. data/spec/support/fixtures/containers/cgroups/docker_systemd +0 -8
  97. data/spec/support/fixtures/containers/cgroups/lxc +0 -10
  98. data/spec/support/fixtures/containers/cgroups/no_permission +0 -0
  99. data/spec/support/fixtures/containers/cgroups/none +0 -1
@@ -12,8 +12,8 @@ begin
12
12
  end
13
13
  rescue LoadError => err
14
14
  Appsignal.logger.error(
15
- "Failed to load extension (#{err}), please check the install.log file in " \
16
- "the ext directory of the gem and email us at support@appsignal.com"
15
+ "Failed to load extension (#{err}), please run `appsignal diagnose` " \
16
+ "and email us at support@appsignal.com"
17
17
  )
18
18
  Appsignal.extension_loaded = false
19
19
  end
@@ -0,0 +1,494 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Helpers
5
+ module Instrumentation # rubocop:disable Metrics/ModuleLength
6
+ # Creates an AppSignal transaction for the given block.
7
+ #
8
+ # If AppSignal is not {.active?} it will still execute the block, but not
9
+ # create a transaction for it.
10
+ #
11
+ # A event is created for this transaction with the name given in the
12
+ # `name` argument. The event name must start with either `perform_job` or
13
+ # `process_action` to differentiate between the "web" and "background"
14
+ # namespace. Custom namespaces are not supported by this helper method.
15
+ #
16
+ # This helper method also captures any exception that occurs in the given
17
+ # block.
18
+ #
19
+ # @example
20
+ # Appsignal.monitor_transaction("perform_job.nightly_update") do
21
+ # # your code
22
+ # end
23
+ #
24
+ # @example with an environment
25
+ # Appsignal.monitor_transaction(
26
+ # "perform_job.nightly_update",
27
+ # :metadata => { "user_id" => 1 }
28
+ # ) do
29
+ # # your code
30
+ # end
31
+ #
32
+ # @param name [String] main event name.
33
+ # @param env [Hash<Symbol, Object>]
34
+ # @option env [Hash<Symbol/String, Object>] :params Params for the
35
+ # monitored request/job, see {Appsignal::Transaction#params=} for more
36
+ # information.
37
+ # @option env [String] :controller name of the controller in which the
38
+ # transaction was recorded.
39
+ # @option env [String] :class name of the Ruby class in which the
40
+ # transaction was recorded. If `:controller` is also given,
41
+ # `:controller` is used instead.
42
+ # @option env [String] :action name of the controller action in which the
43
+ # transaction was recorded.
44
+ # @option env [String] :method name of the Ruby method in which the
45
+ # transaction was recorded. If `:action` is also given, `:action`
46
+ # is used instead.
47
+ # @option env [Integer] :queue_start the moment the request/job was
48
+ # queued. Used to track how long requests/jobs were queued before being
49
+ # executed.
50
+ # @option env [Hash<Symbol/String, String/Fixnum>] :metadata Additional
51
+ # metadata for the transaction, see
52
+ # {Appsignal::Transaction#set_metadata} for more information.
53
+ # @yield the block to monitor.
54
+ # @raise [Exception] any exception that occurs within the given block is
55
+ # re-raised by this method.
56
+ # @return [Object] the value of the given block is returned.
57
+ # @since 0.10.0
58
+ def monitor_transaction(name, env = {})
59
+ # Always verify input, even when Appsignal is not active.
60
+ # This makes it more likely invalid arguments get flagged in test/dev
61
+ # environments.
62
+ if name.start_with?("perform_job".freeze)
63
+ namespace = Appsignal::Transaction::BACKGROUND_JOB
64
+ request = Appsignal::Transaction::GenericRequest.new(env)
65
+ elsif name.start_with?("process_action".freeze)
66
+ namespace = Appsignal::Transaction::HTTP_REQUEST
67
+ request = ::Rack::Request.new(env)
68
+ else
69
+ logger.error "Unrecognized name '#{name}': names must start with " \
70
+ "either 'perform_job' (for jobs and tasks) or 'process_action' " \
71
+ "(for HTTP requests)"
72
+ return yield
73
+ end
74
+
75
+ return yield unless active?
76
+
77
+ transaction = Appsignal::Transaction.create(
78
+ SecureRandom.uuid,
79
+ namespace,
80
+ request
81
+ )
82
+ begin
83
+ Appsignal.instrument(name) do
84
+ yield
85
+ end
86
+ rescue Exception => error # rubocop:disable Lint/RescueException
87
+ transaction.set_error(error)
88
+ raise error
89
+ ensure
90
+ transaction.set_http_or_background_action(request.env)
91
+ transaction.set_http_or_background_queue_start
92
+ Appsignal::Transaction.complete_current!
93
+ end
94
+ end
95
+
96
+ # Monitor a transaction, stop AppSignal and wait for this single
97
+ # transaction to be flushed.
98
+ #
99
+ # Useful for cases such as Rake tasks and Resque-like systems where a
100
+ # process is forked and immediately exits after the transaction finishes.
101
+ #
102
+ # @see monitor_transaction
103
+ def monitor_single_transaction(name, env = {}, &block)
104
+ monitor_transaction(name, env, &block)
105
+ ensure
106
+ stop("monitor_single_transaction")
107
+ end
108
+
109
+ # Listen for an error to occur and send it to AppSignal.
110
+ #
111
+ # Uses {.send_error} to directly send the error in a separate
112
+ # transaction. Does not add the error to the current transaction.
113
+ #
114
+ # Make sure that AppSignal is integrated in your application beforehand.
115
+ # AppSignal won't record errors unless {Config#active?} is `true`.
116
+ #
117
+ # @example
118
+ # # my_app.rb
119
+ # # setup AppSignal beforehand
120
+ #
121
+ # Appsignal.listen_for_error do
122
+ # # my code
123
+ # raise "foo"
124
+ # end
125
+ #
126
+ # @see Transaction.set_tags
127
+ # @see Transaction.set_namespace
128
+ # @see .send_error
129
+ # @see https://docs.appsignal.com/ruby/instrumentation/integrating-appsignal.html
130
+ # AppSignal integration guide
131
+ #
132
+ # @param tags [Hash, nil]
133
+ # @param namespace [String] the namespace for this error.
134
+ # @yield yields the given block.
135
+ # @return [Object] returns the return value of the given block.
136
+ def listen_for_error(
137
+ tags = nil,
138
+ namespace = Appsignal::Transaction::HTTP_REQUEST
139
+ )
140
+ yield
141
+ rescue Exception => error # rubocop:disable Lint/RescueException
142
+ send_error(error, tags, namespace)
143
+ raise error
144
+ end
145
+ alias :listen_for_exception :listen_for_error
146
+
147
+ # Send an error to AppSignal regardless of the context.
148
+ #
149
+ # Records and send the exception to AppSignal.
150
+ #
151
+ # This instrumentation helper does not require a transaction to be
152
+ # active, it starts a new transaction by itself.
153
+ #
154
+ # Use {.set_error} if your want to add an exception to the current
155
+ # transaction.
156
+ #
157
+ # **Note**: Does not do anything if AppSignal is not active or when the
158
+ # "error" is not a class extended from Ruby's Exception class.
159
+ #
160
+ # @example Send an exception
161
+ # begin
162
+ # raise "oh no!"
163
+ # rescue => e
164
+ # Appsignal.send_error(e)
165
+ # end
166
+ #
167
+ # @example Send an exception with tags
168
+ # begin
169
+ # raise "oh no!"
170
+ # rescue => e
171
+ # Appsignal.send_error(e, :key => "value")
172
+ # end
173
+ #
174
+ # @example Add more metadata to transaction
175
+ # Appsignal.send_error(e, :key => "value") do |transaction|
176
+ # transaction.params(:search_query => params[:search_query])
177
+ # transaction.set_action("my_action_name")
178
+ # transaction.set_namespace("my_namespace")
179
+ # end
180
+ #
181
+ # @param error [Exception] The error to send to AppSignal.
182
+ # @param tags [Hash{String, Symbol => String, Symbol, Integer}]
183
+ # Additional tags to add to the error. See also {.tag_request}.
184
+ # @param namespace [String] The namespace in which the error occurred.
185
+ # See also {.set_namespace}.
186
+ # @yield [transaction] yields block to allow modification of the
187
+ # transaction before it's send.
188
+ # @yieldparam transaction [Transaction] yields the AppSignal transaction
189
+ # used to send the error.
190
+ # @return [void]
191
+ #
192
+ # @see http://docs.appsignal.com/ruby/instrumentation/exception-handling.html
193
+ # Exception handling guide
194
+ # @see http://docs.appsignal.com/ruby/instrumentation/tagging.html
195
+ # Tagging guide
196
+ # @since 0.6.0
197
+ def send_error(
198
+ error,
199
+ tags = nil,
200
+ namespace = Appsignal::Transaction::HTTP_REQUEST
201
+ )
202
+ return unless active?
203
+ unless error.is_a?(Exception)
204
+ logger.error "Appsignal.send_error: Cannot send error. The given " \
205
+ "value is not an exception: #{error.inspect}"
206
+ return
207
+ end
208
+ transaction = Appsignal::Transaction.new(
209
+ SecureRandom.uuid,
210
+ namespace,
211
+ Appsignal::Transaction::GenericRequest.new({})
212
+ )
213
+ transaction.set_tags(tags) if tags
214
+ transaction.set_error(error)
215
+ yield transaction if block_given?
216
+ transaction.complete
217
+ end
218
+ alias :send_exception :send_error
219
+
220
+ # Set an error on the current transaction.
221
+ #
222
+ # **Note**: Does not do anything if AppSignal is not active, no
223
+ # transaction is currently active or when the "error" is not a class
224
+ # extended from Ruby's Exception class.
225
+ #
226
+ # @example Manual instrumentation of set_error.
227
+ # # Manually starting AppSignal here
228
+ # # Manually starting a transaction here.
229
+ # begin
230
+ # raise "oh no!"
231
+ # rescue => e
232
+ # Appsignal.set_error(error)
233
+ # end
234
+ # # Manually completing the transaction here.
235
+ # # Manually stopping AppSignal here
236
+ #
237
+ # @example In a Rails application
238
+ # class SomeController < ApplicationController
239
+ # # The AppSignal transaction is created by our integration for you.
240
+ # def create
241
+ # # Do something that breaks
242
+ # rescue => e
243
+ # Appsignal.set_error(e)
244
+ # end
245
+ # end
246
+ #
247
+ # @param exception [Exception] The error to add to the current
248
+ # transaction.
249
+ # @param tags [Hash{String, Symbol => String, Symbol, Integer}]
250
+ # Additional tags to add to the error. See also {.tag_request}.
251
+ # @param namespace [String] The namespace in which the error occurred.
252
+ # See also {.set_namespace}.
253
+ # @return [void]
254
+ #
255
+ # @see Transaction#set_error
256
+ # @see http://docs.appsignal.com/ruby/instrumentation/exception-handling.html
257
+ # Exception handling guide
258
+ # @since 0.6.6
259
+ def set_error(exception, tags = nil, namespace = nil)
260
+ unless exception.is_a?(Exception)
261
+ logger.error "Appsignal.set_error: Cannot set error. The given " \
262
+ "value is not an exception: #{exception.inspect}"
263
+ return
264
+ end
265
+ return if !active? || Appsignal::Transaction.current.nil?
266
+ transaction = Appsignal::Transaction.current
267
+ transaction.set_error(exception)
268
+ transaction.set_tags(tags) if tags
269
+ transaction.set_namespace(namespace) if namespace
270
+ end
271
+ alias :set_exception :set_error
272
+ alias :add_exception :set_error
273
+
274
+ # Set a custom action name for the current transaction.
275
+ #
276
+ # When using an integration such as the Rails or Sinatra AppSignal will
277
+ # try to find the action name from the controller or endpoint for you.
278
+ #
279
+ # If you want to customize the action name as it appears on AppSignal.com
280
+ # you can use this method. This overrides the action name AppSignal
281
+ # generates in an integration.
282
+ #
283
+ # @example in a Rails controller
284
+ # class SomeController < ApplicationController
285
+ # before_action :set_appsignal_action
286
+ #
287
+ # def set_appsignal_action
288
+ # Appsignal.set_action("DynamicController#dynamic_method")
289
+ # end
290
+ # end
291
+ #
292
+ # @param action [String]
293
+ # @return [void]
294
+ # @see Transaction#set_action
295
+ # @since 2.2.0
296
+ def set_action(action)
297
+ return if !active? ||
298
+ Appsignal::Transaction.current.nil? ||
299
+ action.nil?
300
+ Appsignal::Transaction.current.set_action(action)
301
+ end
302
+
303
+ # Set a custom namespace for the current transaction.
304
+ #
305
+ # When using an integration such as Rails or Sidekiq AppSignal will try
306
+ # to find a appropriate namespace for the transaction.
307
+ #
308
+ # A Rails controller will be automatically put in the "http_request"
309
+ # namespace, while a Sidekiq background job is put in the
310
+ # "background_job" namespace.
311
+ #
312
+ # Note: The "http_request" namespace gets transformed on AppSignal.com to
313
+ # "Web" and "background_job" gets transformed to "Background".
314
+ #
315
+ # If you want to customize the namespace in which transactions appear you
316
+ # can use this method. This overrides the namespace AppSignal uses by
317
+ # default.
318
+ #
319
+ # A common request we've seen is to split the administration panel from
320
+ # the main application.
321
+ #
322
+ # @example create a custom admin namespace
323
+ # class AdminController < ApplicationController
324
+ # before_action :set_appsignal_namespace
325
+ #
326
+ # def set_appsignal_namespace
327
+ # Appsignal.set_namespace("admin")
328
+ # end
329
+ # end
330
+ #
331
+ # @param namespace [String]
332
+ # @return [void]
333
+ # @see Transaction#set_namespace
334
+ # @since 2.2.0
335
+ def set_namespace(namespace)
336
+ return if !active? ||
337
+ Appsignal::Transaction.current.nil? ||
338
+ namespace.nil?
339
+ Appsignal::Transaction.current.set_namespace(namespace)
340
+ end
341
+
342
+ # Set tags on the current transaction.
343
+ #
344
+ # Tags are extra bits of information that are added to transaction and
345
+ # appear on sample details pages on AppSignal.com.
346
+ #
347
+ # @example
348
+ # Appsignal.tag_request(:locale => "en")
349
+ # Appsignal.tag_request("locale" => "en")
350
+ # Appsignal.tag_request("user_id" => 1)
351
+ #
352
+ # @example Nested hashes are not supported
353
+ # # Bad
354
+ # Appsignal.tag_request(:user => { :locale => "en" })
355
+ #
356
+ # @example in a Rails controller
357
+ # class SomeController < ApplicationController
358
+ # before_action :set_appsignal_tags
359
+ #
360
+ # def set_appsignal_tags
361
+ # Appsignal.tag_request(:locale => I18n.locale)
362
+ # end
363
+ # end
364
+ #
365
+ # @param tags [Hash] Collection of tags.
366
+ # @option tags [String, Symbol, Integer] :any
367
+ # The name of the tag as a Symbol.
368
+ # @option tags [String, Symbol, Integer] "any"
369
+ # The name of the tag as a String.
370
+ # @return [void]
371
+ #
372
+ # @see Transaction.set_tags
373
+ # @see http://docs.appsignal.com/ruby/instrumentation/tagging.html
374
+ # Tagging guide
375
+ def tag_request(tags = {})
376
+ return unless active?
377
+ transaction = Appsignal::Transaction.current
378
+ return false unless transaction
379
+ transaction.set_tags(tags)
380
+ end
381
+ alias :tag_job :tag_request
382
+
383
+ # Instrument helper for AppSignal.
384
+ #
385
+ # For more help, read our custom instrumentation guide, listed under "See
386
+ # also".
387
+ #
388
+ # @example Simple instrumentation
389
+ # Appsignal.instrument("fetch.issue_fetcher") do
390
+ # # To be instrumented code
391
+ # end
392
+ #
393
+ # @example Instrumentation with title and body
394
+ # Appsignal.instrument(
395
+ # "fetch.issue_fetcher",
396
+ # "Fetching issue",
397
+ # "GitHub API"
398
+ # ) do
399
+ # # To be instrumented code
400
+ # end
401
+ #
402
+ # @param name [String] Name of the instrumented event. Read our event
403
+ # naming guide listed under "See also".
404
+ # @param title [String, nil] Human readable name of the event.
405
+ # @param body [String, nil] Value of importance for the event, such as
406
+ # the server against an API call is made.
407
+ # @param body_format [Integer] Enum for the type of event that is
408
+ # instrumented. Accepted values are {EventFormatter::DEFAULT} and
409
+ # {EventFormatter::SQL_BODY_FORMAT}, but we recommend you use
410
+ # {.instrument_sql} instead of {EventFormatter::SQL_BODY_FORMAT}.
411
+ # @yield yields the given block of code instrumented in an AppSignal
412
+ # event.
413
+ # @return [Object] Returns the block's return value.
414
+ #
415
+ # @see Appsignal::Transaction#instrument
416
+ # @see .instrument_sql
417
+ # @see http://docs.appsignal.com/ruby/instrumentation/instrumentation.html
418
+ # AppSignal custom instrumentation guide
419
+ # @see http://docs.appsignal.com/api/event-names.html
420
+ # AppSignal event naming guide
421
+ # @since 1.3.0
422
+ def instrument(
423
+ name,
424
+ title = nil,
425
+ body = nil,
426
+ body_format = Appsignal::EventFormatter::DEFAULT
427
+ )
428
+ Appsignal::Transaction.current.start_event
429
+ yield if block_given?
430
+ ensure
431
+ Appsignal::Transaction
432
+ .current
433
+ .finish_event(name, title, body, body_format)
434
+ end
435
+
436
+ # Instrumentation helper for SQL queries.
437
+ #
438
+ # This helper filters out values from SQL queries so you don't have to.
439
+ #
440
+ # @example SQL query instrumentation
441
+ # body = "SELECT * FROM ..."
442
+ # Appsignal.instrument_sql("perform.query", nil, body) do
443
+ # # To be instrumented code
444
+ # end
445
+ #
446
+ # @example SQL query instrumentation
447
+ # body = "WHERE email = 'foo@..'"
448
+ # Appsignal.instrument_sql("perform.query", nil, body) do
449
+ # # query value will replace 'foo..' with a question mark `?`.
450
+ # end
451
+ #
452
+ # @param name [String] Name of the instrumented event. Read our event
453
+ # naming guide listed under "See also".
454
+ # @param title [String, nil] Human readable name of the event.
455
+ # @param body [String, nil] SQL query that's being executed.
456
+ # @yield yields the given block of code instrumented in an AppSignal
457
+ # event.
458
+ # @return [Object] Returns the block's return value.
459
+ #
460
+ # @see .instrument
461
+ # @see http://docs.appsignal.com/ruby/instrumentation/instrumentation.html
462
+ # AppSignal custom instrumentation guide
463
+ # @see http://docs.appsignal.com/api/event-names.html
464
+ # AppSignal event naming guide
465
+ # @since 2.0.0
466
+ def instrument_sql(name, title = nil, body = nil, &block)
467
+ instrument(
468
+ name,
469
+ title,
470
+ body,
471
+ Appsignal::EventFormatter::SQL_BODY_FORMAT,
472
+ &block
473
+ )
474
+ end
475
+
476
+ # Convenience method for skipping instrumentations around a block of code.
477
+ #
478
+ # @example
479
+ # Appsignal.without_instrumentation do
480
+ # # Complex code here
481
+ # end
482
+ #
483
+ # @yield block of code that shouldn't be instrumented.
484
+ # @return [Object] Returns the return value of the block.
485
+ # @since 0.8.7
486
+ def without_instrumentation
487
+ Appsignal::Transaction.current.pause! if Appsignal::Transaction.current
488
+ yield
489
+ ensure
490
+ Appsignal::Transaction.current.resume! if Appsignal::Transaction.current
491
+ end
492
+ end
493
+ end
494
+ end