logstash-output-scalyr 0.1.17.beta → 0.1.21.beta
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/CHANGELOG.md +16 -0
- data/README.md +7 -1
- data/lib/logstash/outputs/scalyr.rb +28 -2
- data/lib/scalyr/common/util.rb +73 -22
- data/lib/scalyr/constants.rb +1 -1
- data/logstash-output-scalyr.gemspec +1 -1
- data/spec/logstash/outputs/scalyr_spec.rb +98 -0
- data/spec/scalyr/common/util_spec.rb +353 -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: 7dd0297a3ddf2f2acfdad08c410822b8a4ee808800e3cfeffc5df4ec8a991348
|
4
|
+
data.tar.gz: 855b7e3a655656442cf3de76e15ad3d0f90ca460421d4a4f86df46056a4b05a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c3024576c03a088008c5b1acc54d02be67be701921392d91f5991ac4482f85f62ea3b07255e38a972735182feccb1520d558f5cb065538dabe35cc86e1ad719
|
7
|
+
data.tar.gz: c807baca5797bbf47cf69c8816a40300a515162617d4a714e719673572f1fe29fe3fa9ab73fd51a711af3953e212bd3dfe998e73c2c6de574a64ba6121446989
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
# Beta
|
2
2
|
|
3
|
+
## 0.1.21.beta
|
4
|
+
- Fix issue with iterative flattening function when dealing with empty collections.
|
5
|
+
|
6
|
+
## 0.1.20.beta
|
7
|
+
- Rewrite flattening function to no longer be recursive, to help avoid maxing out the stack.
|
8
|
+
- Added a configurable value `flattening_max_key_count` to create a limit on how large of a record we can flatten.
|
9
|
+
It limits the maximum amount of keys we can have in the final flattened record. Defaults to unlimited.
|
10
|
+
|
11
|
+
## 0.1.19.beta
|
12
|
+
- Undo a change to nested value flattening functionality to keep existing formatting. This change can be re-enabled
|
13
|
+
by setting the `fix_deep_flattening_delimiters` configuration option to true.
|
14
|
+
|
15
|
+
## 0.1.18.beta
|
16
|
+
- Add metrics for successfully sent and failed logstash events, and retries.
|
17
|
+
- Make array flattening optional during nested value flattening with the `flatten_nested_arrays` configuration option.
|
18
|
+
|
3
19
|
## 0.1.17.beta
|
4
20
|
- Catch errors relating to Bignum conversions present in the ``json`` library and manually convert to string as
|
5
21
|
a workaround.
|
data/README.md
CHANGED
@@ -10,7 +10,7 @@ You can view documentation for this plugin [on the Scalyr website](https://app.s
|
|
10
10
|
# Quick start
|
11
11
|
|
12
12
|
1. Build the gem, run `gem build logstash-output-scalyr.gemspec`
|
13
|
-
2. Install the gem into a Logstash installation, run `/usr/share/logstash/bin/logstash-plugin install logstash-output-scalyr-0.1.
|
13
|
+
2. Install the gem into a Logstash installation, run `/usr/share/logstash/bin/logstash-plugin install logstash-output-scalyr-0.1.21.beta.gem` or follow the latest official instructions on working with plugins from Logstash.
|
14
14
|
3. Configure the output plugin (e.g. add it to a pipeline .conf)
|
15
15
|
4. Restart Logstash
|
16
16
|
|
@@ -321,6 +321,12 @@ If you want to run just the unit tests, you can run the command displayed below.
|
|
321
321
|
bundle exec rspec spec/logstash/outputs/scalyr_spec.rb spec/scalyr/common/util_spec.rb
|
322
322
|
```
|
323
323
|
|
324
|
+
Or to run a single test function defined on line XXX
|
325
|
+
|
326
|
+
```bash
|
327
|
+
bundle exec rspec spec/scalyr/common/util_spec.rb:XXX
|
328
|
+
```
|
329
|
+
|
324
330
|
## Instrumentation and metrics
|
325
331
|
|
326
332
|
By default, plugin logs a special line with metrics to Scalyr every 5 minutes. This line contains
|
@@ -68,7 +68,10 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
68
68
|
# If true, nested values will be flattened (which changes keys to delimiter-separated concatenation of all
|
69
69
|
# nested keys).
|
70
70
|
config :flatten_nested_values, :validate => :boolean, :default => false
|
71
|
-
config :flatten_nested_values_delimiter, :validate => :string, :default => "_"
|
71
|
+
config :flatten_nested_values_delimiter, :validate => :string, :default => "_"
|
72
|
+
config :flatten_nested_arrays, :validate => :boolean, :default => true
|
73
|
+
config :fix_deep_flattening_delimiters, :validate => :boolean, :default => false
|
74
|
+
config :flattening_max_key_count, :validate => :number, :default => -1
|
72
75
|
|
73
76
|
# If true, the 'tags' field will be flattened into key-values where each key is a tag and each value is set to
|
74
77
|
# :flat_tag_value
|
@@ -241,6 +244,10 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
241
244
|
# level metrics are handled by the HTTP Client class.
|
242
245
|
@multi_receive_statistics = {
|
243
246
|
:total_multi_receive_secs => 0,
|
247
|
+
:total_events_processed => 0,
|
248
|
+
:successful_events_processed => 0,
|
249
|
+
:failed_events_processed => 0,
|
250
|
+
:total_retry_count => 0,
|
244
251
|
:total_java_class_cast_errors => 0
|
245
252
|
}
|
246
253
|
@plugin_metrics = get_new_metrics
|
@@ -345,6 +352,9 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
345
352
|
sleep_interval = sleep_for(sleep_interval)
|
346
353
|
exc_sleep += sleep_interval
|
347
354
|
exc_retries += 1
|
355
|
+
@stats_lock.synchronize do
|
356
|
+
@multi_receive_statistics[:total_retry_count] += 1
|
357
|
+
end
|
348
358
|
message = "Error uploading to Scalyr (will backoff-retry)"
|
349
359
|
exc_data = {
|
350
360
|
:error_class => e.e_class,
|
@@ -394,11 +404,19 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
394
404
|
}
|
395
405
|
exc_sleep += sleep_interval
|
396
406
|
exc_retries += 1
|
407
|
+
@stats_lock.synchronize do
|
408
|
+
@multi_receive_statistics[:total_retry_count] += 1
|
409
|
+
end
|
397
410
|
retry if @running and exc_retries < @max_retries
|
398
411
|
log_retry_failure(multi_event_request, exc_data, exc_retries, exc_sleep)
|
399
412
|
next
|
400
413
|
end
|
401
414
|
|
415
|
+
@stats_lock.synchronize do
|
416
|
+
@multi_receive_statistics[:total_events_processed] += multi_event_request[:logstash_events].length
|
417
|
+
@multi_receive_statistics[:successful_events_processed] += multi_event_request[:logstash_events].length
|
418
|
+
end
|
419
|
+
|
402
420
|
if !exc_data.nil?
|
403
421
|
message = "Retry successful after error."
|
404
422
|
if exc_commonly_retried
|
@@ -437,6 +455,10 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
437
455
|
|
438
456
|
|
439
457
|
def log_retry_failure(multi_event_request, exc_data, exc_retries, exc_sleep)
|
458
|
+
@stats_lock.synchronize do
|
459
|
+
@multi_receive_statistics[:total_events_processed] += multi_event_request[:logstash_events].length
|
460
|
+
@multi_receive_statistics[:failed_events_processed] += multi_event_request[:logstash_events].length
|
461
|
+
end
|
440
462
|
message = "Failed to send #{multi_event_request[:logstash_events].length} events after #{exc_retries} tries."
|
441
463
|
sample_events = Array.new
|
442
464
|
multi_event_request[:logstash_events][0,5].each {|l_event|
|
@@ -612,7 +634,11 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
612
634
|
# flatten record
|
613
635
|
if @flatten_nested_values
|
614
636
|
start_time = Time.now.to_f
|
615
|
-
|
637
|
+
begin
|
638
|
+
record = Scalyr::Common::Util.flatten(record, delimiter=@flatten_nested_values_delimiter, flatten_arrays=@flatten_nested_arrays, fix_deep_flattening_delimiters=@fix_deep_flattening_delimiters, max_key_count=@flattening_max_key_count)
|
639
|
+
rescue Scalyr::Common::Util::MaxKeyCountError => e
|
640
|
+
@logger.warn("Error while flattening record", :error_message => e.message, :sample_keys => e.sample_keys)
|
641
|
+
end
|
616
642
|
end_time = Time.now.to_f
|
617
643
|
flatten_nested_values_duration = end_time - start_time
|
618
644
|
end
|
data/lib/scalyr/common/util.rb
CHANGED
@@ -1,44 +1,95 @@
|
|
1
1
|
module Scalyr; module Common; module Util;
|
2
2
|
|
3
|
+
class MaxKeyCountError < StandardError
|
4
|
+
attr_reader :message, :sample_keys
|
5
|
+
|
6
|
+
def initialize(message, sample_keys)
|
7
|
+
@message = message
|
8
|
+
@sample_keys = sample_keys
|
9
|
+
end
|
10
|
+
end
|
3
11
|
|
4
12
|
# Flattens a hash or array, returning a hash where keys are a delimiter-separated string concatenation of all
|
5
13
|
# nested keys. Returned keys are always strings. If a non-hash or array is provided, raises TypeError.
|
6
14
|
# Please see rspec util_spec.rb for expected behavior.
|
7
|
-
|
15
|
+
# Includes a known bug where defined delimiter will not be used for nesting levels past the first, this is kept
|
16
|
+
# because some queries and dashboards already rely on the broken functionality.
|
17
|
+
def self.flatten(hash_obj, delimiter='_', flatten_arrays=true, fix_deep_flattening_delimiters=false, max_key_count=-1)
|
8
18
|
|
9
19
|
# base case is input object is not enumerable, in which case simply return it
|
10
|
-
if !
|
20
|
+
if !hash_obj.respond_to?(:each)
|
11
21
|
raise TypeError.new('Input must be a hash or array')
|
12
22
|
end
|
23
|
+
# case where we pass in a valid array, but don't want to flatten arrays
|
24
|
+
if !hash_obj.respond_to?(:has_key?) and !flatten_arrays
|
25
|
+
return hash_obj
|
26
|
+
end
|
13
27
|
|
28
|
+
stack = []
|
29
|
+
stack << hash_obj
|
30
|
+
key_stack = []
|
31
|
+
key_stack << ""
|
32
|
+
key_list = []
|
33
|
+
key_list_width = []
|
14
34
|
result = Hash.new
|
15
|
-
|
16
|
-
#
|
35
|
+
test_key = 0
|
36
|
+
#Debugging
|
37
|
+
#require 'pry'
|
38
|
+
#binding.pry
|
39
|
+
|
40
|
+
until stack.empty?
|
41
|
+
obj = stack.pop
|
42
|
+
key_list << key_stack.pop
|
43
|
+
|
44
|
+
# Case when object is a hash
|
45
|
+
if obj.respond_to?(:has_key?) and obj.keys.count > 0
|
46
|
+
key_list_width << obj.keys.count
|
47
|
+
obj.each do |key, value|
|
48
|
+
key_stack << key
|
49
|
+
stack << value
|
50
|
+
end
|
17
51
|
|
18
|
-
|
52
|
+
# Case when object is an array we intend to flatten
|
53
|
+
elsif flatten_arrays and obj.respond_to?(:each) and obj.count > 0
|
54
|
+
key_list_width << obj.count
|
55
|
+
obj.each_with_index do |value, index|
|
56
|
+
key_stack << index
|
57
|
+
stack << value
|
58
|
+
end
|
19
59
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
60
|
+
else
|
61
|
+
result_key = ""
|
62
|
+
delim = delimiter
|
63
|
+
key_list.each_with_index do |key, index|
|
64
|
+
# We have a blank key at the start of the key list to avoid issues with calling pop, so we ignore delimiter
|
65
|
+
# for the first two keys
|
66
|
+
if index > 1
|
67
|
+
result_key += "#{delim}#{key}"
|
68
|
+
if not fix_deep_flattening_delimiters
|
69
|
+
delim = "_"
|
70
|
+
end
|
71
|
+
else
|
72
|
+
result_key += "#{key}"
|
25
73
|
end
|
26
|
-
else
|
27
|
-
result["#{key}"] = value
|
28
74
|
end
|
29
|
-
|
75
|
+
result[result_key] = obj
|
30
76
|
|
31
|
-
|
77
|
+
if max_key_count > -1 and result.keys.count > max_key_count
|
78
|
+
raise MaxKeyCountError.new(
|
79
|
+
"Resulting flattened object will contain more keys than the configured flattening_max_key_count of #{max_key_count}",
|
80
|
+
result.keys[0..6]
|
81
|
+
)
|
82
|
+
end
|
32
83
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
result["#{index}#{delimiter}#{subkey}"] = subvalue
|
38
|
-
end
|
39
|
-
else
|
40
|
-
result["#{index}"] = value
|
84
|
+
throw_away = key_list.pop
|
85
|
+
until key_list_width.empty? or key_list_width[-1] > 1
|
86
|
+
throw_away = key_list_width.pop
|
87
|
+
throw_away = key_list.pop
|
41
88
|
end
|
89
|
+
if not key_list_width.empty?
|
90
|
+
key_list_width[-1] -= 1
|
91
|
+
end
|
92
|
+
|
42
93
|
end
|
43
94
|
end
|
44
95
|
|
data/lib/scalyr/constants.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
PLUGIN_VERSION = "v0.1.
|
2
|
+
PLUGIN_VERSION = "v0.1.21.beta"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'logstash-output-scalyr'
|
3
|
-
s.version = '0.1.
|
3
|
+
s.version = '0.1.21.beta'
|
4
4
|
s.licenses = ['Apache-2.0']
|
5
5
|
s.summary = "Scalyr output plugin for Logstash"
|
6
6
|
s.description = "Sends log data collected by Logstash to Scalyr (https://www.scalyr.com)"
|
@@ -288,6 +288,72 @@ describe LogStash::Outputs::Scalyr do
|
|
288
288
|
end
|
289
289
|
end
|
290
290
|
|
291
|
+
context "when configured to flatten values with custom delimiter and deep delimiter fix" do
|
292
|
+
config = {
|
293
|
+
'api_write_token' => '1234',
|
294
|
+
'flatten_tags' => true,
|
295
|
+
'flat_tag_value' => 'true',
|
296
|
+
'flat_tag_prefix' => 'tag_prefix_',
|
297
|
+
'flatten_nested_values' => true, # this converts into string 'true'
|
298
|
+
'flatten_nested_values_delimiter' => ".",
|
299
|
+
'fix_deep_flattening_delimiters' => true,
|
300
|
+
}
|
301
|
+
plugin = LogStash::Outputs::Scalyr.new(config)
|
302
|
+
it "flattens nested values with a period" do
|
303
|
+
allow(plugin).to receive(:send_status).and_return(nil)
|
304
|
+
plugin.register
|
305
|
+
result = plugin.build_multi_event_request_array(sample_events)
|
306
|
+
body = JSON.parse(result[0][:body])
|
307
|
+
expect(body['events'].size).to eq(3)
|
308
|
+
expect(body['events'][2]['attrs']).to eq({
|
309
|
+
"nested.a" => 1,
|
310
|
+
"nested.b.0" => 3,
|
311
|
+
"nested.b.1" => 4,
|
312
|
+
"nested.b.2" => 5,
|
313
|
+
'seq' => 3,
|
314
|
+
'source_file' => 'my file 3',
|
315
|
+
'source_host' => 'my host 3',
|
316
|
+
'serverHost' => 'Logstash',
|
317
|
+
"tag_prefix_t1" => "true",
|
318
|
+
"tag_prefix_t2" => "true",
|
319
|
+
"tag_prefix_t3" => "true",
|
320
|
+
"parser" => "logstashParser",
|
321
|
+
})
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
context "when configured to flatten values with custom delimiter, no array flattening" do
|
326
|
+
config = {
|
327
|
+
'api_write_token' => '1234',
|
328
|
+
'flatten_tags' => true,
|
329
|
+
'flat_tag_value' => 'true',
|
330
|
+
'flat_tag_prefix' => 'tag_prefix_',
|
331
|
+
'flatten_nested_values' => true, # this converts into string 'true'
|
332
|
+
'flatten_nested_arrays' => false,
|
333
|
+
'flatten_nested_values_delimiter' => ".",
|
334
|
+
}
|
335
|
+
plugin = LogStash::Outputs::Scalyr.new(config)
|
336
|
+
it "flattens nested values with a period" do
|
337
|
+
allow(plugin).to receive(:send_status).and_return(nil)
|
338
|
+
plugin.register
|
339
|
+
result = plugin.build_multi_event_request_array(sample_events)
|
340
|
+
body = JSON.parse(result[0][:body])
|
341
|
+
expect(body['events'].size).to eq(3)
|
342
|
+
expect(body['events'][2]['attrs']).to eq({
|
343
|
+
"nested.a" => 1,
|
344
|
+
"nested.b" => [3, 4, 5],
|
345
|
+
'seq' => 3,
|
346
|
+
'source_file' => 'my file 3',
|
347
|
+
'source_host' => 'my host 3',
|
348
|
+
'serverHost' => 'Logstash',
|
349
|
+
"tag_prefix_t1" => "true",
|
350
|
+
"tag_prefix_t2" => "true",
|
351
|
+
"tag_prefix_t3" => "true",
|
352
|
+
"parser" => "logstashParser",
|
353
|
+
})
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
291
357
|
context "when configured to flatten values and tags" do
|
292
358
|
config = {
|
293
359
|
'api_write_token' => '1234',
|
@@ -343,6 +409,38 @@ describe LogStash::Outputs::Scalyr do
|
|
343
409
|
end
|
344
410
|
end
|
345
411
|
|
412
|
+
context "when configured to flatten with max keys configured to 3" do
|
413
|
+
config = {
|
414
|
+
'api_write_token' => '1234',
|
415
|
+
'flatten_nested_values' => true, # this converts into string 'true'
|
416
|
+
'flattening_max_key_count' => 3,
|
417
|
+
}
|
418
|
+
plugin = LogStash::Outputs::Scalyr.new(config)
|
419
|
+
it "does not flatten" do
|
420
|
+
allow(plugin).to receive(:send_status).and_return(nil)
|
421
|
+
plugin.register
|
422
|
+
allow(plugin.instance_variable_get(:@logger)).to receive(:warn)
|
423
|
+
result = plugin.build_multi_event_request_array(sample_events)
|
424
|
+
body = JSON.parse(result[0][:body])
|
425
|
+
expect(body['events'].size).to eq(3)
|
426
|
+
expect(body['events'][2]['attrs']).to eq({
|
427
|
+
"nested" => {'a'=>1, 'b'=>[3,4,5]},
|
428
|
+
'seq' => 3,
|
429
|
+
'source_file' => 'my file 3',
|
430
|
+
'source_host' => 'my host 3',
|
431
|
+
'serverHost' => 'Logstash',
|
432
|
+
"tags" => ["t1", "t2", "t3"],
|
433
|
+
"parser" => "logstashParser",
|
434
|
+
})
|
435
|
+
expect(plugin.instance_variable_get(:@logger)).to have_received(:warn).with("Error while flattening record",
|
436
|
+
{
|
437
|
+
:error_message=>"Resulting flattened object will contain more keys than the configured flattening_max_key_count of 3",
|
438
|
+
:sample_keys=>["serverHost", "parser", "tags_2", "tags_1"]
|
439
|
+
}
|
440
|
+
).exactly(3).times
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
346
444
|
context "when receiving an event with Bignums" do
|
347
445
|
config = {
|
348
446
|
'api_write_token' => '1234',
|
@@ -1,7 +1,113 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require "scalyr/common/util"
|
3
3
|
|
4
|
+
LARGE_OBJECT_IN = {
|
5
|
+
"level": "info",
|
6
|
+
"ts": "2020-08-11T02:26:17.078Z",
|
7
|
+
"caller": "api/foo:480",
|
8
|
+
"msg": "assign active task foobar",
|
9
|
+
"accountId": 12345,
|
10
|
+
"cycleId": 6789,
|
11
|
+
"uuid": "a405a4b58810e3aaa078f751bd32baa8b60aaad1",
|
12
|
+
"task": {
|
13
|
+
"Id": 1211111181111111400,
|
14
|
+
"TaskTypes": [
|
15
|
+
4,
|
16
|
+
11,
|
17
|
+
10,
|
18
|
+
12,
|
19
|
+
17,
|
20
|
+
14
|
21
|
+
],
|
22
|
+
"Ips": [
|
23
|
+
"127.0.0.1",
|
24
|
+
"127.0.0.2",
|
25
|
+
"127.0.0.3",
|
26
|
+
"127.0.0.4",
|
27
|
+
"127.0.0.5",
|
28
|
+
],
|
29
|
+
"FooProps": {
|
30
|
+
"10": {
|
31
|
+
"TcpPorts": [
|
32
|
+
22,
|
33
|
+
23,
|
34
|
+
25,
|
35
|
+
80,
|
36
|
+
55,
|
37
|
+
8000,
|
38
|
+
8080,
|
39
|
+
],
|
40
|
+
"UdpPorts": []
|
41
|
+
}
|
42
|
+
},
|
43
|
+
"Subnet": "127.0.0.0/24"
|
44
|
+
},
|
45
|
+
"relevance": 0,
|
46
|
+
"scannerIp": "10.0.0.2",
|
47
|
+
"gatewayIp": "10.0.0.1",
|
48
|
+
"gatewayMac": "fa:fa:fa:fa",
|
49
|
+
"wired": true,
|
50
|
+
"elapsed": 74.86664
|
51
|
+
}
|
4
52
|
|
53
|
+
LARGE_OBJECT_OUT = {
|
54
|
+
"accountId" => 12345,
|
55
|
+
"caller" => "api/foo:480",
|
56
|
+
"cycleId" => 6789,
|
57
|
+
"elapsed" => 74.86664,
|
58
|
+
"gatewayIp" => "10.0.0.1",
|
59
|
+
"gatewayMac" => "fa:fa:fa:fa",
|
60
|
+
"level" => "info",
|
61
|
+
"msg" => "assign active task foobar",
|
62
|
+
"relevance" => 0,
|
63
|
+
"scannerIp" => "10.0.0.2",
|
64
|
+
"task_FooProps_10_TcpPorts_0" => 22,
|
65
|
+
"task_FooProps_10_TcpPorts_1" => 23,
|
66
|
+
"task_FooProps_10_TcpPorts_2" => 25,
|
67
|
+
"task_FooProps_10_TcpPorts_3" => 80,
|
68
|
+
"task_FooProps_10_TcpPorts_4" => 55,
|
69
|
+
"task_FooProps_10_TcpPorts_5" => 8000,
|
70
|
+
"task_FooProps_10_TcpPorts_6" => 8080,
|
71
|
+
"task_FooProps_10_UdpPorts" => [],
|
72
|
+
"task_Id" => 1211111181111111400,
|
73
|
+
"task_Ips_0" => "127.0.0.1",
|
74
|
+
"task_Ips_1" => "127.0.0.2",
|
75
|
+
"task_Ips_2" => "127.0.0.3",
|
76
|
+
"task_Ips_3" => "127.0.0.4",
|
77
|
+
"task_Ips_4" => "127.0.0.5",
|
78
|
+
"task_Subnet" => "127.0.0.0/24",
|
79
|
+
"task_TaskTypes_0" => 4,
|
80
|
+
"task_TaskTypes_1" => 11,
|
81
|
+
"task_TaskTypes_2" => 10,
|
82
|
+
"task_TaskTypes_3" => 12,
|
83
|
+
"task_TaskTypes_4" => 17,
|
84
|
+
"task_TaskTypes_5" => 14,
|
85
|
+
"ts" => "2020-08-11T02:26:17.078Z",
|
86
|
+
"uuid" => "a405a4b58810e3aaa078f751bd32baa8b60aaad1",
|
87
|
+
"wired" => true,
|
88
|
+
}
|
89
|
+
|
90
|
+
LARGE_OBJECT_OUT_NO_FLATTEN_ARRAYS = {
|
91
|
+
"accountId" => 12345,
|
92
|
+
"caller" => "api/foo:480",
|
93
|
+
"cycleId" => 6789,
|
94
|
+
"elapsed" => 74.86664,
|
95
|
+
"gatewayIp" => "10.0.0.1",
|
96
|
+
"gatewayMac" => "fa:fa:fa:fa",
|
97
|
+
"level" => "info",
|
98
|
+
"msg" => "assign active task foobar",
|
99
|
+
"relevance" => 0,
|
100
|
+
"scannerIp" => "10.0.0.2",
|
101
|
+
"task_FooProps_10_TcpPorts" => [22, 23, 25, 80, 55, 8000, 8080],
|
102
|
+
"task_FooProps_10_UdpPorts" => [],
|
103
|
+
"task_Id" => 1211111181111111400,
|
104
|
+
"task_Ips" => ["127.0.0.1", "127.0.0.2", "127.0.0.3", "127.0.0.4", "127.0.0.5"],
|
105
|
+
"task_Subnet" => "127.0.0.0/24",
|
106
|
+
"task_TaskTypes" => [4, 11, 10, 12, 17, 14],
|
107
|
+
"ts" => "2020-08-11T02:26:17.078Z",
|
108
|
+
"uuid" => "a405a4b58810e3aaa078f751bd32baa8b60aaad1",
|
109
|
+
"wired" => true,
|
110
|
+
}
|
5
111
|
describe Scalyr::Common::Util do
|
6
112
|
it "does not flatten an already-flat dict" do
|
7
113
|
din = {
|
@@ -132,6 +238,198 @@ describe Scalyr::Common::Util do
|
|
132
238
|
expect(Scalyr::Common::Util.flatten(din)).to eq(dout)
|
133
239
|
end
|
134
240
|
|
241
|
+
it "flattens a single-level array, no array flattening" do
|
242
|
+
din = [1, 2, 3]
|
243
|
+
dout = [1, 2, 3]
|
244
|
+
expect(Scalyr::Common::Util.flatten(din, "_", flatten_arrays=false)).to eq(dout)
|
245
|
+
end
|
246
|
+
|
247
|
+
it "flattens a multi-level array, no array flattening" do
|
248
|
+
din = ['a', 'b', ['c', ['d', 'e', 'f'], 'g'], 'h', 'i']
|
249
|
+
dout = ['a', 'b', ['c', ['d', 'e', 'f'], 'g'], 'h', 'i']
|
250
|
+
expect(Scalyr::Common::Util.flatten(din, "_", flatten_arrays=false)).to eq(dout)
|
251
|
+
end
|
252
|
+
|
253
|
+
it "flattens a hash that contains an array with hashes, no array flattening" do
|
254
|
+
din = {
|
255
|
+
'a' => 1,
|
256
|
+
"b" => {"a": "a"},
|
257
|
+
'c' => { "f" => [100, 200, {"g" => 1}] }
|
258
|
+
}
|
259
|
+
dout = {
|
260
|
+
'a' => 1,
|
261
|
+
"b_a" => "a",
|
262
|
+
'c_f' => [100, 200, {"g" => 1}]
|
263
|
+
}
|
264
|
+
expect(Scalyr::Common::Util.flatten(din, "_", flatten_arrays=false)).to eq(dout)
|
265
|
+
end
|
266
|
+
|
267
|
+
it "flattens a hash that contains an array, no array flattening" do
|
268
|
+
din = {
|
269
|
+
'a' => 1,
|
270
|
+
'c' => [100, 200, 300]
|
271
|
+
}
|
272
|
+
dout = {
|
273
|
+
'a' => 1,
|
274
|
+
'c' => [100, 200, 300]
|
275
|
+
}
|
276
|
+
expect(Scalyr::Common::Util.flatten(din, "_", flatten_arrays=false)).to eq(dout)
|
277
|
+
end
|
278
|
+
|
279
|
+
it "flattens a hash that contains an array that contains a hash, no array flattening" do
|
280
|
+
din = {
|
281
|
+
'a' => 1,
|
282
|
+
'c' => [
|
283
|
+
100,
|
284
|
+
{'d' => 1000, 'e' => 2000},
|
285
|
+
300
|
286
|
+
]
|
287
|
+
}
|
288
|
+
dout = {
|
289
|
+
'a' => 1,
|
290
|
+
'c' => [
|
291
|
+
100,
|
292
|
+
{'d' => 1000, 'e' => 2000},
|
293
|
+
300
|
294
|
+
]
|
295
|
+
}
|
296
|
+
expect(Scalyr::Common::Util.flatten(din, "_", flatten_arrays=false)).to eq(dout)
|
297
|
+
end
|
298
|
+
|
299
|
+
it "flattens a hash that contains an array that contains a hash that contains an array, no array flattening" do
|
300
|
+
din = {
|
301
|
+
'a' => 1,
|
302
|
+
'c' => [
|
303
|
+
100,
|
304
|
+
{'d' => 1000, 'e' => 2000, 'f' => [4, 5, 6]},
|
305
|
+
300
|
306
|
+
]
|
307
|
+
}
|
308
|
+
dout = {
|
309
|
+
'a' => 1,
|
310
|
+
'c' => [
|
311
|
+
100,
|
312
|
+
{'d' => 1000, 'e' => 2000, 'f' => [4, 5, 6]},
|
313
|
+
300
|
314
|
+
]
|
315
|
+
}
|
316
|
+
expect(Scalyr::Common::Util.flatten(din, "_", flatten_arrays=false)).to eq(dout)
|
317
|
+
end
|
318
|
+
|
319
|
+
it "flattens large hash correctly" do
|
320
|
+
expect(Scalyr::Common::Util.flatten(LARGE_OBJECT_IN, "_", flatten_arrays=true)).to eq(LARGE_OBJECT_OUT)
|
321
|
+
end
|
322
|
+
|
323
|
+
it "flattens large hash correctly not flatten arrays" do
|
324
|
+
expect(Scalyr::Common::Util.flatten(LARGE_OBJECT_IN, "_", flatten_arrays=false)).to eq(LARGE_OBJECT_OUT_NO_FLATTEN_ARRAYS)
|
325
|
+
end
|
326
|
+
|
327
|
+
it "flattens hash containing empty list correctly" do
|
328
|
+
obj_in = {
|
329
|
+
"abc" => 123,
|
330
|
+
"array" => [],
|
331
|
+
"hash" => {
|
332
|
+
"value" => "abc123",
|
333
|
+
"another_array" => []
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
obj_out = {
|
338
|
+
"abc" => 123,
|
339
|
+
"array" => [],
|
340
|
+
"hash_value" => "abc123",
|
341
|
+
"hash_another_array" => []
|
342
|
+
}
|
343
|
+
expect(Scalyr::Common::Util.flatten(obj_in, "_", flatten_arrays=true)).to eq(obj_out)
|
344
|
+
end
|
345
|
+
|
346
|
+
it "flattens hash containing empty list correctly not flatten arrays" do
|
347
|
+
obj_in = {
|
348
|
+
"abc" => 123,
|
349
|
+
"array" => [],
|
350
|
+
"hash" => {
|
351
|
+
"value" => "abc123",
|
352
|
+
"another_array" => []
|
353
|
+
}
|
354
|
+
}
|
355
|
+
|
356
|
+
obj_out = {
|
357
|
+
"abc" => 123,
|
358
|
+
"array" => [],
|
359
|
+
"hash_value" => "abc123",
|
360
|
+
"hash_another_array" => []
|
361
|
+
}
|
362
|
+
expect(Scalyr::Common::Util.flatten(obj_in, "_", flatten_arrays=false)).to eq(obj_out)
|
363
|
+
end
|
364
|
+
|
365
|
+
it "flattens hash containing empty hash correctly" do
|
366
|
+
obj_in = {
|
367
|
+
"abc" => 123,
|
368
|
+
"empty_hash" => {},
|
369
|
+
"hash" => {
|
370
|
+
"value" => "abc123",
|
371
|
+
"another_hash" => {}
|
372
|
+
}
|
373
|
+
}
|
374
|
+
|
375
|
+
obj_out = {
|
376
|
+
"abc" => 123,
|
377
|
+
"empty_hash" => {},
|
378
|
+
"hash_value" => "abc123",
|
379
|
+
"hash_another_hash" => {}
|
380
|
+
}
|
381
|
+
expect(Scalyr::Common::Util.flatten(obj_in, "_", flatten_arrays=true)).to eq(obj_out)
|
382
|
+
end
|
383
|
+
|
384
|
+
it "flattens hash containing nested bignum correctly" do
|
385
|
+
obj_in = {
|
386
|
+
"abc" => 123,
|
387
|
+
"hash" => {
|
388
|
+
"value" => 2000023030042002050202030320240
|
389
|
+
}
|
390
|
+
}
|
391
|
+
|
392
|
+
obj_out = {
|
393
|
+
"abc" => 123,
|
394
|
+
"hash_value" => 2000023030042002050202030320240
|
395
|
+
}
|
396
|
+
expect(Scalyr::Common::Util.flatten(obj_in, "_", flatten_arrays=true)).to eq(obj_out)
|
397
|
+
end
|
398
|
+
|
399
|
+
it "flattens hash containing nested boolean correctly" do
|
400
|
+
obj_in = {
|
401
|
+
"abc" => 123,
|
402
|
+
"hash" => {
|
403
|
+
"value" => true,
|
404
|
+
"otherValue" => false
|
405
|
+
}
|
406
|
+
}
|
407
|
+
|
408
|
+
obj_out = {
|
409
|
+
"abc" => 123,
|
410
|
+
"hash_value" => true,
|
411
|
+
"hash_otherValue" => false
|
412
|
+
}
|
413
|
+
expect(Scalyr::Common::Util.flatten(obj_in, "_", flatten_arrays=true)).to eq(obj_out)
|
414
|
+
end
|
415
|
+
|
416
|
+
it "flattens hash containing nested float correctly" do
|
417
|
+
obj_in = {
|
418
|
+
"abc" => 123,
|
419
|
+
"hash" => {
|
420
|
+
"value" => 321.12345,
|
421
|
+
"otherValue" => 0.0000003
|
422
|
+
}
|
423
|
+
}
|
424
|
+
|
425
|
+
obj_out = {
|
426
|
+
"abc" => 123,
|
427
|
+
"hash_value" => 321.12345,
|
428
|
+
"hash_otherValue" => 0.0000003
|
429
|
+
}
|
430
|
+
expect(Scalyr::Common::Util.flatten(obj_in, "_", flatten_arrays=true)).to eq(obj_out)
|
431
|
+
end
|
432
|
+
|
135
433
|
it "accepts custom delimiters" do
|
136
434
|
din = {
|
137
435
|
'a' => 1,
|
@@ -148,6 +446,42 @@ describe Scalyr::Common::Util do
|
|
148
446
|
expect(Scalyr::Common::Util.flatten(din, ':')).to eq(dout)
|
149
447
|
end
|
150
448
|
|
449
|
+
it "accepts custom delimiters with greater depth" do
|
450
|
+
din = {
|
451
|
+
'a' => 1,
|
452
|
+
'b' => {
|
453
|
+
'c' => {
|
454
|
+
'e' => 100
|
455
|
+
},
|
456
|
+
'd' => 200,
|
457
|
+
}
|
458
|
+
}
|
459
|
+
dout = {
|
460
|
+
'a' => 1,
|
461
|
+
'b:c_e' => 100,
|
462
|
+
'b:d' => 200,
|
463
|
+
}
|
464
|
+
expect(Scalyr::Common::Util.flatten(din, ':')).to eq(dout)
|
465
|
+
end
|
466
|
+
|
467
|
+
it "accepts custom delimiters with greater depth and deep delimiters fix" do
|
468
|
+
din = {
|
469
|
+
'a' => 1,
|
470
|
+
'b' => {
|
471
|
+
'c' => {
|
472
|
+
'e' => 100
|
473
|
+
},
|
474
|
+
'd' => 200,
|
475
|
+
}
|
476
|
+
}
|
477
|
+
dout = {
|
478
|
+
'a' => 1,
|
479
|
+
'b:c:e' => 100,
|
480
|
+
'b:d' => 200,
|
481
|
+
}
|
482
|
+
expect(Scalyr::Common::Util.flatten(din, ':', true, true)).to eq(dout)
|
483
|
+
end
|
484
|
+
|
151
485
|
it "stringifies non-string keys" do
|
152
486
|
din = {
|
153
487
|
'a' => 1,
|
@@ -183,4 +517,23 @@ describe Scalyr::Common::Util do
|
|
183
517
|
it "raises exception if a non-dict is provided" do
|
184
518
|
expect {Scalyr::Common::Util.flatten(1)}.to raise_error(TypeError)
|
185
519
|
end
|
520
|
+
|
521
|
+
it "flattens a hash 5000 layers deep" do
|
522
|
+
din = {
|
523
|
+
'a' => {},
|
524
|
+
}
|
525
|
+
hash = din
|
526
|
+
for i in 0...4999
|
527
|
+
hash = hash["a"]
|
528
|
+
hash["a"] = {}
|
529
|
+
if i == 4998
|
530
|
+
hash["a"] = "b"
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
dout = {
|
535
|
+
'a' + "_a" * 4999 => "b",
|
536
|
+
}
|
537
|
+
expect(Scalyr::Common::Util.flatten(din, '_')).to eq(dout)
|
538
|
+
end
|
186
539
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-output-scalyr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.21.beta
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Edward Chee
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|