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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec49fe6284202d7f57bdd3b59b06f5248d5345e84b7fd955f69b47c069db28e6
4
- data.tar.gz: 80389b25c291df5258a4313f1025e5e84507207f5f11911b768ecc273391738b
3
+ metadata.gz: 52b0c77e900d61ba360421933440383208033b29b0f08b012250519189872f24
4
+ data.tar.gz: ff86f952744e650e30ba94d1185f4d270a3d8d6e7bfee5f30a0810e043a9483f
5
5
  SHA512:
6
- metadata.gz: 3fb838f98317572424ad7969450b4f7811a94a80de123e39ce8da9ff0dc9fb1448e1262686e930260142816732ae0ce0420b9b3b52f9d9ade5807e86951dd9e0
7
- data.tar.gz: 8a19573a03b6dd156fa25f74aeff3adb9059877f5ffcd1701c25f3a359d83e04668d9647c70dc1eb47f3e5cf3da6c2b6e3cb57c2ebec8cf9205d64b3e911bbe5
6
+ metadata.gz: 74c43d262f35e2767d170a05dbec58911590c0851a724f6c4adb0047a26601383e4d8fa5e2671c845bbd89a65781fb0d35e4bc2f8d7ed6bd0f0e376d6c7ec958
7
+ data.tar.gz: 73b8ea2a6dbf4593eca7d4a702eeea14f27728bd7aa28ddc15e968301a5a2eb21f76ae18f1be2dd759f42cb357c2ceda92aec49e136755d76c954ebded889cdd
@@ -0,0 +1,5 @@
1
+ # Learn about CODEOWNERS file format:
2
+ # https://help.github.com/en/articles/about-code-owners
3
+
4
+ * @SumoLogic/open-source-collection-team
5
+
data/.gitignore CHANGED
@@ -2,4 +2,5 @@
2
2
  *.rbc
3
3
  Gemfile.lock
4
4
  .idea/
5
- TAGS
5
+ TAGS
6
+ .vagrant
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.6.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
- def initialize(endpoint, verify_ssl, connect_timeout, proxy_uri, disable_cookies)
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
- create_http_client(verify_ssl, connect_timeout, proxy_uri, disable_cookies)
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' => 'fluentd-output'
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
- @sumo_conn = SumologicConnection.new(conf['endpoint'], conf['verify_ssl'], conf['open_timeout'].to_i, conf['proxy_uri'], conf['disable_cookies'])
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
- parser = Yajl::Parser.new
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
- end
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.6.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: 2019-10-25 00:00:00.000000000 Z
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
- rubyforge_project:
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