appsignal 3.9.3-java → 3.11.0-java

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +22 -19
  3. data/.rubocop.yml +1 -1
  4. data/CHANGELOG.md +180 -0
  5. data/Gemfile +1 -0
  6. data/README.md +0 -1
  7. data/Rakefile +1 -1
  8. data/benchmark.rake +99 -42
  9. data/build_matrix.yml +10 -12
  10. data/gemfiles/webmachine1.gemfile +5 -4
  11. data/lib/appsignal/cli/demo.rb +0 -1
  12. data/lib/appsignal/config.rb +57 -97
  13. data/lib/appsignal/demo.rb +15 -20
  14. data/lib/appsignal/environment.rb +6 -1
  15. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
  16. data/lib/appsignal/event_formatter.rb +3 -2
  17. data/lib/appsignal/helpers/instrumentation.rb +490 -16
  18. data/lib/appsignal/hooks/action_cable.rb +21 -16
  19. data/lib/appsignal/hooks/active_job.rb +15 -14
  20. data/lib/appsignal/hooks/delayed_job.rb +1 -1
  21. data/lib/appsignal/hooks/shoryuken.rb +3 -63
  22. data/lib/appsignal/integrations/action_cable.rb +5 -7
  23. data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
  24. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
  25. data/lib/appsignal/integrations/data_mapper.rb +1 -0
  26. data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
  27. data/lib/appsignal/integrations/dry_monitor.rb +1 -0
  28. data/lib/appsignal/integrations/excon.rb +1 -0
  29. data/lib/appsignal/integrations/http.rb +1 -0
  30. data/lib/appsignal/integrations/net_http.rb +1 -0
  31. data/lib/appsignal/integrations/object.rb +6 -0
  32. data/lib/appsignal/integrations/padrino.rb +21 -25
  33. data/lib/appsignal/integrations/que.rb +13 -20
  34. data/lib/appsignal/integrations/railtie.rb +1 -1
  35. data/lib/appsignal/integrations/rake.rb +45 -15
  36. data/lib/appsignal/integrations/redis.rb +1 -0
  37. data/lib/appsignal/integrations/redis_client.rb +1 -0
  38. data/lib/appsignal/integrations/resque.rb +2 -5
  39. data/lib/appsignal/integrations/shoryuken.rb +75 -0
  40. data/lib/appsignal/integrations/sidekiq.rb +7 -25
  41. data/lib/appsignal/integrations/unicorn.rb +1 -0
  42. data/lib/appsignal/integrations/webmachine.rb +12 -9
  43. data/lib/appsignal/logger.rb +7 -3
  44. data/lib/appsignal/probes/helpers.rb +1 -0
  45. data/lib/appsignal/probes/mri.rb +1 -0
  46. data/lib/appsignal/probes/sidekiq.rb +1 -0
  47. data/lib/appsignal/probes.rb +3 -0
  48. data/lib/appsignal/rack/abstract_middleware.rb +67 -24
  49. data/lib/appsignal/rack/body_wrapper.rb +143 -0
  50. data/lib/appsignal/rack/event_handler.rb +39 -8
  51. data/lib/appsignal/rack/generic_instrumentation.rb +6 -4
  52. data/lib/appsignal/rack/grape_middleware.rb +3 -2
  53. data/lib/appsignal/rack/hanami_middleware.rb +1 -1
  54. data/lib/appsignal/rack/instrumentation_middleware.rb +62 -0
  55. data/lib/appsignal/rack/rails_instrumentation.rb +1 -3
  56. data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -3
  57. data/lib/appsignal/rack/streaming_listener.rb +14 -59
  58. data/lib/appsignal/rack.rb +60 -0
  59. data/lib/appsignal/span.rb +1 -0
  60. data/lib/appsignal/transaction.rb +353 -104
  61. data/lib/appsignal/utils/data.rb +0 -1
  62. data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
  63. data/lib/appsignal/utils/integration_logger.rb +0 -13
  64. data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
  65. data/lib/appsignal/utils/json.rb +0 -1
  66. data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
  67. data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
  68. data/lib/appsignal/utils.rb +6 -0
  69. data/lib/appsignal/version.rb +1 -1
  70. data/lib/appsignal.rb +9 -6
  71. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  72. data/spec/lib/appsignal/config_spec.rb +139 -43
  73. data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
  74. data/spec/lib/appsignal/hooks/activejob_spec.rb +9 -0
  75. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
  76. data/spec/lib/appsignal/hooks/rake_spec.rb +100 -17
  77. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
  78. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
  79. data/spec/lib/appsignal/integrations/padrino_spec.rb +181 -131
  80. data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
  81. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
  82. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +4 -4
  83. data/spec/lib/appsignal/integrations/sinatra_spec.rb +10 -2
  84. data/spec/lib/appsignal/integrations/webmachine_spec.rb +77 -17
  85. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +144 -11
  86. data/spec/lib/appsignal/rack/body_wrapper_spec.rb +263 -0
  87. data/spec/lib/appsignal/rack/event_handler_spec.rb +81 -10
  88. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +70 -17
  89. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +1 -1
  90. data/spec/lib/appsignal/rack/instrumentation_middleware_spec.rb +38 -0
  91. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
  92. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +43 -120
  93. data/spec/lib/appsignal/rack_spec.rb +63 -0
  94. data/spec/lib/appsignal/transaction_spec.rb +1675 -953
  95. data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
  96. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
  97. data/spec/lib/appsignal_spec.rb +517 -13
  98. data/spec/support/helpers/transaction_helpers.rb +44 -20
  99. data/spec/support/matchers/transaction.rb +15 -1
  100. data/spec/support/mocks/dummy_app.rb +1 -1
  101. data/spec/support/testing.rb +1 -1
  102. metadata +12 -4
  103. data/support/check_versions +0 -22
