fluent-plugin-vmware-log-intelligence 2.0.5 → 2.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/Gemfile +10 -10
- data/LICENSE.txt +15 -15
- data/README.rdoc +113 -106
- data/Rakefile +15 -15
- data/VERSION +1 -1
- data/fluent-plugin-vmware-log-intelligence.gemspec +53 -53
- data/lib/fluent/plugin/http_client.rb +72 -72
- data/lib/fluent/plugin/out_vmware_log_intelligence.rb +201 -179
- data/test/helper.rb +31 -31
- data/test/plugin/test_http_client.rb +95 -95
- data/test/plugin/test_out_vmware_log_intelligence.rb +153 -153
- metadata +3 -2
@@ -1,179 +1,201 @@
|
|
1
|
-
# Copyright (c) 2013 ablagoev
|
2
|
-
# Copyright 2018 VMware, Inc.
|
3
|
-
# SPDX-License-Identifier: MIT
|
4
|
-
|
5
|
-
|
6
|
-
require
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
config_param :
|
15
|
-
config_param :
|
16
|
-
|
17
|
-
config_param :
|
18
|
-
config_param :
|
19
|
-
|
20
|
-
#
|
21
|
-
config_param :
|
22
|
-
|
23
|
-
|
24
|
-
#
|
25
|
-
config_param :
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
if
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
end
|
178
|
-
|
179
|
-
|
1
|
+
# Copyright (c) 2013 ablagoev
|
2
|
+
# Copyright 2018 VMware, Inc.
|
3
|
+
# SPDX-License-Identifier: MIT
|
4
|
+
|
5
|
+
|
6
|
+
require 'zlib'
|
7
|
+
require "fluent/plugin/output"
|
8
|
+
require "fluent/plugin/http_client"
|
9
|
+
|
10
|
+
module Fluent::Plugin
|
11
|
+
class LogIntelligenceOutput < Output
|
12
|
+
Fluent::Plugin.register_output('vmware_log_intelligence', self)
|
13
|
+
|
14
|
+
config_param :http_compress, :bool, :default => false
|
15
|
+
config_param :endpoint_url, :string
|
16
|
+
config_param :bearer_token, :string, default: ''
|
17
|
+
config_param :http_retry_statuses, :string, default: ''
|
18
|
+
config_param :read_timeout, :integer, default: 60
|
19
|
+
config_param :open_timeout, :integer, default: 60
|
20
|
+
# config_param :use_ssl, :bool, :default => false
|
21
|
+
config_param :verify_ssl, :bool, :default => true
|
22
|
+
config_param :rate_limit_msec, :integer, :default => 0
|
23
|
+
# Keys from log event whose values should be added as log message/text
|
24
|
+
# to log-intelligence. Note these key/value pairs won't be added as metadata/fields
|
25
|
+
config_param :log_text_keys, :array, default: ["log", "message", "msg"], value_type: :string
|
26
|
+
# Flatten hashes to create one key/val pair w/o losing log data
|
27
|
+
config_param :flatten_hashes, :bool, :default => true
|
28
|
+
# Seperator to use for joining flattened keys
|
29
|
+
config_param :flatten_hashes_separator, :string, :default => "_"
|
30
|
+
|
31
|
+
config_section :buffer do
|
32
|
+
config_set_default :@type, "memory"
|
33
|
+
config_set_default :chunk_keys, []
|
34
|
+
config_set_default :timekey_use_utc, true
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
super
|
39
|
+
require 'http'
|
40
|
+
require 'uri'
|
41
|
+
end
|
42
|
+
|
43
|
+
def validate_uri(uri_string)
|
44
|
+
unless uri_string =~ /^#{URI.regexp}$/
|
45
|
+
fail Fluent::ConfigError, 'endpoint_url invalid'
|
46
|
+
end
|
47
|
+
|
48
|
+
begin
|
49
|
+
@uri = URI.parse(uri_string)
|
50
|
+
rescue URI::InvalidURIError
|
51
|
+
raise Fluent::ConfigError, 'endpoint_url invalid'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def retrieve_headers(conf)
|
56
|
+
headers = {}
|
57
|
+
conf.elements.each do |element|
|
58
|
+
if @http_compress
|
59
|
+
set_gzip_header(element)
|
60
|
+
end
|
61
|
+
if element.name == 'headers'
|
62
|
+
if @bearer_token != ''
|
63
|
+
element['Authorization'] = 'Bearer ' + @bearer_token
|
64
|
+
end
|
65
|
+
headers = element.to_hash
|
66
|
+
end
|
67
|
+
end
|
68
|
+
headers
|
69
|
+
end
|
70
|
+
|
71
|
+
def set_gzip_header(element)
|
72
|
+
element['Content-Encoding'] = 'gzip'
|
73
|
+
element
|
74
|
+
end
|
75
|
+
|
76
|
+
def shorten_key(key)
|
77
|
+
# LINT doesn't allow some characters in field 'name'
|
78
|
+
# like '/', '-', '\', '.', etc. so replace them with @flatten_hashes_separator
|
79
|
+
key = key.gsub(/[\/\.\-\\]/,@flatten_hashes_separator).downcase
|
80
|
+
key
|
81
|
+
end
|
82
|
+
|
83
|
+
def create_lint_event(record)
|
84
|
+
flattened_records = {}
|
85
|
+
merged_records = {}
|
86
|
+
if @flatten_hashes
|
87
|
+
flattened_records = flatten_record(record, [])
|
88
|
+
else
|
89
|
+
flattened_records = record
|
90
|
+
end
|
91
|
+
|
92
|
+
keys = []
|
93
|
+
log = ''
|
94
|
+
flattened_records.each do |key, value|
|
95
|
+
begin
|
96
|
+
next if value.nil?
|
97
|
+
# LINT doesn't support duplicate fields, make unique names by appending underscore
|
98
|
+
key = shorten_key(key)
|
99
|
+
if keys.include?(key)
|
100
|
+
value = merged_records[key] + " " + value
|
101
|
+
end
|
102
|
+
keys.push(key)
|
103
|
+
key.force_encoding("utf-8")
|
104
|
+
|
105
|
+
if value.is_a?(String)
|
106
|
+
value.force_encoding("utf-8")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
if @log_text_keys.include?(key)
|
111
|
+
if log != "#{value}"
|
112
|
+
if log.empty?
|
113
|
+
log = "#{value}"
|
114
|
+
else
|
115
|
+
log += " #{value}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
else
|
119
|
+
merged_records[key] = value
|
120
|
+
end
|
121
|
+
end
|
122
|
+
merged_records["text"] = log
|
123
|
+
|
124
|
+
if log == "\\n"
|
125
|
+
{}
|
126
|
+
else
|
127
|
+
merged_records
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def flatten_record(record, prefix=[])
|
132
|
+
ret = {}
|
133
|
+
|
134
|
+
case record
|
135
|
+
when Hash
|
136
|
+
record.each do |key, value|
|
137
|
+
if @log_text_keys.include?(key)
|
138
|
+
ret.merge!({key.to_s => value})
|
139
|
+
else
|
140
|
+
ret.merge! flatten_record(value, prefix + [key.to_s])
|
141
|
+
end
|
142
|
+
end
|
143
|
+
when Array
|
144
|
+
record.each do |value|
|
145
|
+
ret.merge! flatten_record(value, prefix)
|
146
|
+
end
|
147
|
+
else
|
148
|
+
return {prefix.join(@flatten_hashes_separator) => record}
|
149
|
+
end
|
150
|
+
ret
|
151
|
+
end
|
152
|
+
|
153
|
+
def configure(conf)
|
154
|
+
super
|
155
|
+
validate_uri(@endpoint_url)
|
156
|
+
|
157
|
+
@statuses = @http_retry_statuses.split(',').map { |status| status.to_i }
|
158
|
+
@statuses = [] if @statuses.nil?
|
159
|
+
|
160
|
+
@headers = retrieve_headers(conf)
|
161
|
+
|
162
|
+
@http_client = Fluent::Plugin::HttpClient.new(
|
163
|
+
@endpoint_url, @verify_ssl, @headers, @statuses,
|
164
|
+
@open_timeout, @read_timeout, @log)
|
165
|
+
end
|
166
|
+
|
167
|
+
def start
|
168
|
+
super
|
169
|
+
end
|
170
|
+
|
171
|
+
def shutdown
|
172
|
+
super
|
173
|
+
begin
|
174
|
+
@http_client.close if @http_client
|
175
|
+
rescue
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def write(chunk)
|
180
|
+
is_rate_limited = (@rate_limit_msec != 0 and not @last_request_time.nil?)
|
181
|
+
if is_rate_limited and ((Time.now.to_f - @last_request_time) * 1000.0 < @rate_limit_msec)
|
182
|
+
@log.info('Dropped request due to rate limiting')
|
183
|
+
return
|
184
|
+
end
|
185
|
+
|
186
|
+
data = []
|
187
|
+
chunk.each do |time, record|
|
188
|
+
data << create_lint_event(record)
|
189
|
+
end
|
190
|
+
|
191
|
+
if @http_compress
|
192
|
+
gzip_body = Zlib::GzipWriter.new(StringIO.new)
|
193
|
+
gzip_body << data.to_json
|
194
|
+
@http_client.post(gzip_body.close.string)
|
195
|
+
else
|
196
|
+
@last_request_time = Time.now.to_f
|
197
|
+
@http_client.post(JSON.dump(data))
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
data/test/helper.rb
CHANGED
@@ -1,31 +1,31 @@
|
|
1
|
-
# Copyright (c) 2013 ablagoev
|
2
|
-
# Copyright 2018 VMware, Inc.
|
3
|
-
# SPDX-License-Identifier: MIT
|
4
|
-
|
5
|
-
require 'coveralls'
|
6
|
-
Coveralls.wear!
|
7
|
-
|
8
|
-
require 'rubygems'
|
9
|
-
require 'bundler'
|
10
|
-
|
11
|
-
begin
|
12
|
-
Bundler.setup(:default, :development)
|
13
|
-
rescue Bundler::BundlerError => e
|
14
|
-
$stderr.puts e.message
|
15
|
-
$stderr.puts 'Run `bundle install` to install missing gems'
|
16
|
-
exit e.status_code
|
17
|
-
end
|
18
|
-
|
19
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
20
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
21
|
-
|
22
|
-
require 'test-unit'
|
23
|
-
require 'fluent/test'
|
24
|
-
require "fluent/test/driver/output"
|
25
|
-
require "fluent/test/helpers"
|
26
|
-
Test::Unit::TestCase.include(Fluent::Test::Helpers)
|
27
|
-
Test::Unit::TestCase.extend(Fluent::Test::Helpers)
|
28
|
-
|
29
|
-
require 'fluent/plugin/out_vmware_log_intelligence'
|
30
|
-
require 'webmock/test_unit'
|
31
|
-
WebMock.disable_net_connect!
|
1
|
+
# Copyright (c) 2013 ablagoev
|
2
|
+
# Copyright 2018 VMware, Inc.
|
3
|
+
# SPDX-License-Identifier: MIT
|
4
|
+
|
5
|
+
require 'coveralls'
|
6
|
+
Coveralls.wear!
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'bundler'
|
10
|
+
|
11
|
+
begin
|
12
|
+
Bundler.setup(:default, :development)
|
13
|
+
rescue Bundler::BundlerError => e
|
14
|
+
$stderr.puts e.message
|
15
|
+
$stderr.puts 'Run `bundle install` to install missing gems'
|
16
|
+
exit e.status_code
|
17
|
+
end
|
18
|
+
|
19
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
20
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
21
|
+
|
22
|
+
require 'test-unit'
|
23
|
+
require 'fluent/test'
|
24
|
+
require "fluent/test/driver/output"
|
25
|
+
require "fluent/test/helpers"
|
26
|
+
Test::Unit::TestCase.include(Fluent::Test::Helpers)
|
27
|
+
Test::Unit::TestCase.extend(Fluent::Test::Helpers)
|
28
|
+
|
29
|
+
require 'fluent/plugin/out_vmware_log_intelligence'
|
30
|
+
require 'webmock/test_unit'
|
31
|
+
WebMock.disable_net_connect!
|
@@ -1,95 +1,95 @@
|
|
1
|
-
# Copyright 2018 VMware, Inc.
|
2
|
-
# SPDX-License-Identifier: MIT
|
3
|
-
|
4
|
-
require 'helper'
|
5
|
-
|
6
|
-
class HttpClientTest < Test::Unit::TestCase
|
7
|
-
DEFAULT_URL = "https://local.endpoint:3000/dummy/xyz"
|
8
|
-
|
9
|
-
def stub_server_out_of_quota(url=DEFAULT_URL)
|
10
|
-
stub_request(:post, url).to_return(:status => [429, "User out of ingestion quota."])
|
11
|
-
end
|
12
|
-
|
13
|
-
def test_check_quota
|
14
|
-
http_client = create_http_client()
|
15
|
-
assert_equal http_client.check_quota, true
|
16
|
-
|
17
|
-
stub_server_out_of_quota
|
18
|
-
http_client.post(JSON.dump(sample_record()))
|
19
|
-
assert_equal http_client.check_quota, false
|
20
|
-
end
|
21
|
-
|
22
|
-
def stub_server_unavailable(url=DEFAULT_URL)
|
23
|
-
stub_request(:post, url).to_return(:status => [503, "Service Unavailable"])
|
24
|
-
end
|
25
|
-
|
26
|
-
def stub_server_returns_500(url=DEFAULT_URL)
|
27
|
-
stub_request(:post, url).to_return(:status => [500, "Internal service error"])
|
28
|
-
end
|
29
|
-
|
30
|
-
def stub_server_raise_error(url=DEFAULT_URL)
|
31
|
-
stub_request(:post, url).with do |req|
|
32
|
-
raise IOError
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def stub_post_logs(url=DEFAULT_URL)
|
37
|
-
stub_request(:post, url)
|
38
|
-
.with(
|
39
|
-
body: "[{\"field1\":26,\"field2\":\"value26\"},{\"field1\":27,\"field2\":\"value27\"},{\"field1\":28,\"field2\":\"value28\"}]",
|
40
|
-
headers: {
|
41
|
-
'Authorization'=>'Bearer EdaNNN68y',
|
42
|
-
'Connection'=>'Keep-Alive',
|
43
|
-
'Content-Type'=>'application/json',
|
44
|
-
'Format'=>'syslog',
|
45
|
-
'Structure'=>'simple'
|
46
|
-
})
|
47
|
-
.to_return(:status => 200, :body => "ok")
|
48
|
-
end
|
49
|
-
|
50
|
-
def sample_record()
|
51
|
-
[
|
52
|
-
{'field1' => 26, 'field2' => 'value26'},
|
53
|
-
{'field1' => 27, 'field2' => 'value27'},
|
54
|
-
{'field1' => 28, 'field2' => 'value28'}
|
55
|
-
]
|
56
|
-
end
|
57
|
-
|
58
|
-
def create_http_client
|
59
|
-
Fluent::Plugin::HttpClient.new(
|
60
|
-
DEFAULT_URL, true,
|
61
|
-
{
|
62
|
-
'Authorization'=>'Bearer EdaNNN68y',
|
63
|
-
'Connection'=>'Keep-Alive',
|
64
|
-
'Content-Type'=>'application/json',
|
65
|
-
'Format'=>'syslog',
|
66
|
-
'Host'=>'local.endpoint:3000',
|
67
|
-
'Structure'=>'simple'
|
68
|
-
}, [500, 510], 60, 60, Logger.new(STDOUT))
|
69
|
-
end
|
70
|
-
|
71
|
-
def test_retry_on_response_status_code
|
72
|
-
http_client = create_http_client()
|
73
|
-
stub_server_returns_500
|
74
|
-
assert_raise RuntimeError do
|
75
|
-
http_client.post(JSON.dump(sample_record()))
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def test_server_raise_error
|
80
|
-
http_client = create_http_client()
|
81
|
-
stub_server_raise_error
|
82
|
-
assert_raise IOError do
|
83
|
-
http_client.post(JSON.dump(sample_record()))
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def test_post_logs
|
88
|
-
stub_post_logs
|
89
|
-
http_client = create_http_client()
|
90
|
-
http_client.post(JSON.dump(sample_record()))
|
91
|
-
|
92
|
-
stub_post_logs
|
93
|
-
http_client.post(JSON.dump(sample_record()))
|
94
|
-
end
|
95
|
-
end
|
1
|
+
# Copyright 2018 VMware, Inc.
|
2
|
+
# SPDX-License-Identifier: MIT
|
3
|
+
|
4
|
+
require 'helper'
|
5
|
+
|
6
|
+
class HttpClientTest < Test::Unit::TestCase
|
7
|
+
DEFAULT_URL = "https://local.endpoint:3000/dummy/xyz"
|
8
|
+
|
9
|
+
def stub_server_out_of_quota(url=DEFAULT_URL)
|
10
|
+
stub_request(:post, url).to_return(:status => [429, "User out of ingestion quota."])
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_check_quota
|
14
|
+
http_client = create_http_client()
|
15
|
+
assert_equal http_client.check_quota, true
|
16
|
+
|
17
|
+
stub_server_out_of_quota
|
18
|
+
http_client.post(JSON.dump(sample_record()))
|
19
|
+
assert_equal http_client.check_quota, false
|
20
|
+
end
|
21
|
+
|
22
|
+
def stub_server_unavailable(url=DEFAULT_URL)
|
23
|
+
stub_request(:post, url).to_return(:status => [503, "Service Unavailable"])
|
24
|
+
end
|
25
|
+
|
26
|
+
def stub_server_returns_500(url=DEFAULT_URL)
|
27
|
+
stub_request(:post, url).to_return(:status => [500, "Internal service error"])
|
28
|
+
end
|
29
|
+
|
30
|
+
def stub_server_raise_error(url=DEFAULT_URL)
|
31
|
+
stub_request(:post, url).with do |req|
|
32
|
+
raise IOError
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def stub_post_logs(url=DEFAULT_URL)
|
37
|
+
stub_request(:post, url)
|
38
|
+
.with(
|
39
|
+
body: "[{\"field1\":26,\"field2\":\"value26\"},{\"field1\":27,\"field2\":\"value27\"},{\"field1\":28,\"field2\":\"value28\"}]",
|
40
|
+
headers: {
|
41
|
+
'Authorization'=>'Bearer EdaNNN68y',
|
42
|
+
'Connection'=>'Keep-Alive',
|
43
|
+
'Content-Type'=>'application/json',
|
44
|
+
'Format'=>'syslog',
|
45
|
+
'Structure'=>'simple'
|
46
|
+
})
|
47
|
+
.to_return(:status => 200, :body => "ok")
|
48
|
+
end
|
49
|
+
|
50
|
+
def sample_record()
|
51
|
+
[
|
52
|
+
{'field1' => 26, 'field2' => 'value26'},
|
53
|
+
{'field1' => 27, 'field2' => 'value27'},
|
54
|
+
{'field1' => 28, 'field2' => 'value28'}
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_http_client
|
59
|
+
Fluent::Plugin::HttpClient.new(
|
60
|
+
DEFAULT_URL, true,
|
61
|
+
{
|
62
|
+
'Authorization'=>'Bearer EdaNNN68y',
|
63
|
+
'Connection'=>'Keep-Alive',
|
64
|
+
'Content-Type'=>'application/json',
|
65
|
+
'Format'=>'syslog',
|
66
|
+
'Host'=>'local.endpoint:3000',
|
67
|
+
'Structure'=>'simple'
|
68
|
+
}, [500, 510], 60, 60, Logger.new(STDOUT))
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_retry_on_response_status_code
|
72
|
+
http_client = create_http_client()
|
73
|
+
stub_server_returns_500
|
74
|
+
assert_raise RuntimeError do
|
75
|
+
http_client.post(JSON.dump(sample_record()))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_server_raise_error
|
80
|
+
http_client = create_http_client()
|
81
|
+
stub_server_raise_error
|
82
|
+
assert_raise IOError do
|
83
|
+
http_client.post(JSON.dump(sample_record()))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_post_logs
|
88
|
+
stub_post_logs
|
89
|
+
http_client = create_http_client()
|
90
|
+
http_client.post(JSON.dump(sample_record()))
|
91
|
+
|
92
|
+
stub_post_logs
|
93
|
+
http_client.post(JSON.dump(sample_record()))
|
94
|
+
end
|
95
|
+
end
|