appsignal 4.10.0-java → 5.0.0.rc.1-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/CHANGELOG.md +42 -0
- data/Rakefile +87 -0
- data/appsignal.gemspec +8 -0
- data/build_matrix.yml +14 -1
- data/ext/appsignal_extension.c +14 -0
- data/ext/base.rb +8 -1
- data/lib/appsignal/backends.rb +55 -0
- data/lib/appsignal/config.rb +140 -1
- data/lib/appsignal/demo.rb +1 -1
- data/lib/appsignal/event_formatter/action_view/render_formatter.rb +34 -22
- data/lib/appsignal/event_formatter/active_job/perform_formatter.rb +35 -0
- data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +19 -0
- data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +27 -0
- data/lib/appsignal/event_formatter/recorded_elsewhere.rb +17 -0
- data/lib/appsignal/event_formatter/rom/sql_formatter.rb +24 -0
- data/lib/appsignal/event_formatter/sequel/sql_formatter.rb +5 -0
- data/lib/appsignal/event_formatter/view_component/render_formatter.rb +21 -10
- data/lib/appsignal/event_formatter.rb +78 -0
- data/lib/appsignal/extension.rb +4 -0
- data/lib/appsignal/helpers/instrumentation.rb +236 -20
- data/lib/appsignal/helpers/metrics.rb +3 -24
- data/lib/appsignal/hooks/action_cable.rb +18 -6
- data/lib/appsignal/hooks/active_job.rb +128 -39
- data/lib/appsignal/hooks/at_exit.rb +4 -1
- data/lib/appsignal/hooks/excon.rb +20 -0
- data/lib/appsignal/hooks/faraday.rb +16 -0
- data/lib/appsignal/hooks/http.rb +5 -0
- data/lib/appsignal/hooks/resque.rb +1 -1
- data/lib/appsignal/hooks/sequel.rb +32 -2
- data/lib/appsignal/hooks/shoryuken.rb +3 -3
- data/lib/appsignal/hooks/sidekiq.rb +1 -1
- data/lib/appsignal/integrations/action_cable.rb +5 -2
- data/lib/appsignal/integrations/active_support_notifications.rb +59 -14
- data/lib/appsignal/integrations/data_mapper.rb +14 -2
- data/lib/appsignal/integrations/delayed_job_plugin.rb +45 -5
- data/lib/appsignal/integrations/dry_monitor.rb +39 -15
- data/lib/appsignal/integrations/excon/appsignal_middleware.rb +21 -0
- data/lib/appsignal/integrations/excon.rb +52 -15
- data/lib/appsignal/integrations/faraday.rb +47 -12
- data/lib/appsignal/integrations/http.rb +43 -1
- data/lib/appsignal/integrations/mongo_ruby_driver.rb +73 -4
- data/lib/appsignal/integrations/net_http.rb +31 -2
- data/lib/appsignal/integrations/puma.rb +4 -1
- data/lib/appsignal/integrations/que.rb +224 -37
- data/lib/appsignal/integrations/railtie.rb +4 -1
- data/lib/appsignal/integrations/rake.rb +9 -3
- data/lib/appsignal/integrations/redis.rb +22 -1
- data/lib/appsignal/integrations/redis_client.rb +22 -1
- data/lib/appsignal/integrations/resque.rb +57 -10
- data/lib/appsignal/integrations/shoryuken.rb +140 -12
- data/lib/appsignal/integrations/sidekiq.rb +73 -16
- data/lib/appsignal/integrations/webmachine.rb +47 -4
- data/lib/appsignal/loaders/padrino.rb +2 -1
- data/lib/appsignal/logger/extension_backend.rb +24 -0
- data/lib/appsignal/logger/opentelemetry_backend.rb +66 -0
- data/lib/appsignal/logger.rb +13 -9
- data/lib/appsignal/metrics/extension_backend.rb +47 -0
- data/lib/appsignal/metrics/opentelemetry_backend.rb +89 -0
- data/lib/appsignal/opentelemetry/attributes.rb +31 -0
- data/lib/appsignal/opentelemetry/dependencies.rb +35 -0
- data/lib/appsignal/opentelemetry/error_type.rb +37 -0
- data/lib/appsignal/opentelemetry/http_client_request.rb +83 -0
- data/lib/appsignal/opentelemetry/http_method.rb +59 -0
- data/lib/appsignal/opentelemetry/http_response.rb +30 -0
- data/lib/appsignal/opentelemetry/http_server_request.rb +45 -0
- data/lib/appsignal/opentelemetry/messaging.rb +82 -0
- data/lib/appsignal/opentelemetry/rendering.rb +29 -0
- data/lib/appsignal/opentelemetry/sql_db_system.rb +89 -0
- data/lib/appsignal/opentelemetry.rb +337 -0
- data/lib/appsignal/rack/abstract_middleware.rb +63 -4
- data/lib/appsignal/rack/body_wrapper.rb +18 -5
- data/lib/appsignal/rack/event_handler.rb +36 -3
- data/lib/appsignal/rack/grape_middleware.rb +1 -0
- data/lib/appsignal/rack/hanami_middleware.rb +2 -1
- data/lib/appsignal/rack/instrumentation_middleware.rb +1 -0
- data/lib/appsignal/rack/rails_instrumentation.rb +1 -0
- data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -0
- data/lib/appsignal/rack.rb +39 -11
- data/lib/appsignal/sample_data.rb +4 -0
- data/lib/appsignal/transaction/base_backend.rb +107 -0
- data/lib/appsignal/transaction/extension_backend.rb +203 -0
- data/lib/appsignal/transaction/opentelemetry_backend.rb +796 -0
- data/lib/appsignal/transaction.rb +537 -144
- data/lib/appsignal/utils/stdout_and_logger_message.rb +9 -0
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +9 -0
- data/sig/appsignal.rbi +426 -37
- data/sig/appsignal.rbs +386 -27
- metadata +23 -1
|
@@ -2,21 +2,152 @@
|
|
|
2
2
|
|
|
3
3
|
module Appsignal
|
|
4
4
|
module Integrations
|
|
5
|
+
# @!visibility private
|
|
6
|
+
#
|
|
7
|
+
# Reads and writes W3C trace context the way OpenTelemetry's Que
|
|
8
|
+
# instrumentation does: as `"key:value"` strings in the job's tags array
|
|
9
|
+
# (the only carrier Que's enqueue API exposes). Collector mode only.
|
|
10
|
+
module QueTraceContext
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
# Que has no header map, so context rides in the tags array. OTel writes
|
|
14
|
+
# each header as a `"key:value"` tag; mirror that exact format.
|
|
15
|
+
module TagSetter
|
|
16
|
+
def self.set(carrier, key, value)
|
|
17
|
+
carrier << "#{key}:#{value}"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Que rejects jobs with too many or too-long tags, so injected context
|
|
22
|
+
# must stay within these or the enqueue would raise. Read the limits from
|
|
23
|
+
# Que when available, with the documented defaults as a fallback.
|
|
24
|
+
MAX_TAGS_COUNT =
|
|
25
|
+
defined?(::Que::Job::MAXIMUM_TAGS_COUNT) ? ::Que::Job::MAXIMUM_TAGS_COUNT : 5
|
|
26
|
+
MAX_TAG_LENGTH =
|
|
27
|
+
defined?(::Que::Job::MAXIMUM_TAG_LENGTH) ? ::Que::Job::MAXIMUM_TAG_LENGTH : 100
|
|
28
|
+
|
|
29
|
+
# Marks a job as one of a batch enqueued by `bulk_enqueue`, so the worker
|
|
30
|
+
# can tell the two enqueue paths apart. Que itself records nothing that
|
|
31
|
+
# distinguishes them: both insert paths write the same columns, and the
|
|
32
|
+
# job's `data` only ever holds its tags. So the enqueue side has to say so.
|
|
33
|
+
#
|
|
34
|
+
# This deliberately contains no colon. The trace context rides in the same
|
|
35
|
+
# tags array as `"key:value"` strings, so a reader that splits tags on the
|
|
36
|
+
# colon to rebuild the carrier drops this tag on its own. That keeps it out
|
|
37
|
+
# of the carrier for us and for OpenTelemetry's own Que instrumentation.
|
|
38
|
+
BULK_TAG = "appsignal.bulk_enqueue"
|
|
39
|
+
|
|
40
|
+
# Que only has tags from version 1.0 on, and they are the only carrier its
|
|
41
|
+
# enqueue API exposes. On Que 0.x there is nowhere to put the trace context
|
|
42
|
+
# that survives to the worker, so propagation is skipped there. Writing the
|
|
43
|
+
# context into the job's arguments instead would change the arguments the
|
|
44
|
+
# job is called with, which breaks the job.
|
|
45
|
+
TAGS_SUPPORTED = defined?(::Que::Job::MAXIMUM_TAGS_COUNT)
|
|
46
|
+
|
|
47
|
+
# Read the incoming context off the job's tags. Splits each `"key:value"`
|
|
48
|
+
# tag on the first colon back into a carrier hash, then extracts. Returns
|
|
49
|
+
# an `OpenTelemetry::Context`, or `nil` outside collector mode and on Que
|
|
50
|
+
# versions without tags.
|
|
51
|
+
def extract(tags)
|
|
52
|
+
return unless TAGS_SUPPORTED
|
|
53
|
+
|
|
54
|
+
Appsignal::OpenTelemetry.if_started do
|
|
55
|
+
carrier = Array(tags)
|
|
56
|
+
.map { |tag| tag.split(":", 2) }
|
|
57
|
+
.select { |pair| pair.size == 2 }
|
|
58
|
+
.to_h
|
|
59
|
+
::OpenTelemetry.propagation.extract(carrier)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Returns the tags array to enqueue the job with. In collector mode that is
|
|
64
|
+
# a copy with the current context injected, kept only if it still fits
|
|
65
|
+
# Que's limits: propagation is skipped rather than break the enqueue.
|
|
66
|
+
# Outside collector mode, and on Que versions without tags, the tags are
|
|
67
|
+
# returned unchanged.
|
|
68
|
+
#
|
|
69
|
+
# Pass `bulk` for a `bulk_enqueue` batch, which adds the bulk marker too.
|
|
70
|
+
# Both are kept or dropped together, because a batch missing its marker
|
|
71
|
+
# would parent every job in it to the one producer span.
|
|
72
|
+
def inject(tags, bulk: false)
|
|
73
|
+
original = Array(tags)
|
|
74
|
+
return original unless TAGS_SUPPORTED
|
|
75
|
+
|
|
76
|
+
injected = Appsignal::OpenTelemetry.if_started do
|
|
77
|
+
copy = original.dup
|
|
78
|
+
::OpenTelemetry.propagation.inject(copy, :setter => TagSetter)
|
|
79
|
+
# The marker has nothing to link back to without a trace context, so
|
|
80
|
+
# only add it when the context was actually injected. The propagator
|
|
81
|
+
# writes nothing when there is no valid span to propagate.
|
|
82
|
+
copy << BULK_TAG if bulk && copy.length > original.length
|
|
83
|
+
copy
|
|
84
|
+
end
|
|
85
|
+
return original if injected.nil? || !within_limits?(injected)
|
|
86
|
+
|
|
87
|
+
injected
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Whether the job being performed was enqueued as part of a batch, which
|
|
91
|
+
# the enqueue side records by adding `BULK_TAG` to the job's tags.
|
|
92
|
+
def bulk?(tags)
|
|
93
|
+
Array(tags).include?(BULK_TAG)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def within_limits?(tags)
|
|
97
|
+
tags.length <= MAX_TAGS_COUNT && tags.all? { |tag| tag.length <= MAX_TAG_LENGTH }
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
5
101
|
# @!visibility private
|
|
6
102
|
module QuePlugin
|
|
7
103
|
def _run(*args)
|
|
104
|
+
local_attrs = respond_to?(:que_attrs) ? que_attrs : attrs
|
|
105
|
+
tags = local_attrs.dig(:data, :tags)
|
|
106
|
+
|
|
107
|
+
# A job enqueued on its own is the only job its producer span produced, so
|
|
108
|
+
# it can be a child of that span as well as link to it. Every job in a
|
|
109
|
+
# batch shares one producer span, and a span can only have one parent, so
|
|
110
|
+
# parenting a batch would hang the whole batch off that single span. Only
|
|
111
|
+
# link those, which is what the OpenTelemetry messaging conventions ask
|
|
112
|
+
# for: they use links as the default, and allow the producer to be the
|
|
113
|
+
# parent only when it produced a single message.
|
|
114
|
+
relationship = QueTraceContext.bulk?(tags) ? :link : :both
|
|
115
|
+
|
|
116
|
+
# Read the incoming trace context off the job's tags so the transaction
|
|
117
|
+
# links back to the enqueuer. No-op outside collector mode.
|
|
8
118
|
transaction =
|
|
9
|
-
Appsignal::Transaction.create(
|
|
119
|
+
Appsignal::Transaction.create(
|
|
120
|
+
Appsignal::Transaction::BACKGROUND_JOB,
|
|
121
|
+
:opentelemetry_context => QueTraceContext.extract(tags),
|
|
122
|
+
:opentelemetry_scope => ["appsignal-ruby/que", Appsignal::VERSION],
|
|
123
|
+
:opentelemetry_kind => :consumer,
|
|
124
|
+
:opentelemetry_relationship => relationship
|
|
125
|
+
)
|
|
126
|
+
# Describes this span as a job being performed. The messaging system is
|
|
127
|
+
# what the trace timeline reads to recognize background job work, and
|
|
128
|
+
# `que` is the value OpenTelemetry's own Que instrumentation uses.
|
|
129
|
+
transaction.add_opentelemetry_attributes(
|
|
130
|
+
Appsignal::OpenTelemetry::Messaging
|
|
131
|
+
.perform_attributes("que", :destination => local_attrs[:queue])
|
|
132
|
+
)
|
|
10
133
|
|
|
11
134
|
begin
|
|
12
|
-
Appsignal.instrument(
|
|
135
|
+
Appsignal.instrument(
|
|
136
|
+
"perform_job.que",
|
|
137
|
+
:opentelemetry_scope => ["appsignal-ruby/que", Appsignal::VERSION]
|
|
138
|
+
) do
|
|
139
|
+
Appsignal::Transaction.current.add_opentelemetry_attributes(
|
|
140
|
+
Appsignal::OpenTelemetry::Messaging
|
|
141
|
+
.perform_attributes("que", :destination => local_attrs[:queue])
|
|
142
|
+
)
|
|
143
|
+
super
|
|
144
|
+
end
|
|
13
145
|
rescue Exception => error
|
|
14
146
|
transaction.set_error(error)
|
|
15
147
|
raise error
|
|
16
148
|
ensure
|
|
17
|
-
local_attrs = respond_to?(:que_attrs) ? que_attrs : attrs
|
|
18
149
|
transaction.set_action_if_nil("#{local_attrs[:job_class]}#run")
|
|
19
|
-
transaction.
|
|
150
|
+
transaction.add_function_parameters_if_nil do
|
|
20
151
|
{
|
|
21
152
|
:arguments => local_attrs[:args]
|
|
22
153
|
}.tap do |hash|
|
|
@@ -37,36 +168,91 @@ module Appsignal
|
|
|
37
168
|
|
|
38
169
|
# @!visibility private
|
|
39
170
|
#
|
|
40
|
-
# Prepended to `Que::Job`'s singleton so it
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
171
|
+
# Prepended to `Que::Job`'s singleton so it wraps enqueues. Records the
|
|
172
|
+
# enqueue as an AppSignal event (a producer span in collector mode), and in
|
|
173
|
+
# collector mode writes the current trace context onto the job's tags so the
|
|
174
|
+
# job that later performs links back to it. Like all AppSignal events, the
|
|
175
|
+
# enqueue only records when there's an active transaction; otherwise it's a
|
|
176
|
+
# transparent pass-through.
|
|
45
177
|
module QueClientPlugin
|
|
46
|
-
# The keyword arguments are captured into a single
|
|
47
|
-
# than declaring a `job_options:` keyword with a default, so that
|
|
48
|
-
#
|
|
49
|
-
# `
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
# Inside a `bulk_enqueue` block the batch is recorded once by the
|
|
57
|
-
# `bulk_enqueue` wrapper, so each inner enqueue is a pass-through to
|
|
58
|
-
# avoid recording an event per job.
|
|
178
|
+
# The keyword arguments are captured into a single `kwargs` hash, rather
|
|
179
|
+
# than declaring a `job_options:` keyword with a default, so that a
|
|
180
|
+
# keyword the caller did not pass is never forwarded to Que. See
|
|
181
|
+
# `forward_job_options` for why that matters.
|
|
182
|
+
def enqueue(*args, **kwargs)
|
|
183
|
+
# Inside a `bulk_enqueue` block the per-job enqueue must stay a
|
|
184
|
+
# pass-through: tags come from `bulk_enqueue`'s own `job_options` (Que
|
|
185
|
+
# raises if an inner enqueue passes them), and the batch's event and
|
|
186
|
+
# propagation are recorded once by the `bulk_enqueue` wrapper.
|
|
59
187
|
return super if Thread.current[:appsignal_que_bulk_enqueue]
|
|
60
188
|
|
|
61
|
-
|
|
62
|
-
# `enqueue.active_job` event, so skip recording it again here.
|
|
63
|
-
return super if Appsignal::Transaction.current? &&
|
|
64
|
-
Appsignal::Transaction.current.job_enqueue_events_suppressed?
|
|
189
|
+
job_options = kwargs[:job_options] || {}
|
|
65
190
|
|
|
66
191
|
# Resolve the job class the way Que does: an explicit `:job_class`, else
|
|
67
192
|
# the class `enqueue` was called on.
|
|
68
193
|
title = "enqueue #{job_options[:job_class] || name} job"
|
|
69
|
-
|
|
194
|
+
record_enqueue(job_options, "enqueue.que", title) do |merged|
|
|
195
|
+
super(*args, **forward_job_options(kwargs, merged))
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
private
|
|
200
|
+
|
|
201
|
+
# Builds the keyword arguments to enqueue the job with. Passes
|
|
202
|
+
# `job_options` on to Que only when the caller passed it, or when the
|
|
203
|
+
# trace context was injected into it. Adding a `job_options` keyword to a
|
|
204
|
+
# call that did not have one breaks Que 0.x: it does not recognise the
|
|
205
|
+
# keyword, so it stores it as an extra job argument, and the job then
|
|
206
|
+
# fails because it is called with one argument too many.
|
|
207
|
+
def forward_job_options(kwargs, job_options)
|
|
208
|
+
return kwargs unless kwargs.key?(:job_options) || job_options.any?
|
|
209
|
+
|
|
210
|
+
kwargs.merge(:job_options => job_options)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Records the enqueue as a producer event and, in collector mode, injects
|
|
214
|
+
# the current trace context into the job's tags so the job that later
|
|
215
|
+
# performs links back. Yields the (possibly tag-augmented) `job_options` to
|
|
216
|
+
# do the actual enqueue.
|
|
217
|
+
def record_enqueue(job_options, event_name, title, bulk: false)
|
|
218
|
+
# When enqueue instrumentation is disabled, drop the trace context along
|
|
219
|
+
# with the event, so yield the job options untouched. Without an enqueue
|
|
220
|
+
# event there is no producer span, so the context we would write is that
|
|
221
|
+
# of whatever span is current, such as the surrounding web request. The
|
|
222
|
+
# job that performs later would then link back to a span that is not a
|
|
223
|
+
# producer.
|
|
224
|
+
if Appsignal.config && !Appsignal.config[:enable_job_enqueue_instrumentation]
|
|
225
|
+
return yield job_options
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Under Active Job the enqueue is already recorded as an
|
|
229
|
+
# `enqueue.active_job` event, so skip recording it again here. The trace
|
|
230
|
+
# context is still injected so the performed job links back.
|
|
231
|
+
if Appsignal::Transaction.current? &&
|
|
232
|
+
Appsignal::Transaction.current.job_enqueue_events_suppressed?
|
|
233
|
+
return yield job_options_with_context(job_options, :bulk => bulk)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
Appsignal.instrument(
|
|
237
|
+
event_name,
|
|
238
|
+
title,
|
|
239
|
+
:opentelemetry_kind => :producer,
|
|
240
|
+
:opentelemetry_scope => ["appsignal-ruby/que", Appsignal::VERSION]
|
|
241
|
+
) do
|
|
242
|
+
Appsignal::Transaction.current.add_opentelemetry_attributes(
|
|
243
|
+
Appsignal::OpenTelemetry::Messaging
|
|
244
|
+
.enqueue_attributes("que", :destination => job_options[:queue])
|
|
245
|
+
)
|
|
246
|
+
yield job_options_with_context(job_options, :bulk => bulk)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# In collector mode, injects the current trace context into a copy of the
|
|
251
|
+
# job's tags and returns the tag-augmented `job_options`; a no-op that
|
|
252
|
+
# returns `job_options` unchanged outside collector mode.
|
|
253
|
+
def job_options_with_context(job_options, bulk: false)
|
|
254
|
+
tags = QueTraceContext.inject(job_options[:tags], :bulk => bulk)
|
|
255
|
+
tags.empty? ? job_options : job_options.merge(:tags => tags)
|
|
70
256
|
end
|
|
71
257
|
end
|
|
72
258
|
|
|
@@ -74,22 +260,23 @@ module Appsignal
|
|
|
74
260
|
#
|
|
75
261
|
# `bulk_enqueue` exists only on Que 2+, so this lives in its own module that
|
|
76
262
|
# the hook prepends only when Que has the method -- otherwise we'd define a
|
|
77
|
-
# `bulk_enqueue` on Que versions that have none. The whole batch
|
|
78
|
-
# single `bulk_enqueue.que` event
|
|
263
|
+
# `bulk_enqueue` on Que versions that have none. The whole batch shares one
|
|
264
|
+
# `job_options`, so it records a single `bulk_enqueue.que` producer event and
|
|
265
|
+
# the inner enqueues are pass-throughs.
|
|
79
266
|
module QueBulkClientPlugin
|
|
80
|
-
def bulk_enqueue(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
267
|
+
def bulk_enqueue(job_options: {}, **rest, &block)
|
|
268
|
+
record_enqueue(
|
|
269
|
+
job_options,
|
|
270
|
+
"bulk_enqueue.que",
|
|
271
|
+
bulk_enqueue_title(job_options),
|
|
272
|
+
:bulk => true
|
|
273
|
+
) do |merged|
|
|
87
274
|
# Flag the batch so the enqueues this block triggers pass through
|
|
88
275
|
# without recording, without reading Que's internal bulk state.
|
|
89
276
|
was_bulk = Thread.current[:appsignal_que_bulk_enqueue]
|
|
90
277
|
Thread.current[:appsignal_que_bulk_enqueue] = true
|
|
91
278
|
begin
|
|
92
|
-
super
|
|
279
|
+
super(:job_options => merged, **rest, &block)
|
|
93
280
|
ensure
|
|
94
281
|
Thread.current[:appsignal_que_bulk_enqueue] = was_bulk
|
|
95
282
|
end
|
|
@@ -96,7 +96,10 @@ module Appsignal
|
|
|
96
96
|
is_rails_runner = source == "application.runner.railties"
|
|
97
97
|
namespace, action_name, tags, custom_data = context_for(context.dup)
|
|
98
98
|
|
|
99
|
-
Appsignal.report_error(
|
|
99
|
+
Appsignal.report_error(
|
|
100
|
+
error,
|
|
101
|
+
:opentelemetry_scope => ["appsignal-ruby/rails", Appsignal::VERSION]
|
|
102
|
+
) do |transaction|
|
|
100
103
|
if namespace
|
|
101
104
|
transaction.set_namespace(namespace)
|
|
102
105
|
elsif is_rails_runner
|
|
@@ -22,7 +22,10 @@ module Appsignal
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
begin
|
|
25
|
-
Appsignal.instrument
|
|
25
|
+
Appsignal.instrument(
|
|
26
|
+
"task.rake",
|
|
27
|
+
:opentelemetry_scope => ["appsignal-ruby/rake", Appsignal::VERSION]
|
|
28
|
+
) do
|
|
26
29
|
super
|
|
27
30
|
end
|
|
28
31
|
rescue Exception => error
|
|
@@ -38,7 +41,7 @@ module Appsignal
|
|
|
38
41
|
params, _ = args
|
|
39
42
|
params = params.to_hash if params.respond_to?(:to_hash)
|
|
40
43
|
transaction.set_action(name)
|
|
41
|
-
transaction.
|
|
44
|
+
transaction.add_request_payload_if_nil(params)
|
|
42
45
|
transaction.complete
|
|
43
46
|
end
|
|
44
47
|
end
|
|
@@ -47,7 +50,10 @@ module Appsignal
|
|
|
47
50
|
private
|
|
48
51
|
|
|
49
52
|
def _appsignal_create_transaction
|
|
50
|
-
Appsignal::Transaction.create(
|
|
53
|
+
Appsignal::Transaction.create(
|
|
54
|
+
"rake",
|
|
55
|
+
:opentelemetry_scope => ["appsignal-ruby/rake", Appsignal::VERSION]
|
|
56
|
+
)
|
|
51
57
|
end
|
|
52
58
|
end
|
|
53
59
|
|
|
@@ -11,8 +11,29 @@ module Appsignal
|
|
|
11
11
|
else
|
|
12
12
|
"#{command[0]}#{" ?" * (command.size - 1)}"
|
|
13
13
|
end
|
|
14
|
+
operation_name = command[0].to_s
|
|
14
15
|
|
|
15
|
-
Appsignal.instrument
|
|
16
|
+
Appsignal.instrument(
|
|
17
|
+
"query.redis",
|
|
18
|
+
id,
|
|
19
|
+
sanitized_command,
|
|
20
|
+
:opentelemetry_kind => :client,
|
|
21
|
+
:opentelemetry_scope => ["appsignal-ruby/redis", Appsignal::VERSION]
|
|
22
|
+
) do
|
|
23
|
+
# Names the datastore this span talks to, which is what the trace
|
|
24
|
+
# timeline reads to recognize a cache call, along with the command and
|
|
25
|
+
# the database it ran against.
|
|
26
|
+
#
|
|
27
|
+
# The command stays in the event body rather than moving to
|
|
28
|
+
# `db.query.text`: it is sanitized here already, and the collector
|
|
29
|
+
# sanitizes `db.query.text` again for Redis, which would mangle it.
|
|
30
|
+
Appsignal::Transaction.current.add_opentelemetry_attributes(
|
|
31
|
+
{
|
|
32
|
+
"db.system.name" => "redis",
|
|
33
|
+
"db.operation.name" => (operation_name unless operation_name.empty?),
|
|
34
|
+
"db.namespace" => (db.to_s if respond_to?(:db) && db)
|
|
35
|
+
}.compact
|
|
36
|
+
)
|
|
16
37
|
super
|
|
17
38
|
end
|
|
18
39
|
end
|
|
@@ -11,8 +11,29 @@ module Appsignal
|
|
|
11
11
|
else
|
|
12
12
|
"#{command[0]}#{" ?" * (command.size - 1)}"
|
|
13
13
|
end
|
|
14
|
+
operation_name = command[0].to_s
|
|
14
15
|
|
|
15
|
-
Appsignal.instrument
|
|
16
|
+
Appsignal.instrument(
|
|
17
|
+
"query.redis",
|
|
18
|
+
@config.id,
|
|
19
|
+
sanitized_command,
|
|
20
|
+
:opentelemetry_kind => :client,
|
|
21
|
+
:opentelemetry_scope => ["appsignal-ruby/redis_client", Appsignal::VERSION]
|
|
22
|
+
) do
|
|
23
|
+
# Names the datastore this span talks to, which is what the trace
|
|
24
|
+
# timeline reads to recognize a cache call, along with the command and
|
|
25
|
+
# the database it ran against.
|
|
26
|
+
#
|
|
27
|
+
# The command stays in the event body rather than moving to
|
|
28
|
+
# `db.query.text`: it is sanitized here already, and the collector
|
|
29
|
+
# sanitizes `db.query.text` again for Redis, which would mangle it.
|
|
30
|
+
Appsignal::Transaction.current.add_opentelemetry_attributes(
|
|
31
|
+
{
|
|
32
|
+
"db.system.name" => "redis",
|
|
33
|
+
"db.operation.name" => (operation_name unless operation_name.empty?),
|
|
34
|
+
"db.namespace" => (@config.db.to_s if @config.respond_to?(:db) && @config.db)
|
|
35
|
+
}.compact
|
|
36
|
+
)
|
|
16
37
|
super
|
|
17
38
|
end
|
|
18
39
|
end
|
|
@@ -5,9 +5,31 @@ module Appsignal
|
|
|
5
5
|
# @!visibility private
|
|
6
6
|
module ResqueIntegration
|
|
7
7
|
def perform
|
|
8
|
-
transaction
|
|
8
|
+
# Read trace context off the job so the transaction links back to the
|
|
9
|
+
# enqueuer. No-op outside collector mode.
|
|
10
|
+
transaction = Appsignal::Transaction.create(
|
|
11
|
+
Appsignal::Transaction::BACKGROUND_JOB,
|
|
12
|
+
:opentelemetry_context => Appsignal::OpenTelemetry.extract_job_context(payload),
|
|
13
|
+
:opentelemetry_scope => ["appsignal-ruby/resque", Appsignal::VERSION],
|
|
14
|
+
:opentelemetry_kind => :consumer,
|
|
15
|
+
:opentelemetry_relationship => :both
|
|
16
|
+
)
|
|
17
|
+
# Describes this span as a job being performed. The messaging system is
|
|
18
|
+
# what the trace timeline reads to recognize background job work, and
|
|
19
|
+
# `resque` is the value OpenTelemetry's own Resque instrumentation uses.
|
|
20
|
+
transaction.add_opentelemetry_attributes(
|
|
21
|
+
Appsignal::OpenTelemetry::Messaging
|
|
22
|
+
.perform_attributes("resque", :destination => queue)
|
|
23
|
+
)
|
|
9
24
|
|
|
10
|
-
Appsignal.instrument
|
|
25
|
+
Appsignal.instrument(
|
|
26
|
+
"perform.resque",
|
|
27
|
+
:opentelemetry_scope => ["appsignal-ruby/resque", Appsignal::VERSION]
|
|
28
|
+
) do
|
|
29
|
+
Appsignal::Transaction.current.add_opentelemetry_attributes(
|
|
30
|
+
Appsignal::OpenTelemetry::Messaging
|
|
31
|
+
.perform_attributes("resque", :destination => queue)
|
|
32
|
+
)
|
|
11
33
|
super
|
|
12
34
|
end
|
|
13
35
|
rescue Exception => exception
|
|
@@ -16,7 +38,7 @@ module Appsignal
|
|
|
16
38
|
ensure
|
|
17
39
|
if transaction
|
|
18
40
|
transaction.set_action_if_nil("#{payload["class"]}#perform")
|
|
19
|
-
transaction.
|
|
41
|
+
transaction.add_function_parameters_if_nil { ResqueHelpers.arguments(payload) }
|
|
20
42
|
transaction.add_tags("queue" => queue)
|
|
21
43
|
|
|
22
44
|
Appsignal::Transaction.complete_current!
|
|
@@ -25,8 +47,10 @@ module Appsignal
|
|
|
25
47
|
end
|
|
26
48
|
end
|
|
27
49
|
|
|
28
|
-
# Wraps `Resque.push` to record an `enqueue.resque` event so the
|
|
29
|
-
# shows up under the active transaction
|
|
50
|
+
# Wraps `Resque.push` to record an `enqueue.resque` event so the
|
|
51
|
+
# enqueue shows up under the active transaction (both modes), and in
|
|
52
|
+
# collector mode writes the trace context onto the job hash so the job that
|
|
53
|
+
# later performs links back to it.
|
|
30
54
|
#
|
|
31
55
|
# Like all AppSignal events, this only records when there's an active
|
|
32
56
|
# transaction (e.g. enqueuing from within a web request or another job).
|
|
@@ -34,13 +58,36 @@ module Appsignal
|
|
|
34
58
|
#
|
|
35
59
|
# @!visibility private
|
|
36
60
|
module ResquePushIntegration
|
|
37
|
-
def push(
|
|
61
|
+
def push(queue, item)
|
|
62
|
+
# When enqueue instrumentation is disabled, drop the trace context along
|
|
63
|
+
# with the event. Without an enqueue event there is no producer span, so
|
|
64
|
+
# the context we would write is that of whatever span is current, such as
|
|
65
|
+
# the surrounding web request. The job that performs later would then
|
|
66
|
+
# link back to a span that is not a producer.
|
|
67
|
+
return super if Appsignal.config && !Appsignal.config[:enable_job_enqueue_instrumentation]
|
|
68
|
+
|
|
38
69
|
# Under Active Job the enqueue is already recorded as an
|
|
39
|
-
# `enqueue.active_job` event, so skip recording it again here.
|
|
40
|
-
|
|
41
|
-
|
|
70
|
+
# `enqueue.active_job` event, so skip recording it again here. The trace
|
|
71
|
+
# context is still injected so the performed job links back.
|
|
72
|
+
if Appsignal::Transaction.current? &&
|
|
73
|
+
Appsignal::Transaction.current.job_enqueue_events_suppressed?
|
|
74
|
+
Appsignal::OpenTelemetry.inject_context(item)
|
|
75
|
+
return super
|
|
76
|
+
end
|
|
42
77
|
|
|
43
|
-
Appsignal.instrument(
|
|
78
|
+
Appsignal.instrument(
|
|
79
|
+
"enqueue.resque",
|
|
80
|
+
"enqueue #{item["class"]} job",
|
|
81
|
+
:opentelemetry_kind => :producer,
|
|
82
|
+
:opentelemetry_scope => ["appsignal-ruby/resque", Appsignal::VERSION]
|
|
83
|
+
) do
|
|
84
|
+
Appsignal::Transaction.current.add_opentelemetry_attributes(
|
|
85
|
+
Appsignal::OpenTelemetry::Messaging
|
|
86
|
+
.enqueue_attributes("resque", :destination => queue)
|
|
87
|
+
)
|
|
88
|
+
Appsignal::OpenTelemetry.inject_context(item)
|
|
89
|
+
super
|
|
90
|
+
end
|
|
44
91
|
end
|
|
45
92
|
end
|
|
46
93
|
|