@@ -5,6 +5,161 @@ module Appsignal
5
5
  module Instrumentation
6
6
  include Appsignal::Utils::StdoutAndLoggerMessage
7
7
 
8
+ # Monitor a block of code with AppSignal.
9
+ #
10
+ # This is a helper to create an AppSignal transaction, track any errors
11
+ # that may occur and complete the transaction.
12
+ #
13
+ # This helper is recommended to be used in Ruby scripts and parts of an
14
+ # app not already instrumented by AppSignal's automatic instrumentations.
15
+ #
16
+ # Use this helper in combination with our {.instrument} helper to track
17
+ # instrumentation events.
18
+ #
19
+ # If AppSignal is not active ({Appsignal.active?}) it will still execute
20
+ # the block, but not create a transaction for it.
21
+ #
22
+ # @example Instrument a block of code
23
+ # Appsignal.monitor(
24
+ # :namespace => "my_namespace",
25
+ # :action => "MyClass#my_method"
26
+ # ) do
27
+ # # Some code
28
+ # end
29
+ #
30
+ # @example Instrument a block of code using the default namespace
31
+ # Appsignal.monitor(
32
+ # :action => "MyClass#my_method"
33
+ # ) do
34
+ # # Some code
35
+ # end
36
+ #
37
+ # @example Instrument a block of code with an instrumentation event
38
+ # Appsignal.monitor(
39
+ # :namespace => "my_namespace",
40
+ # :action => "MyClass#my_method"
41
+ # ) do
42
+ # Appsignal.instrument("some_event.some_group") do
43
+ # # Some code
44
+ # end
45
+ # end
46
+ #
47
+ # @example Set the action name in the monitor block
48
+ # Appsignal.monitor(
49
+ # :action => nil
50
+ # ) do
51
+ # # Some code
52
+ #
53
+ # Appsignal.set_action("GET /resource/:id")
54
+ # end
55
+ #
56
+ # @example Set the action name in the monitor block
57
+ # Appsignal.monitor(
58
+ # :action => :set_later # Explicit placeholder
59
+ # ) do
60
+ # # Some code
61
+ #
62
+ # Appsignal.set_action("GET /resource/:id")
63
+ # end
64
+ #
65
+ # @example Set custom metadata on the transaction
66
+ # Appsignal.monitor(
67
+ # :namespace => "my_namespace",
68
+ # :action => "MyClass#my_method"
69
+ # ) do
70
+ # # Some code
71
+ #
72
+ # Appsignal.set_tags(:tag1 => "value1", :tag2 => "value2")
73
+ # Appsignal.set_params(:param1 => "value1", :param2 => "value2")
74
+ # end
75
+ #
76
+ # @example Call monitor within monitor will do nothing
77
+ # Appsignal.monitor(
78
+ # :namespace => "my_namespace",
79
+ # :action => "MyClass#my_method"
80
+ # ) do
81
+ # # This will _not_ update the namespace and action name
82
+ # Appsignal.monitor(
83
+ # :namespace => "my_other_namespace",
84
+ # :action => "MyOtherClass#my_other_method"
85
+ # ) do
86
+ # # Some code
87
+ #
88
+ # # The reported namespace will be "my_namespace"
89
+ # # The reported action will be "MyClass#my_method"
90
+ # end
91
+ # end
92
+ #
93
+ # @param namespace [String/Symbol] The namespace to set on the new
94
+ # transaction.
95
+ # Defaults to the 'web' namespace.
96
+ # This will not update the active transaction's namespace if
97
+ # {.monitor} is called when another transaction is already active.
98
+ # @param action [String, Symbol, NilClass]
99
+ # The action name for the transaction.
100
+ # The action name is required to be set for the transaction to be
101
+ # reported.
102
+ # The argument can be set to `nil` or `:set_later` if the action is set
103
+ # within the block with {#set_action}.
104
+ # This will not update the active transaction's action if
105
+ # {.monitor} is called when another transaction is already active.
106
+ # @yield The block to monitor.
107
+ # @raise [Exception] Any exception that occurs within the given block is
108
+ # re-raised by this method.
109
+ # @return [Object] The value of the given block is returned.
110
+ # @since 3.11.0
111
+ def monitor(
112
+ action:, namespace: nil
113
+ )
114
+ return yield unless active?
115
+
116
+ has_parent_transaction = Appsignal::Transaction.current?
117
+ if has_parent_transaction
118
+ callers = caller
119
+ Appsignal::Utils::StdoutAndLoggerMessage.warning \
120
+ "An active transaction around this 'Appsignal.monitor' call. " \
121
+ "Calling `Appsignal.monitor` in another `Appsignal.monitor` block has no effect. " \
122
+ "The namespace and action are not updated for the active transaction." \
123
+ "Did you mean to use `Appsignal.instrument`? " \
124
+ "Update the 'Appsignal.monitor' call in: #{callers.first}"
125
+ return yield if block_given?
126
+
127
+ return
128
+ end
129
+
130
+ transaction =
131
+ if has_parent_transaction
132
+ Appsignal::Transaction.current
133
+ else
134
+ Appsignal::Transaction.create(namespace || Appsignal::Transaction::HTTP_REQUEST)
135
+ end
136
+
137
+ begin
138
+ yield if block_given?
139
+ rescue Exception => error # rubocop:disable Lint/RescueException
140
+ transaction.set_error(error)
141
+ raise error
142
+ ensure
143
+ transaction.set_action_if_nil(action.to_s) if action && action != :set_later
144
+ Appsignal::Transaction.complete_current!
145
+ end
146
+ end
147
+
148
+ # Instrument a block of code and stop AppSignal.
149
+ #
150
+ # Useful for cases such as one-off scripts where there is no long running
151
+ # process active and the data needs to be sent after the process exists.
152
+ #
153
+ # Acts the same way as {.monitor}. See that method for more
154
+ # documentation.
155
+ #
156
+ # @see monitor
157
+ def monitor_and_stop(action:, namespace: nil)
158
+ monitor(:namespace => namespace, :action => action)
159
+ ensure
160
+ Appsignal.stop("monitor_and_stop")
161
+ end
162
+
8
163
  # Creates an AppSignal transaction for the given block.
