fluent-plugin-sumologic_output 1.6.1 → 1.7.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +5 -0
- data/.gitignore +2 -1
- data/CHANGELOG.md +10 -0
- data/README.md +5 -3
- data/Vagrantfile +27 -0
- data/fluent-plugin-sumologic_output.gemspec +1 -1
- data/lib/fluent/plugin/out_sumologic.rb +140 -19
- data/test/plugin/test_out_sumologic.rb +323 -1
- data/vagrant/README.md +43 -0
- data/vagrant/provision.sh +27 -0
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52b0c77e900d61ba360421933440383208033b29b0f08b012250519189872f24
|
4
|
+
data.tar.gz: ff86f952744e650e30ba94d1185f4d270a3d8d6e7bfee5f30a0810e043a9483f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74c43d262f35e2767d170a05dbec58911590c0851a724f6c4adb0047a26601383e4d8fa5e2671c845bbd89a65781fb0d35e4bc2f8d7ed6bd0f0e376d6c7ec958
|
7
|
+
data.tar.gz: 73b8ea2a6dbf4593eca7d4a702eeea14f27728bd7aa28ddc15e968301a5a2eb21f76ae18f1be2dd759f42cb357c2ceda92aec49e136755d76c954ebded889cdd
|
data/.github/CODEOWNERS
ADDED
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
All notable changes to this project will be documented in this file. Tracking did not begin until version 1.10.
|
4
4
|
|
5
|
+
<a name="1.7.1"></a>
|
6
|
+
# [1.7.1] (2020-04-28)
|
7
|
+
- Fix configuration for older fluentd versions [#63](https://github.com/SumoLogic/fluentd-output-sumologic/pull/63)
|
8
|
+
|
9
|
+
<a name="1.7.0"></a>
|
10
|
+
# [1.7.0] (2020-04-23)
|
11
|
+
- Add option for specifing custom fields for logs: [#56](https://github.com/SumoLogic/fluentd-output-sumologic/pull/56)
|
12
|
+
- Add option for specifing custom dimensions for metrics: [#57](https://github.com/SumoLogic/fluentd-output-sumologic/pull/57)
|
13
|
+
- Add support for compression: [#58](https://github.com/SumoLogic/fluentd-output-sumologic/pull/58)
|
14
|
+
|
5
15
|
<a name="1.5.0"></a>
|
6
16
|
# [1.5.0] (2019-06-26)
|
7
17
|
- Add support for new log format fields: [#49](https://github.com/SumoLogic/fluentd-output-sumologic/pull/49)
|
data/README.md
CHANGED
@@ -4,9 +4,6 @@
|
|
4
4
|
|
5
5
|
This plugin has been designed to output logs or metrics to [SumoLogic](http://www.sumologic.com) via a [HTTP collector endpoint](http://help.sumologic.com/Send_Data/Sources/02Sources_for_Hosted_Collectors/HTTP_Source)
|
6
6
|
|
7
|
-
## Support
|
8
|
-
The code in this repository has been developed in collaboration with the Sumo Logic community and is not supported via standard Sumo Logic Support channels. For any issues or questions please submit an issue within the GitHub repository. The maintainers of this project will work directly with the community to answer any questions, address bugs, or review any requests for new features.
|
9
|
-
|
10
7
|
## License
|
11
8
|
Released under Apache 2.0 License.
|
12
9
|
|
@@ -31,11 +28,16 @@ Configuration options for fluent.conf are:
|
|
31
28
|
* json_merge - Same as json but merge content of `log_key` into the top level and strip `log_key`
|
32
29
|
* `log_key` - Used to specify the key when merging json or sending logs in text format (default `message`)
|
33
30
|
* `open_timeout` - Set timeout seconds to wait until connection is opened.
|
31
|
+
* `send_timeout` - Timeout for sending to SumoLogic in seconds. Don't modify unless you see `HTTPClient::SendTimeoutError` in your Fluentd logs. (default `120`)
|
34
32
|
* `add_timestamp` - Add `timestamp` (or `timestamp_key`) field to logs before sending to sumologic (default `true`)
|
35
33
|
* `timestamp_key` - Field name when `add_timestamp` is on (default `timestamp`)
|
36
34
|
* `proxy_uri` - Add the `uri` of the `proxy` environment if present.
|
37
35
|
* `metric_data_format` - The format of metrics you will be sending, either `graphite` or `carbon2` or `prometheus` (Default is `graphite `)
|
38
36
|
* `disable_cookies` - Option to disable cookies on the HTTP Client. (Default is `false `)
|
37
|
+
* `compress` - Option to enable compression (default `false`)
|
38
|
+
* `compress_encoding` - Compression encoding format, either `gzip` or `deflate` (default `gzip`)
|
39
|
+
* `custom_fields` - Comma-separated key=value list of fields to apply to every log. [more information](https://help.sumologic.com/Manage/Fields#http-source-fields)
|
40
|
+
* `custom_dimensions` - Comma-separated key=value list of dimensions to apply to every metric. [more information](https://help.sumologic.com/03Send-Data/Sources/02Sources-for-Hosted-Collectors/HTTP-Source/Upload-Metrics-to-an-HTTP-Source#supported-http-headers)
|
39
41
|
|
40
42
|
__NOTE:__ <sup>*</sup> [Placeholders](https://docs.fluentd.org/v1.0/articles/buffer-section#placeholders) are supported
|
41
43
|
|
data/Vagrantfile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- mode: ruby -*-
|
2
|
+
# vi: set ft=ruby :
|
3
|
+
|
4
|
+
unless Vagrant.has_plugin?("vagrant-disksize")
|
5
|
+
puts "vagrant-disksize plugin unavailable\n" +
|
6
|
+
"please install it via 'vagrant plugin install vagrant-disksize'"
|
7
|
+
exit 1
|
8
|
+
end
|
9
|
+
|
10
|
+
Vagrant.configure('2') do |config|
|
11
|
+
config.vm.box = 'ubuntu/focal64'
|
12
|
+
config.disksize.size = '50GB'
|
13
|
+
config.vm.box_check_update = false
|
14
|
+
config.vm.host_name = 'fluentd-output-sumologic'
|
15
|
+
config.vm.network :private_network, ip: "192.168.78.45"
|
16
|
+
|
17
|
+
config.vm.provider 'virtualbox' do |vb|
|
18
|
+
vb.gui = false
|
19
|
+
vb.cpus = 8
|
20
|
+
vb.memory = 16384
|
21
|
+
vb.name = 'fluentd-output-sumologic'
|
22
|
+
end
|
23
|
+
|
24
|
+
config.vm.provision 'shell', path: 'vagrant/provision.sh'
|
25
|
+
|
26
|
+
config.vm.synced_folder ".", "/sumologic"
|
27
|
+
end
|
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |gem|
|
6
6
|
gem.name = "fluent-plugin-sumologic_output"
|
7
|
-
gem.version = "1.
|
7
|
+
gem.version = "1.7.3"
|
8
8
|
gem.authors = ["Steven Adams", "Frank Reno"]
|
9
9
|
gem.email = ["stevezau@gmail.com", "frank.reno@me.com"]
|
10
10
|
gem.description = %q{Output plugin to SumoLogic HTTP Endpoint}
|
@@ -1,31 +1,49 @@
|
|
1
1
|
require 'fluent/plugin/output'
|
2
2
|
require 'net/https'
|
3
|
+
require 'json'
|
3
4
|
require 'yajl'
|
4
5
|
require 'httpclient'
|
6
|
+
require 'zlib'
|
7
|
+
require 'stringio'
|
5
8
|
|
6
9
|
class SumologicConnection
|
7
10
|
|
8
11
|
attr_reader :http
|
9
12
|
|
10
|
-
|
13
|
+
COMPRESS_DEFLATE = 'deflate'
|
14
|
+
COMPRESS_GZIP = 'gzip'
|
15
|
+
|
16
|
+
def initialize(endpoint, verify_ssl, connect_timeout, send_timeout, proxy_uri, disable_cookies, sumo_client, compress_enabled, compress_encoding)
|
11
17
|
@endpoint = endpoint
|
12
|
-
|
18
|
+
@sumo_client = sumo_client
|
19
|
+
create_http_client(verify_ssl, connect_timeout, send_timeout, proxy_uri, disable_cookies)
|
20
|
+
@compress = compress_enabled
|
21
|
+
@compress_encoding = (compress_encoding ||= COMPRESS_GZIP).downcase
|
22
|
+
|
23
|
+
unless [COMPRESS_DEFLATE, COMPRESS_GZIP].include? @compress_encoding
|
24
|
+
raise "Invalid compression encoding #{@compress_encoding} must be gzip or deflate"
|
25
|
+
end
|
13
26
|
end
|
14
27
|
|
15
|
-
def publish(raw_data, source_host=nil, source_category=nil, source_name=nil, data_type, metric_data_type, collected_fields)
|
16
|
-
response = http.post(@endpoint, raw_data, request_headers(source_host, source_category, source_name, data_type, metric_data_type, collected_fields))
|
28
|
+
def publish(raw_data, source_host=nil, source_category=nil, source_name=nil, data_type, metric_data_type, collected_fields, dimensions)
|
29
|
+
response = http.post(@endpoint, compress(raw_data), request_headers(source_host, source_category, source_name, data_type, metric_data_type, collected_fields, dimensions))
|
17
30
|
unless response.ok?
|
18
31
|
raise RuntimeError, "Failed to send data to HTTP Source. #{response.code} - #{response.body}"
|
19
32
|
end
|
20
33
|
end
|
21
34
|
|
22
|
-
def request_headers(source_host, source_category, source_name, data_type, metric_data_format, collected_fields)
|
35
|
+
def request_headers(source_host, source_category, source_name, data_type, metric_data_format, collected_fields, dimensions)
|
23
36
|
headers = {
|
24
37
|
'X-Sumo-Name' => source_name,
|
25
38
|
'X-Sumo-Category' => source_category,
|
26
39
|
'X-Sumo-Host' => source_host,
|
27
|
-
'X-Sumo-Client' =>
|
40
|
+
'X-Sumo-Client' => @sumo_client,
|
28
41
|
}
|
42
|
+
|
43
|
+
if @compress
|
44
|
+
headers['Content-Encoding'] = @compress_encoding
|
45
|
+
end
|
46
|
+
|
29
47
|
if data_type == 'metrics'
|
30
48
|
case metric_data_format
|
31
49
|
when 'graphite'
|
@@ -37,6 +55,10 @@ class SumologicConnection
|
|
37
55
|
else
|
38
56
|
raise RuntimeError, "Invalid #{metric_data_format}, must be graphite or carbon2 or prometheus"
|
39
57
|
end
|
58
|
+
|
59
|
+
unless dimensions.nil?
|
60
|
+
headers['X-Sumo-Dimensions'] = dimensions
|
61
|
+
end
|
40
62
|
end
|
41
63
|
unless collected_fields.nil?
|
42
64
|
headers['X-Sumo-Fields'] = collected_fields
|
@@ -48,14 +70,38 @@ class SumologicConnection
|
|
48
70
|
verify_ssl==true ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
49
71
|
end
|
50
72
|
|
51
|
-
def create_http_client(verify_ssl, connect_timeout, proxy_uri, disable_cookies)
|
73
|
+
def create_http_client(verify_ssl, connect_timeout, send_timeout, proxy_uri, disable_cookies)
|
52
74
|
@http = HTTPClient.new(proxy_uri)
|
53
75
|
@http.ssl_config.verify_mode = ssl_options(verify_ssl)
|
54
76
|
@http.connect_timeout = connect_timeout
|
77
|
+
@http.send_timeout = send_timeout
|
55
78
|
if disable_cookies
|
56
79
|
@http.cookie_manager = nil
|
57
80
|
end
|
58
81
|
end
|
82
|
+
|
83
|
+
def compress(content)
|
84
|
+
if @compress
|
85
|
+
if @compress_encoding == COMPRESS_GZIP
|
86
|
+
result = gzip(content)
|
87
|
+
result.bytes.to_a.pack("c*")
|
88
|
+
else
|
89
|
+
Zlib::Deflate.deflate(content)
|
90
|
+
end
|
91
|
+
else
|
92
|
+
content
|
93
|
+
end
|
94
|
+
end # def compress
|
95
|
+
|
96
|
+
def gzip(content)
|
97
|
+
stream = StringIO.new("w")
|
98
|
+
stream.set_encoding("ASCII")
|
99
|
+
gz = Zlib::GzipWriter.new(stream)
|
100
|
+
gz.mtime=1 # Ensure that for same content there is same output
|
101
|
+
gz.write(content)
|
102
|
+
gz.close
|
103
|
+
stream.string.bytes.to_a.pack("c*")
|
104
|
+
end # def gzip
|
59
105
|
end
|
60
106
|
|
61
107
|
class Fluent::Plugin::Sumologic < Fluent::Plugin::Output
|
@@ -82,10 +128,23 @@ class Fluent::Plugin::Sumologic < Fluent::Plugin::Output
|
|
82
128
|
config_param :verify_ssl, :bool, :default => true
|
83
129
|
config_param :delimiter, :string, :default => "."
|
84
130
|
config_param :open_timeout, :integer, :default => 60
|
131
|
+
config_param :send_timeout, :integer, :default => 120
|
85
132
|
config_param :add_timestamp, :bool, :default => true
|
86
133
|
config_param :timestamp_key, :string, :default => 'timestamp'
|
87
134
|
config_param :proxy_uri, :string, :default => nil
|
88
135
|
config_param :disable_cookies, :bool, :default => false
|
136
|
+
# https://help.sumologic.com/Manage/Fields
|
137
|
+
desc 'Fields string (eg "cluster=payment, service=credit_card") which is going to be added to every log record.'
|
138
|
+
config_param :custom_fields, :string, :default => nil
|
139
|
+
desc 'Name of sumo client which is send as X-Sumo-Client header'
|
140
|
+
config_param :sumo_client, :string, :default => 'fluentd-output'
|
141
|
+
desc 'Compress payload'
|
142
|
+
config_param :compress, :bool, :default => false
|
143
|
+
desc 'Encoding method of compresssion (either gzip or deflate)'
|
144
|
+
config_param :compress_encoding, :string, :default => SumologicConnection::COMPRESS_GZIP
|
145
|
+
# https://help.sumologic.com/03Send-Data/Sources/02Sources-for-Hosted-Collectors/HTTP-Source/Upload-Metrics-to-an-HTTP-Source#supported-http-headers
|
146
|
+
desc 'Dimensions string (eg "cluster=payment, service=credit_card") which is going to be added to every metric record.'
|
147
|
+
config_param :custom_dimensions, :string, :default => nil
|
89
148
|
|
90
149
|
config_section :buffer do
|
91
150
|
config_set_default :@type, DEFAULT_BUFFER_TYPE
|
@@ -129,7 +188,38 @@ class Fluent::Plugin::Sumologic < Fluent::Plugin::Output
|
|
129
188
|
end
|
130
189
|
end
|
131
190
|
|
132
|
-
|
191
|
+
conf['custom_fields'] = validate_key_value_pairs(conf['custom_fields'])
|
192
|
+
if conf['custom_fields'].nil?
|
193
|
+
conf.delete 'custom_fields'
|
194
|
+
end
|
195
|
+
unless conf['custom_fields']
|
196
|
+
@log.debug "Custom fields: #{conf['custom_fields']}"
|
197
|
+
end
|
198
|
+
|
199
|
+
conf['custom_dimensions'] = validate_key_value_pairs(conf['custom_dimensions'])
|
200
|
+
if conf['custom_dimensions'].nil?
|
201
|
+
conf.delete 'custom_dimensions'
|
202
|
+
end
|
203
|
+
unless conf['custom_dimensions']
|
204
|
+
@log.debug "Custom dimensions: #{conf['custom_dimensions']}"
|
205
|
+
end
|
206
|
+
|
207
|
+
# For some reason default is set incorrectly in unit-tests
|
208
|
+
if conf['sumo_client'].nil? || conf['sumo_client'].strip.length == 0
|
209
|
+
conf['sumo_client'] = 'fluentd-output'
|
210
|
+
end
|
211
|
+
|
212
|
+
@sumo_conn = SumologicConnection.new(
|
213
|
+
conf['endpoint'],
|
214
|
+
conf['verify_ssl'],
|
215
|
+
conf['open_timeout'].to_i,
|
216
|
+
conf['send_timeout'].to_i,
|
217
|
+
conf['proxy_uri'],
|
218
|
+
conf['disable_cookies'],
|
219
|
+
conf['sumo_client'],
|
220
|
+
conf['compress'],
|
221
|
+
conf['compress_encoding']
|
222
|
+
)
|
133
223
|
super
|
134
224
|
end
|
135
225
|
|
@@ -163,8 +253,7 @@ class Fluent::Plugin::Sumologic < Fluent::Plugin::Output
|
|
163
253
|
def dump_log(log)
|
164
254
|
log.delete('_sumo_metadata')
|
165
255
|
begin
|
166
|
-
|
167
|
-
hash = parser.parse(log[@log_key])
|
256
|
+
hash = JSON.parse(log[@log_key])
|
168
257
|
log[@log_key] = hash
|
169
258
|
Yajl.dump(log)
|
170
259
|
rescue
|
@@ -207,6 +296,19 @@ class Fluent::Plugin::Sumologic < Fluent::Plugin::Output
|
|
207
296
|
time.to_s.length == 13 ? time : time * 1000
|
208
297
|
end
|
209
298
|
|
299
|
+
# Convert log to string and strip it
|
300
|
+
def log_to_str(log)
|
301
|
+
if log.is_a?(Array) or log.is_a?(Hash)
|
302
|
+
log = Yajl.dump(log)
|
303
|
+
end
|
304
|
+
|
305
|
+
unless log.nil?
|
306
|
+
log.strip!
|
307
|
+
end
|
308
|
+
|
309
|
+
return log
|
310
|
+
end
|
311
|
+
|
210
312
|
# This method is called every flush interval. Write the buffer chunk
|
211
313
|
def write(chunk)
|
212
314
|
messages_list = {}
|
@@ -227,10 +329,7 @@ class Fluent::Plugin::Sumologic < Fluent::Plugin::Output
|
|
227
329
|
when 'logs'
|
228
330
|
case log_format
|
229
331
|
when 'text'
|
230
|
-
log = record[@log_key]
|
231
|
-
unless log.nil?
|
232
|
-
log.strip!
|
233
|
-
end
|
332
|
+
log = log_to_str(record[@log_key])
|
234
333
|
when 'json_merge'
|
235
334
|
if @add_timestamp
|
236
335
|
record = { @timestamp_key => sumo_timestamp(time) }.merge(record)
|
@@ -248,10 +347,7 @@ class Fluent::Plugin::Sumologic < Fluent::Plugin::Output
|
|
248
347
|
log = dump_log(record)
|
249
348
|
end
|
250
349
|
when 'metrics'
|
251
|
-
log = record[@log_key]
|
252
|
-
unless log.nil?
|
253
|
-
log.strip!
|
254
|
-
end
|
350
|
+
log = log_to_str(record[@log_key])
|
255
351
|
end
|
256
352
|
|
257
353
|
unless log.nil?
|
@@ -268,6 +364,14 @@ class Fluent::Plugin::Sumologic < Fluent::Plugin::Output
|
|
268
364
|
messages_list.each do |key, messages|
|
269
365
|
source_name, source_category, source_host, fields = key[:source_name], key[:source_category],
|
270
366
|
key[:source_host], key[:fields]
|
367
|
+
|
368
|
+
# Merge custom and record fields
|
369
|
+
if fields.nil? || fields.strip.length == 0
|
370
|
+
fields = @custom_fields
|
371
|
+
else
|
372
|
+
fields = [fields,@custom_fields].compact.join(",")
|
373
|
+
end
|
374
|
+
|
271
375
|
@sumo_conn.publish(
|
272
376
|
messages.join("\n"),
|
273
377
|
source_host =source_host,
|
@@ -275,9 +379,26 @@ class Fluent::Plugin::Sumologic < Fluent::Plugin::Output
|
|
275
379
|
source_name =source_name,
|
276
380
|
data_type =@data_type,
|
277
381
|
metric_data_format =@metric_data_format,
|
278
|
-
collected_fields =fields
|
382
|
+
collected_fields =fields,
|
383
|
+
dimensions =@custom_dimensions
|
279
384
|
)
|
280
385
|
end
|
281
386
|
|
282
387
|
end
|
388
|
+
|
389
|
+
def validate_key_value_pairs(fields)
|
390
|
+
if fields.nil?
|
391
|
+
return fields
|
392
|
+
end
|
393
|
+
|
394
|
+
fields = fields.split(",").select { |field|
|
395
|
+
field.split('=').length == 2
|
396
|
+
}
|
397
|
+
|
398
|
+
if fields.length == 0
|
399
|
+
return nil
|
400
|
+
end
|
401
|
+
|
402
|
+
fields.join(',')
|
403
|
+
end
|
283
404
|
end
|
@@ -72,6 +72,8 @@ class SumologicOutput < Test::Unit::TestCase
|
|
72
72
|
assert_equal instance.timestamp_key, 'timestamp'
|
73
73
|
assert_equal instance.proxy_uri, nil
|
74
74
|
assert_equal instance.disable_cookies, false
|
75
|
+
assert_equal instance.sumo_client, 'fluentd-output'
|
76
|
+
assert_equal instance.compress_encoding, 'gzip'
|
75
77
|
end
|
76
78
|
|
77
79
|
def test_emit_text
|
@@ -95,6 +97,28 @@ class SumologicOutput < Test::Unit::TestCase
|
|
95
97
|
times:1
|
96
98
|
end
|
97
99
|
|
100
|
+
def test_emit_text_custom_sumo_client
|
101
|
+
config = %{
|
102
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
103
|
+
log_format text
|
104
|
+
source_category test
|
105
|
+
source_host test
|
106
|
+
source_name test
|
107
|
+
sumo_client 'fluentd-custom-sender'
|
108
|
+
|
109
|
+
}
|
110
|
+
driver = create_driver(config)
|
111
|
+
time = event_time
|
112
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
113
|
+
driver.run do
|
114
|
+
driver.feed("output.test", time, {'foo' => 'bar', 'message' => 'test'})
|
115
|
+
end
|
116
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
117
|
+
headers: {'X-Sumo-Category'=>'test', 'X-Sumo-Client'=>'fluentd-custom-sender', 'X-Sumo-Host'=>'test', 'X-Sumo-Name'=>'test'},
|
118
|
+
body: "test",
|
119
|
+
times:1
|
120
|
+
end
|
121
|
+
|
98
122
|
def test_emit_json
|
99
123
|
config = %{
|
100
124
|
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
@@ -267,6 +291,78 @@ class SumologicOutput < Test::Unit::TestCase
|
|
267
291
|
times:1
|
268
292
|
end
|
269
293
|
|
294
|
+
def test_emit_with_sumo_metadata_with_fields_and_custom_fields_fields_format
|
295
|
+
config = %{
|
296
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
297
|
+
log_format fields
|
298
|
+
custom_fields "lorem=ipsum,dolor=amet"
|
299
|
+
}
|
300
|
+
driver = create_driver(config)
|
301
|
+
time = event_time
|
302
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
303
|
+
ENV['HOST'] = "foo"
|
304
|
+
driver.run do
|
305
|
+
driver.feed("output.test", time, {'foo' => 'shark', 'message' => 'test', '_sumo_metadata' => {
|
306
|
+
"host": "#{ENV['HOST']}",
|
307
|
+
"source": "${tag}",
|
308
|
+
"category": "test",
|
309
|
+
"fields": "foo=bar, sumo = logic"
|
310
|
+
}})
|
311
|
+
end
|
312
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
313
|
+
headers: {'X-Sumo-Category'=>'test', 'X-Sumo-Client'=>'fluentd-output', 'X-Sumo-Host'=>'foo', 'X-Sumo-Name'=>'output.test', 'X-Sumo-Fields' => 'foo=bar, sumo = logic,lorem=ipsum,dolor=amet'},
|
314
|
+
body: /\A{"timestamp":\d+.,"foo":"shark","message":"test"}\z/,
|
315
|
+
times:1
|
316
|
+
end
|
317
|
+
|
318
|
+
def test_emit_with_sumo_metadata_with_fields_and_empty_custom_fields_fields_format
|
319
|
+
config = %{
|
320
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
321
|
+
log_format fields
|
322
|
+
custom_fields ""
|
323
|
+
}
|
324
|
+
driver = create_driver(config)
|
325
|
+
time = event_time
|
326
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
327
|
+
ENV['HOST'] = "foo"
|
328
|
+
driver.run do
|
329
|
+
driver.feed("output.test", time, {'foo' => 'shark', 'message' => 'test', '_sumo_metadata' => {
|
330
|
+
"host": "#{ENV['HOST']}",
|
331
|
+
"source": "${tag}",
|
332
|
+
"category": "test",
|
333
|
+
"fields": "foo=bar, sumo = logic"
|
334
|
+
}})
|
335
|
+
end
|
336
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
337
|
+
headers: {'X-Sumo-Category'=>'test', 'X-Sumo-Client'=>'fluentd-output', 'X-Sumo-Host'=>'foo', 'X-Sumo-Name'=>'output.test', 'X-Sumo-Fields' => 'foo=bar, sumo = logic'},
|
338
|
+
body: /\A{"timestamp":\d+.,"foo":"shark","message":"test"}\z/,
|
339
|
+
times:1
|
340
|
+
end
|
341
|
+
|
342
|
+
def test_emit_with_sumo_metadata_with_empty_fields_and_custom_fields_fields_format
|
343
|
+
config = %{
|
344
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
345
|
+
log_format fields
|
346
|
+
custom_fields "lorem=ipsum,invalid"
|
347
|
+
}
|
348
|
+
driver = create_driver(config)
|
349
|
+
time = event_time
|
350
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
351
|
+
ENV['HOST'] = "foo"
|
352
|
+
driver.run do
|
353
|
+
driver.feed("output.test", time, {'foo' => 'shark', 'message' => 'test', '_sumo_metadata' => {
|
354
|
+
"host": "#{ENV['HOST']}",
|
355
|
+
"source": "${tag}",
|
356
|
+
"category": "test",
|
357
|
+
"fields": ""
|
358
|
+
}})
|
359
|
+
end
|
360
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
361
|
+
headers: {'X-Sumo-Category'=>'test', 'X-Sumo-Client'=>'fluentd-output', 'X-Sumo-Host'=>'foo', 'X-Sumo-Name'=>'output.test', 'X-Sumo-Fields' => 'lorem=ipsum'},
|
362
|
+
body: /\A{"timestamp":\d+.,"foo":"shark","message":"test"}\z/,
|
363
|
+
times:1
|
364
|
+
end
|
365
|
+
|
270
366
|
def test_emit_with_sumo_metadata
|
271
367
|
config = %{
|
272
368
|
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
@@ -394,6 +490,61 @@ class SumologicOutput < Test::Unit::TestCase
|
|
394
490
|
times:1
|
395
491
|
end
|
396
492
|
|
493
|
+
def test_emit_prometheus_with_custom_dimensions
|
494
|
+
config = %{
|
495
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
496
|
+
data_type metrics
|
497
|
+
metric_data_format prometheus
|
498
|
+
source_category test
|
499
|
+
source_host test
|
500
|
+
source_name test
|
501
|
+
custom_dimensions 'foo=bar, dolor=sit,amet,test'
|
502
|
+
}
|
503
|
+
driver = create_driver(config)
|
504
|
+
time = event_time
|
505
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
506
|
+
driver.run do
|
507
|
+
driver.feed("output.test", time, {'message' =>'cpu{cluster="prod", node="lb-1"} 87.2 1501753030'})
|
508
|
+
end
|
509
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
510
|
+
headers: {
|
511
|
+
'X-Sumo-Category'=>'test',
|
512
|
+
'X-Sumo-Client'=>'fluentd-output',
|
513
|
+
'X-Sumo-Host'=>'test',
|
514
|
+
'X-Sumo-Name'=>'test',
|
515
|
+
'X-Sumo-Dimensions'=>'foo=bar, dolor=sit',
|
516
|
+
'Content-Type'=>'application/vnd.sumologic.prometheus'},
|
517
|
+
body: 'cpu{cluster="prod", node="lb-1"} 87.2 1501753030',
|
518
|
+
times:1
|
519
|
+
end
|
520
|
+
|
521
|
+
def test_emit_prometheus_with_empty_custom_metadata
|
522
|
+
config = %{
|
523
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
524
|
+
data_type metrics
|
525
|
+
metric_data_format prometheus
|
526
|
+
source_category test
|
527
|
+
source_host test
|
528
|
+
source_name test
|
529
|
+
custom_metadata " "
|
530
|
+
}
|
531
|
+
driver = create_driver(config)
|
532
|
+
time = event_time
|
533
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
534
|
+
driver.run do
|
535
|
+
driver.feed("output.test", time, {'message' =>'cpu{cluster="prod", node="lb-1"} 87.2 1501753030'})
|
536
|
+
end
|
537
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
538
|
+
headers: {
|
539
|
+
'X-Sumo-Category'=>'test',
|
540
|
+
'X-Sumo-Client'=>'fluentd-output',
|
541
|
+
'X-Sumo-Host'=>'test',
|
542
|
+
'X-Sumo-Name'=>'test',
|
543
|
+
'Content-Type'=>'application/vnd.sumologic.prometheus'},
|
544
|
+
body: 'cpu{cluster="prod", node="lb-1"} 87.2 1501753030',
|
545
|
+
times:1
|
546
|
+
end
|
547
|
+
|
397
548
|
def test_batching_same_headers
|
398
549
|
config = %{
|
399
550
|
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
@@ -475,4 +626,175 @@ class SumologicOutput < Test::Unit::TestCase
|
|
475
626
|
times:1
|
476
627
|
end
|
477
628
|
|
478
|
-
|
629
|
+
def test_emit_json_merge_timestamp_compress_deflate
|
630
|
+
config = %{
|
631
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
632
|
+
log_format json_merge
|
633
|
+
source_category test
|
634
|
+
source_host test
|
635
|
+
source_name test
|
636
|
+
compress true
|
637
|
+
compress_encoding deflate
|
638
|
+
|
639
|
+
}
|
640
|
+
driver = create_driver(config)
|
641
|
+
time = event_time
|
642
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
643
|
+
driver.run do
|
644
|
+
driver.feed("output.test", time, {'message' => '{"timestamp":123}'})
|
645
|
+
end
|
646
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
647
|
+
headers: {'X-Sumo-Category'=>'test', 'X-Sumo-Client'=>'fluentd-output', 'X-Sumo-Host'=>'test', 'X-Sumo-Name'=>'test', 'Content-Encoding'=>'deflate'},
|
648
|
+
body: "\x78\x9c\xab\x56\x2a\xc9\xcc\x4d\x2d\x2e\x49\xcc\x2d\x50\xb2\x32\x34\x32\xae\x05\x00\x38\xb0\x05\xe1".force_encoding("ASCII-8BIT"),
|
649
|
+
times:1
|
650
|
+
end
|
651
|
+
|
652
|
+
def test_emit_json_merge_timestamp_compress_gzip
|
653
|
+
config = %{
|
654
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
655
|
+
log_format json_merge
|
656
|
+
source_category test
|
657
|
+
source_host test
|
658
|
+
source_name test
|
659
|
+
compress true
|
660
|
+
|
661
|
+
}
|
662
|
+
driver = create_driver(config)
|
663
|
+
time = event_time
|
664
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
665
|
+
driver.run do
|
666
|
+
driver.feed("output.test", time, {'message' => '{"timestamp":1234}'})
|
667
|
+
end
|
668
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
669
|
+
headers: {'X-Sumo-Category'=>'test', 'X-Sumo-Client'=>'fluentd-output', 'X-Sumo-Host'=>'test', 'X-Sumo-Name'=>'test', 'Content-Encoding'=>'gzip'},
|
670
|
+
body: "\x1f\x8b\x08\x00\x01\x00\x00\x00\x00\x03\xab\x56\x2a\xc9\xcc\x4d\x2d\x2e\x49\xcc\x2d\x50\xb2\x32\x34\x32\x36\xa9\x05\x00\xfe\x53\xbe\x14\x12\x00\x00\x00".force_encoding("ASCII-8BIT"),
|
671
|
+
times:1
|
672
|
+
end
|
673
|
+
|
674
|
+
def test_emit_text_from_array
|
675
|
+
config = %{
|
676
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
677
|
+
log_format text
|
678
|
+
source_category test
|
679
|
+
source_host test
|
680
|
+
source_name test
|
681
|
+
|
682
|
+
}
|
683
|
+
driver = create_driver(config)
|
684
|
+
time = event_time
|
685
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
686
|
+
driver.run do
|
687
|
+
driver.feed("output.test", time, {'foo' => 'bar', 'message' => ['test', 'test2']})
|
688
|
+
end
|
689
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
690
|
+
headers: {'X-Sumo-Category'=>'test', 'X-Sumo-Client'=>'fluentd-output', 'X-Sumo-Host'=>'test', 'X-Sumo-Name'=>'test'},
|
691
|
+
body: '["test","test2"]',
|
692
|
+
times:1
|
693
|
+
end
|
694
|
+
|
695
|
+
def test_emit_text_from_dict
|
696
|
+
config = %{
|
697
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
698
|
+
log_format text
|
699
|
+
source_category test
|
700
|
+
source_host test
|
701
|
+
source_name test
|
702
|
+
|
703
|
+
}
|
704
|
+
driver = create_driver(config)
|
705
|
+
time = event_time
|
706
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
707
|
+
driver.run do
|
708
|
+
driver.feed("output.test", time, {'foo' => 'bar', 'message' => {'test': 'test2', 'test3': 'test4'}})
|
709
|
+
end
|
710
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
711
|
+
headers: {'X-Sumo-Category'=>'test', 'X-Sumo-Client'=>'fluentd-output', 'X-Sumo-Host'=>'test', 'X-Sumo-Name'=>'test'},
|
712
|
+
body: '{"test":"test2","test3":"test4"}',
|
713
|
+
times:1
|
714
|
+
end
|
715
|
+
|
716
|
+
def test_emit_fields_string_based
|
717
|
+
config = %{
|
718
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
719
|
+
log_format fields
|
720
|
+
source_category test
|
721
|
+
source_host test
|
722
|
+
source_name test
|
723
|
+
|
724
|
+
}
|
725
|
+
driver = create_driver(config)
|
726
|
+
time = event_time
|
727
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
728
|
+
driver.run do
|
729
|
+
driver.feed("output.test", time, {'message' => '{"foo": "bar", "message": "test"}'})
|
730
|
+
end
|
731
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
732
|
+
headers: {'X-Sumo-Category'=>'test', 'X-Sumo-Client'=>'fluentd-output', 'X-Sumo-Host'=>'test', 'X-Sumo-Name'=>'test'},
|
733
|
+
body: /\A{"timestamp":\d+.,"message":{"foo":"bar","message":"test"}}\z/,
|
734
|
+
times:1
|
735
|
+
end
|
736
|
+
|
737
|
+
def test_emit_fields_invalid_json_string_based_1
|
738
|
+
config = %{
|
739
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
740
|
+
log_format fields
|
741
|
+
source_category test
|
742
|
+
source_host test
|
743
|
+
source_name test
|
744
|
+
|
745
|
+
}
|
746
|
+
driver = create_driver(config)
|
747
|
+
time = event_time
|
748
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
749
|
+
driver.run do
|
750
|
+
driver.feed("output.test", time, {'message' => '{"foo": "bar", "message": "test"'})
|
751
|
+
end
|
752
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
753
|
+
headers: {'X-Sumo-Category'=>'test', 'X-Sumo-Client'=>'fluentd-output', 'X-Sumo-Host'=>'test', 'X-Sumo-Name'=>'test'},
|
754
|
+
body: /\A{"timestamp":\d+.,"message":"{\\"foo\\": \\"bar\\", \\"message\\": \\"test\\""}\z/,
|
755
|
+
times:1
|
756
|
+
end
|
757
|
+
|
758
|
+
def test_emit_fields_invalid_json_string_based_2
|
759
|
+
config = %{
|
760
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
761
|
+
log_format fields
|
762
|
+
source_category test
|
763
|
+
source_host test
|
764
|
+
source_name test
|
765
|
+
|
766
|
+
}
|
767
|
+
driver = create_driver(config)
|
768
|
+
time = event_time
|
769
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
770
|
+
driver.run do
|
771
|
+
driver.feed("output.test", time, {'message' => '{"foo": "bar", "message"'})
|
772
|
+
end
|
773
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
774
|
+
headers: {'X-Sumo-Category'=>'test', 'X-Sumo-Client'=>'fluentd-output', 'X-Sumo-Host'=>'test', 'X-Sumo-Name'=>'test'},
|
775
|
+
body: /\A{"timestamp":\d+.,"message":"{\\"foo\\": \\"bar\\", \\"message\\""}\z/,
|
776
|
+
times:1
|
777
|
+
end
|
778
|
+
|
779
|
+
def test_emit_fields_invalid_json_string_based_3
|
780
|
+
config = %{
|
781
|
+
endpoint https://collectors.sumologic.com/v1/receivers/http/1234
|
782
|
+
log_format fields
|
783
|
+
source_category test
|
784
|
+
source_host test
|
785
|
+
source_name test
|
786
|
+
|
787
|
+
}
|
788
|
+
driver = create_driver(config)
|
789
|
+
time = event_time
|
790
|
+
stub_request(:post, 'https://collectors.sumologic.com/v1/receivers/http/1234')
|
791
|
+
driver.run do
|
792
|
+
driver.feed("output.test", time, {'message' => '"foo\": \"bar\", \"mess'})
|
793
|
+
end
|
794
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
795
|
+
headers: {'X-Sumo-Category'=>'test', 'X-Sumo-Client'=>'fluentd-output', 'X-Sumo-Host'=>'test', 'X-Sumo-Name'=>'test'},
|
796
|
+
body: /\A{"timestamp":\d+.,"message":"\\"foo\\\\\\": \\\\\\"bar\\\\\\", \\\\\\"mess"}\z/,
|
797
|
+
times:1
|
798
|
+
end
|
799
|
+
|
800
|
+
end
|
data/vagrant/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Vagrant
|
2
|
+
|
3
|
+
## Prerequisites
|
4
|
+
|
5
|
+
Please install the following:
|
6
|
+
|
7
|
+
- [VirtualBox](https://www.virtualbox.org/)
|
8
|
+
- [Vagrant](https://www.vagrantup.com/)
|
9
|
+
- [vagrant-disksize](https://github.com/sprotheroe/vagrant-disksize) plugin
|
10
|
+
|
11
|
+
### MacOS
|
12
|
+
|
13
|
+
```bash
|
14
|
+
brew cask install virtualbox
|
15
|
+
brew cask install vagrant
|
16
|
+
vagrant plugin install vagrant-disksize
|
17
|
+
```
|
18
|
+
|
19
|
+
## Setting up
|
20
|
+
|
21
|
+
You can set up the Vagrant environment with just one command:
|
22
|
+
|
23
|
+
```bash
|
24
|
+
vagrant up
|
25
|
+
```
|
26
|
+
|
27
|
+
After successfull installation you can ssh to the virtual machine with:
|
28
|
+
|
29
|
+
```bash
|
30
|
+
vagrant ssh
|
31
|
+
```
|
32
|
+
|
33
|
+
NOTICE: The directory with fluentd-output-sumologic repository on the host is synced with `/sumologic/` directory on the virtual machine.
|
34
|
+
|
35
|
+
## Runing tests
|
36
|
+
|
37
|
+
You can run tests using following commands:
|
38
|
+
|
39
|
+
```bash
|
40
|
+
cd /sumologic
|
41
|
+
bundle install
|
42
|
+
bundle exec rake
|
43
|
+
```
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -x
|
4
|
+
|
5
|
+
export DEBIAN_FRONTEND=noninteractive
|
6
|
+
apt-get update
|
7
|
+
apt-get --yes upgrade
|
8
|
+
apt-get --yes install apt-transport-https
|
9
|
+
|
10
|
+
echo "export EDITOR=vim" >> /home/vagrant/.bashrc
|
11
|
+
|
12
|
+
# Install docker
|
13
|
+
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
|
14
|
+
add-apt-repository \
|
15
|
+
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
|
16
|
+
$(lsb_release -cs) \
|
17
|
+
stable"
|
18
|
+
apt-get install -y docker-ce docker-ce-cli containerd.io
|
19
|
+
usermod -aG docker vagrant
|
20
|
+
|
21
|
+
# Install make
|
22
|
+
apt-get install -y make
|
23
|
+
|
24
|
+
# install requirements for ruby
|
25
|
+
snap install ruby --channel=2.6/stable --classic
|
26
|
+
su vagrant -c 'gem install bundler:2.1.4'
|
27
|
+
apt install -y gcc g++ libsnappy-dev libicu-dev zlib1g-dev cmake pkg-config libssl-dev
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-sumologic_output
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steven Adams
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2021-10-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -103,6 +103,7 @@ executables: []
|
|
103
103
|
extensions: []
|
104
104
|
extra_rdoc_files: []
|
105
105
|
files:
|
106
|
+
- ".github/CODEOWNERS"
|
106
107
|
- ".gitignore"
|
107
108
|
- ".travis.yml"
|
108
109
|
- CHANGELOG.md
|
@@ -110,11 +111,14 @@ files:
|
|
110
111
|
- LICENSE
|
111
112
|
- README.md
|
112
113
|
- Rakefile
|
114
|
+
- Vagrantfile
|
113
115
|
- ci/build.sh
|
114
116
|
- fluent-plugin-sumologic_output.gemspec
|
115
117
|
- lib/fluent/plugin/out_sumologic.rb
|
116
118
|
- test/helper.rb
|
117
119
|
- test/plugin/test_out_sumologic.rb
|
120
|
+
- vagrant/README.md
|
121
|
+
- vagrant/provision.sh
|
118
122
|
homepage: https://github.com/SumoLogic/fluentd-output-sumologic
|
119
123
|
licenses:
|
120
124
|
- Apache-2.0
|
@@ -134,8 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
138
|
- !ruby/object:Gem::Version
|
135
139
|
version: '0'
|
136
140
|
requirements: []
|
137
|
-
|
138
|
-
rubygems_version: 2.7.7
|
141
|
+
rubygems_version: 3.0.8
|
139
142
|
signing_key:
|
140
143
|
specification_version: 4
|
141
144
|
summary: Output plugin to SumoLogic HTTP Endpoint
|