appsignal 3.10.0-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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +88 -0
  4. data/Gemfile +1 -0
  5. data/benchmark.rake +99 -42
  6. data/lib/appsignal/cli/demo.rb +0 -1
  7. data/lib/appsignal/config.rb +54 -98
  8. data/lib/appsignal/demo.rb +15 -20
  9. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
  10. data/lib/appsignal/event_formatter.rb +3 -2
  11. data/lib/appsignal/helpers/instrumentation.rb +331 -19
  12. data/lib/appsignal/hooks/action_cable.rb +21 -16
  13. data/lib/appsignal/hooks/active_job.rb +14 -8
  14. data/lib/appsignal/hooks/delayed_job.rb +1 -1
  15. data/lib/appsignal/hooks/shoryuken.rb +3 -63
  16. data/lib/appsignal/integrations/action_cable.rb +5 -7
  17. data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
  18. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
  19. data/lib/appsignal/integrations/data_mapper.rb +1 -0
  20. data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
  21. data/lib/appsignal/integrations/dry_monitor.rb +1 -0
  22. data/lib/appsignal/integrations/excon.rb +1 -0
  23. data/lib/appsignal/integrations/http.rb +1 -0
  24. data/lib/appsignal/integrations/net_http.rb +1 -0
  25. data/lib/appsignal/integrations/object.rb +6 -0
  26. data/lib/appsignal/integrations/que.rb +13 -20
  27. data/lib/appsignal/integrations/railtie.rb +1 -1
  28. data/lib/appsignal/integrations/rake.rb +1 -5
  29. data/lib/appsignal/integrations/redis.rb +1 -0
  30. data/lib/appsignal/integrations/redis_client.rb +1 -0
  31. data/lib/appsignal/integrations/resque.rb +2 -5
  32. data/lib/appsignal/integrations/shoryuken.rb +75 -0
  33. data/lib/appsignal/integrations/sidekiq.rb +7 -15
  34. data/lib/appsignal/integrations/unicorn.rb +1 -0
  35. data/lib/appsignal/integrations/webmachine.rb +2 -5
  36. data/lib/appsignal/logger.rb +7 -3
  37. data/lib/appsignal/probes/helpers.rb +1 -0
  38. data/lib/appsignal/probes/mri.rb +1 -0
  39. data/lib/appsignal/probes/sidekiq.rb +1 -0
  40. data/lib/appsignal/probes.rb +3 -0
  41. data/lib/appsignal/rack/abstract_middleware.rb +18 -12
  42. data/lib/appsignal/rack/event_handler.rb +39 -8
  43. data/lib/appsignal/rack/generic_instrumentation.rb +1 -0
  44. data/lib/appsignal/rack/grape_middleware.rb +2 -1
  45. data/lib/appsignal/rack/streaming_listener.rb +1 -0
  46. data/lib/appsignal/rack.rb +29 -0
  47. data/lib/appsignal/span.rb +1 -0
  48. data/lib/appsignal/transaction.rb +308 -101
  49. data/lib/appsignal/utils/data.rb +0 -1
  50. data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
  51. data/lib/appsignal/utils/integration_logger.rb +0 -13
  52. data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
  53. data/lib/appsignal/utils/json.rb +0 -1
  54. data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
  55. data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
  56. data/lib/appsignal/utils.rb +6 -0
  57. data/lib/appsignal/version.rb +1 -1
  58. data/lib/appsignal.rb +6 -5
  59. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  60. data/spec/lib/appsignal/config_spec.rb +138 -43
  61. data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
  62. data/spec/lib/appsignal/hooks/activejob_spec.rb +9 -0
  63. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
  64. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
  65. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
  66. data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
  67. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
  68. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +4 -4
  69. data/spec/lib/appsignal/integrations/webmachine_spec.rb +13 -1
  70. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +48 -3
  71. data/spec/lib/appsignal/rack/event_handler_spec.rb +81 -10
  72. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
  73. data/spec/lib/appsignal/rack_spec.rb +63 -0
  74. data/spec/lib/appsignal/transaction_spec.rb +1634 -1071
  75. data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
  76. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
  77. data/spec/lib/appsignal_spec.rb +323 -10
  78. data/spec/support/helpers/transaction_helpers.rb +44 -20
  79. data/spec/support/matchers/transaction.rb +15 -1
  80. data/spec/support/testing.rb +1 -1
  81. metadata +6 -2
@@ -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")
@@ -235,8 +407,7 @@ module Appsignal
235
407
  end
236
408
  transaction = Appsignal::Transaction.new(
237
409
  SecureRandom.uuid,
238
- namespace || Appsignal::Transaction::HTTP_REQUEST,
239
- Appsignal::Transaction::GenericRequest.new({})
410
+ namespace || Appsignal::Transaction::HTTP_REQUEST
240
411
  )