9
164
  #
10
165
  # If AppSignal is not {Appsignal.active?} it will still execute the
@@ -58,12 +213,22 @@ module Appsignal
58
213
  # @return [Object] the value of the given block is returned.
59
214
  # @since 0.10.0
60
215
  def monitor_transaction(name, env = {}, &block)
216
+ stdout_and_logger_warning \
217
+ "The `Appsignal.monitor_transaction` helper is deprecated. " \
218
+ "Please use `Appsignal.monitor` and `Appsignal.instrument` instead. " \
219
+ "Read our instrumentation documentation: " \
220
+ "https://docs.appsignal.com/ruby/instrumentation/instrumentation.html"
221
+ _monitor_transaction(name, env, &block)
222
+ end
223
+
224
+ # @api private
225
+ def _monitor_transaction(name, env = {}, &block)
61
226
  # Always verify input, even when Appsignal is not active.
62
227
  # This makes it more likely invalid arguments get flagged in test/dev
63
228
  # environments.
64
229
  if name.start_with?("perform_job")
65
230
  namespace = Appsignal::Transaction::BACKGROUND_JOB
66
- request = Appsignal::Transaction::GenericRequest.new(env)
231
+ request = Appsignal::Transaction::InternalGenericRequest.new(env)
67
232
  elsif name.start_with?("process_action")
