logstash-output-scalyr 0.1.16.beta → 0.1.20.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 +17 -0
- data/README.md +1 -1
- data/lib/logstash/outputs/scalyr.rb +55 -4
- data/lib/scalyr/common/util.rb +94 -22
- data/lib/scalyr/constants.rb +1 -1
- data/logstash-output-scalyr.gemspec +1 -1
- data/spec/benchmarks/bignum_fixing.rb +90 -0
- data/spec/logstash/outputs/scalyr_spec.rb +116 -0
- data/spec/scalyr/common/util_spec.rb +119 -0
- data/vendor/bundle/jruby/2.5.0/bin/htmldiff +1 -1
- data/vendor/bundle/jruby/2.5.0/bin/ldiff +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23ede2e07ed42483146d4a267c8517fed2f8639fb27998e3f37e8732bd50a45d
|
4
|
+
data.tar.gz: 4d3594dd94a908d5852d58fc6e5214982ae731ef8b3de3eb3707455bfa77bd3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b11f8aaeea8530b316de904d2fd4b654b8b2a33c2c13ca10614ca3cb346a154e494d9ec7c831a1cf0a83449101ed2bb59c97cc5506870bd252c31cbf9dbc9052
|
7
|
+
data.tar.gz: dc6a23b5eae108b7ac8818cfd018c9a9670f69c31fb582b7c8568ebb33720fef19564b54420b2cfc4e7fc04e4a742836bdd2eefc013b15e240379726858fe15f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# Beta
|
2
2
|
|
3
|
+
## 0.1.20.beta
|
4
|
+
- Rewrite flattening function to no longer be recursive, to help avoid maxing out the stack.
|
5
|
+
- Added a configurable value `flattening_max_key_count` to create a limit on how large of a record we can flatten.
|
6
|
+
It limits the maximum amount of keys we can have in the final flattened record. Defaults to unlimited.
|
7
|
+
|
8
|
+
## 0.1.19.beta
|
9
|
+
- Undo a change to nested value flattening functionality to keep existing formatting. This change can be re-enabled
|
10
|
+
by setting the `fix_deep_flattening_delimiters` configuration option to true.
|
11
|
+
|
12
|
+
## 0.1.18.beta
|
13
|
+
- Add metrics for successfully sent and failed logstash events, and retries.
|
14
|
+
- Make array flattening optional during nested value flattening with the `flatten_nested_arrays` configuration option.
|
15
|
+
|
16
|
+
## 0.1.17.beta
|
17
|
+
- Catch errors relating to Bignum conversions present in the ``json`` library and manually convert to string as
|
18
|
+
a workaround.
|
19
|
+
|
3
20
|
## 0.1.16.beta
|
4
21
|
- Fix race condition in ``register()`` method.
|
5
22
|
|
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.20.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
|
|
@@ -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
|
@@ -240,7 +243,12 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
240
243
|
# Plugin level (either per batch or event level metrics). Other request
|
241
244
|
# level metrics are handled by the HTTP Client class.
|
242
245
|
@multi_receive_statistics = {
|
243
|
-
:total_multi_receive_secs => 0
|
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,
|
251
|
+
:total_java_class_cast_errors => 0
|
244
252
|
}
|
245
253
|
@plugin_metrics = get_new_metrics
|
246
254
|
|
@@ -344,6 +352,9 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
344
352
|
sleep_interval = sleep_for(sleep_interval)
|
345
353
|
exc_sleep += sleep_interval
|
346
354
|
exc_retries += 1
|
355
|
+
@stats_lock.synchronize do
|
356
|
+
@multi_receive_statistics[:total_retry_count] += 1
|
357
|
+
end
|
347
358
|
message = "Error uploading to Scalyr (will backoff-retry)"
|
348
359
|
exc_data = {
|
349
360
|
:error_class => e.e_class,
|
@@ -393,11 +404,19 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
393
404
|
}
|
394
405
|
exc_sleep += sleep_interval
|
395
406
|
exc_retries += 1
|
407
|
+
@stats_lock.synchronize do
|
408
|
+
@multi_receive_statistics[:total_retry_count] += 1
|
409
|
+
end
|
396
410
|
retry if @running and exc_retries < @max_retries
|
397
411
|
log_retry_failure(multi_event_request, exc_data, exc_retries, exc_sleep)
|
398
412
|
next
|
399
413
|
end
|
400
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
|
+
|
401
420
|
if !exc_data.nil?
|
402
421
|
message = "Retry successful after error."
|
403
422
|
if exc_commonly_retried
|
@@ -436,6 +455,10 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
436
455
|
|
437
456
|
|
438
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
|
439
462
|
message = "Failed to send #{multi_event_request[:logstash_events].length} events after #{exc_retries} tries."
|
440
463
|
sample_events = Array.new
|
441
464
|
multi_event_request[:logstash_events][0,5].each {|l_event|
|
@@ -611,7 +634,11 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
611
634
|
# flatten record
|
612
635
|
if @flatten_nested_values
|
613
636
|
start_time = Time.now.to_f
|
614
|
-
|
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
|
615
642
|
end_time = Time.now.to_f
|
616
643
|
flatten_nested_values_duration = end_time - start_time
|
617
644
|
end
|
@@ -661,6 +688,21 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
661
688
|
).force_encoding('UTF-8')
|
662
689
|
end
|
663
690
|
event_json = scalyr_event.to_json
|
691
|
+
rescue Java::JavaLang::ClassCastException => e
|
692
|
+
# Most likely we ran into the issue described here: https://github.com/flori/json/issues/336
|
693
|
+
# Because of the version of jruby logstash works with we don't have the option to just update this away,
|
694
|
+
# so if we run into it we convert bignums into strings so we can get the data in at least.
|
695
|
+
# This is fixed in JRuby 9.2.7, which includes json 2.2.0
|
696
|
+
@logger.warn("Error serializing events to JSON, likely due to the presence of Bignum values. Converting Bignum values to strings.")
|
697
|
+
@stats_lock.synchronize do
|
698
|
+
@multi_receive_statistics[:total_java_class_cast_errors] += 1
|
699
|
+
end
|
700
|
+
Scalyr::Common::Util.convert_bignums(scalyr_event)
|
701
|
+
event_json = scalyr_event.to_json
|
702
|
+
log_json = nil
|
703
|
+
if add_log
|
704
|
+
log_json = logs[log_identifier].to_json
|
705
|
+
end
|
664
706
|
end
|
665
707
|
|
666
708
|
# generate new request if json size of events in the array exceed maximum request buffer size
|
@@ -749,7 +791,16 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
749
791
|
|
750
792
|
# We time serialization to get some insight on how long it takes to serialize the request body
|
751
793
|
start_time = Time.now.to_f
|
752
|
-
|
794
|
+
begin
|
795
|
+
serialized_body = body.to_json
|
796
|
+
rescue Java::JavaLang::ClassCastException => e
|
797
|
+
@logger.warn("Error serializing events to JSON, likely due to the presence of Bignum values. Converting Bignum values to strings.")
|
798
|
+
@stats_lock.synchronize do
|
799
|
+
@multi_receive_statistics[:total_java_class_cast_errors] += 1
|
800
|
+
end
|
801
|
+
Scalyr::Common::Util.convert_bignums(body)
|
802
|
+
serialized_body = body.to_json
|
803
|
+
end
|
753
804
|
end_time = Time.now.to_f
|
754
805
|
serialization_duration = end_time - start_time
|
755
806
|
{
|
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
|
17
39
|
|
18
|
-
|
40
|
+
until stack.empty?
|
41
|
+
obj = stack.pop
|
42
|
+
key_list << key_stack.pop
|
19
43
|
|
20
|
-
#
|
21
|
-
obj.
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
else
|
27
|
-
result["#{key}"] = value
|
44
|
+
# Case when object is a hash
|
45
|
+
if obj.respond_to?(:has_key?)
|
46
|
+
key_list_width << obj.keys.count
|
47
|
+
obj.each do |key, value|
|
48
|
+
key_stack << key
|
49
|
+
stack << value
|
28
50
|
end
|
29
|
-
end
|
30
51
|
|
31
|
-
|
52
|
+
# Case when object is an array we intend to flatten
|
53
|
+
elsif flatten_arrays and obj.respond_to?(:each)
|
54
|
+
key_list_width << obj.count
|
55
|
+
obj.each_with_index do |value, index|
|
56
|
+
key_stack << index
|
57
|
+
stack << value
|
58
|
+
end
|
32
59
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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}"
|
38
73
|
end
|
39
|
-
else
|
40
|
-
result["#{index}"] = value
|
41
74
|
end
|
75
|
+
result[result_key] = obj
|
76
|
+
|
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
|
83
|
+
|
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
|
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
|
|
@@ -52,5 +103,26 @@ def self.truncate(content, max)
|
|
52
103
|
return content
|
53
104
|
end
|
54
105
|
|
106
|
+
def self.convert_bignums(obj)
|
107
|
+
if obj.respond_to?(:has_key?) and obj.respond_to?(:each)
|
108
|
+
# input object is a hash
|
109
|
+
obj.each do |key, value|
|
110
|
+
obj[key] = convert_bignums(value)
|
111
|
+
end
|
112
|
+
|
113
|
+
elsif obj.respond_to?(:each)
|
114
|
+
# input object is an array or set
|
115
|
+
obj.each_with_index do |value, index|
|
116
|
+
obj[index] = convert_bignums(value)
|
117
|
+
end
|
118
|
+
|
119
|
+
elsif obj.is_a? Bignum
|
120
|
+
return obj.to_s
|
121
|
+
|
122
|
+
else
|
123
|
+
return obj
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
55
127
|
end; end; end;
|
56
128
|
|
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.20.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.20.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)"
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'quantile'
|
3
|
+
|
4
|
+
require_relative '../../lib/scalyr/common/util'
|
5
|
+
|
6
|
+
# Micro benchmark which measures how long it takes to find all the Bignums in a record and convert them to strings
|
7
|
+
|
8
|
+
ITERATIONS = 500
|
9
|
+
|
10
|
+
def rand_str(len)
|
11
|
+
return (0...len).map { (65 + rand(26)).chr }.join
|
12
|
+
end
|
13
|
+
|
14
|
+
def rand_bignum()
|
15
|
+
return 200004000020304050300 + rand(999999)
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate_hash(widths)
|
19
|
+
result = {}
|
20
|
+
if widths.empty?
|
21
|
+
return rand_bignum()
|
22
|
+
else
|
23
|
+
widths[0].times do
|
24
|
+
result[rand_str(9)] = generate_hash(widths[1..widths.length])
|
25
|
+
end
|
26
|
+
return result
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate_data_array_for_spec(spec)
|
31
|
+
data = []
|
32
|
+
ITERATIONS.times do
|
33
|
+
data << generate_hash(spec)
|
34
|
+
end
|
35
|
+
|
36
|
+
data
|
37
|
+
end
|
38
|
+
|
39
|
+
def run_benchmark_and_print_results(data, run_benchmark_func)
|
40
|
+
puts ""
|
41
|
+
puts "Using %s total keys in a hash" % [Scalyr::Common::Util.flatten(data[0]).count]
|
42
|
+
puts ""
|
43
|
+
|
44
|
+
result = []
|
45
|
+
ITERATIONS.times do |i|
|
46
|
+
result << Benchmark.measure { run_benchmark_func.(data[0]) }
|
47
|
+
end
|
48
|
+
|
49
|
+
sum = result.inject(nil) { |sum, t| sum.nil? ? sum = t : sum += t }
|
50
|
+
avg = sum / result.size
|
51
|
+
|
52
|
+
Benchmark.bm(7, "sum:", "avg:") do |b|
|
53
|
+
[sum, avg]
|
54
|
+
end
|
55
|
+
puts ""
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
puts "Using %s iterations" % [ITERATIONS]
|
60
|
+
puts ""
|
61
|
+
|
62
|
+
@value = Quantile::Estimator.new
|
63
|
+
@prng = Random.new
|
64
|
+
|
65
|
+
def convert_bignums(record)
|
66
|
+
Scalyr::Common::Util.convert_bignums(record)
|
67
|
+
end
|
68
|
+
|
69
|
+
puts "Util.convert_bignums()"
|
70
|
+
puts "==============================="
|
71
|
+
|
72
|
+
# Around ~200 keys in a hash
|
73
|
+
data = generate_data_array_for_spec([4, 4, 3, 4])
|
74
|
+
run_benchmark_and_print_results(data, method(:convert_bignums))
|
75
|
+
|
76
|
+
# Around ~200 keys in a hash (single level)
|
77
|
+
data = generate_data_array_for_spec([200])
|
78
|
+
run_benchmark_and_print_results(data, method(:convert_bignums))
|
79
|
+
|
80
|
+
# Around ~512 keys in a hash
|
81
|
+
data = generate_data_array_for_spec([8, 4, 4, 4])
|
82
|
+
run_benchmark_and_print_results(data, method(:convert_bignums))
|
83
|
+
|
84
|
+
# Around ~960 keys in a hash
|
85
|
+
data = generate_data_array_for_spec([12, 5, 4, 4])
|
86
|
+
run_benchmark_and_print_results(data, method(:convert_bignums))
|
87
|
+
|
88
|
+
# Around ~2700 keys in a hash
|
89
|
+
data = generate_data_array_for_spec([14, 8, 6, 4])
|
90
|
+
run_benchmark_and_print_results(data, method(:convert_bignums))
|
@@ -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',
|
@@ -342,5 +408,55 @@ describe LogStash::Outputs::Scalyr do
|
|
342
408
|
})
|
343
409
|
end
|
344
410
|
end
|
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
|
+
|
444
|
+
context "when receiving an event with Bignums" do
|
445
|
+
config = {
|
446
|
+
'api_write_token' => '1234',
|
447
|
+
}
|
448
|
+
plugin = LogStash::Outputs::Scalyr.new(config)
|
449
|
+
it "doesn't throw an error" do
|
450
|
+
allow(plugin).to receive(:send_status).and_return(nil)
|
451
|
+
plugin.register
|
452
|
+
e = LogStash::Event.new
|
453
|
+
e.set('bignumber', 2000023030042002050202030320240)
|
454
|
+
allow(plugin.instance_variable_get(:@logger)).to receive(:error)
|
455
|
+
result = plugin.build_multi_event_request_array([e])
|
456
|
+
body = JSON.parse(result[0][:body])
|
457
|
+
expect(body['events'].size).to eq(1)
|
458
|
+
expect(plugin.instance_variable_get(:@logger)).to_not receive(:error)
|
459
|
+
end
|
460
|
+
end
|
345
461
|
end
|
346
462
|
end
|
@@ -132,6 +132,70 @@ describe Scalyr::Common::Util do
|
|
132
132
|
expect(Scalyr::Common::Util.flatten(din)).to eq(dout)
|
133
133
|
end
|
134
134
|
|
135
|
+
it "flattens a single-level array, no array flattening" do
|
136
|
+
din = [1, 2, 3]
|
137
|
+
dout = [1, 2, 3]
|
138
|
+
expect(Scalyr::Common::Util.flatten(din, "_", flatten_arrays=false)).to eq(dout)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "flattens a multi-level array, no array flattening" do
|
142
|
+
din = ['a', 'b', ['c', ['d', 'e', 'f'], 'g'], 'h', 'i']
|
143
|
+
dout = ['a', 'b', ['c', ['d', 'e', 'f'], 'g'], 'h', 'i']
|
144
|
+
expect(Scalyr::Common::Util.flatten(din, "_", flatten_arrays=false)).to eq(dout)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "flattens a hash that contains an array, no array flattening" do
|
148
|
+
din = {
|
149
|
+
'a' => 1,
|
150
|
+
'c' => [100, 200, 300]
|
151
|
+
}
|
152
|
+
dout = {
|
153
|
+
'a' => 1,
|
154
|
+
'c' => [100, 200, 300]
|
155
|
+
}
|
156
|
+
expect(Scalyr::Common::Util.flatten(din, "_", flatten_arrays=false)).to eq(dout)
|
157
|
+
end
|
158
|
+
|
159
|
+
it "flattens a hash that contains an array that contains a hash, no array flattening" do
|
160
|
+
din = {
|
161
|
+
'a' => 1,
|
162
|
+
'c' => [
|
163
|
+
100,
|
164
|
+
{'d' => 1000, 'e' => 2000},
|
165
|
+
300
|
166
|
+
]
|
167
|
+
}
|
168
|
+
dout = {
|
169
|
+
'a' => 1,
|
170
|
+
'c' => [
|
171
|
+
100,
|
172
|
+
{'d' => 1000, 'e' => 2000},
|
173
|
+
300
|
174
|
+
]
|
175
|
+
}
|
176
|
+
expect(Scalyr::Common::Util.flatten(din, "_", flatten_arrays=false)).to eq(dout)
|
177
|
+
end
|
178
|
+
|
179
|
+
it "flattens a hash that contains an array that contains a hash that contains an array, no array flattening" do
|
180
|
+
din = {
|
181
|
+
'a' => 1,
|
182
|
+
'c' => [
|
183
|
+
100,
|
184
|
+
{'d' => 1000, 'e' => 2000, 'f' => [4, 5, 6]},
|
185
|
+
300
|
186
|
+
]
|
187
|
+
}
|
188
|
+
dout = {
|
189
|
+
'a' => 1,
|
190
|
+
'c' => [
|
191
|
+
100,
|
192
|
+
{'d' => 1000, 'e' => 2000, 'f' => [4, 5, 6]},
|
193
|
+
300
|
194
|
+
]
|
195
|
+
}
|
196
|
+
expect(Scalyr::Common::Util.flatten(din, "_", flatten_arrays=false)).to eq(dout)
|
197
|
+
end
|
198
|
+
|
135
199
|
it "accepts custom delimiters" do
|
136
200
|
din = {
|
137
201
|
'a' => 1,
|
@@ -148,6 +212,42 @@ describe Scalyr::Common::Util do
|
|
148
212
|
expect(Scalyr::Common::Util.flatten(din, ':')).to eq(dout)
|
149
213
|
end
|
150
214
|
|
215
|
+
it "accepts custom delimiters with greater depth" do
|
216
|
+
din = {
|
217
|
+
'a' => 1,
|
218
|
+
'b' => {
|
219
|
+
'c' => {
|
220
|
+
'e' => 100
|
221
|
+
},
|
222
|
+
'd' => 200,
|
223
|
+
}
|
224
|
+
}
|
225
|
+
dout = {
|
226
|
+
'a' => 1,
|
227
|
+
'b:c_e' => 100,
|
228
|
+
'b:d' => 200,
|
229
|
+
}
|
230
|
+
expect(Scalyr::Common::Util.flatten(din, ':')).to eq(dout)
|
231
|
+
end
|
232
|
+
|
233
|
+
it "accepts custom delimiters with greater depth and deep delimiters fix" do
|
234
|
+
din = {
|
235
|
+
'a' => 1,
|
236
|
+
'b' => {
|
237
|
+
'c' => {
|
238
|
+
'e' => 100
|
239
|
+
},
|
240
|
+
'd' => 200,
|
241
|
+
}
|
242
|
+
}
|
243
|
+
dout = {
|
244
|
+
'a' => 1,
|
245
|
+
'b:c:e' => 100,
|
246
|
+
'b:d' => 200,
|
247
|
+
}
|
248
|
+
expect(Scalyr::Common::Util.flatten(din, ':', true, true)).to eq(dout)
|
249
|
+
end
|
250
|
+
|
151
251
|
it "stringifies non-string keys" do
|
152
252
|
din = {
|
153
253
|
'a' => 1,
|
@@ -183,4 +283,23 @@ describe Scalyr::Common::Util do
|
|
183
283
|
it "raises exception if a non-dict is provided" do
|
184
284
|
expect {Scalyr::Common::Util.flatten(1)}.to raise_error(TypeError)
|
185
285
|
end
|
286
|
+
|
287
|
+
it "flattens a hash 5000 layers deep" do
|
288
|
+
din = {
|
289
|
+
'a' => {},
|
290
|
+
}
|
291
|
+
hash = din
|
292
|
+
for i in 0...4999
|
293
|
+
hash = hash["a"]
|
294
|
+
hash["a"] = {}
|
295
|
+
if i == 4998
|
296
|
+
hash["a"] = "b"
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
dout = {
|
301
|
+
'a' + "_a" * 4999 => "b",
|
302
|
+
}
|
303
|
+
expect(Scalyr::Common::Util.flatten(din, '_')).to eq(dout)
|
304
|
+
end
|
186
305
|
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.20.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-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -132,6 +132,7 @@ files:
|
|
132
132
|
- lib/scalyr/common/util.rb
|
133
133
|
- lib/scalyr/constants.rb
|
134
134
|
- logstash-output-scalyr.gemspec
|
135
|
+
- spec/benchmarks/bignum_fixing.rb
|
135
136
|
- spec/benchmarks/flattening_and_serialization.rb
|
136
137
|
- spec/benchmarks/metrics_overhead.rb
|
137
138
|
- spec/logstash/outputs/scalyr_integration_spec.rb
|
@@ -4062,6 +4063,7 @@ signing_key:
|
|
4062
4063
|
specification_version: 4
|
4063
4064
|
summary: Scalyr output plugin for Logstash
|
4064
4065
|
test_files:
|
4066
|
+
- spec/benchmarks/bignum_fixing.rb
|
4065
4067
|
- spec/benchmarks/flattening_and_serialization.rb
|
4066
4068
|
- spec/benchmarks/metrics_overhead.rb
|
4067
4069
|
- spec/logstash/outputs/scalyr_integration_spec.rb
|