fluent-plugin-sumologic_output 1.7.5 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +15 -0
- data/fluent-plugin-sumologic_output.gemspec +1 -1
- data/lib/fluent/plugin/out_sumologic.rb +86 -12
- data/test/plugin/test_out_sumologic.rb +153 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 280c08203277770f0dc9277d9df2f8249fba58283816e4fd5b511560fb533ac3
|
4
|
+
data.tar.gz: ad2b07fadad2698bf9169a0556a3c63422446267c292c27c71e0d5a8e7b5669a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32636372bdbb936f6d3c68a223b1cdd4b9e716522fd526df80b079f1712c77f658f16c3f60d5554f80d53354006e8d11e40a9fbe68c0b4fbd767fe053a9cc8bb
|
7
|
+
data.tar.gz: 76b52ddad11c35a1e2be4a6f818e6f2f60f10ad6a74147c43fb05f2689dbc4603c9429437ff6c7cdd9a1cdf398894096adb925f1dd922b117bf509ba12c8ffa4
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,22 @@
|
|
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.8.0"></a>
|
6
|
+
# [1.8.0] (2022-04-22)
|
7
|
+
|
8
|
+
- feat: add exponential backoff for sending data [#76](https://github.com/SumoLogic/fluentd-output-sumologic/pull/76)
|
9
|
+
- feat(max_request_size): add max_request_size to limit size of requests [#78](https://github.com/SumoLogic/fluentd-output-sumologic/pull/78)
|
10
|
+
|
11
|
+
<a name="1.7.5"></a>
|
12
|
+
# [1.7.5] (2022-04-11)
|
13
|
+
|
14
|
+
- refactor: add a debug log on sending [#75](https://github.com/SumoLogic/fluentd-output-sumologic/pull/75)
|
15
|
+
|
16
|
+
<a name="1.7.4"></a>
|
17
|
+
# [1.7.4] (2021-04-08)
|
18
|
+
|
19
|
+
- fix: handle receiver warning messages [#73](https://github.com/SumoLogic/fluentd-output-sumologic/pull/73)
|
20
|
+
|
5
21
|
<a name="1.7.3"></a>
|
6
22
|
# [1.7.3] (2021-10-19)
|
7
23
|
- Expose httpclient send_timeout [#66](https://github.com/SumoLogic/fluentd-output-sumologic/pull/68)
|
data/README.md
CHANGED
@@ -38,6 +38,13 @@ Configuration options for fluent.conf are:
|
|
38
38
|
* `compress_encoding` - Compression encoding format, either `gzip` or `deflate` (default `gzip`)
|
39
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
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)
|
41
|
+
* `use_internal_retry` - Enable custom retry mechanism. As this is `false` by default due to backward compatibility,
|
42
|
+
we recommend to enable it and configure the following parameters (`retry_min_interval`, `retry_max_interval`, `retry_timeout`, `retry_max_times`)
|
43
|
+
* `retry_min_interval` - Minimum interval to wait between sending tries (default is `1s`)
|
44
|
+
* `retry_max_interval` - Maximum interval to wait between sending tries (default is `5m`)
|
45
|
+
* `retry_timeout` - Time after which the data is going to be dropped (default is `72h`) (`0s` means that there is no timeout)
|
46
|
+
* `retry_max_times` - Maximum number of retries (default is `0`) (`0` means that there is no max retry times, retries will happen forever)
|
47
|
+
* `max_request_size` - Maximum request size (before applying compression). Default is `0k` which means no limit
|
41
48
|
|
42
49
|
__NOTE:__ <sup>*</sup> [Placeholders](https://docs.fluentd.org/v1.0/articles/buffer-section#placeholders) are supported
|
43
50
|
|
@@ -137,6 +144,14 @@ Example
|
|
137
144
|
}
|
138
145
|
```
|
139
146
|
|
147
|
+
## Retry Mechanism
|
148
|
+
|
149
|
+
`retry_min_interval`, `retry_max_interval`, `retry_timeout`, `retry_max_times` are not the [buffer retries parameters][buffer_retries].
|
150
|
+
Due to technical reason, this plugin implements it's own retrying back-off exponential mechanism.
|
151
|
+
It is disabled by default, but we recommend to enable it by setting `use_internal_retry` to `true`.
|
152
|
+
|
153
|
+
[buffer_retries]: https://docs.fluentd.org/configuration/buffer-section#retries-parameters
|
154
|
+
|
140
155
|
### TLS 1.2 Requirement
|
141
156
|
|
142
157
|
Sumo Logic only accepts connections from clients using TLS version 1.2 or greater. To utilize the content of this repo, ensure that it's running in an execution environment that is configured to use TLS 1.2 or greater.
|
@@ -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.8.0"
|
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}
|
@@ -157,6 +157,15 @@ class Fluent::Plugin::Sumologic < Fluent::Plugin::Output
|
|
157
157
|
config_param :timestamp_key, :string, :default => 'timestamp'
|
158
158
|
config_param :proxy_uri, :string, :default => nil
|
159
159
|
config_param :disable_cookies, :bool, :default => false
|
160
|
+
|
161
|
+
config_param :use_internal_retry, :bool, :default => false
|
162
|
+
config_param :retry_timeout, :time, :default => 72 * 3600 # 72h
|
163
|
+
config_param :retry_max_times, :integer, :default => 0
|
164
|
+
config_param :retry_min_interval, :time, :default => 1 # 1s
|
165
|
+
config_param :retry_max_interval, :time, :default => 5*60 # 5m
|
166
|
+
|
167
|
+
config_param :max_request_size, :size, :default => 0
|
168
|
+
|
160
169
|
# https://help.sumologic.com/Manage/Fields
|
161
170
|
desc 'Fields string (eg "cluster=payment, service=credit_card") which is going to be added to every log record.'
|
162
171
|
config_param :custom_fields, :string, :default => nil
|
@@ -245,6 +254,10 @@ class Fluent::Plugin::Sumologic < Fluent::Plugin::Output
|
|
245
254
|
conf['compress_encoding'],
|
246
255
|
log,
|
247
256
|
)
|
257
|
+
|
258
|
+
if !conf['max_request_size'].nil? && conf['max_request_size'].to_i <= 0
|
259
|
+
conf['max_request_size'] = '0'
|
260
|
+
end
|
248
261
|
super
|
249
262
|
end
|
250
263
|
|
@@ -385,6 +398,7 @@ class Fluent::Plugin::Sumologic < Fluent::Plugin::Output
|
|
385
398
|
|
386
399
|
end
|
387
400
|
|
401
|
+
chunk_id = "##{chunk.dump_unique_id_hex(chunk.unique_id)}"
|
388
402
|
# Push logs to sumo
|
389
403
|
messages_list.each do |key, messages|
|
390
404
|
source_name, source_category, source_host, fields = key[:source_name], key[:source_category],
|
@@ -397,18 +411,78 @@ class Fluent::Plugin::Sumologic < Fluent::Plugin::Output
|
|
397
411
|
fields = [fields,@custom_fields].compact.join(",")
|
398
412
|
end
|
399
413
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
414
|
+
if @max_request_size <= 0
|
415
|
+
messages_to_send = [messages]
|
416
|
+
else
|
417
|
+
messages_to_send = []
|
418
|
+
current_message = []
|
419
|
+
current_length = 0
|
420
|
+
messages.each do |message|
|
421
|
+
current_message.push message
|
422
|
+
current_length += message.length
|
423
|
+
|
424
|
+
if current_length > @max_request_size
|
425
|
+
messages_to_send.push(current_message)
|
426
|
+
current_message = []
|
427
|
+
current_length = 0
|
428
|
+
end
|
429
|
+
current_length += 1 # this is for newline
|
430
|
+
end
|
431
|
+
if current_message.length > 0
|
432
|
+
messages_to_send.push(current_message)
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
messages_to_send.each_with_index do |message, i|
|
437
|
+
retries = 0
|
438
|
+
start_time = Time.now
|
439
|
+
sleep_time = @retry_min_interval
|
440
|
+
|
441
|
+
while true
|
442
|
+
common_log_part = "#{@data_type} records with source category '#{source_category}', source host '#{source_host}', source name '#{source_name}', chunk #{chunk_id}, try #{retries}, batch #{i}"
|
443
|
+
|
444
|
+
begin
|
445
|
+
@log.debug { "Sending #{message.count}; #{common_log_part}" }
|
446
|
+
|
447
|
+
@sumo_conn.publish(
|
448
|
+
message.join("\n"),
|
449
|
+
source_host =source_host,
|
450
|
+
source_category =source_category,
|
451
|
+
source_name =source_name,
|
452
|
+
data_type =@data_type,
|
453
|
+
metric_data_format =@metric_data_format,
|
454
|
+
collected_fields =fields,
|
455
|
+
dimensions =@custom_dimensions
|
456
|
+
)
|
457
|
+
break
|
458
|
+
rescue => e
|
459
|
+
if !@use_internal_retry
|
460
|
+
raise e
|
461
|
+
end
|
462
|
+
# increment retries
|
463
|
+
retries += 1
|
464
|
+
|
465
|
+
log.warn "error while sending request to sumo: #{e}; #{common_log_part}"
|
466
|
+
log.warn_backtrace e.backtrace
|
467
|
+
|
468
|
+
# drop data if
|
469
|
+
# - we reached out the @retry_max_times retries
|
470
|
+
# - or we exceeded @retry_timeout
|
471
|
+
if (retries >= @retry_max_times && @retry_max_times > 0) || (Time.now > start_time + @retry_timeout && @retry_timeout > 0)
|
472
|
+
log.warn "dropping records; #{common_log_part}"
|
473
|
+
break
|
474
|
+
end
|
475
|
+
|
476
|
+
log.info "going to retry to send data at #{Time.now + sleep_time}; #{common_log_part}"
|
477
|
+
sleep sleep_time
|
478
|
+
|
479
|
+
sleep_time *= 2
|
480
|
+
if sleep_time > @retry_max_interval
|
481
|
+
sleep_time = @retry_max_interval
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
412
486
|
end
|
413
487
|
|
414
488
|
end
|
@@ -825,4 +825,157 @@ class SumologicOutput < Test::Unit::TestCase
|
|
825
825
|
end
|
826
826
|
end
|
827
827
|
|
828
|
+
def test_resend
|
829
|
+
endpoint = "https://collectors.sumologic.com/v1/receivers/http/1234"
|
830
|
+
config = %{
|
831
|
+
endpoint #{endpoint}
|
832
|
+
retry_min_interval 0s
|
833
|
+
retry_max_times 3
|
834
|
+
use_internal_retry true
|
835
|
+
}
|
836
|
+
time = event_time
|
837
|
+
|
838
|
+
driver = create_driver(config)
|
839
|
+
stub_request(:post, endpoint).to_return(
|
840
|
+
{status: 500, headers: {content_type: 'application/json'}},
|
841
|
+
{status: 200, headers: {content_type: 'application/json'}}
|
842
|
+
)
|
843
|
+
driver.run do
|
844
|
+
driver.feed("test", time, {"message": "test"})
|
845
|
+
end
|
846
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
847
|
+
body: /\A{"timestamp":\d+.,"message":"test"}\z/,
|
848
|
+
times:2
|
849
|
+
end
|
850
|
+
|
851
|
+
def test_resend_failed
|
852
|
+
endpoint = "https://collectors.sumologic.com/v1/receivers/http/1234"
|
853
|
+
config = %{
|
854
|
+
endpoint #{endpoint}
|
855
|
+
retry_min_interval 0s
|
856
|
+
retry_max_times 15
|
857
|
+
use_internal_retry true
|
858
|
+
}
|
859
|
+
time = event_time
|
860
|
+
|
861
|
+
driver = create_driver(config)
|
862
|
+
stub_request(:post, endpoint).to_return(
|
863
|
+
status: 500, headers: {content_type: 'application/json'}
|
864
|
+
)
|
865
|
+
driver.run do
|
866
|
+
driver.feed("test", time, {"message": "test"})
|
867
|
+
end
|
868
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
869
|
+
body: /\A{"timestamp":\d+.,"message":"test"}\z/,
|
870
|
+
times:15
|
871
|
+
end
|
872
|
+
|
873
|
+
def test_resend_forever
|
874
|
+
endpoint = "https://collectors.sumologic.com/v1/receivers/http/1234"
|
875
|
+
config = %{
|
876
|
+
endpoint #{endpoint}
|
877
|
+
retry_min_interval 0s
|
878
|
+
retry_max_times 0
|
879
|
+
retry_timeout 0s
|
880
|
+
use_internal_retry true
|
881
|
+
}
|
882
|
+
time = event_time
|
883
|
+
|
884
|
+
driver = create_driver(config)
|
885
|
+
stub_request(:post, endpoint).to_return(
|
886
|
+
*[{status: 500, headers: {content_type: 'application/json'}}]*123,
|
887
|
+
{status: 200, headers: {content_type: 'application/json'}}
|
888
|
+
)
|
889
|
+
driver.run do
|
890
|
+
driver.feed("test", time, {"message": "test"})
|
891
|
+
end
|
892
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
893
|
+
body: /\A{"timestamp":\d+.,"message":"test"}\z/,
|
894
|
+
times:124
|
895
|
+
end
|
896
|
+
|
897
|
+
def test_skip_retry
|
898
|
+
endpoint = "https://collectors.sumologic.com/v1/receivers/http/1234"
|
899
|
+
config = %{
|
900
|
+
endpoint #{endpoint}
|
901
|
+
}
|
902
|
+
time = event_time
|
903
|
+
|
904
|
+
driver = create_driver(config)
|
905
|
+
stub_request(:post, endpoint).to_return(status: 500, headers: {content_type: 'application/json'})
|
906
|
+
|
907
|
+
exception = assert_raise(RuntimeError) {
|
908
|
+
driver.run do
|
909
|
+
driver.feed("test", time, {"message": "test"})
|
910
|
+
end
|
911
|
+
}
|
912
|
+
assert_equal("Failed to send data to HTTP Source. 500 - ", exception.message)
|
913
|
+
end
|
914
|
+
|
915
|
+
def test_split_negative_or_zero
|
916
|
+
endpoint = "https://collectors.sumologic.com/v1/receivers/http/1234"
|
917
|
+
|
918
|
+
configs = [
|
919
|
+
%{
|
920
|
+
endpoint #{endpoint}
|
921
|
+
max_request_size -5
|
922
|
+
},
|
923
|
+
%{
|
924
|
+
endpoint #{endpoint}
|
925
|
+
max_request_size 0
|
926
|
+
}
|
927
|
+
]
|
928
|
+
|
929
|
+
time = event_time
|
930
|
+
|
931
|
+
configs.each do |config|
|
932
|
+
WebMock.reset_executed_requests!
|
933
|
+
driver = create_driver(config)
|
934
|
+
stub_request(:post, endpoint).to_return(status: 200, headers: {content_type: 'application/json'})
|
935
|
+
|
936
|
+
driver.run do
|
937
|
+
driver.feed("test", time, {"message": "test"})
|
938
|
+
driver.feed("test", time, {"message": "test"})
|
939
|
+
driver.feed("test", time, {"message": "test"})
|
940
|
+
end
|
941
|
+
|
942
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
943
|
+
body: /\A{"timestamp":\d+.,"message":"test"}\n{"timestamp":\d+.,"message":"test"}\n{"timestamp":\d+.,"message":"test"}\z/,
|
944
|
+
times:1
|
945
|
+
end
|
946
|
+
end
|
947
|
+
|
948
|
+
def test_split
|
949
|
+
endpoint = "https://collectors.sumologic.com/v1/receivers/http/1234"
|
950
|
+
|
951
|
+
config = %{
|
952
|
+
endpoint #{endpoint}
|
953
|
+
max_request_size 80
|
954
|
+
}
|
955
|
+
|
956
|
+
time = event_time
|
957
|
+
|
958
|
+
WebMock.reset_executed_requests!
|
959
|
+
driver = create_driver(config)
|
960
|
+
stub_request(:post, endpoint).to_return(status: 200, headers: {content_type: 'application/json'})
|
961
|
+
|
962
|
+
driver.run do
|
963
|
+
driver.feed("test", time, {"message": "test1"})
|
964
|
+
driver.feed("test", time, {"message": "test2"})
|
965
|
+
driver.feed("test", time, {"message": "test3"})
|
966
|
+
driver.feed("test", time, {"message": "test4"})
|
967
|
+
driver.feed("test", time, {"message": "test5"})
|
968
|
+
end
|
969
|
+
|
970
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
971
|
+
body: /\A{"timestamp":\d+.,"message":"test1"}\n{"timestamp":\d+.,"message":"test2"}\z/,
|
972
|
+
times:1
|
973
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
974
|
+
body: /\A{"timestamp":\d+.,"message":"test3"}\n{"timestamp":\d+.,"message":"test4"}\z/,
|
975
|
+
times:1
|
976
|
+
assert_requested :post, "https://collectors.sumologic.com/v1/receivers/http/1234",
|
977
|
+
body: /\A{"timestamp":\d+.,"message":"test5"}\z/,
|
978
|
+
times:1
|
979
|
+
end
|
980
|
+
|
828
981
|
end
|
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.8.0
|
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: 2022-04-
|
12
|
+
date: 2022-04-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|