68
233
  namespace = Appsignal::Transaction::HTTP_REQUEST
69
234
  request = ::Rack::Request.new(env)
@@ -88,7 +253,9 @@ module Appsignal
88
253
  raise error
89
254
  ensure
90
255
  transaction.set_http_or_background_action(request.env)
91
- transaction.set_http_or_background_queue_start
256
+ queue_start = Appsignal::Rack::Utils.queue_start_from(request.env) ||
257
+ (env[:queue_start]&.to_i&.* 1_000)
258
+ transaction.set_queue_start(queue_start) if queue_start
92
259
  Appsignal::Transaction.complete_current!
93
260
  end
94
261
  end
@@ -101,6 +268,11 @@ module Appsignal
101
268
  #
102
269
  # @see monitor_transaction
103
270
  def monitor_single_transaction(name, env = {}, &block)
271
+ stdout_and_logger_warning \
272
+ "The `Appsignal.monitor_single_transaction` helper is deprecated. " \
273
+ "Please use `Appsignal.monitor_and_stop` and `Appsignal.instrument` instead. " \
274
+ "Read our instrumentation documentation: " \
275
+ "https://docs.appsignal.com/ruby/instrumentation/instrumentation.html"
104
276
  monitor_transaction(name, env, &block)
105
277
  ensure
106
278
  stop("monitor_single_transaction")
@@ -195,6 +367,7 @@ module Appsignal
195
367
  # used to send the error.
196
368
  # @return [void]
197
369
  #
370
+ # @see Transaction#report_error
198
371
  # @see https://docs.appsignal.com/ruby/instrumentation/exception-handling.html
199
372
  # Exception handling guide
200
373
  # @see https://docs.appsignal.com/ruby/instrumentation/tagging.html
@@ -234,8 +407,7 @@ module Appsignal
234
407
  end
235
408
  transaction = Appsignal::Transaction.new(
236
409
  SecureRandom.uuid,
237
- namespace || Appsignal::Transaction::HTTP_REQUEST,
238
- Appsignal::Transaction::GenericRequest.new({})
410
+ namespace || Appsignal::Transaction::HTTP_REQUEST
239
411
  )
240
412
  transaction.set_tags(tags) if tags
241
413
  transaction.set_error(error)
@@ -294,6 +466,7 @@ module Appsignal
294
466
  # @return [void]
295
467
  #
296
468
  # @see Transaction#set_error
469
+ # @see Transaction#report_error
297
470
  # @see https://docs.appsignal.com/ruby/instrumentation/exception-handling.html
298
471
  # Exception handling guide
299
472
  # @since 0.6.6
@@ -334,6 +507,73 @@ module Appsignal
334
507
  alias :set_exception :set_error
335
508
  alias :add_exception :set_error
336
509
 
510
+ # Report an error.
511
+ #
512
+ # If a transaction is currently active, it will report the error on the
513
+ # current transaction. If no transaction is active, it will report the
514
+ # error on a new transaction.
515
+ #
516
+ # **Note**: If AppSignal is not active, no error is reported.
517
+ #
518
+ # **Note**: If the given exception argument is not an Exception subclass,
519
+ # it will not be reported.
520
+ #
521
+ # @example
522
+ # class SomeController < ApplicationController
523
+ # def create
524
+ # # Do something that breaks
525
+ # rescue => error
526
+ # Appsignal.report_error(error)
527
+ # end
528
+ # end
529
+ #
530
+ # @example Add more metadata to transaction
531
+ # Appsignal.report_error(error) do |transaction|
532
+ # transaction.set_namespace("my_namespace")
533
+ # transaction.set_action("my_action_name")
534
+ # transaction.set_params(:search_query => params[:search_query])
535
+ # transaction.set_tags(:key => "value")
536
+ # end
537
+ #
538
+ # @param exception [Exception] The error to add to the current
539
+ # transaction.
540
+ # @yield [transaction] yields block to allow modification of the
541
+ # transaction.
542
+ # @yieldparam transaction [Transaction] yields the AppSignal transaction
543
+ # used to report the error.
544
+ # @return [void]
545
+ #
546
+ # @see https://docs.appsignal.com/ruby/instrumentation/exception-handling.html
547
+ # Exception handling guide
548
+ # @since 3.10.0
549
+ def report_error(exception)
550
+ unless exception.is_a?(Exception)
551
+ internal_logger.error "Appsignal.report_error: Cannot set error. " \
552
+ "The given value is not an exception: #{exception.inspect}"
553
+ return
554
+ end
555
+ return unless active?
556
+
557
+ has_parent_transaction = Appsignal::Transaction.current?
558
+ transaction =
559
+ if has_parent_transaction
560
+ Appsignal::Transaction.current
561
+ else
562
+ Appsignal::Transaction.new(
563
+ SecureRandom.uuid,
564
+ Appsignal::Transaction::HTTP_REQUEST
565
+ )
566
+ end
567
+
568
+ transaction.set_error(exception)
569
+ yield transaction if block_given?
570
+
571
+ return if has_parent_transaction
572
+
573
+ transaction.complete
574
+ end
575
+ alias :report_exception :report_error
576
+
337
577
  # Set a custom action name for the current transaction.
