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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +88 -0
- data/Gemfile +1 -0
- data/benchmark.rake +99 -42
- data/lib/appsignal/cli/demo.rb +0 -1
- data/lib/appsignal/config.rb +54 -98
- data/lib/appsignal/demo.rb +15 -20
- data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
- data/lib/appsignal/event_formatter.rb +3 -2
- data/lib/appsignal/helpers/instrumentation.rb +331 -19
- data/lib/appsignal/hooks/action_cable.rb +21 -16
- data/lib/appsignal/hooks/active_job.rb +14 -8
- data/lib/appsignal/hooks/delayed_job.rb +1 -1
- data/lib/appsignal/hooks/shoryuken.rb +3 -63
- data/lib/appsignal/integrations/action_cable.rb +5 -7
- data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
- data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
- data/lib/appsignal/integrations/data_mapper.rb +1 -0
- data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
- data/lib/appsignal/integrations/dry_monitor.rb +1 -0
- data/lib/appsignal/integrations/excon.rb +1 -0
- data/lib/appsignal/integrations/http.rb +1 -0
- data/lib/appsignal/integrations/net_http.rb +1 -0
- data/lib/appsignal/integrations/object.rb +6 -0
- data/lib/appsignal/integrations/que.rb +13 -20
- data/lib/appsignal/integrations/railtie.rb +1 -1
- data/lib/appsignal/integrations/rake.rb +1 -5
- data/lib/appsignal/integrations/redis.rb +1 -0
- data/lib/appsignal/integrations/redis_client.rb +1 -0
- data/lib/appsignal/integrations/resque.rb +2 -5
- data/lib/appsignal/integrations/shoryuken.rb +75 -0
- data/lib/appsignal/integrations/sidekiq.rb +7 -15
- data/lib/appsignal/integrations/unicorn.rb +1 -0
- data/lib/appsignal/integrations/webmachine.rb +2 -5
- data/lib/appsignal/logger.rb +7 -3
- data/lib/appsignal/probes/helpers.rb +1 -0
- data/lib/appsignal/probes/mri.rb +1 -0
- data/lib/appsignal/probes/sidekiq.rb +1 -0
- data/lib/appsignal/probes.rb +3 -0
- data/lib/appsignal/rack/abstract_middleware.rb +18 -12
- data/lib/appsignal/rack/event_handler.rb +39 -8
- data/lib/appsignal/rack/generic_instrumentation.rb +1 -0
- data/lib/appsignal/rack/grape_middleware.rb +2 -1
- data/lib/appsignal/rack/streaming_listener.rb +1 -0
- data/lib/appsignal/rack.rb +29 -0
- data/lib/appsignal/span.rb +1 -0
- data/lib/appsignal/transaction.rb +308 -101
- data/lib/appsignal/utils/data.rb +0 -1
- data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
- data/lib/appsignal/utils/integration_logger.rb +0 -13
- data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
- data/lib/appsignal/utils/json.rb +0 -1
- data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
- data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
- data/lib/appsignal/utils.rb +6 -0
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +6 -5
- data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
- data/spec/lib/appsignal/config_spec.rb +138 -43
- data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
- data/spec/lib/appsignal/hooks/activejob_spec.rb +9 -0
- data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
- data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
- data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
- data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
- data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
- data/spec/lib/appsignal/integrations/sidekiq_spec.rb +4 -4
- data/spec/lib/appsignal/integrations/webmachine_spec.rb +13 -1
- data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +48 -3
- data/spec/lib/appsignal/rack/event_handler_spec.rb +81 -10
- data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
- data/spec/lib/appsignal/rack_spec.rb +63 -0
- data/spec/lib/appsignal/transaction_spec.rb +1634 -1071
- data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
- data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
- data/spec/lib/appsignal_spec.rb +323 -10
- data/spec/support/helpers/transaction_helpers.rb +44 -20
- data/spec/support/matchers/transaction.rb +15 -1
- data/spec/support/testing.rb +1 -1
- 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::
|
|
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
|
-
|
|
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,
|
|
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
|
|
705
|
-
|
|
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
|
|
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.
|
|
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
|
|
762
|
-
|
|
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
|
-
|
|
20
|
+
install_subscribe_callback
|
|
21
|
+
install_unsubscribe_callback
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
private
|
|
24
25
|
|
|
25
|
-
def
|
|
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
|
-
|
|
36
|
-
|
|
36
|
+
request_id = request.request_id || SecureRandom.uuid
|
|
37
|
+
env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||= request_id
|
|
37
38
|
|
|
38
|
-
transaction =
|
|
39
|
-
|
|
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
|
-
|
|
69
|
-
|
|
72
|
+
request_id = request.request_id || SecureRandom.uuid
|
|
73
|
+
env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||= request_id
|
|
70
74
|
|
|
71
|
-
transaction =
|
|
72
|
-
|
|
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::
|
|
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::
|
|
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
|
-
|
|
11
|
-
|
|
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
|