241
412
  transaction.set_tags(tags) if tags
242
413
  transaction.set_error(error)
@@ -390,8 +561,7 @@ module Appsignal
390
561
  else
391
562
  Appsignal::Transaction.new(
392
563
  SecureRandom.uuid,
393
- Appsignal::Transaction::HTTP_REQUEST,
394
- Appsignal::Transaction::GenericRequest.new({})
564
+ Appsignal::Transaction::HTTP_REQUEST
395
565
  )
396
566
  end
397
567
 
@@ -565,8 +735,8 @@ module Appsignal
565
735
  # A block can be given to this method to defer the fetching and parsing
566
736
  # of the parameters until and only when the transaction is sampled.
567
737
  #
568
- # When both the `params` and a block is given to this method, the
569
- # `params` argument is leading and the block will _not_ be called.
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.
570
740
  #
571
741
  # @example Set parameters
572
742
  # Appsignal.set_params("param1" => "value1")
@@ -608,6 +778,125 @@ module Appsignal
608
778
  transaction.set_params(params, &block)
609
779
  end
610
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
899
+
611
900
  # Add breadcrumbs to the transaction.
612
901
  #
613
902
  # Breadcrumbs can be used to trace what path a user has taken
@@ -699,14 +988,11 @@ module Appsignal
699
988
  name,
700
989
  title = nil,
701
990
  body = nil,
702
- body_format = Appsignal::EventFormatter::DEFAULT
991
+ body_format = Appsignal::EventFormatter::DEFAULT,
992
+ &block
703
993
  )
704
- Appsignal::Transaction.current.start_event
705
- yield if block_given?
706
- ensure
707
- Appsignal::Transaction
708
- .current
709
- .finish_event(name, title, body, body_format)
994
+ Appsignal::Transaction.current
995
+ .instrument(name, title, body, body_format, &block)
710
996
  end
711
997
 
712
998
  # Instrumentation helper for SQL queries.
@@ -749,22 +1035,48 @@ module Appsignal
749
1035
  )
750
1036
  end
751
1037
 
752
- # 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.
753
1047
  #
754
1048
  # @example
755
- # Appsignal.without_instrumentation do
1049
+ # Appsignal.instrument "my_event.my_group" do
756
1050
  # # Complex code here
757
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.
758
1059
  #
759
1060
  # @yield block of code that shouldn't be instrumented.
760
1061
  # @return [Object] Returns the return value of the block.
761
- # @since 0.8.7
762
- 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
763
1066
  Appsignal::Transaction.current&.pause!
764
1067
  yield
765
1068
  ensure
766
1069
  Appsignal::Transaction.current&.resume!
767
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
768
1080
  end
769
1081
  end
770
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
@@ -54,20 +54,13 @@ module Appsignal
54
54
  # we do for Sidekiq.
55
55
  #
56
56
  # Prefer job_id from provider, instead of ActiveJob's internal ID.
57
- Appsignal::Transaction.create(
58
- job["provider_job_id"] || job["job_id"],
59
- Appsignal::Transaction::BACKGROUND_JOB,
60
- Appsignal::Transaction::GenericRequest.new({})
61
- )
57
+ Appsignal::Transaction.create(Appsignal::Transaction::BACKGROUND_JOB)
62
58
  end
63
59
 
64
60
  if transaction
65
61
  transaction.set_params_if_nil(job["arguments"])
66
62
 
67
63
  transaction_tags = ActiveJobHelpers.transaction_tags_for(job)
68
- transaction_tags["active_job_id"] = job["job_id"]
69
- provider_job_id = job["provider_job_id"]
70
- transaction_tags[:provider_job_id] = provider_job_id if provider_job_id
71
64
  transaction.set_tags(transaction_tags)
72
65
 
73
66
  transaction.set_action(ActiveJobHelpers.action_name(job))
@@ -154,12 +147,25 @@ module Appsignal
154
147
 
155
148
  def self.transaction_tags_for(job)
156
149
  tags = {}
150
+
157
151
  queue = job["queue_name"]
158
152
  tags[:queue] = queue if queue
153
+
159
154
  priority = job["priority"]
160
155
  tags[:priority] = priority if priority
156
+
161
157
  executions = job["executions"]
162
158
  tags[:executions] = executions.to_i + 1 if executions
159
+
160
+ job_id = job["job_id"]
161
+ tags[:active_job_id] = job_id
162
+
163
+ provider_job_id = job["provider_job_id"]
164
+ tags[:provider_job_id] = provider_job_id if provider_job_id
165
+
166
+ request_id = provider_job_id || job_id
167
+ tags[:request_id] = request_id if request_id
168
+
163
169
  tags
164
170
  end
165
171
 
