appsignal 4.0.4 → 4.0.6
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/.github/workflows/ci.yml +151 -16
- data/CHANGELOG.md +42 -0
- data/build_matrix.yml +2 -1
- data/ext/agent.rb +27 -27
- data/gemfiles/que-1.gemfile +5 -0
- data/gemfiles/que-2.gemfile +5 -0
- data/lib/appsignal/check_in/scheduler.rb +3 -4
- data/lib/appsignal/config.rb +7 -0
- data/lib/appsignal/hooks/at_exit.rb +1 -0
- data/lib/appsignal/hooks/puma.rb +5 -1
- data/lib/appsignal/integrations/puma.rb +45 -0
- data/lib/appsignal/integrations/que.rb +8 -2
- data/lib/appsignal/rack/abstract_middleware.rb +3 -47
- data/lib/appsignal/rack/event_handler.rb +2 -0
- data/lib/appsignal/rack/hanami_middleware.rb +5 -1
- data/lib/appsignal/rack.rb +68 -0
- data/lib/appsignal/version.rb +1 -1
- data/spec/lib/appsignal/check_in/cron_spec.rb +134 -134
- data/spec/lib/appsignal/check_in/scheduler_spec.rb +297 -224
- data/spec/lib/appsignal/config_spec.rb +13 -0
- data/spec/lib/appsignal/hooks/at_exit_spec.rb +11 -0
- data/spec/lib/appsignal/hooks/puma_spec.rb +31 -23
- data/spec/lib/appsignal/integrations/puma_spec.rb +150 -0
- data/spec/lib/appsignal/integrations/que_spec.rb +56 -21
- data/spec/lib/appsignal/probes_spec.rb +4 -6
- data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +41 -122
- data/spec/lib/appsignal/rack_spec.rb +180 -0
- data/spec/lib/appsignal/transaction_spec.rb +96 -92
- data/spec/spec_helper.rb +6 -7
- data/spec/support/helpers/api_request_helper.rb +40 -0
- data/spec/support/helpers/config_helpers.rb +2 -1
- data/spec/support/helpers/dependency_helper.rb +5 -0
- data/spec/support/matchers/contains_log.rb +10 -3
- data/spec/support/mocks/hash_like.rb +10 -0
- data/spec/support/mocks/puma_mock.rb +43 -0
- data/spec/support/testing.rb +9 -0
- metadata +8 -3
- data/gemfiles/que.gemfile +0 -5
@@ -19,7 +19,6 @@ module Appsignal
|
|
19
19
|
@app = app
|
20
20
|
@options = options
|
21
21
|
@request_class = options.fetch(:request_class, ::Rack::Request)
|
22
|
-
@params_method = options.fetch(:params_method, :params)
|
23
22
|
@instrument_event_name = options.fetch(:instrument_event_name, nil)
|
24
23
|
@report_errors = options.fetch(:report_errors, DEFAULT_ERROR_REPORTING)
|
25
24
|
end
|
@@ -136,52 +135,9 @@ module Appsignal
|
|
136
135
|
# Override this method to set metadata after the app is called.
|
137
136
|
# Call `super` to also include the default set metadata.
|
138
137
|
def add_transaction_metadata_after(transaction, request)
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
transaction.set_metadata("method", request_method) if request_method
|
143
|
-
|
144
|
-
transaction.add_params { params_for(request) }
|
145
|
-
transaction.add_session_data { session_data_for(request) }
|
146
|
-
transaction.add_headers do
|
147
|
-
request.env if request.respond_to?(:env)
|
148
|
-
end
|
149
|
-
|
150
|
-
queue_start = Appsignal::Rack::Utils.queue_start_from(request.env)
|
151
|
-
transaction.set_queue_start(queue_start) if queue_start
|
152
|
-
end
|
153
|
-
|
154
|
-
def params_for(request)
|
155
|
-
return unless request.respond_to?(@params_method)
|
156
|
-
|
157
|
-
request.send(@params_method)
|
158
|
-
rescue => error
|
159
|
-
Appsignal.internal_logger.error(
|
160
|
-
"Exception while fetching params from '#{@request_class}##{@params_method}': " \
|
161
|
-
"#{error.class} #{error}"
|
162
|
-
)
|
163
|
-
nil
|
164
|
-
end
|
165
|
-
|
166
|
-
def request_method_for(request)
|
167
|
-
request.request_method
|
168
|
-
rescue => error
|
169
|
-
Appsignal.internal_logger.error(
|
170
|
-
"Exception while fetching the HTTP request method: #{error.class}: #{error}"
|
171
|
-
)
|
172
|
-
nil
|
173
|
-
end
|
174
|
-
|
175
|
-
def session_data_for(request)
|
176
|
-
return unless request.respond_to?(:session)
|
177
|
-
|
178
|
-
request.session.to_h
|
179
|
-
rescue => error
|
180
|
-
Appsignal.internal_logger.error(
|
181
|
-
"Exception while fetching session data from '#{@request_class}': " \
|
182
|
-
"#{error.class} #{error}"
|
183
|
-
)
|
184
|
-
nil
|
138
|
+
Appsignal::Rack::ApplyRackRequest
|
139
|
+
.new(request, @options)
|
140
|
+
.apply_to(transaction)
|
185
141
|
end
|
186
142
|
|
187
143
|
def request_for(env)
|
@@ -79,6 +79,8 @@ module Appsignal
|
|
79
79
|
#
|
80
80
|
# The EventHandler.on_finish callback should be called first, this is
|
81
81
|
# just a fallback if that doesn't get called.
|
82
|
+
#
|
83
|
+
# One such scenario is when a Puma "lowlevel_error" occurs.
|
82
84
|
Appsignal::Transaction.complete_current!
|
83
85
|
end
|
84
86
|
transaction.start_event
|
@@ -5,13 +5,17 @@ module Appsignal
|
|
5
5
|
# @api private
|
6
6
|
class HanamiMiddleware < AbstractMiddleware
|
7
7
|
def initialize(app, options = {})
|
8
|
-
options[:params_method]
|
8
|
+
options[:params_method] = nil
|
9
9
|
options[:instrument_event_name] ||= "process_action.hanami"
|
10
10
|
super
|
11
11
|
end
|
12
12
|
|
13
13
|
private
|
14
14
|
|
15
|
+
def add_transaction_metadata_after(transaction, request)
|
16
|
+
transaction.add_params { params_for(request) }
|
17
|
+
end
|
18
|
+
|
15
19
|
def params_for(request)
|
16
20
|
::Hanami::Action.params_class.new(request.env).to_h
|
17
21
|
end
|
data/lib/appsignal/rack.rb
CHANGED
@@ -37,5 +37,73 @@ module Appsignal
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
40
|
+
|
41
|
+
class ApplyRackRequest
|
42
|
+
attr_reader :request, :options
|
43
|
+
|
44
|
+
def initialize(request, options = {})
|
45
|
+
@request = request
|
46
|
+
@options = options
|
47
|
+
@params_method = options.fetch(:params_method, :params)
|
48
|
+
end
|
49
|
+
|
50
|
+
def apply_to(transaction)
|
51
|
+
request_path = request.path
|
52
|
+
transaction.set_metadata("request_path", request_path)
|
53
|
+
# TODO: Remove in next major/minor version
|
54
|
+
transaction.set_metadata("path", request_path)
|
55
|
+
|
56
|
+
request_method = request_method_for(request)
|
57
|
+
if request_method
|
58
|
+
transaction.set_metadata("request_method", request_method)
|
59
|
+
# TODO: Remove in next major/minor version
|
60
|
+
transaction.set_metadata("method", request_method)
|
61
|
+
end
|
62
|
+
|
63
|
+
transaction.add_params { params_for(request) }
|
64
|
+
transaction.add_session_data { session_data_for(request) }
|
65
|
+
transaction.add_headers do
|
66
|
+
request.env if request.respond_to?(:env)
|
67
|
+
end
|
68
|
+
|
69
|
+
queue_start = Appsignal::Rack::Utils.queue_start_from(request.env)
|
70
|
+
transaction.set_queue_start(queue_start) if queue_start
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def params_for(request)
|
76
|
+
return if !@params_method || !request.respond_to?(@params_method)
|
77
|
+
|
78
|
+
request.send(@params_method)
|
79
|
+
rescue => error
|
80
|
+
Appsignal.internal_logger.error(
|
81
|
+
"Exception while fetching params from '#{request.class}##{@params_method}': " \
|
82
|
+
"#{error.class} #{error}"
|
83
|
+
)
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
|
87
|
+
def request_method_for(request)
|
88
|
+
request.request_method
|
89
|
+
rescue => error
|
90
|
+
Appsignal.internal_logger.error(
|
91
|
+
"Exception while fetching the HTTP request method: #{error.class}: #{error}"
|
92
|
+
)
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def session_data_for(request)
|
97
|
+
return unless request.respond_to?(:session)
|
98
|
+
|
99
|
+
request.session.to_h
|
100
|
+
rescue => error
|
101
|
+
Appsignal.internal_logger.error(
|
102
|
+
"Exception while fetching session data from '#{request.class}': " \
|
103
|
+
"#{error.class} #{error}"
|
104
|
+
)
|
105
|
+
nil
|
106
|
+
end
|
107
|
+
end
|
40
108
|
end
|
41
109
|
end
|
data/lib/appsignal/version.rb
CHANGED
@@ -1,193 +1,191 @@
|
|
1
1
|
describe Appsignal::CheckIn::Cron do
|
2
|
+
let(:log_stream) { std_stream }
|
3
|
+
let(:logs) { log_contents(log_stream) }
|
4
|
+
let(:appsignal_options) { {} }
|
2
5
|
let(:config) { project_fixture_config }
|
3
6
|
let(:cron_checkin) { described_class.new(:identifier => "cron-checkin-name") }
|
4
|
-
let(:
|
5
|
-
let(:scheduler) { Appsignal::CheckIn::Scheduler.new }
|
7
|
+
let(:scheduler) { Appsignal::CheckIn.scheduler }
|
6
8
|
|
7
9
|
before do
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
start_agent(
|
11
|
+
:options => appsignal_options,
|
12
|
+
:internal_logger => test_logger(log_stream)
|
13
|
+
)
|
12
14
|
end
|
15
|
+
after { stop_scheduler }
|
13
16
|
|
14
|
-
|
17
|
+
def stop_scheduler
|
15
18
|
scheduler.stop
|
16
19
|
end
|
17
20
|
|
18
21
|
describe "when Appsignal is not active" do
|
19
|
-
|
20
|
-
allow(Appsignal).to receive(:active?).and_return(false)
|
22
|
+
let(:appsignal_options) { { :active => false } }
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
message.include?("AppSignal is not active")
|
25
|
-
end)
|
24
|
+
it "does not transmit any events" do
|
25
|
+
expect(Appsignal).to_not be_started
|
26
26
|
|
27
27
|
cron_checkin.start
|
28
|
-
|
29
|
-
expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
|
30
|
-
message.include?("Cannot transmit cron check-in `cron-checkin-name` finish event") &&
|
31
|
-
message.include?("AppSignal is not active")
|
32
|
-
end)
|
33
|
-
|
34
28
|
cron_checkin.finish
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
29
|
+
stop_scheduler
|
30
|
+
|
31
|
+
expect(logs).to contains_log(
|
32
|
+
:debug,
|
33
|
+
/Cannot transmit cron check-in `cron-checkin-name` start event .+: AppSignal is not active/
|
34
|
+
)
|
35
|
+
expect(logs).to contains_log(
|
36
|
+
:debug,
|
37
|
+
/Cannot transmit cron check-in `cron-checkin-name` finish event .+: AppSignal is not active/
|
38
|
+
)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
42
|
describe "when AppSignal is stopped" do
|
43
|
-
it "
|
44
|
-
expect(transmitter).not_to receive(:transmit)
|
45
|
-
|
46
|
-
expect(Appsignal.internal_logger).to receive(:debug).with("Stopping AppSignal")
|
47
|
-
|
43
|
+
it "does not transmit any events" do
|
48
44
|
Appsignal.stop
|
49
45
|
|
50
|
-
expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
|
51
|
-
message.include?("Cannot transmit cron check-in `cron-checkin-name` start event") &&
|
52
|
-
message.include?("AppSignal is stopped")
|
53
|
-
end)
|
54
|
-
|
55
46
|
cron_checkin.start
|
56
|
-
|
57
|
-
expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
|
58
|
-
message.include?("Cannot transmit cron check-in `cron-checkin-name` finish event") &&
|
59
|
-
message.include?("AppSignal is stopped")
|
60
|
-
end)
|
61
|
-
|
62
47
|
cron_checkin.finish
|
63
48
|
|
64
|
-
expect(
|
65
|
-
|
66
|
-
|
49
|
+
expect(logs).to contains_log(
|
50
|
+
:debug,
|
51
|
+
"Cannot transmit cron check-in `cron-checkin-name` start event"
|
52
|
+
)
|
53
|
+
expect(logs).to contains_log(
|
54
|
+
:debug,
|
55
|
+
"Cannot transmit cron check-in `cron-checkin-name` finish event"
|
56
|
+
)
|
67
57
|
end
|
68
58
|
end
|
69
59
|
|
70
60
|
describe "#start" do
|
71
|
-
it "
|
72
|
-
expect(Appsignal.internal_logger).not_to receive(:error)
|
73
|
-
|
74
|
-
expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
|
75
|
-
message.include?("Scheduling cron check-in `cron-checkin-name` start event")
|
76
|
-
end)
|
77
|
-
|
61
|
+
it "sends a cron check-in start" do
|
78
62
|
cron_checkin.start
|
79
63
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
:check_in_type => "cron"
|
88
|
-
)], :format => :ndjson).and_return(Net::HTTPResponse.new(nil, "200", nil))
|
64
|
+
stub_check_in_request(
|
65
|
+
:events => [
|
66
|
+
"identifier" => "cron-checkin-name",
|
67
|
+
"kind" => "start",
|
68
|
+
"check_in_type" => "cron"
|
69
|
+
]
|
70
|
+
)
|
89
71
|
|
90
72
|
scheduler.stop
|
91
|
-
end
|
92
73
|
|
93
|
-
|
94
|
-
expect(
|
95
|
-
|
96
|
-
|
74
|
+
expect(logs).to_not contains_log(:error)
|
75
|
+
expect(logs).to contains_log(
|
76
|
+
:debug,
|
77
|
+
"Scheduling cron check-in `cron-checkin-name` start event"
|
78
|
+
)
|
79
|
+
expect(logs).to contains_log(
|
80
|
+
:debug,
|
81
|
+
"Transmitted cron check-in `cron-checkin-name` start event"
|
82
|
+
)
|
83
|
+
end
|
97
84
|
|
85
|
+
it "logs an error if it fails" do
|
98
86
|
cron_checkin.start
|
99
87
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
:
|
107
|
-
|
108
|
-
:check_in_type => "cron"
|
109
|
-
)], :format => :ndjson).and_return(Net::HTTPResponse.new(nil, "499", nil))
|
88
|
+
stub_check_in_request(
|
89
|
+
:events => [
|
90
|
+
"identifier" => "cron-checkin-name",
|
91
|
+
"kind" => "start",
|
92
|
+
"check_in_type" => "cron"
|
93
|
+
],
|
94
|
+
:response => { :status => 499 }
|
95
|
+
)
|
110
96
|
|
111
97
|
scheduler.stop
|
98
|
+
|
99
|
+
expect(logs).to contains_log(
|
100
|
+
:debug,
|
101
|
+
"Scheduling cron check-in `cron-checkin-name` start event"
|
102
|
+
)
|
103
|
+
expect(logs).to contains_log(
|
104
|
+
:error,
|
105
|
+
/Failed to transmit cron check-in `cron-checkin-name` start event .+: 499 status code/
|
106
|
+
)
|
112
107
|
end
|
113
108
|
end
|
114
109
|
|
115
110
|
describe "#finish" do
|
116
|
-
it "
|
117
|
-
expect(Appsignal.internal_logger).not_to receive(:error)
|
118
|
-
|
119
|
-
expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
|
120
|
-
message.include?("Scheduling cron check-in `cron-checkin-name` finish event")
|
121
|
-
end)
|
122
|
-
|
111
|
+
it "sends a cron check-in finish" do
|
123
112
|
cron_checkin.finish
|
124
113
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
:check_in_type => "cron"
|
133
|
-
)], :format => :ndjson).and_return(Net::HTTPResponse.new(nil, "200", nil))
|
114
|
+
stub_check_in_request(
|
115
|
+
:events => [
|
116
|
+
"identifier" => "cron-checkin-name",
|
117
|
+
"kind" => "finish",
|
118
|
+
"check_in_type" => "cron"
|
119
|
+
]
|
120
|
+
)
|
134
121
|
|
135
122
|
scheduler.stop
|
123
|
+
expect(logs).to_not contains_log(:error)
|
124
|
+
expect(logs).to contains_log(
|
125
|
+
:debug,
|
126
|
+
"Scheduling cron check-in `cron-checkin-name` finish event"
|
127
|
+
)
|
128
|
+
expect(logs).to contains_log(
|
129
|
+
:debug,
|
130
|
+
"Transmitted cron check-in `cron-checkin-name` finish event"
|
131
|
+
)
|
136
132
|
end
|
137
133
|
|
138
|
-
it "
|
139
|
-
expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
|
140
|
-
message.include?("Scheduling cron check-in `cron-checkin-name` finish event")
|
141
|
-
end)
|
142
|
-
|
134
|
+
it "logs an error if it fails" do
|
143
135
|
cron_checkin.finish
|
144
136
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
:
|
152
|
-
|
153
|
-
:check_in_type => "cron"
|
154
|
-
)], :format => :ndjson).and_return(Net::HTTPResponse.new(nil, "499", nil))
|
137
|
+
stub_check_in_request(
|
138
|
+
:events => [
|
139
|
+
"identifier" => "cron-checkin-name",
|
140
|
+
"kind" => "finish",
|
141
|
+
"check_in_type" => "cron"
|
142
|
+
],
|
143
|
+
:response => { :status => 499 }
|
144
|
+
)
|
155
145
|
|
156
146
|
scheduler.stop
|
147
|
+
|
148
|
+
expect(logs).to contains_log(
|
149
|
+
:debug,
|
150
|
+
"Scheduling cron check-in `cron-checkin-name` finish event"
|
151
|
+
)
|
152
|
+
expect(logs).to contains_log(
|
153
|
+
:error,
|
154
|
+
/Failed to transmit cron check-in `cron-checkin-name` finish event .+: 499 status code/
|
155
|
+
)
|
157
156
|
end
|
158
157
|
end
|
159
158
|
|
160
159
|
describe ".cron" do
|
161
160
|
describe "when a block is given" do
|
162
|
-
it "
|
163
|
-
|
164
|
-
:
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
:
|
172
|
-
|
173
|
-
|
161
|
+
it "sends a cron check-in start and finish and return the block output" do
|
162
|
+
stub_check_in_request(
|
163
|
+
:events => [
|
164
|
+
"identifier" => "cron-checkin-with-block",
|
165
|
+
"kind" => "start",
|
166
|
+
"check_in_type" => "cron"
|
167
|
+
]
|
168
|
+
)
|
169
|
+
stub_check_in_request(
|
170
|
+
:events => [
|
171
|
+
"identifier" => "cron-checkin-with-block",
|
172
|
+
"kind" => "finish",
|
173
|
+
"check_in_type" => "cron"
|
174
|
+
]
|
175
|
+
)
|
174
176
|
|
175
177
|
output = Appsignal::CheckIn.cron("cron-checkin-with-block") { "output" }
|
176
178
|
expect(output).to eq("output")
|
177
179
|
end
|
178
180
|
|
179
|
-
it "
|
180
|
-
|
181
|
-
:
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
:kind => "finish",
|
188
|
-
:identifier => "cron-checkin-with-block",
|
189
|
-
:check_in_type => "cron"
|
190
|
-
))
|
181
|
+
it "does not send a cron check-in finish event when an error is raised" do
|
182
|
+
stub_check_in_request(
|
183
|
+
:events => [
|
184
|
+
"identifier" => "cron-checkin-with-block",
|
185
|
+
"kind" => "start",
|
186
|
+
"check_in_type" => "cron"
|
187
|
+
]
|
188
|
+
)
|
191
189
|
|
192
190
|
expect do
|
193
191
|
Appsignal::CheckIn.cron("cron-checkin-with-block") { raise "error" }
|
@@ -196,12 +194,14 @@ describe Appsignal::CheckIn::Cron do
|
|
196
194
|
end
|
197
195
|
|
198
196
|
describe "when no block is given" do
|
199
|
-
it "
|
200
|
-
|
201
|
-
:
|
202
|
-
|
203
|
-
|
204
|
-
|
197
|
+
it "only sends a cron check-in finish event" do
|
198
|
+
stub_check_in_request(
|
199
|
+
:events => [
|
200
|
+
"identifier" => "cron-checkin-without-block",
|
201
|
+
"kind" => "finish",
|
202
|
+
"check_in_type" => "cron"
|
203
|
+
]
|
204
|
+
)
|
205
205
|
|
206
206
|
Appsignal::CheckIn.cron("cron-checkin-without-block")
|
207
207
|
end
|