appsignal 4.2.0 → 4.5.17
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 +443 -0
- data/README.md +0 -3
- data/Rakefile +1 -1
- data/appsignal.gemspec +2 -1
- data/build_matrix.yml +33 -0
- data/ext/agent.rb +27 -27
- data/ext/appsignal_extension.c +90 -73
- data/ext/base.rb +3 -1
- data/lib/appsignal/check_in/event.rb +55 -0
- data/lib/appsignal/check_in/scheduler.rb +8 -2
- data/lib/appsignal/check_in.rb +9 -8
- data/lib/appsignal/config.rb +36 -15
- data/lib/appsignal/custom_marker.rb +72 -0
- data/lib/appsignal/environment.rb +1 -0
- data/lib/appsignal/event_formatter/action_view/render_formatter.rb +4 -6
- data/lib/appsignal/event_formatter/view_component/render_formatter.rb +4 -6
- data/lib/appsignal/helpers/instrumentation.rb +5 -0
- data/lib/appsignal/hooks/active_job.rb +25 -5
- data/lib/appsignal/hooks/at_exit.rb +18 -4
- data/lib/appsignal/hooks/ownership.rb +44 -0
- data/lib/appsignal/hooks.rb +1 -0
- data/lib/appsignal/integrations/capistrano/appsignal.cap +4 -8
- data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +8 -11
- data/lib/appsignal/integrations/http.rb +2 -1
- data/lib/appsignal/integrations/ownership.rb +51 -0
- data/lib/appsignal/integrations/rake.rb +14 -2
- data/lib/appsignal/integrations/sidekiq.rb +14 -3
- data/lib/appsignal/internal_errors.rb +19 -0
- data/lib/appsignal/logger.rb +121 -69
- data/lib/appsignal/marker.rb +1 -1
- data/lib/appsignal/probes/sidekiq.rb +5 -1
- data/lib/appsignal/rack/body_wrapper.rb +1 -1
- data/lib/appsignal/rack/event_handler.rb +7 -5
- data/lib/appsignal/transaction.rb +91 -13
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +82 -11
- data/resources/cacert.pem +164 -87
- metadata +8 -4
@@ -58,6 +58,12 @@ module Appsignal
|
|
58
58
|
error_message failed_at jid retried_at retry wrapped
|
59
59
|
].freeze
|
60
60
|
|
61
|
+
def self.sidekiq8?
|
62
|
+
return false unless ::Sidekiq.respond_to?(:gem_version)
|
63
|
+
|
64
|
+
@sidekiq8 ||= ::Sidekiq.gem_version >= Gem::Version.new("8.0.0")
|
65
|
+
end
|
66
|
+
|
61
67
|
def call(_worker, item, _queue, &block)
|
62
68
|
job_status = nil
|
63
69
|
transaction = Appsignal::Transaction.create(Appsignal::Transaction::BACKGROUND_JOB)
|
@@ -74,7 +80,14 @@ module Appsignal
|
|
74
80
|
ensure
|
75
81
|
if transaction
|
76
82
|
transaction.add_params_if_nil { parse_arguments(item) }
|
77
|
-
|
83
|
+
enqueued_at = item["enqueued_at"]
|
84
|
+
queue_start =
|
85
|
+
if self.class.sidekiq8?
|
86
|
+
enqueued_at.to_i # Sidekiq 8 stores it as epoc milliseconds
|
87
|
+
else
|
88
|
+
# Convert seconds to milliseconds for Sidekiq 7 and older
|
89
|
+
(enqueued_at.to_f * 1000.0).to_i
|
90
|
+
end
|
78
91
|
transaction.set_queue_start(queue_start)
|
79
92
|
transaction.add_tags(:request_id => item["jid"])
|
80
93
|
Appsignal::Transaction.complete_current! unless exception
|
@@ -143,8 +156,6 @@ module Appsignal
|
|
143
156
|
safe_load(args[0], args) do |_, _, arg|
|
144
157
|
arg
|
145
158
|
end
|
146
|
-
when "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
|
147
|
-
nil # Set in the ActiveJob integration
|
148
159
|
else
|
149
160
|
# Sidekiq Enterprise argument encryption.
|
150
161
|
# More information: https://github.com/mperham/sidekiq/wiki/Ent-Encryption
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Appsignal
|
4
|
+
# @api private
|
5
|
+
class InternalError < StandardError; end
|
6
|
+
|
7
|
+
# @api private
|
8
|
+
class NotStartedError < InternalError
|
9
|
+
MESSAGE = <<~MESSAGE
|
10
|
+
The AppSignal Ruby gem was not started!
|
11
|
+
|
12
|
+
This error was raised by calling `Appsignal.check_if_started!`
|
13
|
+
MESSAGE
|
14
|
+
|
15
|
+
def message
|
16
|
+
MESSAGE
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/appsignal/logger.rb
CHANGED
@@ -9,6 +9,41 @@ module Appsignal
|
|
9
9
|
# @see https://docs.appsignal.com/logging/platforms/integrations/ruby.html
|
10
10
|
# AppSignal Ruby logging documentation.
|
11
11
|
class Logger < ::Logger
|
12
|
+
# A wrapper for a block that ensures it is only called once.
|
13
|
+
# If called again, it will return the result of the first call.
|
14
|
+
# This is useful for ensuring that a block is not executed multiple
|
15
|
+
# times when it is broadcasted to multiple loggers.
|
16
|
+
class BlockOnce
|
17
|
+
def initialize(&block)
|
18
|
+
@block = block
|
19
|
+
@called = false
|
20
|
+
@success = nil
|
21
|
+
@result = nil
|
22
|
+
@error = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def call(*args, **kwargs)
|
26
|
+
if @called
|
27
|
+
return @result if @success
|
28
|
+
|
29
|
+
raise @error
|
30
|
+
end
|
31
|
+
|
32
|
+
@called = true
|
33
|
+
@result = @block.call(*args, **kwargs)
|
34
|
+
@success = true
|
35
|
+
@result
|
36
|
+
rescue StandardError => e
|
37
|
+
@success = false
|
38
|
+
@error = e
|
39
|
+
raise @error
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_proc
|
43
|
+
method(:call).to_proc
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
12
47
|
PLAINTEXT = 0
|
13
48
|
LOGFMT = 1
|
14
49
|
JSON = 2
|
@@ -34,45 +69,75 @@ module Appsignal
|
|
34
69
|
|
35
70
|
@group = group
|
36
71
|
@level = level
|
72
|
+
@silenced = false
|
37
73
|
@format = format
|
38
74
|
@mutex = Mutex.new
|
39
75
|
@default_attributes = attributes
|
76
|
+
@appsignal_attributes = attributes
|
77
|
+
@loggers = []
|
78
|
+
end
|
79
|
+
|
80
|
+
# When a formatter is set on the logger (e.g. when wrapping the logger in
|
81
|
+
# `ActiveSupport::TaggedLogging`) we want to set that formatter on all the
|
82
|
+
# loggers that are being broadcasted to.
|
83
|
+
def formatter=(formatter)
|
84
|
+
super
|
85
|
+
@loggers.each { |logger| logger.formatter = formatter }
|
40
86
|
end
|
41
87
|
|
42
88
|
# We support the various methods in the Ruby
|
43
89
|
# logger class by supplying this method.
|
44
90
|
# @api private
|
45
|
-
def add(severity, message = nil, group = nil)
|
91
|
+
def add(severity, message = nil, group = nil, &block) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
92
|
+
# If we do not need to broadcast to any loggers and the severity is
|
93
|
+
# below the log level, we can return early.
|
46
94
|
severity ||= UNKNOWN
|
47
|
-
return true if severity < level
|
95
|
+
return true if severity < level && @loggers.empty?
|
96
|
+
|
97
|
+
# If the logger is silenced, we do not log *or broadcast* messages
|
98
|
+
# below the log level.
|
99
|
+
return true if @silenced && severity < @level
|
48
100
|
|
101
|
+
# Ensure that the block is only run once, even if several loggers
|
102
|
+
# are being broadcasted to.
|
103
|
+
block = BlockOnce.new(&block) unless block.nil?
|
104
|
+
|
105
|
+
# If the group is not set, we use the default group.
|
49
106
|
group = @group if group.nil?
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
107
|
+
|
108
|
+
did_not_log = true
|
109
|
+
|
110
|
+
@loggers.each do |logger|
|
111
|
+
# Loggers should return true if they did *not* log the message.
|
112
|
+
# If any of the broadcasted loggers logs the message, that counts
|
113
|
+
# as having logged the message.
|
114
|
+
did_not_log &&= logger.add(severity, message, group, &block)
|
115
|
+
rescue
|
116
|
+
nil
|
57
117
|
end
|
58
|
-
return if message.nil?
|
59
118
|
|
60
|
-
|
119
|
+
# If the severity is below the log level, we do not log the message.
|
120
|
+
return did_not_log if severity < level
|
61
121
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
122
|
+
message = block.call if block && message.nil?
|
123
|
+
|
124
|
+
return if message.nil?
|
125
|
+
|
126
|
+
if message.is_a?(Exception)
|
127
|
+
message = "#{message.class}: #{message.message} (#{message.backtrace[0]})"
|
67
128
|
end
|
68
129
|
|
130
|
+
message = formatter.call(severity, Time.now, group, message) if formatter
|
131
|
+
|
69
132
|
Appsignal::Extension.log(
|
70
133
|
group,
|
71
134
|
SEVERITY_MAP.fetch(severity, 0),
|
72
135
|
@format,
|
73
|
-
message,
|
136
|
+
message.to_s,
|
74
137
|
Appsignal::Utils::Data.generate(appsignal_attributes)
|
75
138
|
)
|
139
|
+
|
140
|
+
false
|
76
141
|
end
|
77
142
|
alias log add
|
78
143
|
|
@@ -80,96 +145,83 @@ module Appsignal
|
|
80
145
|
# @param message Message to log
|
81
146
|
# @param attributes Attributes to tag the log with
|
82
147
|
# @return [void]
|
83
|
-
def debug(message = nil, attributes = {})
|
84
|
-
|
85
|
-
|
86
|
-
message = yield if message.nil? && block_given?
|
87
|
-
return if message.nil?
|
88
|
-
|
89
|
-
add_with_attributes(DEBUG, message, @group, attributes)
|
148
|
+
def debug(message = nil, attributes = {}, &block)
|
149
|
+
add_with_attributes(DEBUG, message, @group, attributes, &block)
|
90
150
|
end
|
91
151
|
|
92
152
|
# Log an info level message
|
93
153
|
# @param message Message to log
|
94
154
|
# @param attributes Attributes to tag the log with
|
95
155
|
# @return [void]
|
96
|
-
def info(message = nil, attributes = {})
|
97
|
-
|
98
|
-
|
99
|
-
message = yield if message.nil? && block_given?
|
100
|
-
return if message.nil?
|
101
|
-
|
102
|
-
add_with_attributes(INFO, message, @group, attributes)
|
156
|
+
def info(message = nil, attributes = {}, &block)
|
157
|
+
add_with_attributes(INFO, message, @group, attributes, &block)
|
103
158
|
end
|
104
159
|
|
105
160
|
# Log a warn level message
|
106
161
|
# @param message Message to log
|
107
162
|
# @param attributes Attributes to tag the log with
|
108
163
|
# @return [void]
|
109
|
-
def warn(message = nil, attributes = {})
|
110
|
-
|
111
|
-
|
112
|
-
message = yield if message.nil? && block_given?
|
113
|
-
return if message.nil?
|
114
|
-
|
115
|
-
add_with_attributes(WARN, message, @group, attributes)
|
164
|
+
def warn(message = nil, attributes = {}, &block)
|
165
|
+
add_with_attributes(WARN, message, @group, attributes, &block)
|
116
166
|
end
|
117
167
|
|
118
168
|
# Log an error level message
|
119
169
|
# @param message Message to log
|
120
170
|
# @param attributes Attributes to tag the log with
|
121
171
|
# @return [void]
|
122
|
-
def error(message = nil, attributes = {})
|
123
|
-
|
124
|
-
|
125
|
-
message = yield if message.nil? && block_given?
|
126
|
-
return if message.nil?
|
127
|
-
|
128
|
-
message = "#{message.class}: #{message.message}" if message.is_a?(Exception)
|
129
|
-
|
130
|
-
add_with_attributes(ERROR, message, @group, attributes)
|
172
|
+
def error(message = nil, attributes = {}, &block)
|
173
|
+
add_with_attributes(ERROR, message, @group, attributes, &block)
|
131
174
|
end
|
132
175
|
|
133
176
|
# Log a fatal level message
|
134
177
|
# @param message Message to log
|
135
178
|
# @param attributes Attributes to tag the log with
|
136
179
|
# @return [void]
|
137
|
-
def fatal(message = nil, attributes = {})
|
138
|
-
|
139
|
-
|
140
|
-
message = yield if message.nil? && block_given?
|
141
|
-
return if message.nil?
|
180
|
+
def fatal(message = nil, attributes = {}, &block)
|
181
|
+
add_with_attributes(FATAL, message, @group, attributes, &block)
|
182
|
+
end
|
142
183
|
|
143
|
-
|
184
|
+
# Log an info level message
|
185
|
+
#
|
186
|
+
# Returns the number of characters written.
|
187
|
+
#
|
188
|
+
# @param message Message to log
|
189
|
+
# @return [Integer]
|
190
|
+
def <<(message)
|
191
|
+
info(message)
|
192
|
+
message.length
|
144
193
|
end
|
145
194
|
|
146
195
|
# When using ActiveSupport::TaggedLogging without the broadcast feature,
|
147
196
|
# the passed logger is required to respond to the `silence` method.
|
148
|
-
# In our case it behaves as the broadcast feature of the Rails logger, but
|
149
|
-
# we don't have to check if the parent logger has the `silence` method defined
|
150
|
-
# as our logger directly inherits from Ruby base logger.
|
151
197
|
#
|
152
|
-
#
|
198
|
+
# Reference links:
|
153
199
|
#
|
154
200
|
# - https://github.com/rails/rails/blob/e11ebc04cfbe41c06cdfb70ee5a9fdbbd98bb263/activesupport/lib/active_support/logger.rb#L60-L76
|
155
|
-
# - https://github.com/rails/rails/blob/
|
156
|
-
def silence(
|
157
|
-
|
201
|
+
# - https://github.com/rails/rails/blob/e11ebc04cfbe41c06cdfb70ee5a9fdbbd98bb263/activesupport/lib/active_support/logger_silence.rb
|
202
|
+
def silence(severity = ERROR, &block)
|
203
|
+
previous_level = @level
|
204
|
+
@level = severity
|
205
|
+
@silenced = true
|
206
|
+
block.call(self)
|
207
|
+
ensure
|
208
|
+
@level = previous_level
|
209
|
+
@silenced = false
|
210
|
+
end
|
211
|
+
|
212
|
+
def broadcast_to(logger)
|
213
|
+
@loggers << logger
|
158
214
|
end
|
159
215
|
|
160
216
|
private
|
161
217
|
|
162
|
-
attr_reader :default_attributes
|
218
|
+
attr_reader :default_attributes, :appsignal_attributes
|
163
219
|
|
164
|
-
def add_with_attributes(severity, message, group, attributes)
|
165
|
-
|
166
|
-
add(severity, message, group)
|
220
|
+
def add_with_attributes(severity, message, group, attributes, &block)
|
221
|
+
@appsignal_attributes = default_attributes.merge(attributes)
|
222
|
+
add(severity, message, group, &block)
|
167
223
|
ensure
|
168
|
-
|
169
|
-
end
|
170
|
-
|
171
|
-
def appsignal_attributes
|
172
|
-
Thread.current.fetch(:appsignal_logger_attributes, {})
|
224
|
+
@appsignal_attributes = default_attributes
|
173
225
|
end
|
174
226
|
end
|
175
227
|
end
|
data/lib/appsignal/marker.rb
CHANGED
@@ -49,7 +49,7 @@ module Appsignal
|
|
49
49
|
# @return [void]
|
50
50
|
def transmit
|
51
51
|
transmitter = Transmitter.new(ACTION, config)
|
52
|
-
puts "Notifying AppSignal of deploy with
|
52
|
+
puts "Notifying AppSignal of '#{config.env}' deploy with " \
|
53
53
|
"revision: #{marker_data[:revision]}, user: #{marker_data[:user]}"
|
54
54
|
|
55
55
|
response = transmitter.transmit(marker_data)
|
@@ -59,7 +59,11 @@ module Appsignal
|
|
59
59
|
is_sidekiq7 = self.class.sidekiq7_and_greater?
|
60
60
|
@adapter = is_sidekiq7 ? Sidekiq7Adapter : Sidekiq6Adapter
|
61
61
|
|
62
|
-
|
62
|
+
unless config.empty?
|
63
|
+
formatted_config =
|
64
|
+
config.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
|
65
|
+
config_string = " with config: #{formatted_config}"
|
66
|
+
end
|
63
67
|
Appsignal.internal_logger.debug("Initializing Sidekiq probe#{config_string}")
|
64
68
|
require "sidekiq/api"
|
65
69
|
end
|
@@ -4,7 +4,7 @@ module Appsignal
|
|
4
4
|
module Rack
|
5
5
|
# @api private
|
6
6
|
class BodyWrapper
|
7
|
-
IGNORED_ERRORS = [Errno::EPIPE].freeze
|
7
|
+
IGNORED_ERRORS = [Errno::EPIPE, Errno::ECONNRESET].freeze
|
8
8
|
|
9
9
|
def self.wrap(original_body, appsignal_transaction)
|
10
10
|
# The logic of how Rack treats a response body differs based on which methods
|
@@ -59,6 +59,7 @@ module Appsignal
|
|
59
59
|
return unless request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
|
60
60
|
|
61
61
|
transaction = Appsignal::Transaction.create(Appsignal::Transaction::HTTP_REQUEST)
|
62
|
+
transaction.start_event
|
62
63
|
request.env[APPSIGNAL_TRANSACTION] = transaction
|
63
64
|
|
64
65
|
request.env[RACK_AFTER_REPLY] ||= []
|
@@ -67,7 +68,7 @@ module Appsignal
|
|
67
68
|
|
68
69
|
Appsignal::Rack::EventHandler
|
69
70
|
.safe_execution("Appsignal::Rack::EventHandler's after_reply") do
|
70
|
-
transaction.finish_event("process_request.rack", "", "")
|
71
|
+
transaction.finish_event("process_request.rack", "callback: after_reply", "")
|
71
72
|
queue_start = Appsignal::Rack::Utils.queue_start_from(request.env)
|
72
73
|
transaction.set_queue_start(queue_start) if queue_start
|
73
74
|
end
|
@@ -81,9 +82,9 @@ module Appsignal
|
|
81
82
|
# just a fallback if that doesn't get called.
|
82
83
|
#
|
83
84
|
# One such scenario is when a Puma "lowlevel_error" occurs.
|
84
|
-
|
85
|
+
transaction.complete
|
86
|
+
Appsignal::Transaction.clear_current_transaction!
|
85
87
|
end
|
86
|
-
transaction.start_event
|
87
88
|
end
|
88
89
|
end
|
89
90
|
|
@@ -111,7 +112,7 @@ module Appsignal
|
|
111
112
|
return unless transaction
|
112
113
|
|
113
114
|
self.class.safe_execution("Appsignal::Rack::EventHandler#on_finish") do
|
114
|
-
transaction.finish_event("process_request.rack", "", "")
|
115
|
+
transaction.finish_event("process_request.rack", "callback: on_finish", "")
|
115
116
|
transaction.add_params_if_nil { request.params }
|
116
117
|
transaction.add_headers_if_nil { request.env }
|
117
118
|
transaction.add_session_data_if_nil do
|
@@ -138,7 +139,8 @@ module Appsignal
|
|
138
139
|
|
139
140
|
# Make sure the current transaction is always closed when the request
|
140
141
|
# is finished
|
141
|
-
|
142
|
+
transaction.complete
|
143
|
+
Appsignal::Transaction.clear_current_transaction!
|
142
144
|
end
|
143
145
|
|
144
146
|
private
|
@@ -27,23 +27,62 @@ module Appsignal
|
|
27
27
|
# @param namespace [String] Namespace of the to be created transaction.
|
28
28
|
# @return [Transaction]
|
29
29
|
def create(namespace)
|
30
|
-
#
|
30
|
+
# Reset the transaction if it was already completed but not cleared
|
31
|
+
if Thread.current[:appsignal_transaction]&.completed?
|
32
|
+
Thread.current[:appsignal_transaction] = nil
|
33
|
+
end
|
34
|
+
|
31
35
|
if Thread.current[:appsignal_transaction].nil?
|
32
36
|
# If not, start a new transaction
|
33
37
|
set_current_transaction(Appsignal::Transaction.new(namespace))
|
34
38
|
else
|
39
|
+
transaction = current
|
35
40
|
# Otherwise, log the issue about trying to start another transaction
|
36
41
|
Appsignal.internal_logger.warn(
|
37
42
|
"Trying to start new transaction, but a transaction " \
|
38
|
-
"with id '#{
|
39
|
-
"Using transaction '#{
|
43
|
+
"with id '#{transaction.transaction_id}' is already running. " \
|
44
|
+
"Using transaction '#{transaction.transaction_id}'."
|
40
45
|
)
|
41
46
|
|
42
47
|
# And return the current transaction instead
|
43
|
-
|
48
|
+
transaction
|
44
49
|
end
|
45
50
|
end
|
46
51
|
|
52
|
+
# @api private
|
53
|
+
# @return [Array<Proc>]
|
54
|
+
# Add a block, if given, to be executed after a transaction is created.
|
55
|
+
# The block will be called with the transaction as an argument.
|
56
|
+
# Returns the array of blocks that will be executed after a transaction
|
57
|
+
# is created.
|
58
|
+
def after_create(&block)
|
59
|
+
@after_create ||= Set.new
|
60
|
+
|
61
|
+
return @after_create if block.nil?
|
62
|
+
|
63
|
+
@after_create << block
|
64
|
+
end
|
65
|
+
|
66
|
+
# @api private
|
67
|
+
# @return [Array<Proc>]
|
68
|
+
# Add a block, if given, to be executed before a transaction is completed.
|
69
|
+
# This happens after duplicating the transaction for each error that was
|
70
|
+
# reported in the transaction -- that is, when a transaction with
|
71
|
+
# several errors is completed, the block will be called once for each
|
72
|
+
# error, with the transaction (either the original one or a duplicate of it)
|
73
|
+
# that has each of the errors set.
|
74
|
+
# The block will be called with the transaction as the first argument,
|
75
|
+
# and the error reported by the transaction, if any, as the second argument.
|
76
|
+
# Returns the array of blocks that will be executed before a transaction is
|
77
|
+
# completed.
|
78
|
+
def before_complete(&block)
|
79
|
+
@before_complete ||= Set.new
|
80
|
+
|
81
|
+
return @before_complete if block.nil?
|
82
|
+
|
83
|
+
@before_complete << block
|
84
|
+
end
|
85
|
+
|
47
86
|
# @api private
|
48
87
|
def set_current_transaction(transaction)
|
49
88
|
Thread.current[:appsignal_transaction] = transaction
|
@@ -120,6 +159,7 @@ module Appsignal
|
|
120
159
|
@namespace = namespace
|
121
160
|
@paused = false
|
122
161
|
@discarded = false
|
162
|
+
@completed = false
|
123
163
|
@tags = {}
|
124
164
|
@breadcrumbs = []
|
125
165
|
@store = Hash.new { |hash, key| hash[key] = {} }
|
@@ -137,6 +177,8 @@ module Appsignal
|
|
137
177
|
@namespace,
|
138
178
|
0
|
139
179
|
) || Appsignal::Extension::MockTransaction.new
|
180
|
+
|
181
|
+
run_after_create_hooks
|
140
182
|
end
|
141
183
|
|
142
184
|
# @api private
|
@@ -149,6 +191,11 @@ module Appsignal
|
|
149
191
|
false
|
150
192
|
end
|
151
193
|
|
194
|
+
# @api private
|
195
|
+
def completed?
|
196
|
+
@completed
|
197
|
+
end
|
198
|
+
|
152
199
|
# @api private
|
153
200
|
def complete
|
154
201
|
if discarded?
|
@@ -177,7 +224,7 @@ module Appsignal
|
|
177
224
|
# In the duplicate transaction for each error, set an error
|
178
225
|
# with a block that calls all the blocks set for that error
|
179
226
|
# in the original transaction.
|
180
|
-
transaction.
|
227
|
+
transaction.internal_set_error(error) do
|
181
228
|
blocks.each { |block| block.call(transaction) }
|
182
229
|
end
|
183
230
|
|
@@ -192,7 +239,12 @@ module Appsignal
|
|
192
239
|
end
|
193
240
|
end
|
194
241
|
end
|
242
|
+
|
243
|
+
run_before_complete_hooks
|
244
|
+
|
195
245
|
sample_data if should_sample
|
246
|
+
|
247
|
+
@completed = true
|
196
248
|
@ext.complete
|
197
249
|
end
|
198
250
|
|
@@ -520,17 +572,18 @@ module Appsignal
|
|
520
572
|
return unless error
|
521
573
|
return unless Appsignal.active?
|
522
574
|
|
523
|
-
|
524
|
-
|
525
|
-
if !@error_blocks.include?(error) && @error_blocks.length >= ERRORS_LIMIT
|
526
|
-
Appsignal.internal_logger.warn "Appsignal::Transaction#add_error: Transaction has more " \
|
527
|
-
"than #{ERRORS_LIMIT} distinct errors. Only the first " \
|
528
|
-
"#{ERRORS_LIMIT} distinct errors will be reported."
|
575
|
+
if error.instance_variable_get(:@__appsignal_error_reported) && !@error_blocks.include?(error)
|
529
576
|
return
|
530
577
|
end
|
531
578
|
|
532
|
-
|
533
|
-
|
579
|
+
internal_set_error(error, &block)
|
580
|
+
|
581
|
+
# Mark errors and their causes as tracked so we don't report duplicates,
|
582
|
+
# but also not error causes if the wrapper error is already reported.
|
583
|
+
while error
|
584
|
+
error.instance_variable_set(:@__appsignal_error_reported, true) unless error.frozen?
|
585
|
+
error = error.cause
|
586
|
+
end
|
534
587
|
end
|
535
588
|
alias :set_error :add_error
|
536
589
|
alias_method :add_exception, :add_error
|
@@ -592,10 +645,35 @@ module Appsignal
|
|
592
645
|
attr_writer :is_duplicate, :tags, :custom_data, :breadcrumbs, :params,
|
593
646
|
:session_data, :headers
|
594
647
|
|
648
|
+
def internal_set_error(error, &block)
|
649
|
+
_set_error(error) if @error_blocks.empty?
|
650
|
+
|
651
|
+
if !@error_blocks.include?(error) && @error_blocks.length >= ERRORS_LIMIT
|
652
|
+
Appsignal.internal_logger.warn "Appsignal::Transaction#add_error: Transaction has more " \
|
653
|
+
"than #{ERRORS_LIMIT} distinct errors. Only the first " \
|
654
|
+
"#{ERRORS_LIMIT} distinct errors will be reported."
|
655
|
+
return
|
656
|
+
end
|
657
|
+
@error_blocks[error] << block
|
658
|
+
@error_blocks[error].compact!
|
659
|
+
end
|
660
|
+
|
595
661
|
private
|
596
662
|
|
597
663
|
attr_reader :breadcrumbs
|
598
664
|
|
665
|
+
def run_after_create_hooks
|
666
|
+
self.class.after_create.each do |block|
|
667
|
+
block.call(self)
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
def run_before_complete_hooks
|
672
|
+
self.class.before_complete.each do |block|
|
673
|
+
block.call(self, @error_set)
|
674
|
+
end
|
675
|
+
end
|
676
|
+
|
599
677
|
def _set_error(error)
|
600
678
|
backtrace = cleaned_backtrace(error.backtrace)
|
601
679
|
@ext.set_error(
|
data/lib/appsignal/version.rb
CHANGED