appsignal 4.0.3 → 4.0.4
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 +17 -0
- data/lib/appsignal/check_in/cron.rb +2 -34
- data/lib/appsignal/check_in/scheduler.rb +192 -0
- data/lib/appsignal/check_in.rb +18 -0
- data/lib/appsignal/cli/diagnose.rb +1 -1
- data/lib/appsignal/hooks/at_exit.rb +2 -1
- data/lib/appsignal/rack/body_wrapper.rb +15 -0
- data/lib/appsignal/transmitter.rb +30 -7
- data/lib/appsignal/utils/ndjson.rb +15 -0
- data/lib/appsignal/utils.rb +1 -0
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +1 -0
- data/spec/lib/appsignal/check_in/cron_spec.rb +210 -0
- data/spec/lib/appsignal/check_in/scheduler_spec.rb +484 -0
- data/spec/lib/appsignal/environment_spec.rb +1 -1
- data/spec/lib/appsignal/hooks/at_exit_spec.rb +11 -0
- data/spec/lib/appsignal/rack/body_wrapper_spec.rb +29 -21
- data/spec/lib/appsignal/transmitter_spec.rb +48 -2
- data/spec/lib/appsignal_spec.rb +5 -0
- data/spec/support/helpers/take_at_most_helper.rb +21 -0
- metadata +7 -3
- data/spec/lib/appsignal/check_in_spec.rb +0 -136
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91e5aad04da2524d3d2f4bd983ecd76cf1a33380a1bc2a6bf8aab1bd4d91db5b
|
4
|
+
data.tar.gz: c7d0582debd5c6d9ed29f2239a4baeed92ebdad46606c423e7537495b4dea3ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a482b32ffa5d9ddc805a65507ab4d526f9c299969c86123743e8f0c4830af6f8abd3eecd805cb7c4eec6abcd474a2f97704ac9f5129825b7958245cc973ff6db
|
7
|
+
data.tar.gz: c0bf6a6ba6454fee105c6890db3154e03c0ad64c6b5ab325fb0186edf99066e923d7b6b4043ea4d77428155fbc58371ece1309517109437edc8cc265ffda554a
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# AppSignal for Ruby gem Changelog
|
2
2
|
|
3
|
+
## 4.0.4
|
4
|
+
|
5
|
+
_Published on 2024-08-29._
|
6
|
+
|
7
|
+
### Changed
|
8
|
+
|
9
|
+
- Send check-ins concurrently. When calling `Appsignal::CheckIn.cron`, instead of blocking the current thread while the check-in events are sent, schedule them to be sent in a separate thread.
|
10
|
+
|
11
|
+
When shutting down your application manually, call `Appsignal.stop` to block until all scheduled check-ins have been sent.
|
12
|
+
|
13
|
+
(patch [46d4ca74](https://github.com/appsignal/appsignal-ruby/commit/46d4ca74f4c188cc011653ed23969ad7ec770812))
|
14
|
+
|
15
|
+
### Fixed
|
16
|
+
|
17
|
+
- Make our Rack BodyWrapper behave like a Rack BodyProxy. If a method doesn't exist on our BodyWrapper class, but it does exist on the body, behave like the Rack BodyProxy and call the method on the wrapped body. (patch [e2376305](https://github.com/appsignal/appsignal-ruby/commit/e23763058a3fb980f1054e9c1eaf7e0f25f75666))
|
18
|
+
- Do not report `SignalException` errors from our `at_exit` error reporter. (patch [3ba3ce31](https://github.com/appsignal/appsignal-ruby/commit/3ba3ce31ee3f3e84665c9f2f18d488c689cff6c2))
|
19
|
+
|
3
20
|
## 4.0.3
|
4
21
|
|
5
22
|
_Published on 2024-08-26._
|
@@ -3,15 +3,6 @@
|
|
3
3
|
module Appsignal
|
4
4
|
module CheckIn
|
5
5
|
class Cron
|
6
|
-
class << self
|
7
|
-
# @api private
|
8
|
-
def transmitter
|
9
|
-
@transmitter ||= Appsignal::Transmitter.new(
|
10
|
-
"#{Appsignal.config[:logging_endpoint]}/check_ins/json"
|
11
|
-
)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
6
|
# @api private
|
16
7
|
attr_reader :identifier, :digest
|
17
8
|
|
@@ -21,11 +12,11 @@ module Appsignal
|
|
21
12
|
end
|
22
13
|
|
23
14
|
def start
|
24
|
-
|
15
|
+
CheckIn.scheduler.schedule(event("start"))
|
25
16
|
end
|
26
17
|
|
27
18
|
def finish
|
28
|
-
|
19
|
+
CheckIn.scheduler.schedule(event("finish"))
|
29
20
|
end
|
30
21
|
|
31
22
|
private
|
@@ -39,29 +30,6 @@ module Appsignal
|
|
39
30
|
:check_in_type => "cron"
|
40
31
|
}
|
41
32
|
end
|
42
|
-
|
43
|
-
def transmit_event(kind)
|
44
|
-
unless Appsignal.active?
|
45
|
-
Appsignal.internal_logger.debug(
|
46
|
-
"AppSignal not active, not transmitting cron check-in event"
|
47
|
-
)
|
48
|
-
return
|
49
|
-
end
|
50
|
-
|
51
|
-
response = self.class.transmitter.transmit(event(kind))
|
52
|
-
|
53
|
-
if response.code.to_i >= 200 && response.code.to_i < 300
|
54
|
-
Appsignal.internal_logger.debug(
|
55
|
-
"Transmitted cron check-in `#{identifier}` (#{digest}) #{kind} event"
|
56
|
-
)
|
57
|
-
else
|
58
|
-
Appsignal.internal_logger.error(
|
59
|
-
"Failed to transmit cron check-in #{kind} event: status code was #{response.code}"
|
60
|
-
)
|
61
|
-
end
|
62
|
-
rescue => e
|
63
|
-
Appsignal.internal_logger.error("Failed to transmit cron check-in #{kind} event: #{e}")
|
64
|
-
end
|
65
33
|
end
|
66
34
|
end
|
67
35
|
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Appsignal
|
4
|
+
module CheckIn
|
5
|
+
class Scheduler
|
6
|
+
INITIAL_DEBOUNCE_SECONDS = 0.1
|
7
|
+
BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS = 10
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
# The mutex is used to synchronize access to the events array, the
|
11
|
+
# waker thread and the main thread, as well as queue writes
|
12
|
+
# (which depend on the events array) and closes (so they do not
|
13
|
+
# happen at the same time that an event is added to the scheduler)
|
14
|
+
@mutex = Mutex.new
|
15
|
+
# The transmitter thread will be started when an event is first added.
|
16
|
+
@thread = nil
|
17
|
+
@queue = Thread::Queue.new
|
18
|
+
# Scheduled events that have not been sent to the transmitter thread
|
19
|
+
# yet. A copy of this array is pushed to the queue by the waker thread
|
20
|
+
# after it has awaited the debounce period.
|
21
|
+
@events = []
|
22
|
+
# The waker thread is used to schedule debounces. It will be started
|
23
|
+
# when an event is first added.
|
24
|
+
@waker = nil
|
25
|
+
# For internal testing purposes.
|
26
|
+
@transmitted = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def schedule(event)
|
30
|
+
unless Appsignal.active?
|
31
|
+
Appsignal.internal_logger.debug(
|
32
|
+
"Cannot transmit #{describe([event])}: AppSignal is not active"
|
33
|
+
)
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
@mutex.synchronize do
|
38
|
+
if @queue.closed?
|
39
|
+
Appsignal.internal_logger.debug(
|
40
|
+
"Cannot transmit #{describe([event])}: AppSignal is stopped"
|
41
|
+
)
|
42
|
+
return
|
43
|
+
end
|
44
|
+
add_event(event)
|
45
|
+
# If we're not already waiting to be awakened from a scheduled
|
46
|
+
# debounce, schedule a short debounce, which will push the events
|
47
|
+
# to the queue and schedule a long debounce.
|
48
|
+
start_waker(INITIAL_DEBOUNCE_SECONDS) if @waker.nil?
|
49
|
+
|
50
|
+
Appsignal.internal_logger.debug(
|
51
|
+
"Scheduling #{describe([event])} to be transmitted"
|
52
|
+
)
|
53
|
+
|
54
|
+
# Make sure to start the thread after an event has been added.
|
55
|
+
@thread ||= Thread.new(&method(:run))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def stop
|
60
|
+
@mutex.synchronize do
|
61
|
+
# Flush all events before closing the queue.
|
62
|
+
push_events
|
63
|
+
rescue ClosedQueueError
|
64
|
+
# The queue is already closed (by a previous call to `#stop`)
|
65
|
+
# so it is not possible to push events to it anymore.
|
66
|
+
ensure
|
67
|
+
# Ensure calling `#stop` closes the queue and kills
|
68
|
+
# the waker thread, disallowing any further events from being
|
69
|
+
# scheduled with `#schedule`.
|
70
|
+
stop_waker
|
71
|
+
@queue.close
|
72
|
+
|
73
|
+
# Block until the thread has finished.
|
74
|
+
@thread&.join
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# @api private
|
79
|
+
# For internal testing purposes.
|
80
|
+
attr_reader :thread, :waker, :queue, :events, :transmitted
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def run
|
85
|
+
loop do
|
86
|
+
events = @queue.pop
|
87
|
+
break if events.nil?
|
88
|
+
|
89
|
+
transmit(events)
|
90
|
+
@transmitted += 1
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def transmit(events)
|
95
|
+
description = describe(events)
|
96
|
+
|
97
|
+
begin
|
98
|
+
response = CheckIn.transmitter.transmit(events, :format => :ndjson)
|
99
|
+
|
100
|
+
if (200...300).include?(response.code.to_i)
|
101
|
+
Appsignal.internal_logger.debug(
|
102
|
+
"Transmitted #{description}"
|
103
|
+
)
|
104
|
+
else
|
105
|
+
Appsignal.internal_logger.error(
|
106
|
+
"Failed to transmit #{description}: #{response.code} status code"
|
107
|
+
)
|
108
|
+
end
|
109
|
+
rescue => e
|
110
|
+
Appsignal.internal_logger.error("Failed to transmit #{description}: #{e.message}")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def describe(events)
|
115
|
+
if events.empty?
|
116
|
+
# This shouldn't happen.
|
117
|
+
"no check-in events"
|
118
|
+
elsif events.length > 1
|
119
|
+
"#{events.length} check-in events"
|
120
|
+
else
|
121
|
+
event = events.first
|
122
|
+
if event[:check_in_type] == "cron"
|
123
|
+
"cron check-in `#{event[:identifier] || "unknown"}` " \
|
124
|
+
"#{event[:kind] || "unknown"} event (digest #{event[:digest] || "unknown"})" \
|
125
|
+
else
|
126
|
+
"unknown check-in event"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Must be called from within a `@mutex.synchronize` block.
|
132
|
+
def add_event(event)
|
133
|
+
# Remove redundant events, keeping the newly added one, which
|
134
|
+
# should be the one with the most recent timestamp.
|
135
|
+
if event[:check_in_type] == "cron"
|
136
|
+
# Remove any existing cron check-in event with the same identifier,
|
137
|
+
# digest and kind as the one we're adding.
|
138
|
+
@events.reject! do |existing_event|
|
139
|
+
next unless existing_event[:identifier] == event[:identifier] &&
|
140
|
+
existing_event[:digest] == event[:digest] &&
|
141
|
+
existing_event[:kind] == event[:kind] &&
|
142
|
+
existing_event[:check_in_type] == "cron"
|
143
|
+
|
144
|
+
Appsignal.internal_logger.debug(
|
145
|
+
"Replacing previously scheduled #{describe([existing_event])}"
|
146
|
+
)
|
147
|
+
|
148
|
+
true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
@events << event
|
153
|
+
end
|
154
|
+
|
155
|
+
# Must be called from within a `@mutex.synchronize` block.
|
156
|
+
def start_waker(debounce)
|
157
|
+
stop_waker
|
158
|
+
|
159
|
+
@waker = Thread.new do
|
160
|
+
sleep(debounce)
|
161
|
+
|
162
|
+
@mutex.synchronize do
|
163
|
+
# Make sure this waker doesn't get killed, so it can push
|
164
|
+
# events and schedule a new waker.
|
165
|
+
@waker = nil
|
166
|
+
push_events
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Must be called from within a `@mutex.synchronize` block.
|
172
|
+
def stop_waker
|
173
|
+
@waker&.kill
|
174
|
+
@waker&.join
|
175
|
+
@waker = nil
|
176
|
+
end
|
177
|
+
|
178
|
+
# Must be called from within a `@mutex.synchronize` block.
|
179
|
+
def push_events
|
180
|
+
return if @events.empty?
|
181
|
+
|
182
|
+
# Push a copy of the events to the queue, and clear the events array.
|
183
|
+
# This ensures that `@events` always contains events that have not
|
184
|
+
# yet been pushed to the queue.
|
185
|
+
@queue.push(@events.dup)
|
186
|
+
@events.clear
|
187
|
+
|
188
|
+
start_waker(BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
data/lib/appsignal/check_in.rb
CHANGED
@@ -39,8 +39,26 @@ module Appsignal
|
|
39
39
|
cron.finish
|
40
40
|
output
|
41
41
|
end
|
42
|
+
|
43
|
+
# @api private
|
44
|
+
def transmitter
|
45
|
+
@transmitter ||= Transmitter.new(
|
46
|
+
"#{Appsignal.config[:logging_endpoint]}/check_ins/json"
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @api private
|
51
|
+
def scheduler
|
52
|
+
@scheduler ||= Scheduler.new
|
53
|
+
end
|
54
|
+
|
55
|
+
# @api private
|
56
|
+
def stop
|
57
|
+
scheduler&.stop
|
58
|
+
end
|
42
59
|
end
|
43
60
|
end
|
44
61
|
end
|
45
62
|
|
63
|
+
require "appsignal/check_in/scheduler"
|
46
64
|
require "appsignal/check_in/cron"
|
@@ -150,7 +150,7 @@ module Appsignal
|
|
150
150
|
ENV.fetch("APPSIGNAL_DIAGNOSE_ENDPOINT", DIAGNOSE_ENDPOINT),
|
151
151
|
Appsignal.config
|
152
152
|
)
|
153
|
-
response = transmitter.transmit(:diagnose => data)
|
153
|
+
response = transmitter.transmit({ :diagnose => data })
|
154
154
|
|
155
155
|
unless response.code == "200"
|
156
156
|
puts " Error: Something went wrong while submitting the report " \
|
@@ -57,6 +57,21 @@ module Appsignal
|
|
57
57
|
@transaction.set_error(error)
|
58
58
|
raise error
|
59
59
|
end
|
60
|
+
|
61
|
+
# Return whether the wrapped body responds to the method if this class does not.
|
62
|
+
# Based on:
|
63
|
+
# https://github.com/rack/rack/blob/0ed580bbe3858ffe5d530adf1bdad9ef9c03407c/lib/rack/body_proxy.rb#L16-L24
|
64
|
+
def respond_to_missing?(method_name, include_all = false)
|
65
|
+
super || @body.respond_to?(method_name, include_all)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Delegate missing methods to the wrapped body.
|
69
|
+
# Based on:
|
70
|
+
# https://github.com/rack/rack/blob/0ed580bbe3858ffe5d530adf1bdad9ef9c03407c/lib/rack/body_proxy.rb#L44-L61
|
71
|
+
def method_missing(method_name, *args, &block)
|
72
|
+
@body.__send__(method_name, *args, &block)
|
73
|
+
end
|
74
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
60
75
|
end
|
61
76
|
|
62
77
|
# The standard Rack body wrapper which exposes "each" for iterating
|
@@ -9,7 +9,8 @@ require "json"
|
|
9
9
|
module Appsignal
|
10
10
|
# @api private
|
11
11
|
class Transmitter
|
12
|
-
|
12
|
+
JSON_CONTENT_TYPE = "application/json; charset=UTF-8"
|
13
|
+
NDJSON_CONTENT_TYPE = "application/x-ndjson; charset=UTF-8"
|
13
14
|
|
14
15
|
HTTP_ERRORS = [
|
15
16
|
EOFError,
|
@@ -53,17 +54,39 @@ module Appsignal
|
|
53
54
|
end
|
54
55
|
end
|
55
56
|
|
56
|
-
def transmit(payload)
|
57
|
-
|
58
|
-
http_client.request(http_post(payload))
|
57
|
+
def transmit(payload, format: :json)
|
58
|
+
Appsignal.internal_logger.debug "Transmitting payload to #{uri}"
|
59
|
+
http_client.request(http_post(payload, :format => format))
|
59
60
|
end
|
60
61
|
|
61
62
|
private
|
62
63
|
|
63
|
-
def http_post(payload)
|
64
|
+
def http_post(payload, format: :json)
|
64
65
|
Net::HTTP::Post.new(uri.request_uri).tap do |request|
|
65
|
-
request["Content-Type"] =
|
66
|
-
request.body =
|
66
|
+
request["Content-Type"] = content_type_for(format)
|
67
|
+
request.body = generate_body_for(format, payload)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def content_type_for(format)
|
72
|
+
case format
|
73
|
+
when :json
|
74
|
+
JSON_CONTENT_TYPE
|
75
|
+
when :ndjson
|
76
|
+
NDJSON_CONTENT_TYPE
|
77
|
+
else
|
78
|
+
raise ArgumentError, "Unknown Content-Type header for format: #{format}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def generate_body_for(format, payload)
|
83
|
+
case format
|
84
|
+
when :json
|
85
|
+
Appsignal::Utils::JSON.generate(payload)
|
86
|
+
when :ndjson
|
87
|
+
Appsignal::Utils::NDJSON.generate(payload)
|
88
|
+
else
|
89
|
+
raise ArgumentError, "Unknown body generator for format: #{format}"
|
67
90
|
end
|
68
91
|
end
|
69
92
|
|
data/lib/appsignal/utils.rb
CHANGED
data/lib/appsignal/version.rb
CHANGED
data/lib/appsignal.rb
CHANGED
@@ -0,0 +1,210 @@
|
|
1
|
+
describe Appsignal::CheckIn::Cron do
|
2
|
+
let(:config) { project_fixture_config }
|
3
|
+
let(:cron_checkin) { described_class.new(:identifier => "cron-checkin-name") }
|
4
|
+
let(:transmitter) { Appsignal::Transmitter.new("https://checkin-endpoint.invalid") }
|
5
|
+
let(:scheduler) { Appsignal::CheckIn::Scheduler.new }
|
6
|
+
|
7
|
+
before do
|
8
|
+
allow(Appsignal).to receive(:active?).and_return(true)
|
9
|
+
config.logger = Logger.new(StringIO.new)
|
10
|
+
allow(Appsignal::CheckIn).to receive(:scheduler).and_return(scheduler)
|
11
|
+
allow(Appsignal::CheckIn).to receive(:transmitter).and_return(transmitter)
|
12
|
+
end
|
13
|
+
|
14
|
+
after do
|
15
|
+
scheduler.stop
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "when Appsignal is not active" do
|
19
|
+
it "should not transmit any events" do
|
20
|
+
allow(Appsignal).to receive(:active?).and_return(false)
|
21
|
+
|
22
|
+
expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
|
23
|
+
message.include?("Cannot transmit cron check-in `cron-checkin-name` start event") &&
|
24
|
+
message.include?("AppSignal is not active")
|
25
|
+
end)
|
26
|
+
|
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
|
+
cron_checkin.finish
|
35
|
+
|
36
|
+
expect(transmitter).not_to receive(:transmit)
|
37
|
+
|
38
|
+
scheduler.stop
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "when AppSignal is stopped" do
|
43
|
+
it "should not transmit any events" do
|
44
|
+
expect(transmitter).not_to receive(:transmit)
|
45
|
+
|
46
|
+
expect(Appsignal.internal_logger).to receive(:debug).with("Stopping AppSignal")
|
47
|
+
|
48
|
+
Appsignal.stop
|
49
|
+
|
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
|
+
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
|
+
cron_checkin.finish
|
63
|
+
|
64
|
+
expect(Appsignal.internal_logger).to receive(:debug).with("Stopping AppSignal")
|
65
|
+
|
66
|
+
Appsignal.stop
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#start" do
|
71
|
+
it "should send a cron check-in start" do
|
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
|
+
|
78
|
+
cron_checkin.start
|
79
|
+
|
80
|
+
expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
|
81
|
+
message.include?("Transmitted cron check-in `cron-checkin-name` start event")
|
82
|
+
end)
|
83
|
+
|
84
|
+
expect(transmitter).to receive(:transmit).with([hash_including(
|
85
|
+
:identifier => "cron-checkin-name",
|
86
|
+
:kind => "start",
|
87
|
+
:check_in_type => "cron"
|
88
|
+
)], :format => :ndjson).and_return(Net::HTTPResponse.new(nil, "200", nil))
|
89
|
+
|
90
|
+
scheduler.stop
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should log an error if it fails" do
|
94
|
+
expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
|
95
|
+
message.include?("Scheduling cron check-in `cron-checkin-name` start event")
|
96
|
+
end)
|
97
|
+
|
98
|
+
cron_checkin.start
|
99
|
+
|
100
|
+
expect(Appsignal.internal_logger).to receive(:error).with(satisfy do |message|
|
101
|
+
message.include?("Failed to transmit cron check-in `cron-checkin-name` start event") &&
|
102
|
+
message.include?("499 status code")
|
103
|
+
end)
|
104
|
+
|
105
|
+
expect(transmitter).to receive(:transmit).with([hash_including(
|
106
|
+
:identifier => "cron-checkin-name",
|
107
|
+
:kind => "start",
|
108
|
+
:check_in_type => "cron"
|
109
|
+
)], :format => :ndjson).and_return(Net::HTTPResponse.new(nil, "499", nil))
|
110
|
+
|
111
|
+
scheduler.stop
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "#finish" do
|
116
|
+
it "should send a cron check-in finish" do
|
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
|
+
|
123
|
+
cron_checkin.finish
|
124
|
+
|
125
|
+
expect(Appsignal.internal_logger).to receive(:debug).with(satisfy do |message|
|
126
|
+
message.include?("Transmitted cron check-in `cron-checkin-name` finish event")
|
127
|
+
end)
|
128
|
+
|
129
|
+
expect(transmitter).to receive(:transmit).with([hash_including(
|
130
|
+
:identifier => "cron-checkin-name",
|
131
|
+
:kind => "finish",
|
132
|
+
:check_in_type => "cron"
|
133
|
+
)], :format => :ndjson).and_return(Net::HTTPResponse.new(nil, "200", nil))
|
134
|
+
|
135
|
+
scheduler.stop
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should log an error if it fails" do
|
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
|
+
|
143
|
+
cron_checkin.finish
|
144
|
+
|
145
|
+
expect(Appsignal.internal_logger).to receive(:error).with(satisfy do |message|
|
146
|
+
message.include?("Failed to transmit cron check-in `cron-checkin-name` finish event") &&
|
147
|
+
message.include?("499 status code")
|
148
|
+
end)
|
149
|
+
|
150
|
+
expect(transmitter).to receive(:transmit).with([hash_including(
|
151
|
+
:identifier => "cron-checkin-name",
|
152
|
+
:kind => "finish",
|
153
|
+
:check_in_type => "cron"
|
154
|
+
)], :format => :ndjson).and_return(Net::HTTPResponse.new(nil, "499", nil))
|
155
|
+
|
156
|
+
scheduler.stop
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe ".cron" do
|
161
|
+
describe "when a block is given" do
|
162
|
+
it "should send a cron check-in start and finish and return the block output" do
|
163
|
+
expect(scheduler).to receive(:schedule).with(hash_including(
|
164
|
+
:kind => "start",
|
165
|
+
:identifier => "cron-checkin-with-block",
|
166
|
+
:check_in_type => "cron"
|
167
|
+
))
|
168
|
+
|
169
|
+
expect(scheduler).to receive(:schedule).with(hash_including(
|
170
|
+
:kind => "finish",
|
171
|
+
:identifier => "cron-checkin-with-block",
|
172
|
+
:check_in_type => "cron"
|
173
|
+
))
|
174
|
+
|
175
|
+
output = Appsignal::CheckIn.cron("cron-checkin-with-block") { "output" }
|
176
|
+
expect(output).to eq("output")
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should not send a cron check-in finish event when an error is raised" do
|
180
|
+
expect(scheduler).to receive(:schedule).with(hash_including(
|
181
|
+
:kind => "start",
|
182
|
+
:identifier => "cron-checkin-with-block",
|
183
|
+
:check_in_type => "cron"
|
184
|
+
))
|
185
|
+
|
186
|
+
expect(scheduler).not_to receive(:schedule).with(hash_including(
|
187
|
+
:kind => "finish",
|
188
|
+
:identifier => "cron-checkin-with-block",
|
189
|
+
:check_in_type => "cron"
|
190
|
+
))
|
191
|
+
|
192
|
+
expect do
|
193
|
+
Appsignal::CheckIn.cron("cron-checkin-with-block") { raise "error" }
|
194
|
+
end.to raise_error(RuntimeError, "error")
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe "when no block is given" do
|
199
|
+
it "should only send a cron check-in finish event" do
|
200
|
+
expect(scheduler).to receive(:schedule).with(hash_including(
|
201
|
+
:kind => "finish",
|
202
|
+
:identifier => "cron-checkin-without-block",
|
203
|
+
:check_in_type => "cron"
|
204
|
+
))
|
205
|
+
|
206
|
+
Appsignal::CheckIn.cron("cron-checkin-without-block")
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|