appsignal 3.10.0 → 3.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +197 -0
- data/Gemfile +1 -0
- data/Rakefile +1 -1
- data/benchmark.rake +99 -42
- data/lib/appsignal/cli/demo.rb +0 -1
- data/lib/appsignal/cli/diagnose.rb +1 -1
- data/lib/appsignal/config.rb +204 -130
- data/lib/appsignal/demo.rb +16 -26
- 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/grape.rb +7 -0
- data/lib/appsignal/integrations/hanami.rb +8 -43
- 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/padrino.rb +8 -73
- data/lib/appsignal/integrations/que.rb +13 -20
- data/lib/appsignal/integrations/railtie.rb +36 -14
- 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/sinatra.rb +8 -19
- data/lib/appsignal/integrations/unicorn.rb +1 -0
- data/lib/appsignal/integrations/webmachine.rb +2 -5
- data/lib/appsignal/loaders/grape.rb +13 -0
- data/lib/appsignal/loaders/hanami.rb +40 -0
- data/lib/appsignal/loaders/padrino.rb +68 -0
- data/lib/appsignal/loaders/sinatra.rb +24 -0
- data/lib/appsignal/loaders.rb +92 -0
- 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 +20 -13
- data/lib/appsignal/rack/event_handler.rb +44 -13
- 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 +35 -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 +169 -14
- data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
- data/spec/lib/appsignal/cli/demo_spec.rb +0 -1
- data/spec/lib/appsignal/cli/diagnose/paths_spec.rb +1 -1
- data/spec/lib/appsignal/cli/diagnose_spec.rb +0 -1
- data/spec/lib/appsignal/config_spec.rb +291 -44
- data/spec/lib/appsignal/demo_spec.rb +1 -2
- data/spec/lib/appsignal/environment_spec.rb +4 -2
- data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
- data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +3 -6
- data/spec/lib/appsignal/hooks/activejob_spec.rb +12 -3
- data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
- data/spec/lib/appsignal/hooks/dry_monitor_spec.rb +4 -7
- data/spec/lib/appsignal/hooks/excon_spec.rb +3 -6
- data/spec/lib/appsignal/hooks/gvl_spec.rb +2 -2
- data/spec/lib/appsignal/hooks/http_spec.rb +1 -3
- data/spec/lib/appsignal/hooks/net_http_spec.rb +1 -1
- data/spec/lib/appsignal/hooks/redis_client_spec.rb +5 -8
- data/spec/lib/appsignal/hooks/redis_spec.rb +3 -6
- data/spec/lib/appsignal/hooks/resque_spec.rb +1 -1
- data/spec/lib/appsignal/hooks/sequel_spec.rb +3 -5
- data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +1 -1
- data/spec/lib/appsignal/hooks/webmachine_spec.rb +1 -1
- data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
- data/spec/lib/appsignal/integrations/grape_spec.rb +36 -0
- data/spec/lib/appsignal/integrations/hanami_spec.rb +9 -178
- data/spec/lib/appsignal/integrations/http_spec.rb +1 -5
- data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +4 -2
- data/spec/lib/appsignal/integrations/net_http_spec.rb +1 -1
- data/spec/lib/appsignal/integrations/object_spec.rb +1 -3
- data/spec/lib/appsignal/integrations/padrino_spec.rb +8 -330
- data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
- data/spec/lib/appsignal/integrations/railtie_spec.rb +275 -191
- data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
- data/spec/lib/appsignal/integrations/sidekiq_spec.rb +15 -13
- data/spec/lib/appsignal/integrations/sinatra_spec.rb +9 -104
- data/spec/lib/appsignal/integrations/webmachine_spec.rb +13 -1
- data/spec/lib/appsignal/loaders/grape_spec.rb +12 -0
- data/spec/lib/appsignal/loaders/hanami_spec.rb +95 -0
- data/spec/lib/appsignal/loaders/padrino_spec.rb +277 -0
- data/spec/lib/appsignal/loaders/sinatra_spec.rb +47 -0
- data/spec/lib/appsignal/loaders_spec.rb +137 -0
- data/spec/lib/appsignal/probes/sidekiq_spec.rb +1 -1
- data/spec/lib/appsignal/probes_spec.rb +6 -5
- data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +51 -5
- data/spec/lib/appsignal/rack/event_handler_spec.rb +114 -10
- data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +1 -1
- data/spec/lib/appsignal/rack/grape_middleware_spec.rb +2 -35
- data/spec/lib/appsignal/rack/hanami_middleware_spec.rb +1 -1
- data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
- data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +3 -3
- data/spec/lib/appsignal/rack_spec.rb +63 -0
- data/spec/lib/appsignal/span_spec.rb +1 -3
- data/spec/lib/appsignal/transaction_spec.rb +1640 -1075
- 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 +601 -36
- data/spec/lib/puma/appsignal_spec.rb +0 -3
- data/spec/spec_helper.rb +5 -4
- data/spec/support/helpers/config_helpers.rb +2 -1
- data/spec/support/helpers/loader_helper.rb +21 -0
- data/spec/support/helpers/transaction_helpers.rb +44 -20
- data/spec/support/matchers/transaction.rb +15 -1
- data/spec/support/stubs/appsignal/loaders/loader_stub.rb +7 -0
- data/spec/support/testing.rb +47 -1
- metadata +19 -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
|