@@ -14,7 +14,7 @@ module Appsignal
14
14
  # The DJ plugin is a subclass of Delayed::Plugin, so we can only
15
15
  # require this code if we're actually installing.
16
16
  require "appsignal/integrations/delayed_job_plugin"
17
- ::Delayed::Worker.plugins << Appsignal::Hooks::DelayedJobPlugin
17
+ ::Delayed::Worker.plugins << Appsignal::Integrations::DelayedJobPlugin
18
18
  end
19
19
  end
20
20
  end
@@ -3,68 +3,6 @@
3
3
  module Appsignal
4
4
  class Hooks
5
5
  # @api private
6
- class ShoryukenMiddleware
7
- def call(worker_instance, queue, sqs_msg, body, &block)
8
- batch = sqs_msg.is_a?(Array)
9
- attributes =
10
- if batch
11
- # We can't instrument batched message separately, the `yield` will
12
- # perform all the batched messages.
13
- # To provide somewhat useful metadata, Get first message based on
14
- # SentTimestamp, and use its attributes as metadata for the
15
- # transaction. We can't combine them all because then they would
16
- # overwrite each other and the last message (in an sorted order)
17
- # would be used as the source of the metadata. With the
18
- # oldest/first message at least some useful information is stored
19
- # such as the first received time and the number of retries for the
20
- # first message. The newer message should have lower values and
21
- # timestamps in their metadata.
22
- first_msg = sqs_msg.min do |a, b|
23
- a.attributes["SentTimestamp"].to_i <=> b.attributes["SentTimestamp"].to_i
24
- end
25
- # Add batch => true metadata so people can recognize when a
26
- # transaction is about a batch of messages.
27
- first_msg.attributes.merge(:batch => true)
28
- else
29
- sqs_msg.attributes.merge(:message_id => sqs_msg.message_id)
30
- end
31
- metadata = { :queue => queue }.merge(attributes)
32
- options = {
33
- :class => worker_instance.class.name,
34
- :method => "perform",
35
- :metadata => metadata
36
- }
37
-
38
- args =
39
- if batch
40
- bodies = {}
41
- sqs_msg.each_with_index do |msg, index|
42
- # Store all separate bodies on a hash with the key being the
43
- # message_id
44
- bodies[msg.message_id] = body[index]
45
- end
46
- bodies
47
- else
48
- case body
49
- when Hash
50
- body
51
- else
52
- { :params => body }
53
- end
54
- end
55
- options[:params] = Appsignal::Utils::HashSanitizer.sanitize(
56
- args,
57
- Appsignal.config[:filter_parameters]
58
- )
59
-
60
- if attributes.key?("SentTimestamp")
61
- options[:queue_start] = Time.at(attributes["SentTimestamp"].to_i / 1000)
62
- end
63
-
64
- Appsignal.monitor_transaction("perform_job.shoryuken", options, &block)
65
- end
66
- end
67
-
68
6
  class ShoryukenHook < Appsignal::Hooks::Hook
69
7
  register :shoryuken
70
8
 
@@ -73,9 +11,11 @@ module Appsignal
73
11
  end
74
12
 
75
13
  def install
14
+ require "appsignal/integrations/shoryuken"
15
+
76
16
  ::Shoryuken.configure_server do |config|
77
17
  config.server_middleware do |chain|
78
- chain.add Appsignal::Hooks::ShoryukenMiddleware
18
+ chain.add Appsignal::Integrations::ShoryukenMiddleware
79
19
  end
80
20
  end
81
21
  end
@@ -2,19 +2,16 @@
2
2
 
3
3
  module Appsignal
4
4
  module Integrations
5
+ # @api private
5
6
  module ActionCableIntegration
6
7
  def perform_action(*args, &block)
7
8
  # The request is only the original websocket request
8
9
  env = connection.env
9
10
  request = ActionDispatch::Request.new(env)
10
- env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
11
- request.request_id || SecureRandom.uuid
11
+ request_id = request.request_id || SecureRandom.uuid
12
+ env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||= request_id
12
13
 
13
- transaction = Appsignal::Transaction.create(
14
- env[Appsignal::Hooks::ActionCableHook::REQUEST_ID],
15
- Appsignal::Transaction::ACTION_CABLE,
16
- request
17
- )
14
+ transaction = Appsignal::Transaction.create(Appsignal::Transaction::ACTION_CABLE)
18
15
 
19
16
  begin
20
17
  super
@@ -26,6 +23,7 @@ module Appsignal
26
23
  transaction.set_action_if_nil("#{self.class}##{args.first["action"]}")
27
24
  transaction.set_metadata("path", request.path)
28
25
  transaction.set_metadata("method", "websocket")
26
+ transaction.set_tags(:request_id => request_id) if request_id
29
27
  Appsignal::Transaction.complete_current!
30
28
  end
31
29
  end