logtail 0.1.19 → 0.1.20
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/lib/logtail/config.rb +4 -0
- data/lib/logtail/log_devices/http.rb +16 -2
- data/lib/logtail/version.rb +1 -1
- data/spec/logtail/config_spec.rb +16 -0
- data/spec/logtail/log_devices/http_spec.rb +81 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0d8397873f23b58750258e46a3162866eaa3ab6ffaf551078d28b25b44b5ad69
|
|
4
|
+
data.tar.gz: 83a5def59147d9b94a890db6e7f07dd2107bdfa68ba7eb929ca47fa5a44a212b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d710225c254e58dbdfa66e6e87515167fcc651ce4cd14d7ddeb6cb682d9b547f8eba13c9729a939249ca565229fcd68e4f9867db17900e798051abe5b0699473
|
|
7
|
+
data.tar.gz: 5d785a4b048a73403e2d389780d5e83702b0ada279fb2dd727e2b192176f15133620668e6f2a6eccdb5b142a29d9283fd03a3058f2d3f31aace47f42794ca820
|
data/lib/logtail/config.rb
CHANGED
|
@@ -32,6 +32,10 @@ module Logtail
|
|
|
32
32
|
|
|
33
33
|
attr_writer :http_body_limit
|
|
34
34
|
|
|
35
|
+
def initialize
|
|
36
|
+
@debug_logger = nil
|
|
37
|
+
end
|
|
38
|
+
|
|
35
39
|
# Whether a particular {Logtail::LogEntry} should be sent to Better Stack
|
|
36
40
|
def send_to_better_stack?(log_entry)
|
|
37
41
|
!@better_stack_filters&.any? { |blocker| blocker.call(log_entry) }
|
|
@@ -22,6 +22,8 @@ module Logtail
|
|
|
22
22
|
DEFAULT_INGESTING_SCHEME = "https".freeze
|
|
23
23
|
CONTENT_TYPE = "application/msgpack".freeze
|
|
24
24
|
USER_AGENT = "Logtail Ruby/#{Logtail::VERSION} (HTTP)".freeze
|
|
25
|
+
INITIAL_RECONNECT_WAIT = 1 # second
|
|
26
|
+
MAX_RECONNECT_WAIT = 30 # seconds
|
|
25
27
|
|
|
26
28
|
# Instantiates a new HTTP log device that can be passed to {Logtail::Logger#initialize}.
|
|
27
29
|
#
|
|
@@ -83,6 +85,7 @@ module Logtail
|
|
|
83
85
|
@request_queue = options[:request_queue] || FlushableDroppingSizedQueue.new(25)
|
|
84
86
|
@successive_error_count = 0
|
|
85
87
|
@requests_in_flight = 0
|
|
88
|
+
@reconnect_wait = INITIAL_RECONNECT_WAIT
|
|
86
89
|
end
|
|
87
90
|
|
|
88
91
|
# Write a new log line message to the buffer, and flush asynchronously if the
|
|
@@ -298,15 +301,19 @@ Logtail::Config.instance.debug_logger = ::Logger.new(STDOUT)
|
|
|
298
301
|
http
|
|
299
302
|
end
|
|
300
303
|
|
|
301
|
-
# Creates a loop that processes the `@request_queue` on an interval.
|
|
304
|
+
# Creates a loop that processes the `@request_queue` on an interval. After a failed
|
|
305
|
+
# connection it waits before reconnecting, twice as long after every consecutive
|
|
306
|
+
# failure up to {MAX_RECONNECT_WAIT}, so an unreachable host is not retried in a busy
|
|
307
|
+
# loop. A delivered request starts the wait over (see {#deliver_requests}).
|
|
302
308
|
def request_outlet
|
|
303
309
|
loop do
|
|
304
310
|
http = build_http
|
|
311
|
+
connection_healthy = false
|
|
305
312
|
|
|
306
313
|
begin
|
|
307
314
|
Logtail::Config.instance.debug { "Starting HTTP connection" }
|
|
308
315
|
|
|
309
|
-
http.start do |conn|
|
|
316
|
+
connection_healthy = http.start do |conn|
|
|
310
317
|
deliver_requests(conn)
|
|
311
318
|
end
|
|
312
319
|
rescue => e
|
|
@@ -315,6 +322,12 @@ Logtail::Config.instance.debug_logger = ::Logger.new(STDOUT)
|
|
|
315
322
|
Logtail::Config.instance.debug { "Finishing HTTP connection" }
|
|
316
323
|
http.finish if http.started?
|
|
317
324
|
end
|
|
325
|
+
|
|
326
|
+
next if connection_healthy
|
|
327
|
+
|
|
328
|
+
Logtail::Config.instance.debug { "Reconnecting in #{@reconnect_wait} seconds" }
|
|
329
|
+
sleep(@reconnect_wait)
|
|
330
|
+
@reconnect_wait = [@reconnect_wait * 2, MAX_RECONNECT_WAIT].min
|
|
318
331
|
end
|
|
319
332
|
end
|
|
320
333
|
|
|
@@ -360,6 +373,7 @@ Logtail::Config.instance.debug_logger = ::Logger.new(STDOUT)
|
|
|
360
373
|
num_reqs += 1
|
|
361
374
|
|
|
362
375
|
@last_resp = resp
|
|
376
|
+
@reconnect_wait = INITIAL_RECONNECT_WAIT
|
|
363
377
|
|
|
364
378
|
Logtail::Config.instance.debug do
|
|
365
379
|
if resp.code == "202"
|
data/lib/logtail/version.rb
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Logtail::Config do
|
|
4
|
+
describe "#debug_logger" do
|
|
5
|
+
# Ruby before 3.0 warns under `ruby -w` when an unset instance variable is read, and the
|
|
6
|
+
# HTTP device reads the debug logger on every connection attempt.
|
|
7
|
+
it "can be read before it is set without an uninitialized instance variable warning" do
|
|
8
|
+
config = described_class.send(:new)
|
|
9
|
+
verbose, $VERBOSE = $VERBOSE, true
|
|
10
|
+
|
|
11
|
+
expect { config.debug_logger }.not_to output.to_stderr
|
|
12
|
+
ensure
|
|
13
|
+
$VERBOSE = verbose
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -148,6 +148,87 @@ describe Logtail::LogDevices::HTTP do
|
|
|
148
148
|
|
|
149
149
|
http.close
|
|
150
150
|
end
|
|
151
|
+
|
|
152
|
+
context "when connecting or delivering fails" do
|
|
153
|
+
# Raised from the stubs below to leave the outlet's endless loop. It is not a
|
|
154
|
+
# StandardError, so the outlet's own `rescue => e` lets it through.
|
|
155
|
+
let(:stop_outlet) { Class.new(Exception) }
|
|
156
|
+
let(:http_device) { described_class.new("MYKEY", flush_continuously: false, requests_per_conn: 1) }
|
|
157
|
+
let(:request_queue) { http_device.instance_variable_get(:@request_queue) }
|
|
158
|
+
let(:waits) { [] }
|
|
159
|
+
|
|
160
|
+
before do
|
|
161
|
+
allow(http_device).to receive(:sleep) { |seconds| waits << seconds }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
it "waits before reconnecting, twice as long after every refused connection, up to 30 seconds" do
|
|
165
|
+
connection_attempts = 0
|
|
166
|
+
allow_any_instance_of(Net::HTTP).to receive(:start) do
|
|
167
|
+
connection_attempts += 1
|
|
168
|
+
raise stop_outlet if connection_attempts > 7
|
|
169
|
+
raise Errno::ECONNREFUSED
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
expect { http_device.send(:request_outlet) }.to raise_error(stop_outlet)
|
|
173
|
+
expect(waits).to eq([1, 2, 4, 8, 16, 30, 30])
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "waits the same way when a request fails on an open connection, and still drops it after 3 attempts" do
|
|
177
|
+
request_queue.enq(Logtail::LogDevices::HTTP::RequestAttempt.new(Net::HTTP::Post.new("/")))
|
|
178
|
+
request_attempts = 0
|
|
179
|
+
allow_any_instance_of(Net::HTTP).to receive(:request) do
|
|
180
|
+
request_attempts += 1
|
|
181
|
+
raise Errno::ECONNRESET
|
|
182
|
+
end
|
|
183
|
+
allow(http_device).to receive(:sleep) do |seconds|
|
|
184
|
+
waits << seconds
|
|
185
|
+
raise stop_outlet if waits.size == 3
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
expect { http_device.send(:request_outlet) }.to raise_error(stop_outlet)
|
|
189
|
+
expect(waits).to eq([1, 2, 4])
|
|
190
|
+
expect(request_attempts).to eq(3)
|
|
191
|
+
expect(request_queue.size).to eq(0)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "starts over at 1 second once a request is delivered" do
|
|
195
|
+
request_queue.enq(Logtail::LogDevices::HTTP::RequestAttempt.new(Net::HTTP::Post.new("/")))
|
|
196
|
+
connection = double("connection", request: double("response", code: "202"))
|
|
197
|
+
connections = [:refused, :refused, :delivers, :refused, :refused]
|
|
198
|
+
allow_any_instance_of(Net::HTTP).to receive(:start) do |_http, &block|
|
|
199
|
+
case connections.shift
|
|
200
|
+
when :refused then raise Errno::ECONNREFUSED
|
|
201
|
+
when :delivers then block.call(connection)
|
|
202
|
+
else raise stop_outlet
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
expect { http_device.send(:request_outlet) }.to raise_error(stop_outlet)
|
|
207
|
+
expect(waits).to eq([1, 2, 1, 2])
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it "lets close stop the outlet while it waits to reconnect" do
|
|
212
|
+
connection_attempts = 0
|
|
213
|
+
allow_any_instance_of(Net::HTTP).to receive(:start) do
|
|
214
|
+
connection_attempts += 1
|
|
215
|
+
raise Errno::ECONNREFUSED
|
|
216
|
+
end
|
|
217
|
+
http_device = described_class.new("MYKEY")
|
|
218
|
+
http_device.send(:ensure_flush_threads_are_started)
|
|
219
|
+
outlet = http_device.instance_variable_get(:@request_outlet_thread)
|
|
220
|
+
# Up to 5 seconds for the thread's first attempt, which is slow on a cold TruffleRuby.
|
|
221
|
+
500.times do
|
|
222
|
+
break if connection_attempts > 0 && outlet.status == "sleep"
|
|
223
|
+
sleep 0.01
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
closing = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
227
|
+
http_device.close
|
|
228
|
+
expect(Process.clock_gettime(Process::CLOCK_MONOTONIC) - closing).to be < 0.5
|
|
229
|
+
expect(outlet).not_to be_alive
|
|
230
|
+
expect(connection_attempts).to eq(1)
|
|
231
|
+
end
|
|
151
232
|
end
|
|
152
233
|
|
|
153
234
|
describe "#deliver_requests" do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: logtail
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.20
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Better Stack
|
|
@@ -188,6 +188,7 @@ files:
|
|
|
188
188
|
- lib/logtail/version.rb
|
|
189
189
|
- logtail.gemspec
|
|
190
190
|
- spec/README.md
|
|
191
|
+
- spec/logtail/config_spec.rb
|
|
191
192
|
- spec/logtail/current_context_spec.rb
|
|
192
193
|
- spec/logtail/events/controller_call_spec.rb
|
|
193
194
|
- spec/logtail/events/error_spec.rb
|