logstash-output-scalyr 0.1.22.beta → 0.1.26.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/Gemfile +1 -0
  4. data/README.md +1 -2
  5. data/lib/logstash/outputs/scalyr.rb +116 -70
  6. data/lib/scalyr/constants.rb +1 -1
  7. data/logstash-output-scalyr.gemspec +3 -2
  8. data/spec/benchmarks/flattening_and_serialization.rb +1 -26
  9. data/spec/benchmarks/json_serialization.rb +85 -0
  10. data/spec/benchmarks/util.rb +24 -0
  11. data/spec/logstash/outputs/scalyr_spec.rb +172 -0
  12. data/vendor/bundle/jruby/2.5.0/cache/jrjackson-0.4.14-java.gem +0 -0
  13. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/Gemfile +0 -0
  14. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/Mavenfile +1 -7
  15. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/README.md +0 -0
  16. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/Rakefile +8 -2
  17. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/alt_bench.rb +0 -0
  18. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/changelog.md +25 -2
  19. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/jrjackson.gemspec +2 -1
  20. data/vendor/bundle/jruby/2.5.0/gems/jrjackson-0.4.14-java/lib/com/fasterxml/jackson/core/jackson-annotations/2.9.10/jackson-annotations-2.9.10.jar +0 -0
  21. data/vendor/bundle/jruby/2.5.0/gems/jrjackson-0.4.14-java/lib/com/fasterxml/jackson/core/jackson-core/2.9.10/jackson-core-2.9.10.jar +0 -0
  22. data/vendor/bundle/jruby/2.5.0/gems/jrjackson-0.4.14-java/lib/com/fasterxml/jackson/core/jackson-databind/2.9.10.8/jackson-databind-2.9.10.8.jar +0 -0
  23. data/vendor/bundle/jruby/2.5.0/gems/jrjackson-0.4.14-java/lib/com/fasterxml/jackson/module/jackson-module-afterburner/2.9.10/jackson-module-afterburner-2.9.10.jar +0 -0
  24. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/lib/jrjackson/build_info.rb +9 -5
  25. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java/lib/jrjackson/jars/jrjackson-1.2.26.jar → jrjackson-0.4.14-java/lib/jrjackson/jars/jrjackson-1.2.32.jar} +0 -0
  26. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/lib/jrjackson/jrjackson.rb +0 -0
  27. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/lib/jrjackson.rb +0 -0
  28. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/lib/jrjackson_jars.rb +8 -8
  29. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/lib/require_relative_patch.rb +0 -0
  30. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/pom.xml +8 -25
  31. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/profiling/profiled.rb +0 -0
  32. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/run_all_individual_bench.sh +0 -0
  33. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/run_jruby_individual_bench.sh +0 -0
  34. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/run_mri_individual_bench.sh +0 -0
  35. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/IParseHandler.java +0 -0
  36. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JavaBigDecimalValueConverter.java +0 -0
  37. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JavaBigIntValueConverter.java +0 -0
  38. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JavaConverter.java +0 -0
  39. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JavaFloatValueConverter.java +0 -0
  40. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JavaHandler.java +0 -0
  41. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JavaLongValueConverter.java +0 -0
  42. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JjParse.java +0 -0
  43. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JrJacksonBase.java +0 -0
  44. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JrJacksonJava.java +0 -0
  45. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JrJacksonRaw.java +0 -0
  46. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JrJacksonRuby.java +0 -0
  47. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JrJacksonSaj.java +0 -0
  48. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JrJacksonSch.java +0 -0
  49. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JrJacksonService.java +0 -0
  50. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/JrParse.java +0 -0
  51. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/ParseError.java +0 -0
  52. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyAnySerializer.java +4 -4
  53. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyBigDecimalValueConverter.java +0 -0
  54. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyBigIntValueConverter.java +0 -0
  55. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyConverter.java +0 -0
  56. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyDateFormat.java +0 -0
  57. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyFloatValueConverter.java +0 -0
  58. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyHandler.java +0 -0
  59. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyIntValueConverter.java +0 -0
  60. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyJacksonModule.java +18 -6
  61. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyKeyConverter.java +0 -0
  62. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyNameConverter.java +0 -0
  63. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyObjectDeserializer.java +0 -0
  64. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyStringConverter.java +0 -0
  65. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyStringKeyConverter.java +0 -0
  66. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyStringNameConverter.java +0 -0
  67. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubySymbolKeyConverter.java +0 -0
  68. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubySymbolNameConverter.java +0 -0
  69. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/RubyUtils.java +0 -0
  70. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/SajParse.java +0 -0
  71. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/SchParse.java +0 -0
  72. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/main/java/com/jrjackson/StreamParse.java +0 -0
  73. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/src/test/java/com/jrjackson/RubyAnySerializerTest.java +0 -0
  74. data/vendor/bundle/jruby/2.5.0/gems/{jrjackson-0.4.8-java → jrjackson-0.4.14-java}/test/jrjackson_test.rb +15 -0
  75. data/vendor/bundle/jruby/2.5.0/specifications/{jrjackson-0.4.8-java.gemspec → jrjackson-0.4.14-java.gemspec} +7 -7
  76. metadata +98 -66
  77. data/vendor/bundle/jruby/2.5.0/cache/jrjackson-0.4.8-java.gem +0 -0
  78. data/vendor/bundle/jruby/2.5.0/gems/jrjackson-0.4.8-java/lib/com/fasterxml/jackson/core/jackson-annotations/2.9.7/jackson-annotations-2.9.7.jar +0 -0
  79. data/vendor/bundle/jruby/2.5.0/gems/jrjackson-0.4.8-java/lib/com/fasterxml/jackson/core/jackson-core/2.9.7/jackson-core-2.9.7.jar +0 -0
  80. data/vendor/bundle/jruby/2.5.0/gems/jrjackson-0.4.8-java/lib/com/fasterxml/jackson/core/jackson-databind/2.9.7/jackson-databind-2.9.7.jar +0 -0
  81. data/vendor/bundle/jruby/2.5.0/gems/jrjackson-0.4.8-java/lib/com/fasterxml/jackson/module/jackson-module-afterburner/2.9.7/jackson-module-afterburner-2.9.7.jar +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ea703e55bc28d2bb00445c2a3e2d822bfe978bbe889292caafa6265dff1de2b
