logstash-output-scalyr 0.1.14.beta → 0.1.19.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 +21 -0
- data/README.md +1 -1
- data/lib/logstash/outputs/scalyr.rb +82 -9
- data/lib/scalyr/common/client.rb +1 -0
- data/lib/scalyr/common/util.rb +33 -5
- 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 +110 -2
- data/spec/scalyr/common/util_spec.rb +100 -0
- 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: '084d41a09e7ef86214868f26a97639169df8982d9488eb827d337ef53f02fc07'
|
4
|
+
data.tar.gz: 5bd3b17804c60044901d2f40c40419c119e680f7ac4a4ffc543701a885cb0c2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 837bd2d4d142f31bcd30494175731818de9f60f9f39ebc13cf0ae92b36e58ad4b6d288ba2f92109323f096d30a1774334651907b07e1d1310f6b42dd24698b2e
|
7
|
+
data.tar.gz: 221e47feab585a99dc69b475f446f9a9b4f9d2bd91ffd60753886438c348e0139618afa46f3889180aba68582ed9532376ee5449fb0cf91bb05cbb796c11a08c
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# Beta
|
2
2
|
|
3
|
+
## 0.1.19.beta
|
4
|
+
- Undo a change to nested value flattening functionality to keep existing formatting. This change can be re-enabled
|
5
|
+
by setting the `fix_deep_flattening_delimiters` configuration option to true.
|
6
|
+
|
7
|
+
## 0.1.18.beta
|
8
|
+
- Add metrics for successfully sent and failed logstash events, and retries.
|
9
|
+
- Make array flattening optional during nested value flattening with the `flatten_nested_arrays` configuration option.
|
10
|
+
|
11
|
+
## 0.1.17.beta
|
12
|
+
- Catch errors relating to Bignum conversions present in the ``json`` library and manually convert to string as
|
13
|
+
a workaround.
|
14
|
+
|
15
|
+
## 0.1.16.beta
|
16
|
+
- Fix race condition in ``register()`` method.
|
17
|
+
|
18
|
+
## 0.1.15.beta
|
19
|
+
- Only call ``send_status`` method at the end of ``multi_receive()`` if there is at least one
|
20
|
+
record in the batch when ``report_status_for_empty_batches`` config option is set to ``false``.
|
21
|
+
- Update ``register()`` method to use a separate short-lived client session for sending initial
|
22
|
+
client status.
|
23
|
+
|
3
24
|
## 0.1.14.beta
|
4
25
|
- Add configurable max retries for requests when running into errors.
|
5
26
|
- Add ability to send messages to the dead letter queue if we exhaust all retries and if it is configured.
|
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.19.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,9 @@ 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
|
72
74
|
|
73
75
|
# If true, the 'tags' field will be flattened into key-values where each key is a tag and each value is set to
|
74
76
|
# :flat_tag_value
|
@@ -109,6 +111,11 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
109
111
|
# minutes.
|
110
112
|
config :status_report_interval, :validate => :number, :default => 300
|
111
113
|
|
114
|
+
# True to also call send_status when multi_receive() is called with no events.
|
115
|
+
# In some situations (e.g. when logstash is configured with multiple scalyr
|
116
|
+
# plugins conditionally where most are idle) you may want to set this to false
|
117
|
+
config :report_status_for_empty_batches, :validate => :boolean, :default => true
|
118
|
+
|
112
119
|
# Set to true to also log status messages with various metrics to stdout in addition to sending
|
113
120
|
# this data to Scalyr
|
114
121
|
config :log_status_messages_to_stdout, :validate => :boolean, :default => false
|
@@ -235,7 +242,12 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
235
242
|
# Plugin level (either per batch or event level metrics). Other request
|
236
243
|
# level metrics are handled by the HTTP Client class.
|
237
244
|
@multi_receive_statistics = {
|
238
|
-
:total_multi_receive_secs => 0
|
245
|
+
:total_multi_receive_secs => 0,
|
246
|
+
:total_events_processed => 0,
|
247
|
+
:successful_events_processed => 0,
|
248
|
+
:failed_events_processed => 0,
|
249
|
+
:total_retry_count => 0,
|
250
|
+
:total_java_class_cast_errors => 0
|
239
251
|
}
|
240
252
|
@plugin_metrics = get_new_metrics
|
241
253
|
|
@@ -251,7 +263,25 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
251
263
|
@logger.info(sprintf("Started Scalyr output plugin (%s)." % [PLUGIN_VERSION]), :class => self.class.name)
|
252
264
|
|
253
265
|
# Finally, send a status line to Scalyr
|
254
|
-
|
266
|
+
# We use a special separate short lived client session for sending the initial client status.
|
267
|
+
# This is done to avoid the overhead in case single logstash instance has many scalyr output
|
268
|
+
# plugins configured with conditionals and majority of them are inactive (aka receive no data).
|
269
|
+
# This way we don't need to keep idle long running connection open.
|
270
|
+
initial_send_status_client_session = Scalyr::Common::Client::ClientSession.new(
|
271
|
+
@logger, @add_events_uri,
|
272
|
+
@compression_type, @compression_level, @ssl_verify_peer, @ssl_ca_bundle_path, @append_builtin_cert,
|
273
|
+
@record_stats_for_status, @flush_quantile_estimates_on_status_send,
|
274
|
+
@http_connect_timeout, @http_socket_timeout, @http_request_timeout, @http_pool_max, @http_pool_max_per_route
|
275
|
+
)
|
276
|
+
send_status(initial_send_status_client_session)
|
277
|
+
initial_send_status_client_session.close
|
278
|
+
|
279
|
+
# We also "prime" the main HTTP client here, one which is used for sending subsequent requests.
|
280
|
+
# Here priming just means setting up the client parameters without opening any connections.
|
281
|
+
# Since client writes certs to a temporary file there could be a race in case we don't do that
|
282
|
+
# here since multi_receive() is multi threaded. An alternative would be to put a look around
|
283
|
+
# client init method (aka client_config())
|
284
|
+
@client_session.client
|
255
285
|
|
256
286
|
end # def register
|
257
287
|
|
@@ -321,6 +351,9 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
321
351
|
sleep_interval = sleep_for(sleep_interval)
|
322
352
|
exc_sleep += sleep_interval
|
323
353
|
exc_retries += 1
|
354
|
+
@stats_lock.synchronize do
|
355
|
+
@multi_receive_statistics[:total_retry_count] += 1
|
356
|
+
end
|
324
357
|
message = "Error uploading to Scalyr (will backoff-retry)"
|
325
358
|
exc_data = {
|
326
359
|
:error_class => e.e_class,
|
@@ -370,11 +403,19 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
370
403
|
}
|
371
404
|
exc_sleep += sleep_interval
|
372
405
|
exc_retries += 1
|
406
|
+
@stats_lock.synchronize do
|
407
|
+
@multi_receive_statistics[:total_retry_count] += 1
|
408
|
+
end
|
373
409
|
retry if @running and exc_retries < @max_retries
|
374
410
|
log_retry_failure(multi_event_request, exc_data, exc_retries, exc_sleep)
|
375
411
|
next
|
376
412
|
end
|
377
413
|
|
414
|
+
@stats_lock.synchronize do
|
415
|
+
@multi_receive_statistics[:total_events_processed] += multi_event_request[:logstash_events].length
|
416
|
+
@multi_receive_statistics[:successful_events_processed] += multi_event_request[:logstash_events].length
|
417
|
+
end
|
418
|
+
|
378
419
|
if !exc_data.nil?
|
379
420
|
message = "Retry successful after error."
|
380
421
|
if exc_commonly_retried
|
@@ -394,7 +435,10 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
394
435
|
end
|
395
436
|
end
|
396
437
|
|
397
|
-
|
438
|
+
if @report_status_for_empty_batches or records_count > 0
|
439
|
+
send_status
|
440
|
+
end
|
441
|
+
|
398
442
|
return result
|
399
443
|
|
400
444
|
rescue => e
|
@@ -410,6 +454,10 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
410
454
|
|
411
455
|
|
412
456
|
def log_retry_failure(multi_event_request, exc_data, exc_retries, exc_sleep)
|
457
|
+
@stats_lock.synchronize do
|
458
|
+
@multi_receive_statistics[:total_events_processed] += multi_event_request[:logstash_events].length
|
459
|
+
@multi_receive_statistics[:failed_events_processed] += multi_event_request[:logstash_events].length
|
460
|
+
end
|
413
461
|
message = "Failed to send #{multi_event_request[:logstash_events].length} events after #{exc_retries} tries."
|
414
462
|
sample_events = Array.new
|
415
463
|
multi_event_request[:logstash_events][0,5].each {|l_event|
|
@@ -585,7 +633,7 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
585
633
|
# flatten record
|
586
634
|
if @flatten_nested_values
|
587
635
|
start_time = Time.now.to_f
|
588
|
-
record = Scalyr::Common::Util.flatten(record, delimiter=@flatten_nested_values_delimiter)
|
636
|
+
record = Scalyr::Common::Util.flatten(record, delimiter=@flatten_nested_values_delimiter, flatten_arrays=@flatten_nested_arrays, fix_deep_flattening_delimiters=@fix_deep_flattening_delimiters)
|
589
637
|
end_time = Time.now.to_f
|
590
638
|
flatten_nested_values_duration = end_time - start_time
|
591
639
|
end
|
@@ -635,6 +683,21 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
635
683
|
).force_encoding('UTF-8')
|
636
684
|
end
|
637
685
|
event_json = scalyr_event.to_json
|
686
|
+
rescue Java::JavaLang::ClassCastException => e
|
687
|
+
# Most likely we ran into the issue described here: https://github.com/flori/json/issues/336
|
688
|
+
# Because of the version of jruby logstash works with we don't have the option to just update this away,
|
689
|
+
# so if we run into it we convert bignums into strings so we can get the data in at least.
|
690
|
+
# This is fixed in JRuby 9.2.7, which includes json 2.2.0
|
691
|
+
@logger.warn("Error serializing events to JSON, likely due to the presence of Bignum values. Converting Bignum values to strings.")
|
692
|
+
@stats_lock.synchronize do
|
693
|
+
@multi_receive_statistics[:total_java_class_cast_errors] += 1
|
694
|
+
end
|
695
|
+
Scalyr::Common::Util.convert_bignums(scalyr_event)
|
696
|
+
event_json = scalyr_event.to_json
|
697
|
+
log_json = nil
|
698
|
+
if add_log
|
699
|
+
log_json = logs[log_identifier].to_json
|
700
|
+
end
|
638
701
|
end
|
639
702
|
|
640
703
|
# generate new request if json size of events in the array exceed maximum request buffer size
|
@@ -723,7 +786,16 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
723
786
|
|
724
787
|
# We time serialization to get some insight on how long it takes to serialize the request body
|
725
788
|
start_time = Time.now.to_f
|
726
|
-
|
789
|
+
begin
|
790
|
+
serialized_body = body.to_json
|
791
|
+
rescue Java::JavaLang::ClassCastException => e
|
792
|
+
@logger.warn("Error serializing events to JSON, likely due to the presence of Bignum values. Converting Bignum values to strings.")
|
793
|
+
@stats_lock.synchronize do
|
794
|
+
@multi_receive_statistics[:total_java_class_cast_errors] += 1
|
795
|
+
end
|
796
|
+
Scalyr::Common::Util.convert_bignums(body)
|
797
|
+
serialized_body = body.to_json
|
798
|
+
end
|
727
799
|
end_time = Time.now.to_f
|
728
800
|
serialization_duration = end_time - start_time
|
729
801
|
{
|
@@ -778,7 +850,8 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
778
850
|
# Finally, note that there could be multiple instances of this plugin (one per worker), in which case each worker
|
779
851
|
# thread sends their own status updates. This is intentional so that we know how much data each worker thread is
|
780
852
|
# uploading to Scalyr over time.
|
781
|
-
def send_status
|
853
|
+
def send_status(client_session = nil)
|
854
|
+
client_session = @client_session if client_session.nil?
|
782
855
|
|
783
856
|
status_event = {
|
784
857
|
:ts => (Time.now.to_f * (10**9)).round,
|
@@ -797,7 +870,7 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
797
870
|
# echee TODO: get instance stats from session and create a status log line
|
798
871
|
msg = 'plugin_status: '
|
799
872
|
cnt = 0
|
800
|
-
|
873
|
+
client_session.get_stats.each do |k, v|
|
801
874
|
val = v.instance_of?(Float) ? sprintf("%.4f", v) : v
|
802
875
|
val = val.nil? ? 0 : val
|
803
876
|
msg << ' ' if cnt > 0
|
@@ -817,7 +890,7 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
|
|
817
890
|
end
|
818
891
|
multi_event_request = create_multi_event_request([status_event], nil, nil, nil)
|
819
892
|
begin
|
820
|
-
|
893
|
+
client_session.post_add_events(multi_event_request[:body], true, 0)
|
821
894
|
rescue => e
|
822
895
|
if e.body
|
823
896
|
@logger.warn(
|
data/lib/scalyr/common/client.rb
CHANGED
data/lib/scalyr/common/util.rb
CHANGED
@@ -4,7 +4,9 @@ module Scalyr; module Common; module Util;
|
|
4
4
|
# Flattens a hash or array, returning a hash where keys are a delimiter-separated string concatenation of all
|
5
5
|
# nested keys. Returned keys are always strings. If a non-hash or array is provided, raises TypeError.
|
6
6
|
# Please see rspec util_spec.rb for expected behavior.
|
7
|
-
|
7
|
+
# Includes a known bug where defined delimiter will not be used for nesting levels past the first, this is kept
|
8
|
+
# because some queries and dashboards already rely on the broken functionality.
|
9
|
+
def self.flatten(obj, delimiter='_', flatten_arrays=true, fix_deep_flattening_delimiters=false)
|
8
10
|
|
9
11
|
# base case is input object is not enumerable, in which case simply return it
|
10
12
|
if !obj.respond_to?(:each)
|
@@ -19,8 +21,8 @@ def self.flatten(obj, delimiter='_')
|
|
19
21
|
|
20
22
|
# input object is a hash
|
21
23
|
obj.each do |key, value|
|
22
|
-
if value.respond_to?(:each)
|
23
|
-
flatten(value).each do |subkey, subvalue|
|
24
|
+
if (flatten_arrays and value.respond_to?(:each)) or value.respond_to?(:has_key?)
|
25
|
+
flatten(value, fix_deep_flattening_delimiters ? delimiter : '_', flatten_arrays).each do |subkey, subvalue|
|
24
26
|
result["#{key}#{delimiter}#{subkey}"] = subvalue
|
25
27
|
end
|
26
28
|
else
|
@@ -28,18 +30,23 @@ def self.flatten(obj, delimiter='_')
|
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
31
|
-
|
33
|
+
elsif flatten_arrays
|
32
34
|
|
33
35
|
# input object is an array or set
|
34
36
|
obj.each_with_index do |value, index|
|
35
37
|
if value.respond_to?(:each)
|
36
|
-
flatten(value).each do |subkey, subvalue|
|
38
|
+
flatten(value, fix_deep_flattening_delimiters ? delimiter : '_', flatten_arrays).each do |subkey, subvalue|
|
37
39
|
result["#{index}#{delimiter}#{subkey}"] = subvalue
|
38
40
|
end
|
39
41
|
else
|
40
42
|
result["#{index}"] = value
|
41
43
|
end
|
42
44
|
end
|
45
|
+
|
46
|
+
else
|
47
|
+
|
48
|
+
result = obj
|
49
|
+
|
43
50
|
end
|
44
51
|
|
45
52
|
return result
|
@@ -52,5 +59,26 @@ def self.truncate(content, max)
|
|
52
59
|
return content
|
53
60
|
end
|
54
61
|
|
62
|
+
def self.convert_bignums(obj)
|
63
|
+
if obj.respond_to?(:has_key?) and obj.respond_to?(:each)
|
64
|
+
# input object is a hash
|
65
|
+
obj.each do |key, value|
|
66
|
+
obj[key] = convert_bignums(value)
|
67
|
+
end
|
68
|
+
|
69
|
+
elsif obj.respond_to?(:each)
|
70
|
+
# input object is an array or set
|
71
|
+
obj.each_with_index do |value, index|
|
72
|
+
obj[index] = convert_bignums(value)
|
73
|
+
end
|
74
|
+
|
75
|
+
elsif obj.is_a? Bignum
|
76
|
+
return obj.to_s
|
77
|
+
|
78
|
+
else
|
79
|
+
return obj
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
55
83
|
end; end; end;
|
56
84
|
|
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.19.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.19.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))
|
@@ -69,7 +69,7 @@ describe LogStash::Outputs::Scalyr do
|
|
69
69
|
end
|
70
70
|
|
71
71
|
it "it doesnt include flatten metrics if flattening is disabled" do
|
72
|
-
|
72
|
+
plugin1 = LogStash::Outputs::Scalyr.new({
|
73
73
|
'api_write_token' => '1234',
|
74
74
|
'serverhost_field' => 'source_host',
|
75
75
|
'log_constants' => ['tags'],
|
@@ -122,7 +122,7 @@ describe LogStash::Outputs::Scalyr do
|
|
122
122
|
expect(status_event[:attrs]["message"]).to eq("plugin_status: total_requests_sent=20 total_requests_failed=10 total_request_bytes_sent=100 total_compressed_request_bytes_sent=50 total_response_bytes_received=100 total_request_latency_secs=100 total_serialization_duration_secs=100.5000 total_compression_duration_secs=10.2000 compression_type=deflate compression_level=9 total_multi_receive_secs=0 multi_receive_duration_p50=10 multi_receive_duration_p90=18 multi_receive_duration_p99=19 multi_receive_event_count_p50=0 multi_receive_event_count_p90=0 multi_receive_event_count_p99=0 event_attributes_count_p50=0 event_attributes_count_p90=0 event_attributes_count_p99=0 batches_per_multi_receive_p50=0 batches_per_multi_receive_p90=0 batches_per_multi_receive_p99=0 flatten_values_duration_secs_p50=0 flatten_values_duration_secs_p90=0 flatten_values_duration_secs_p99=0")
|
123
123
|
end
|
124
124
|
|
125
|
-
it "send_stats is called when events list is empty, but otherwise noop" do
|
125
|
+
it "send_stats is called when events list is empty, but otherwise is noop" do
|
126
126
|
quantile_estimator = Quantile::Estimator.new
|
127
127
|
plugin.instance_variable_set(:@plugin_metrics, {
|
128
128
|
:multi_receive_duration_secs => Quantile::Estimator.new,
|
@@ -137,6 +137,30 @@ describe LogStash::Outputs::Scalyr do
|
|
137
137
|
plugin.multi_receive([])
|
138
138
|
end
|
139
139
|
|
140
|
+
it "send_stats is not called when events list is empty and report_status_for_empty_batches is false" do
|
141
|
+
plugin2 = LogStash::Outputs::Scalyr.new({
|
142
|
+
'api_write_token' => '1234',
|
143
|
+
'serverhost_field' => 'source_host',
|
144
|
+
'log_constants' => ['tags'],
|
145
|
+
'flatten_nested_values' => false,
|
146
|
+
'report_status_for_empty_batches' => false,
|
147
|
+
})
|
148
|
+
|
149
|
+
mock_client_session = MockClientSession.new
|
150
|
+
quantile_estimator = Quantile::Estimator.new
|
151
|
+
plugin2.instance_variable_set(:@plugin_metrics, {
|
152
|
+
:multi_receive_duration_secs => Quantile::Estimator.new,
|
153
|
+
:multi_receive_event_count => Quantile::Estimator.new,
|
154
|
+
:event_attributes_count => Quantile::Estimator.new,
|
155
|
+
:flatten_values_duration_secs => Quantile::Estimator.new
|
156
|
+
})
|
157
|
+
plugin2.instance_variable_set(:@client_session, mock_client_session)
|
158
|
+
expect(plugin2).not_to receive(:send_status)
|
159
|
+
expect(quantile_estimator).not_to receive(:observe)
|
160
|
+
expect(mock_client_session).not_to receive(:post_add_events)
|
161
|
+
plugin2.multi_receive([])
|
162
|
+
end
|
163
|
+
|
140
164
|
# Kind of a weak test but I don't see a decent way to write a stronger one without a live client session
|
141
165
|
it "send_status only sends posts with is_status = true" do
|
142
166
|
# 1. Initial send
|
@@ -264,6 +288,72 @@ describe LogStash::Outputs::Scalyr do
|
|
264
288
|
end
|
265
289
|
end
|
266
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
|
+
|
267
357
|
context "when configured to flatten values and tags" do
|
268
358
|
config = {
|
269
359
|
'api_write_token' => '1234',
|
@@ -318,5 +408,23 @@ describe LogStash::Outputs::Scalyr do
|
|
318
408
|
})
|
319
409
|
end
|
320
410
|
end
|
411
|
+
|
412
|
+
context "when receiving an event with Bignums" do
|
413
|
+
config = {
|
414
|
+
'api_write_token' => '1234',
|
415
|
+
}
|
416
|
+
plugin = LogStash::Outputs::Scalyr.new(config)
|
417
|
+
it "doesn't throw an error" do
|
418
|
+
allow(plugin).to receive(:send_status).and_return(nil)
|
419
|
+
plugin.register
|
420
|
+
e = LogStash::Event.new
|
421
|
+
e.set('bignumber', 2000023030042002050202030320240)
|
422
|
+
allow(plugin.instance_variable_get(:@logger)).to receive(:error)
|
423
|
+
result = plugin.build_multi_event_request_array([e])
|
424
|
+
body = JSON.parse(result[0][:body])
|
425
|
+
expect(body['events'].size).to eq(1)
|
426
|
+
expect(plugin.instance_variable_get(:@logger)).to_not receive(:error)
|
427
|
+
end
|
428
|
+
end
|
321
429
|
end
|
322
430
|
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,
|
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.19.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-07-08 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
|