logtail-ruby 0.1.0
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 +7 -0
- data/.github/workflows/main.yml +33 -0
- data/.gitignore +24 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +10 -0
- data/LICENSE.md +15 -0
- data/README.md +4 -0
- data/Rakefile +72 -0
- data/lib/logtail.rb +36 -0
- data/lib/logtail/config.rb +154 -0
- data/lib/logtail/config/integrations.rb +17 -0
- data/lib/logtail/context.rb +9 -0
- data/lib/logtail/contexts.rb +12 -0
- data/lib/logtail/contexts/http.rb +31 -0
- data/lib/logtail/contexts/release.rb +52 -0
- data/lib/logtail/contexts/runtime.rb +23 -0
- data/lib/logtail/contexts/session.rb +24 -0
- data/lib/logtail/contexts/system.rb +29 -0
- data/lib/logtail/contexts/user.rb +28 -0
- data/lib/logtail/current_context.rb +168 -0
- data/lib/logtail/event.rb +36 -0
- data/lib/logtail/events.rb +10 -0
- data/lib/logtail/events/controller_call.rb +44 -0
- data/lib/logtail/events/error.rb +40 -0
- data/lib/logtail/events/exception.rb +10 -0
- data/lib/logtail/events/sql_query.rb +26 -0
- data/lib/logtail/events/template_render.rb +25 -0
- data/lib/logtail/integration.rb +40 -0
- data/lib/logtail/integrator.rb +50 -0
- data/lib/logtail/log_devices.rb +8 -0
- data/lib/logtail/log_devices/http.rb +368 -0
- data/lib/logtail/log_devices/http/flushable_dropping_sized_queue.rb +52 -0
- data/lib/logtail/log_devices/http/request_attempt.rb +20 -0
- data/lib/logtail/log_entry.rb +110 -0
- data/lib/logtail/logger.rb +270 -0
- data/lib/logtail/logtail.rb +36 -0
- data/lib/logtail/timer.rb +21 -0
- data/lib/logtail/util.rb +7 -0
- data/lib/logtail/util/non_nil_hash_builder.rb +40 -0
- data/lib/logtail/version.rb +3 -0
- data/logtail-ruby.gemspec +43 -0
- data/spec/README.md +13 -0
- data/spec/logtail/current_context_spec.rb +113 -0
- data/spec/logtail/events/controller_call_spec.rb +12 -0
- data/spec/logtail/events/error_spec.rb +15 -0
- data/spec/logtail/log_devices/http_spec.rb +185 -0
- data/spec/logtail/log_entry_spec.rb +22 -0
- data/spec/logtail/logger_spec.rb +227 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/logtail.rb +5 -0
- data/spec/support/socket_hostname.rb +12 -0
- data/spec/support/timecop.rb +3 -0
- data/spec/support/webmock.rb +3 -0
- metadata +238 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
# Base (must come first, order matters)
|
2
|
+
require "logtail/version"
|
3
|
+
require "logtail/config"
|
4
|
+
require "logtail/util"
|
5
|
+
|
6
|
+
# Load frameworks
|
7
|
+
|
8
|
+
# Other (sorted alphabetically)
|
9
|
+
require "logtail/contexts"
|
10
|
+
require "logtail/current_context"
|
11
|
+
require "logtail/events"
|
12
|
+
require "logtail/integration"
|
13
|
+
require "logtail/log_devices"
|
14
|
+
require "logtail/log_entry"
|
15
|
+
require "logtail/logger"
|
16
|
+
require "logtail/timer"
|
17
|
+
require "logtail/integrator"
|
18
|
+
require "logtail/integration"
|
19
|
+
|
20
|
+
module Logtail
|
21
|
+
# Access the main configuration object. Please see {{Logtail::Config}} for more details.
|
22
|
+
def self.config
|
23
|
+
Config.instance
|
24
|
+
end
|
25
|
+
|
26
|
+
# Starts a timer for timing events. Please see {{Logtail::Logtail.start}} for more details.
|
27
|
+
def self.start_timer
|
28
|
+
Timer.start
|
29
|
+
end
|
30
|
+
|
31
|
+
# Adds context to all logs written within the passed block. Please see
|
32
|
+
# {{Logtail::CurrentContext.with}} for a more detailed description with examples.
|
33
|
+
def self.with_context(context, &block)
|
34
|
+
CurrentContext.with(context, &block)
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Logtail
|
2
|
+
# This is an ultra-simple abstraction for timing code. This provides a little
|
3
|
+
# more control around how Logtail automatically processes "timers".
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# timer = Logtail::Timer.start
|
7
|
+
# # ... code to time
|
8
|
+
# logger.info("My log message", my_event: {time_ms: timer})
|
9
|
+
module Timer
|
10
|
+
# Abstract for starting a logtail. Currently this is simply calling `Time.now`.
|
11
|
+
def self.start
|
12
|
+
Time.now
|
13
|
+
end
|
14
|
+
|
15
|
+
# Get the duration in milliseconds from the object returned in {#start}
|
16
|
+
def self.duration_ms(timer)
|
17
|
+
now = Time.now
|
18
|
+
(now - timer) * 1000.0
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/logtail/util.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Logtail
|
4
|
+
module Util
|
5
|
+
# @private
|
6
|
+
#
|
7
|
+
# The purpose of this class is to efficiently build a hash that does not
|
8
|
+
# include nil values. It's proactive instead of reactive, avoiding the
|
9
|
+
# need to traverse and reduce a new hash dropping blanks.
|
10
|
+
class NonNilHashBuilder
|
11
|
+
class << self
|
12
|
+
def build(&block)
|
13
|
+
builder = new
|
14
|
+
yield builder
|
15
|
+
builder.target
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :target
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@target = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def add(k, v, options = {})
|
26
|
+
if !v.nil?
|
27
|
+
if options[:json_encode]
|
28
|
+
v = v.to_json
|
29
|
+
end
|
30
|
+
|
31
|
+
if options[:limit]
|
32
|
+
v = v.byteslice(0, options[:limit])
|
33
|
+
end
|
34
|
+
|
35
|
+
@target[k] = v
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$LOAD_PATH.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "logtail/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "logtail-ruby"
|
7
|
+
spec.version = Logtail::VERSION
|
8
|
+
spec.platform = Gem::Platform::RUBY
|
9
|
+
spec.authors = ["Logtail"]
|
10
|
+
spec.email = ["hi@logtail.com"]
|
11
|
+
spec.homepage = "https://github.com/logtail/logtail-ruby"
|
12
|
+
spec.license = "ISC"
|
13
|
+
|
14
|
+
spec.summary = "Query logs like you query your database. https://logtail.com"
|
15
|
+
|
16
|
+
spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
|
17
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/tree/master/CHANGELOG.md"
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
19
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
20
|
+
|
21
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.2.0")
|
22
|
+
|
23
|
+
spec.files = `git ls-files`.split("\n")
|
24
|
+
spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
25
|
+
spec.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ["lib"]
|
27
|
+
|
28
|
+
spec.add_dependency('msgpack', '~> 1.0')
|
29
|
+
|
30
|
+
spec.add_development_dependency('bundler-audit', '>= 0')
|
31
|
+
spec.add_development_dependency('rails_stdout_logging', '>= 0')
|
32
|
+
spec.add_development_dependency('rake', '>= 0')
|
33
|
+
spec.add_development_dependency('rspec', '~> 3.4')
|
34
|
+
spec.add_development_dependency('rspec-its', '>= 0')
|
35
|
+
spec.add_development_dependency('timecop', '>= 0')
|
36
|
+
spec.add_development_dependency('webmock', '~> 2.3')
|
37
|
+
|
38
|
+
if RUBY_PLATFORM == "java"
|
39
|
+
spec.add_development_dependency('activerecord-jdbcsqlite3-adapter', '>= 0')
|
40
|
+
else
|
41
|
+
spec.add_development_dependency('sqlite3', '>= 0')
|
42
|
+
end
|
43
|
+
end
|
data/spec/README.md
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Logtail::CurrentContext do
|
4
|
+
describe ".initialize" do
|
5
|
+
it "should not set the release context" do
|
6
|
+
context = described_class.send(:new)
|
7
|
+
expect(context.send(:hash)[:release]).to be_nil
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should set the system context" do
|
11
|
+
context = described_class.send(:new)
|
12
|
+
system_content = context.fetch(:system)
|
13
|
+
expect(system_content[:hostname]).to_not be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should set the runtime context" do
|
17
|
+
context = described_class.send(:new)
|
18
|
+
runtime_context = context.fetch(:runtime)
|
19
|
+
expect(runtime_context[:thread_id]).to_not be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
context "with Heroku dyno metadata" do
|
23
|
+
around(:each) do |example|
|
24
|
+
ENV['HEROKU_SLUG_COMMIT'] = "2c3a0b24069af49b3de35b8e8c26765c1dba9ff0"
|
25
|
+
ENV['HEROKU_RELEASE_CREATED_AT'] = "2015-04-02T18:00:42Z"
|
26
|
+
ENV['HEROKU_RELEASE_VERSION'] = "v2.3.1"
|
27
|
+
|
28
|
+
example.run
|
29
|
+
|
30
|
+
ENV.delete('HEROKU_SLUG_COMMIT')
|
31
|
+
ENV.delete('HEROKU_RELEASE_CREATED_AT')
|
32
|
+
ENV.delete('HEROKU_RELEASE_VERSION')
|
33
|
+
|
34
|
+
described_class.reset
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should automatically set the release context" do
|
38
|
+
context = described_class.send(:new)
|
39
|
+
expect(context.send(:hash)[:release]).to eq({:commit_hash=>"2c3a0b24069af49b3de35b8e8c26765c1dba9ff0", :created_at=>"2015-04-02T18:00:42Z", :version=>"v2.3.1"})
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "with generic env vars" do
|
44
|
+
around(:each) do |example|
|
45
|
+
ENV['RELEASE_COMMIT'] = "2c3a0b24069af49b3de35b8e8c26765c1dba9ff0"
|
46
|
+
ENV['RELEASE_CREATED_AT'] = "2015-04-02T18:00:42Z"
|
47
|
+
ENV['RELEASE_VERSION'] = "v2.3.1"
|
48
|
+
|
49
|
+
example.run
|
50
|
+
|
51
|
+
ENV.delete('RELEASE_COMMIT')
|
52
|
+
ENV.delete('RELEASE_CREATED_AT')
|
53
|
+
ENV.delete('RELEASE_VERSION')
|
54
|
+
|
55
|
+
described_class.reset
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should automatically set the release context" do
|
59
|
+
context = described_class.send(:new)
|
60
|
+
expect(context.send(:hash)[:release]).to eq({:commit_hash=>"2c3a0b24069af49b3de35b8e8c26765c1dba9ff0", :created_at=>"2015-04-02T18:00:42Z", :version=>"v2.3.1"})
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe ".add" do
|
66
|
+
after(:each) do
|
67
|
+
described_class.reset
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should add the context" do
|
71
|
+
expect(described_class.instance.send(:hash)[:build]).to be_nil
|
72
|
+
|
73
|
+
described_class.add({build: {version: "1.0.0"}})
|
74
|
+
expect(described_class.instance.send(:hash)[:build]).to eq({:version=>"1.0.0"})
|
75
|
+
|
76
|
+
described_class.add({testing: {key: "value"}})
|
77
|
+
expect(described_class.instance.send(:hash)[:build]).to eq({:version=>"1.0.0"})
|
78
|
+
expect(described_class.instance.send(:hash)[:testing]).to eq({:key=>"value"})
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe ".remove" do
|
83
|
+
it "should remove context by key" do
|
84
|
+
context = {:build=>{:version=>"1.0.0"}}
|
85
|
+
described_class.add(context)
|
86
|
+
expect(described_class.instance.send(:hash)[:build]).to eq({:version=>"1.0.0"})
|
87
|
+
|
88
|
+
described_class.remove(:build)
|
89
|
+
expect(described_class.instance.send(:hash)[:build]).to be_nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe ".with" do
|
94
|
+
it "should merge the context and cleanup on block exit" do
|
95
|
+
expect(described_class.instance.send(:hash)[:build]).to be_nil
|
96
|
+
|
97
|
+
described_class.with({build: {version: "1.0.0"}}) do
|
98
|
+
expect(described_class.instance.send(:hash)[:build]).to eq({:version=>"1.0.0"})
|
99
|
+
|
100
|
+
described_class.with({testing: {key: "value"}}) do
|
101
|
+
expect(described_class.instance.send(:hash)[:build]).to eq({:version=>"1.0.0"})
|
102
|
+
expect(described_class.instance.send(:hash)[:testing]).to eq({:key=>"value"})
|
103
|
+
end
|
104
|
+
|
105
|
+
expect(described_class.instance.send(:hash)[:build]).to eq({:version=>"1.0.0"})
|
106
|
+
expect(described_class.instance.send(:hash)[:testing]).to be_nil
|
107
|
+
end
|
108
|
+
|
109
|
+
expect(described_class.instance.send(:hash)[:build]).to be_nil
|
110
|
+
expect(described_class.instance.send(:hash)[:testing]).to be_nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Logtail::Events::ControllerCall do
|
6
|
+
describe ".initialize" do
|
7
|
+
it "sanitizes the password param" do
|
8
|
+
# event = described_class.new(controller: 'controller', action: 'action', params: {password: 'password'})
|
9
|
+
# expect(event.params).to eq({'password' => '[sanitized]'})
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Logtail::Events::Error do
|
4
|
+
describe "#to_hash" do
|
5
|
+
it "should jsonify the stacktrace" do
|
6
|
+
backtrace = [
|
7
|
+
"/path/to/file1.rb:26:in `function1'",
|
8
|
+
"path/to/file2.rb:86:in `function2'"
|
9
|
+
]
|
10
|
+
|
11
|
+
exception_event = described_class.new(name: "RuntimeError", error_message: "Boom", backtrace: backtrace)
|
12
|
+
expect(exception_event.backtrace_json).to eq("[\"/path/to/file1.rb:26:in `function1'\",\"path/to/file2.rb:86:in `function2'\"]")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
# Note: these tests access instance variables and private methods as a means of
|
4
|
+
# not muddying the public API. This object should expose a simple buffer like
|
5
|
+
# API, tests should not alter that.
|
6
|
+
describe Logtail::LogDevices::HTTP do
|
7
|
+
describe "#initialize" do
|
8
|
+
it "should initialize properly" do
|
9
|
+
http = described_class.new("MYKEY", flush_interval: 0.1)
|
10
|
+
|
11
|
+
# Ensure that threads have not started
|
12
|
+
thread = http.instance_variable_get(:@flush_thread)
|
13
|
+
expect(thread).to be_nil
|
14
|
+
thread = http.instance_variable_get(:@request_outlet_thread)
|
15
|
+
expect(thread).to be_nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#write" do
|
20
|
+
let(:http) { described_class.new("MYKEY") }
|
21
|
+
let(:msg_queue) { http.instance_variable_get(:@msg_queue) }
|
22
|
+
|
23
|
+
it "should buffer the messages" do
|
24
|
+
http.write("test log message")
|
25
|
+
expect(msg_queue.flush).to eq(["test log message"])
|
26
|
+
http.close
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should start the flush threads" do
|
30
|
+
http.write("test log message")
|
31
|
+
|
32
|
+
thread = http.instance_variable_get(:@flush_thread)
|
33
|
+
expect(thread).to be_alive
|
34
|
+
thread = http.instance_variable_get(:@request_outlet_thread)
|
35
|
+
expect(thread).to be_alive
|
36
|
+
expect(http).to receive(:flush).exactly(1).times
|
37
|
+
http.close
|
38
|
+
end
|
39
|
+
|
40
|
+
context "with a low batch size" do
|
41
|
+
let(:http) { described_class.new("MYKEY", :batch_size => 2) }
|
42
|
+
|
43
|
+
it "should attempt a delivery when the limit is exceeded" do
|
44
|
+
http.write("test")
|
45
|
+
expect(http).to receive(:flush_async).exactly(1).times
|
46
|
+
http.write("my log message")
|
47
|
+
expect(http).to receive(:flush).exactly(1).times
|
48
|
+
http.close
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#close" do
|
54
|
+
let(:http) { described_class.new("MYKEY") }
|
55
|
+
|
56
|
+
it "should kill the threads" do
|
57
|
+
http.send(:ensure_flush_threads_are_started)
|
58
|
+
http.close
|
59
|
+
thread = http.instance_variable_get(:@flush_thread)
|
60
|
+
sleep 0.1 # too fast!
|
61
|
+
expect(thread).to_not be_alive
|
62
|
+
thread = http.instance_variable_get(:@request_outlet_thread)
|
63
|
+
sleep 0.1 # too fast!
|
64
|
+
expect(thread).to_not be_alive
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should attempt a delivery" do
|
68
|
+
message = "a" * 19
|
69
|
+
http.write(message)
|
70
|
+
expect(http).to receive(:flush).exactly(1).times
|
71
|
+
http.close
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Testing a private method because it helps break down our tests
|
76
|
+
describe "#flush" do
|
77
|
+
let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
|
78
|
+
|
79
|
+
it "should deliver the request" do
|
80
|
+
http = described_class.new("MYKEY", flush_continuously: false)
|
81
|
+
log_entry = Logtail::LogEntry.new("INFO", time, nil, "test log message 1", nil, nil)
|
82
|
+
http.write(log_entry)
|
83
|
+
log_entry = Logtail::LogEntry.new("INFO", time, nil, "test log message 2", nil, nil)
|
84
|
+
http.write(log_entry)
|
85
|
+
expect(http).to receive(:flush_async).exactly(2).times
|
86
|
+
http.send(:flush)
|
87
|
+
http.close
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Testing a private method because it helps break down our tests
|
92
|
+
describe "#flush_async" do
|
93
|
+
let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
|
94
|
+
|
95
|
+
it "should add a request to the queue" do
|
96
|
+
http = described_class.new("MYKEY", flush_continuously: false)
|
97
|
+
log_entry = Logtail::LogEntry.new("INFO", time, nil, "test log message 1", nil, nil)
|
98
|
+
http.write(log_entry)
|
99
|
+
log_entry = Logtail::LogEntry.new("INFO", time, nil, "test log message 2", nil, nil)
|
100
|
+
http.write(log_entry)
|
101
|
+
http.send(:flush_async)
|
102
|
+
request_queue = http.instance_variable_get(:@request_queue)
|
103
|
+
request_attempt = request_queue.deq
|
104
|
+
expect(request_attempt.request).to be_kind_of(Net::HTTP::Post)
|
105
|
+
expect(request_attempt.request.body).to start_with("\x92\x83\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1".force_encoding("ASCII-8BIT"))
|
106
|
+
|
107
|
+
message_queue = http.instance_variable_get(:@msg_queue)
|
108
|
+
expect(message_queue.size).to eq(0)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Testing a private method because it helps break down our tests
|
113
|
+
describe "#intervaled_flush" do
|
114
|
+
it "should start a intervaled flush thread and flush on an interval" do
|
115
|
+
http = described_class.new("MYKEY", flush_interval: 0.1)
|
116
|
+
http.send(:ensure_flush_threads_are_started)
|
117
|
+
expect(http).to receive(:flush_async).at_least(3).times
|
118
|
+
sleep 1.1 # iterations check every 0.5 seconds
|
119
|
+
http.close
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Outlet
|
124
|
+
describe "#request_outlet" do
|
125
|
+
let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
|
126
|
+
|
127
|
+
it "should deliver requests on an interval" do
|
128
|
+
stub = stub_request(:post, "https://logs.logtail.com/").
|
129
|
+
with(
|
130
|
+
:body => start_with("\x92\x83\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1".force_encoding("ASCII-8BIT")),
|
131
|
+
:headers => {
|
132
|
+
'Authorization' => 'Bearer MYKEY',
|
133
|
+
'Content-Type' => 'application/msgpack',
|
134
|
+
'User-Agent' => "Logtail Ruby/#{Logtail::VERSION} (HTTP)"
|
135
|
+
}
|
136
|
+
).
|
137
|
+
to_return(:status => 200, :body => "", :headers => {})
|
138
|
+
|
139
|
+
http = described_class.new("MYKEY", flush_interval: 0.1)
|
140
|
+
log_entry1 = Logtail::LogEntry.new("INFO", time, nil, "test log message 1", nil, nil)
|
141
|
+
http.write(log_entry1)
|
142
|
+
log_entry2 = Logtail::LogEntry.new("INFO", time, nil, "test log message 2", nil, nil)
|
143
|
+
http.write(log_entry2)
|
144
|
+
sleep 2
|
145
|
+
|
146
|
+
expect(stub).to have_been_requested.times(1)
|
147
|
+
|
148
|
+
http.close
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "#deliver_requests" do
|
153
|
+
it "should handle exceptions properly and return" do
|
154
|
+
allow_any_instance_of(Net::HTTP).to receive(:request).and_raise("boom")
|
155
|
+
|
156
|
+
http_device = described_class.new("MYKEY", flush_continuously: false)
|
157
|
+
req_queue = http_device.instance_variable_get(:@request_queue)
|
158
|
+
|
159
|
+
# Place a request on the queue
|
160
|
+
request = Net::HTTP::Post.new("/")
|
161
|
+
request_attempt = Logtail::LogDevices::HTTP::RequestAttempt.new(request)
|
162
|
+
request_attempt.attempted!
|
163
|
+
req_queue.enq(request_attempt)
|
164
|
+
|
165
|
+
# Start a HTTP connection to test the method directly
|
166
|
+
http = http_device.send(:build_http)
|
167
|
+
http.start do |conn|
|
168
|
+
result = http_device.send(:deliver_requests, conn)
|
169
|
+
expect(result).to eq(false)
|
170
|
+
end
|
171
|
+
|
172
|
+
expect(req_queue.size).to eq(1)
|
173
|
+
|
174
|
+
# Start a HTTP connection to test the method directly
|
175
|
+
http = http_device.send(:build_http)
|
176
|
+
http.start do |conn|
|
177
|
+
result = http_device.send(:deliver_requests, conn)
|
178
|
+
expect(result).to eq(false)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Ensure the request gets discards after 3 attempts
|
182
|
+
expect(req_queue.size).to eq(0)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|