4
- data.tar.gz: 3dc35cf36a356e5651d69e1a7d4623f296e5818a4b1c0fc8965b1a55b94a20ab
3
+ metadata.gz: 5593e93dedad0ddb545f8911e7f1cd8cabde4043a6a76b8c5b78d99c0c4d12a5
4
+ data.tar.gz: 6c821768c3d780529d00bc1f58c4ec8841ecba2758a46c71ecf17201fb8ba3d1
5
5
  SHA512:
6
- metadata.gz: 9306c619ce51c8a9cf83cb545229d4c73ac496a133e07e7e55b434301e1efc2f668187c3aa1584997ab1252ce3c7510ac9c4d719ba363933160bf2f874d0a137
7
- data.tar.gz: 621b8f9b4095f16e51b88b8aec2844d795de75cba980ba27e0510a04f57ee9e4b34eb42513455d7509acaa8af8e085febb92cd248401e642129e09d70987a476
6
+ metadata.gz: 57901dc94f49aa844130f4b2a7ac084c95d473c1a948902b699cbaba8d63049d1e960b69b49b20cf8ffa8b2c1f5f68a53ad9687b72867799f113a384f490e01d
7
+ data.tar.gz: 329f985e8121d49bab836381861ca878c229f2b316e160792263461900a8fc4cf7f46bdc2aba889174a0f35b0c67f567b0e5a09d429f888948b788707ab3c0b7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Beta
2
2
 
3
+ ## 0.1.26.beta
4
+ - Add support for new ``json_library`` config option. Valid values are ``stdlib`` (default) are ``jrjackson``. The later may offer 2-4x faster JSON serialization.
5
+
6
+
7
+ ## 0.1.23.beta
8
+ - Add testing support for disabling estimation of serialized event size for each event in the batch.
9
+
3
10
  ## 0.1.22.beta