338
578
  #
339
579
  # When using an integration such as the Rails or Sinatra AppSignal will
@@ -404,13 +644,44 @@ module Appsignal
404
644
  Appsignal::Transaction.current.set_namespace(namespace)
405
645
  end
406
646
 
647
+ # Set custom data on the current transaction.
648
+ #
649
+ # Add extra information about the request or background that cannot be
650
+ # expressed in tags, like nested data structures.
651
+ #
652
+ # When this method is called multiple times, it will overwrite the
653
+ # previously set value.
654
+ #
655
+ # @example
656
+ # Appsignal.set_custom_data(:user => { :locale => "en" })
657
+ # Appsignal.set_custom_data([
658
+ # "array with data",
659
+ # :options => { :verbose => true }
660
+ # ])
661
+ #
662
+ # @since 3.10.0
663
+ # @see Transaction#set_custom_data
664
+ # @see https://docs.appsignal.com/guides/custom-data/sample-data.html
665
+ # Sample data guide
666
+ # @param data [Hash/Array]
667
+ # @return [void]
668
+ def set_custom_data(data)
669
+ return unless active?
670
+ return unless Appsignal::Transaction.current?
671
+
672
+ transaction = Appsignal::Transaction.current
673
+ transaction.set_custom_data(data)
674
+ end
675
+
407
676
  # Set tags on the current transaction.
408
677
  #
409
678
  # Tags are extra bits of information that are added to transaction and
410
679
  # appear on sample details pages on AppSignal.com.
411
680
  #
681
+ # When this method is called multiple times, it will merge the tags.
682
+ #
412
683
  # @example
413
- # Appsignal.tag_request(:locale => "en")
684
+ # Appsignal.tag_request(:locale => "en", :user_id => 1)
414
685
  # Appsignal.tag_request("locale" => "en")
415
686
  # Appsignal.tag_request("user_id" => 1)
416
687
  #
@@ -445,6 +716,186 @@ module Appsignal
445
716
  transaction.set_tags(tags)
446
717
  end
447
718
  alias :tag_job :tag_request