4
11
  - Add new plugin metric for tracking the duration of ``build_multi_event_request_array`` method.
5
12
  - Update internal dependencies (``manticore``) to latest stable version.
data/Gemfile CHANGED
@@ -18,3 +18,4 @@ gem 'pry'
18
18
  gem 'pry-nav'
19
19
  gem 'quantile', '~> 0.2.1'
20
20
  gem 'manticore', '~> 0.7.1', platform: :jruby
21
+ gem 'jrjackson', '~> 0.4.14', platform: :jruby
data/README.md CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  [![CircleCI](https://circleci.com/gh/scalyr/logstash-output-scalyr.svg?style=svg)](https://circleci.com/gh/scalyr/logstash-output-scalyr)
3
2
 
4
3
  # [Scalyr output plugin for Logstash (Beta release)]
@@ -10,7 +9,7 @@ You can view documentation for this plugin [on the Scalyr website](https://app.s
10
9
  # Quick start
11
10
 
12
11
  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.22.beta.gem` or follow the latest official instructions on working with plugins from Logstash.
12
+ 2. Install the gem into a Logstash installation, run `/usr/share/logstash/bin/logstash-plugin install logstash-output-scalyr-0.1.25.beta.gem` or follow the latest official instructions on working with plugins from Logstash.
14
13
  3. Configure the output plugin (e.g. add it to a pipeline .conf)
15
14
  4. Restart Logstash
16
15
 
@@ -13,6 +13,7 @@ require 'rbzip2'
13
13
  require 'zlib'
14
14
  require 'stringio'
15
15
  require 'quantile'
16
+ require 'jrjackson'
16
17
 
17
18
  require 'scalyr/common/client'
18
19
  require "scalyr/common/util"
@@ -140,11 +141,21 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
140
141
  # Whether or not to create fresh quantile estimators after a status send. Depending on what you want to gather from
141
142
  # these stas this might be wanted or not.
142
143
  config :flush_quantile_estimates_on_status_send, :validate => :boolean, :default => false
143
-
144
+
144
145
  # Causes this plugin to act as if it successfully uploaded the logs, while actually returning as quickly as possible
145
146
  # after no work being done.
146
147
  config :noop_mode, :validate => :boolean, :default => false
147
148
 
149
+ # Set to true to disable estimiating the size of each serialized event to make sure we don't go over the max request
150
+ # size (5.5) and split batch into multiple Scalyr requests, if needed. Since this estimation is not "free", especially
151
+ # for large batches, it may make sense to disable this option when logstash batch size is configured in a way that
152
+ # Scalyr single request limit won't be reached.
153
+ config :estimate_each_event_size, :validate => :boolean, :default => true
154
+
155
+ # Library to use for JSON serialization. Valid values are "stdlib" and "jrjackson". The later may offer 2-4 performance
156
+ # improvements on serialization.
157
+ config :json_library, :validate => :string, :default => "stdlib"
158
+
148
159
  # Manticore related options
149
160
  config :http_connect_timeout, :validate => :number, :default => 10
150
161
  config :http_socket_timeout, :validate => :number, :default => 10
@@ -184,6 +195,24 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
184
195
  @logger.warn "Maximum request buffer > 6 MB. This may result in requests being rejected by Scalyr."
185
196
  end
186
197
 
198
+ if not @estimate_each_event_size
199
+ @logger.warn("estimate_each_event_size config option is false, this means very large batches may be rejected or partially processed by the server")
200
+ end
201
+
202
+ if @json_library != "stdlib" and @json_library != "jrjackson"
203
+ raise LogStash::ConfigurationError, "json_library config option needs to be either stdlib or jrjackson"
204
+ end
205
+
206
+ if @json_library == "stdlib"
207
+ define_singleton_method "json_encode" do |data|
208
+ data.to_json
209
+ end
210
+ elsif @json_library == "jrjackson"
211
+ define_singleton_method "json_encode" do |data|
212
+ JrJackson::Json.dump(data)
213
+ end
214
+ end
215
+
187
216
  @dlq_writer = dlq_enabled? ? execution_context.dlq_writer : nil
188
217
 
189
218
  @message_encoding = nil
@@ -316,16 +345,12 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
316
345
  begin
317
346
  records_count = events.to_a.length
318
347
 
319
- # We also time the duration of the build_multi_event_request_array method
348
+ # We also time the duration of the build_multi_event_request_array method. To avoid locking twice,
349
+ # we store the duration value here and record metric at the end.
320
350
  start_time = Time.now.to_f
321
351
 
322
352
  multi_event_request_array = build_multi_event_request_array(events)
323
-
324
- if records_count > 0
325
- @stats_lock.synchronize do
326
- @plugin_metrics[:build_multi_duration_secs].observe(Time.now.to_f - start_time)
327
- end
328
- end
353
+ build_multi_duration_secs = Time.now.to_f - start_time
329
354
 
330
355
  # Loop over all array of multi-event requests, sending each multi-event to Scalyr
331
356
  sleep_interval = @retry_initial_interval
@@ -439,6 +464,7 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
439
464
  if records_count > 0
440
465
  @stats_lock.synchronize do
441
466
  @multi_receive_statistics[:total_multi_receive_secs] += (Time.now.to_f - start_time)
467
+ @plugin_metrics[:build_multi_duration_secs].observe(build_multi_duration_secs)
442
468
  @plugin_metrics[:multi_receive_duration_secs].observe(Time.now.to_f - start_time)
443
469
  @plugin_metrics[:multi_receive_event_count].observe(records_count)
444
470
  @plugin_metrics[:batches_per_multi_receive].observe(total_batches)
@@ -675,67 +701,75 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
675
701
  scalyr_event[:log] = logs_ids[log_identifier]
676
702
  end
677
703
 
678
- # get json string of event to keep track of how many bytes we are sending
679
- begin
680
- event_json = scalyr_event.to_json
681
- log_json = nil
682
- if add_log
683
- log_json = logs[log_identifier].to_json
684
- end
685
- rescue JSON::GeneratorError, Encoding::UndefinedConversionError => e
686
- @logger.warn "#{e.class}: #{e.message}"
687
-
688
- # Send the faulty event to a label @ERROR block and allow to handle it there (output to exceptions file for ex)
689
- # TODO
690
- # atime = Fluent::EventTime.new( sec, nsec )
691
- # router.emit_error_event(serverHost, time, record, e)
692
-
693
- scalyr_event[:attrs].each do |key, value|
694
- @logger.debug "\t#{key} (#{value.encoding.name}): '#{value}'"
695
- scalyr_event[:attrs][key] = value.encode(
696
- "UTF-8", :invalid => :replace, :undef => :replace, :replace => "<?>"
697
- ).force_encoding('UTF-8')
698
- end
699
- event_json = scalyr_event.to_json
700
- rescue Java::JavaLang::ClassCastException => e
701
- # Most likely we ran into the issue described here: https://github.com/flori/json/issues/336
702
- # Because of the version of jruby logstash works with we don't have the option to just update this away,
703
- # so if we run into it we convert bignums into strings so we can get the data in at least.
704
- # This is fixed in JRuby 9.2.7, which includes json 2.2.0
705
- @logger.warn("Error serializing events to JSON, likely due to the presence of Bignum values. Converting Bignum values to strings.")
706
- @stats_lock.synchronize do
707
- @multi_receive_statistics[:total_java_class_cast_errors] += 1
704
+ if @estimate_each_event_size
705
+ # get json string of event to keep track of how many bytes we are sending
706
+ begin
707
+ event_json = self.json_encode(scalyr_event)
708
+ log_json = nil
709
+ if add_log
710
+ log_json = self.json_encode(logs[log_identifier])
711
+ end
712
+ rescue JSON::GeneratorError, Encoding::UndefinedConversionError => e
713
+ @logger.warn "#{e.class}: #{e.message}"
714
+
715
+ # Send the faulty event to a label @ERROR block and allow to handle it there (output to exceptions file for ex)
716
+ # TODO
717
+ # atime = Fluent::EventTime.new( sec, nsec )
718
+ # router.emit_error_event(serverHost, time, record, e)
719
+
720
+ scalyr_event[:attrs].each do |key, value|
721
+ @logger.debug "\t#{key} (#{value.encoding.name}): '#{value}'"
722
+ scalyr_event[:attrs][key] = value.encode(
723
+ "UTF-8", :invalid => :replace, :undef => :replace, :replace => "<?>"
724
+ ).force_encoding('UTF-8')
725
+ end
726
+ event_json = self.json_encode(scalyr_event)
727
+ rescue Java::JavaLang::ClassCastException => e
728
+ # Most likely we ran into the issue described here: https://github.com/flori/json/issues/336
729
+ # Because of the version of jruby logstash works with we don't have the option to just update this away,
730
+ # so if we run into it we convert bignums into strings so we can get the data in at least.
731
+ # This is fixed in JRuby 9.2.7, which includes json 2.2.0
732
+ @logger.warn("Error serializing events to JSON, likely due to the presence of Bignum values. Converting Bignum values to strings.")
733
+ @stats_lock.synchronize do
734
+ @multi_receive_statistics[:total_java_class_cast_errors] += 1
735
+ end
736
+ Scalyr::Common::Util.convert_bignums(scalyr_event)
737
+ event_json = self.json_encode(scalyr_event)
738
+ log_json = nil
739
+ if add_log
740
+ log_json = self.json_encode(logs[log_identifier])
741
+ end
708
742
  end
709
- Scalyr::Common::Util.convert_bignums(scalyr_event)
710
- event_json = scalyr_event.to_json
711
- log_json = nil
712
- if add_log
713
- log_json = logs[log_identifier].to_json
743
+
744
+ # generate new request if json size of events in the array exceed maximum request buffer size
745
+ append_event = true
746
+ add_bytes = event_json.bytesize
747
+ if log_json
748
+ add_bytes = add_bytes + log_json.bytesize
714
749
  end
715
- end
716
750
 
717
- # generate new request if json size of events in the array exceed maximum request buffer size
718
- append_event = true
719
- add_bytes = event_json.bytesize
720
- if log_json
721
- add_bytes = add_bytes + log_json.bytesize
722
- end
723
- if total_bytes + add_bytes > @max_request_buffer
724
- # make sure we always have at least one event
725
- if scalyr_events.size == 0
726
- scalyr_events << scalyr_event
727
- l_events << l_event
728
- append_event = false
751
+ if total_bytes + add_bytes > @max_request_buffer
752
+ # make sure we always have at least one event
753
+ if scalyr_events.size == 0
754
+ scalyr_events << scalyr_event
755
+ l_events << l_event
756
+ append_event = false
757
+ end
758
+
759
+ multi_event_request = self.create_multi_event_request(scalyr_events, l_events, current_threads, logs)
760
+ multi_event_request_array << multi_event_request
761
+
762
+ total_bytes = 0
763
+ current_threads = Hash.new
764
+ logs = Hash.new
765
+ logs_ids = Hash.new
766
+ scalyr_events = Array.new
767
+ l_events = Array.new
729
768
  end
730
- multi_event_request = self.create_multi_event_request(scalyr_events, l_events, current_threads, logs)
731
- multi_event_request_array << multi_event_request
732
-
733
- total_bytes = 0
734
- current_threads = Hash.new
735
- logs = Hash.new
736
- logs_ids = Hash.new
737
- scalyr_events = Array.new
738
- l_events = Array.new
769
+ else
770
+ # If size estimation is disabled we simply append the event and handle splitting later on (if needed)
771
+ append_event = true
772
+ add_bytes = 0
739
773
  end
740
774
 
741
775
  # if we haven't consumed the current event already
@@ -748,9 +782,12 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
748
782
 
749
783
  }
750
784
 
751
- # create a final request with any left over events
752
- multi_event_request = self.create_multi_event_request(scalyr_events, l_events, current_threads, logs)
753
- multi_event_request_array << multi_event_request
785
+ # create a final request with any left over events (and make sure there is at least one event)
786
+ if scalyr_events.size >= 1
787
+ multi_event_request = self.create_multi_event_request(scalyr_events, l_events, current_threads, logs)
788
+ multi_event_request_array << multi_event_request
789
+ end
790
+
754
791
  multi_event_request_array
755
792
  end
756
793
 
@@ -801,17 +838,26 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
801
838
  # We time serialization to get some insight on how long it takes to serialize the request body
802
839
  start_time = Time.now.to_f
803
840
  begin
804
- serialized_body = body.to_json
841
+ serialized_body = self.json_encode(body)
805
842
  rescue Java::JavaLang::ClassCastException => e
806
843
  @logger.warn("Error serializing events to JSON, likely due to the presence of Bignum values. Converting Bignum values to strings.")
807
844
  @stats_lock.synchronize do
808
845
  @multi_receive_statistics[:total_java_class_cast_errors] += 1
809
846
  end
810
847
  Scalyr::Common::Util.convert_bignums(body)
811
- serialized_body = body.to_json
848
+ serialized_body = self.json_encode(body)
812
849
  end
813
850
  end_time = Time.now.to_f
814
851
  serialization_duration = end_time - start_time
852
+
853
+ serialized_request_size = serialized_body.bytesize
854
+
855
+ # We give it "buffer" since the splitting code allows for some slack and doesn't take into account top-level non-event attributes
856
+ if not @estimate_each_event_size and serialized_request_size >= @max_request_buffer + 5000
857
+ # TODO: If we end up here is estimate config opsion is false, split the request here into multiple ones
858
+ @logger.warn("Serialized request size (#{serialized_request_size}) is larger than max_request_buffer (#{max_request_buffer})!")
859
+ end
860
+
815
861
  {
816
862
  :body => serialized_body, :record_count => scalyr_events.size, :serialization_duration => serialization_duration,
817
863
  :logstash_events => logstash_events
@@ -1,2 +1,2 @@
1
1
  # encoding: utf-8
2
- PLUGIN_VERSION = "v0.1.22.beta"
2
+ PLUGIN_VERSION = "v0.1.26.beta"
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-output-scalyr'
3
- s.version = '0.1.22.beta'
3
+ s.version = '0.1.26.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)"
@@ -18,11 +18,12 @@ Gem::Specification.new do |s|
18
18
  s.metadata = { "logstash_plugin" => "true", "logstash_group" => "output" }
19
19
 
20
20
  # Gem dependencies
21
- #
22
21
  s.add_runtime_dependency 'net-http-persistent'
23
22
  s.add_runtime_dependency 'logstash-core-plugin-api', '>= 1.60', '<= 2.99'
24
23
  s.add_runtime_dependency 'logstash-codec-plain'
25
24
  s.add_runtime_dependency 'quantile'
25
+ s.add_runtime_dependency 'jrjackson'
26
+ s.add_runtime_dependency 'manticore'
26
27
  s.add_runtime_dependency 'ffi', '>= 1.9.18'
27
28
  s.add_runtime_dependency 'rbzip2', '0.3.0'
28
29
  s.add_development_dependency 'logstash-devutils'
@@ -2,6 +2,7 @@ require 'benchmark'
2
2
  require 'json'
3
3
 
4
4
  require_relative '../../lib/scalyr/common/util'
5
+ require_relative './util'
5
6
 
6
7
  # NOTE: When using jRuby using multiple iterations with the same dataset doesn't make
7
8
  # sense since it will just use JITed version of the code which will be very fast. If we
@@ -9,31 +10,6 @@ require_relative '../../lib/scalyr/common/util'
9
10
  # input data for each iteration.
10
11
  ITERATIONS = 500
11
12
 
12
- def rand_str(len)
13
- return (0...len).map { (65 + rand(26)).chr }.join
14
- end
15
-
16
- def generate_hash(widths)
17
- result = {}
18
- if widths.empty?
19
- return rand_str(20)
20
- else
21
- widths[0].times do
22
- result[rand_str(9)] = generate_hash(widths[1..widths.length])
23
- end
24
- return result
25
- end
26
- end
27
-
28
- def generate_data_array_for_spec(spec)
29
- data = []
30
- ITERATIONS.times do
31
- data << generate_hash(spec)
32
- end
33
-
34
- data
35
- end
36
-
37
13
  def run_benchmark_and_print_results(data, run_benchmark_func)
38
14
  puts ""
39
15
  puts "Using %s total keys in a hash" % [Scalyr::Common::Util.flatten(data[0]).count]
@@ -70,7 +46,6 @@ DATASETS = {
70
46
  :keys_2700 => generate_data_array_for_spec([14, 8, 6, 4])
71
47
  }
72
48
 
73
-
74
49
  puts "Using %s iterations" % [ITERATIONS]
75
50
  puts ""
76
51
 
@@ -0,0 +1,85 @@
1
+ require 'benchmark'
2
+ require 'json'
3
+ require 'jrjackson'
4
+
5
+ require_relative '../../lib/scalyr/common/util'
6
+ require_relative './util'
7
+
8
+ ITERATIONS = 500
9
+
10
+ def json_serialize_data_native(data)
11
+ data.to_json
12
+ end
13
+
14
+ def json_serialize_data_jrjackson(data)
15
+ JrJackson::Json.dump(data)
16
+ end
17
+
18
+ DATASETS = {
19
+ :keys_50 => generate_data_array_for_spec([3, 3, 3, 2]),
20
+ :keys_200 => generate_data_array_for_spec([4, 4, 3, 4]),
21
+ :keys_200_flat => generate_data_array_for_spec([200]),
22
+ :keys_512 => generate_data_array_for_spec([8, 4, 4, 4]),
23
+ :keys_960 => generate_data_array_for_spec([12, 5, 4, 4]),
24
+ :keys_2700 => generate_data_array_for_spec([14, 8, 6, 4])
25
+ }
26
+
27
+ def run_benchmark_and_print_results(data, run_benchmark_func)
28
+ puts ""
29
+ puts "Using %s total keys in a hash" % [Scalyr::Common::Util.flatten(data[0]).count]
30
+ puts ""
31
+
32
+ result = []
33
+ ITERATIONS.times do |i|
34
+ result << Benchmark.measure { run_benchmark_func.(data[0]) }
35
+ end
36
+
37
+ sum = result.inject(nil) { |sum, t| sum.nil? ? sum = t : sum += t }
38
+ avg = sum / result.size
39
+
40
+ Benchmark.bm(7, "sum:", "avg:") do |b|
41
+ [sum, avg]
42
+ end
43
+ puts ""
44
+ end
45
+
46
+ puts "Using %s iterations" % [ITERATIONS]
47
+ puts ""
48
+
49
+ puts "native"
50
+ puts "==============================="
51
+
52
+ # Around ~50 keys in a hash
53
+ data = DATASETS[:keys_50]
54
+ run_benchmark_and_print_results(data, method(:json_serialize_data_native))
55
+
56
+ # Around ~200 keys in a hash
57
+ data = DATASETS[:keys_200]
58
+ run_benchmark_and_print_results(data, method(:json_serialize_data_native))
59
+
60
+ # Around ~200 keys in a hash (single level)
61
+ data = DATASETS[:keys_200_flat]
62
+ run_benchmark_and_print_results(data, method(:json_serialize_data_native))
63
+
64
+ # Around ~2700 keys in a hash
65
+ data = DATASETS[:keys_2700]
66
+ run_benchmark_and_print_results(data, method(:json_serialize_data_native))
67
+
68
+ puts "jrjackson"
69
+ puts "==============================="
70
+
71
+ # Around ~50 keys in a hash
72
+ data = DATASETS[:keys_50]
73
+ run_benchmark_and_print_results(data, method(:json_serialize_data_jrjackson))
74
+
75
+ # Around ~200 keys in a hash
76
+ data = DATASETS[:keys_200]
77
+ run_benchmark_and_print_results(data, method(:json_serialize_data_jrjackson))
78
+
79
+ # Around ~200 keys in a hash (single level)
80
+ data = DATASETS[:keys_200_flat]
81
+ run_benchmark_and_print_results(data, method(:json_serialize_data_jrjackson))
82
+
83
+ # Around ~2700 keys in a hash
84
+ data = DATASETS[:keys_2700]
85
+ run_benchmark_and_print_results(data, method(:json_serialize_data_jrjackson))