719
+ alias :set_tags :tag_request
720
+
721
+ # Set parameters on the current transaction.
722
+ #
723
+ # Parameters are automatically set by most of our integrations. It should
724
+ # not be necessary to call this method unless you want to report
725
+ # different parameters.
726
+ #
727
+ # To filter parameters, see our parameter filtering guide.
728
+ #
729
+ # When this method is called multiple times, it will overwrite the
730
+ # previously set value.
731
+ #
732
+ # When no parameters are set this way, the transaction will look for
733
+ # parameters in its request environment.
734
+ #
735
+ # A block can be given to this method to defer the fetching and parsing
736
+ # of the parameters until and only when the transaction is sampled.
737
+ #
738
+ # When both the `params` argument and a block is given to this method,
739
+ # the `params` argument is leading and the block will _not_ be called.
740
+ #
741
+ # @example Set parameters
742
+ # Appsignal.set_params("param1" => "value1")
743
+ #
744
+ # @example Calling `set_params` multiple times will only keep the last call
745
+ # Appsignal.set_params("param1" => "value1")
746
+ # Appsignal.set_params("param2" => "value2")
747
+ # # The parameters are: { "param2" => "value2" }
748
+ #
749
+ # @example Calling `set_params` with a block
750
+ # Appsignal.set_params do
751
+ # # Some slow code to parse parameters
752
+ # JSON.parse('{"param1": "value1"}')
753
+ # end
754
+ # # The parameters are: { "param1" => "value1" }
755
+ #
756
+ # @example Calling `set_params` with a parameter and a block
757
+ # Appsignal.set_params("argument" => "argument value") do
758
+ # # Some slow code to parse parameters
759
+ # JSON.parse('{"param1": "value1"}')
760
+ # end
761
+ # # The parameters are: { "argument" => "argument value" }
762
+ #
763
+ # @since 3.10.0
764
+ # @param params [Hash] The parameters to set on the transaction.
765
+ # @yield This block is called when the transaction is sampled. The block's
766
+ # return value will become the new parameters.
767
+ # @see https://docs.appsignal.com/guides/custom-data/sample-data.html
768
+ # Sample data guide
769
+ # @see https://docs.appsignal.com/guides/filter-data/filter-parameters.html
770
+ # Parameter filtering guide
771
+ # @see Transaction#set_params
772
+ # @return [void]
773
+ def set_params(params = nil, &block)
774
+ return unless active?
775
+ return unless Appsignal::Transaction.current?
776
+
777
+ transaction = Appsignal::Transaction.current
778
+ transaction.set_params(params, &block)
779
+ end
780
+
781
+ # Set session data on the current transaction.
782
+ #
783
+ # Session data is automatically set by most of our integrations. It
784
+ # should not be necessary to call this method unless you want to report
785
+ # different session data.
786
+ #
787
+ # To filter session data, see our session data filtering guide.
788
+ #
789
+ # When this method is called multiple times, it will overwrite the
790
+ # previously set value.
791
+ #
792
+ # A block can be given to this method to defer the fetching and parsing
793
+ # of the session data until and only when the transaction is sampled.
794
+ #
795
+ # When both the `session_data` argument and a block is given to this
796
+ # method, the `session_data` argument is leading and the block will _not_
797
+ # be called.
798
+ #
799
+ # @example Set session data
800
+ # Appsignal.set_session_data("data" => "value")
801
+ #
802
+ # @example Calling `set_session_data` multiple times will only keep the last call
803
+ # Appsignal.set_session_data("data1" => "value1")
804
+ # Appsignal.set_session_data("data2" => "value2")
805
+ # # The session data is: { "data2" => "value2" }
806
+ #
807
+ # @example Calling `set_session_data` with a block
808
+ # Appsignal.set_session_data do
809
+ # # Some slow code to parse session data
810
+ # JSON.parse('{"data": "value"}')
811
+ # end
812
+ # # The session data is: { "data" => "value" }
813
+ #
814
+ # @example Calling `set_session_data` with a session_data argument and a block
815
+ # Appsignal.set_session_data("argument" => "argument value") do
816
+ # # Some slow code to parse session data
817
+ # JSON.parse('{"data": "value"}')
818
+ # end
819
+ # # The session data is: { "argument" => "argument value" }
820
+ #
821
+ # @since 3.11.0
822
+ # @param session_data [Hash] The session data to set on the transaction.
823
+ # @yield This block is called when the transaction is sampled. The block's
824
+ # return value will become the new session data.
825
+ # @see https://docs.appsignal.com/guides/custom-data/sample-data.html
826
+ # Sample data guide
827
+ # @see https://docs.appsignal.com/guides/filter-data/filter-session-data.html
828
+ # Session data filtering guide
829
+ # @see Transaction#set_session_data
830
+ # @return [void]
831
+ def set_session_data(session_data = nil, &block)
832
+ return unless active?
833
+ return unless Appsignal::Transaction.current?
834
+
835
+ transaction = Appsignal::Transaction.current
836
+ transaction.set_session_data(session_data, &block)
837
+ end
838
+
839
+ # Set request headers on the current transaction.
840
+ #
841
+ # Request headers are automatically set by most of our integrations. It
842
+ # should not be necessary to call this method unless you want to report
843
+ # different request headers.
844
+ #
845
+ # To filter request headers, see our session data filtering guide.
846
+ #
847
+ # When this method is called multiple times, it will overwrite the
848
+ # previously set value.
849
+ #
850
+ # A block can be given to this method to defer the fetching and parsing
851
+ # of the request headers until and only when the transaction is sampled.
852
+ #
853
+ # When both the `request_headers` argument and a block is given to this
854
+ # method, the `request_headers` argument is leading and the block will
855
+ # _not_ be called.
856
+ #
857
+ # @example Set request headers
858
+ # Appsignal.set_headers(
859
+ # "PATH_INFO" => "/some-path",
860
+ # "HTTP_USER_AGENT" => "Firefox"
861
+ # )
862
+ #
863
+ # @example Calling `set_headers` multiple times will only keep the last call
864
+ # Appsignal.set_headers("PATH_INFO" => "/some-path")
865
+ # Appsignal.set_headers("HTTP_USER_AGENT" => "Firefox")
866
+ # # The request headers are: { "HTTP_USER_AGENT" => "Firefox" }
867
+ #
868
+ # @example Calling `set_headers` with a block
869
+ # Appsignal.set_headers do
870
+ # # Some slow code to parse request headers
871
+ # JSON.parse('{"PATH_INFO": "/some-path"}')
872
+ # end
873
+ # # The session data is: { "PATH_INFO" => "/some-path" }
874
+ #
875
+ # @example Calling `set_headers` with a headers argument and a block
876
+ # Appsignal.set_headers("PATH_INFO" => "/some-path") do
877
+ # # Some slow code to parse session data
878
+ # JSON.parse('{"PATH_INFO": "/block-path"}')
879
+ # end
880
+ # # The session data is: { "PATH_INFO" => "/some-path" }
881
+ #
882
+ # @since 3.11.0
883
+ # @param headers [Hash] The request headers to set on the transaction.
884
+ # @yield This block is called when the transaction is sampled. The block's
885
+ # return value will become the new request headers.
886
+ # @see https://docs.appsignal.com/guides/custom-data/sample-data.html
887
+ # Sample data guide
888
+ # @see https://docs.appsignal.com/guides/filter-data/filter-headers.html
889
+ # Request headers filtering guide
890
+ # @see Transaction#set_headers
891
+ # @return [void]
892
+ def set_headers(headers = nil, &block)
893
+ return unless active?
894
+ return unless Appsignal::Transaction.current?
895
+
896
+ transaction = Appsignal::Transaction.current
897
+ transaction.set_headers(headers, &block)
898
+ end
448
899
 
449
900
  # Add breadcrumbs to the transaction.
450
901
  #
@@ -537,14 +988,11 @@ module Appsignal
537
988
  name,
538
989
  title = nil,
539
990
  body = nil,
540
- body_format = Appsignal::EventFormatter::DEFAULT
991
+ body_format = Appsignal::EventFormatter::DEFAULT,
992
+ &block
541
993
  )
542
- Appsignal::Transaction.current.start_event
543
- yield if block_given?
544
- ensure
545
- Appsignal::Transaction
546
- .current
547
- .finish_event(name, title, body, body_format)
994
+ Appsignal::Transaction.current
995
+ .instrument(name, title, body, body_format, &block)
548
996
  end
549
997
 
550
998
  # Instrumentation helper for SQL queries.
@@ -587,22 +1035,48 @@ module Appsignal
587
1035
  )
588
1036
  end
589
1037
 
590
- # Convenience method for skipping instrumentations around a block of code.
1038
+ # Convenience method for ignoring instrumentation events in a block of
1039
+ # code.
1040
+ #
1041
+ # - This helper ignores events, like those created
1042
+ # `Appsignal.instrument`, within this block.
1043
+ # This includes custom instrumentation and events recorded by AppSignal
1044
+ # integrations for requests, database queries, view rendering, etc.
1045
+ # - The time spent in the block is still reported on the transaction.
1046
+ # - Errors and metrics are reported from within this block.
591
1047
  #
592
1048
  # @example
593
- # Appsignal.without_instrumentation do
1049
+ # Appsignal.instrument "my_event.my_group" do
594
1050
  # # Complex code here
595
1051
  # end
1052
+ # Appsignal.ignore_instrumentation_events do
1053
+ # Appsignal.instrument "my_ignored_event.my_ignored_group" do
1054
+ # # Complex code here
1055
+ # end
1056
+ # end
1057
+ #
1058
+ # # Only the "my_event.my_group" instrumentation event is reported.
596
1059
  #
597
1060
  # @yield block of code that shouldn't be instrumented.
598
1061
  # @return [Object] Returns the return value of the block.
599
- # @since 0.8.7
600
- def without_instrumentation
1062
+ # @since 3.10.0
1063
+ # @see https://docs.appsignal.com/ruby/instrumentation/ignore-instrumentation.html
1064
+ # Ignore instrumentation guide
1065
+ def ignore_instrumentation_events
601
1066
  Appsignal::Transaction.current&.pause!
602
1067
  yield
603
1068
  ensure
604
1069
  Appsignal::Transaction.current&.resume!
605
1070
  end
1071
+
1072
+ # @deprecated Use {.ignore_instrumentation_events} instead.
1073
+ # @since 0.8.7
1074
+ def without_instrumentation(&block)
1075
+ stdout_and_logger_warning \
1076
+ "The `Appsignal.without_instrumentation` helper is deprecated. " \
1077
+ "Please use `Appsignal.ignore_instrumentation_events` instead."
1078
+ ignore_instrumentation_events(&block)
1079
+ end
606
1080
  end
607
1081
  end
608
1082
  end
@@ -17,12 +17,13 @@ module Appsignal
17
17
  require "appsignal/integrations/action_cable"
18
18
  ActionCable::Channel::Base.prepend Appsignal::Integrations::ActionCableIntegration
19
19
 
20
- install_callbacks
20
+ install_subscribe_callback
21
+ install_unsubscribe_callback
21
22
  end
22
23
 
23
24
  private
24
25
 
25
- def install_callbacks
26
+ def install_subscribe_callback
26
27
  ActionCable::Channel::Base.set_callback :subscribe, :around,
27
28
  :prepend => true do |channel, inner|
28
29
  # The request is only the original websocket request
@@ -32,14 +33,11 @@ module Appsignal
32
33
  # in apps' test suites.
33
34
  env = connection.respond_to?(:env) ? connection.env : {}
34
35
  request = ActionDispatch::Request.new(env)
35
- env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
36
- request.request_id || SecureRandom.uuid
36
+ request_id = request.request_id || SecureRandom.uuid
37
+ env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||= request_id
37
38
 
38
- transaction = Appsignal::Transaction.create(
39
- env[Appsignal::Hooks::ActionCableHook::REQUEST_ID],
40
- Appsignal::Transaction::ACTION_CABLE,
41
- request
42
- )
39
+ transaction =
40
+ Appsignal::Transaction.create(Appsignal::Transaction::ACTION_CABLE)
43
41
 
44
42
  begin
45
43
  Appsignal.instrument "subscribed.action_cable" do
@@ -52,10 +50,16 @@ module Appsignal
52
50
  transaction.set_action_if_nil("#{channel.class}#subscribed")
53
51
  transaction.set_metadata("path", request.path)
54
52
  transaction.set_metadata("method", "websocket")
53
+ transaction.set_params_if_nil { request.params }
54
+ transaction.set_headers_if_nil { request.env }
55
+ transaction.set_session_data { request.session if request.respond_to? :session }
56
+ transaction.set_tags(:request_id => request_id) if request_id
55
57
  Appsignal::Transaction.complete_current!
56
58
  end
57
59
  end
60
+ end
58
61
 
62
+ def install_unsubscribe_callback
59
63
  ActionCable::Channel::Base.set_callback :unsubscribe, :around,
60
64
  :prepend => true do |channel, inner|
61
65
  # The request is only the original websocket request
@@ -65,14 +69,11 @@ module Appsignal
65
69
  # in apps' test suites.
66
70
  env = connection.respond_to?(:env) ? connection.env : {}
67
71
  request = ActionDispatch::Request.new(env)
68
- env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
69
- request.request_id || SecureRandom.uuid
72
+ request_id = request.request_id || SecureRandom.uuid
73
+ env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||= request_id
70
74
 
71
- transaction = Appsignal::Transaction.create(
72
- env[Appsignal::Hooks::ActionCableHook::REQUEST_ID],
73
- Appsignal::Transaction::ACTION_CABLE,
74
- request
75
- )
75
+ transaction =
76
+ Appsignal::Transaction.create(Appsignal::Transaction::ACTION_CABLE)
76
77
 
77
78
  begin
78
79
  Appsignal.instrument "unsubscribed.action_cable" do
@@ -85,6 +86,10 @@ module Appsignal
85
86
  transaction.set_action_if_nil("#{channel.class}#unsubscribed")
86
87
  transaction.set_metadata("path", request.path)
87
88
  transaction.set_metadata("method", "websocket")
89
+ transaction.set_params_if_nil { request.params }
90
+ transaction.set_headers_if_nil { request.env }
91
+ transaction.set_session_data { request.session if request.respond_to? :session }
92
+ transaction.set_tags(:request_id => request_id) if request_id
88
93
  Appsignal::Transaction.complete_current!
89
94
  end
90
95
  end