logstash-output-scalyr 0.1.9 → 0.1.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 542910023101687e2d9bce1d355f3353a9f9eab2dbc8190ae47c948b17b1db58
4
- data.tar.gz: 478acd0ae33f8b102da138ecfebe3a1ecab68f7d4b2d3779695812184e7a5618
3
+ metadata.gz: 290310d36108a3311f55a47efbb3a73e01f19ccdf025a15590f86493459963da
4
+ data.tar.gz: a5bf9f872c496fa65670cab36e0955f1417541db5f151e11f0d914abe5fc9920
5
5
  SHA512:
6
- metadata.gz: 60777fcaababf8cc08559a582380fba24fa5a506a4751a0bb63e2a46957723dd4cbaf8c232ccb0ccfa91694c9723c24c054e708e27f59476c4734b58e1cd8269
7
- data.tar.gz: f0f19ab3392f2d7a8f463b6c74b6e204024701502a86640bbfa3a207554ff69a25f53e36075aa14f65561f3187e2c24b0ae1db2f6ec7ac0630e3c83232a12ce4
6
+ metadata.gz: 35d60d700b6bb6ccc43aea5151d07c306b29c5524ac8d80145ae792263ffe5e7264d22a59de3a21de9aa549c5963b4cd3c9b230b845f67c6ff9618faca863cad
7
+ data.tar.gz: 41cf50b968a9a11efff957cda768cf9d05e1ccf3c36e4f4d8f12c3d1aca9fb9da049647432bf047d51c2b6fe33f7b711484632d108dcd390ca852bc45aa7a00d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Beta
2
2
 
3
+ ## 0.1.10.beta
4
+
5
+ - Switch to shared concurrency to allow the use of multiple worker threads for increased
6
+ throughput.
7
+ - Switch HTTP client library to `manticore` to work better with new shared concurrency.
8
+
3
9
  ## 0.1.9
4
10
 
5
11
  - Add support for logging status messages with metrics to stdout in addition to sending this
data/Gemfile CHANGED
@@ -13,3 +13,4 @@ end
13
13
  gem 'pry'
14
14
  gem 'pry-nav'
15
15
  gem 'quantile'
16
+ gem 'manticore', platform: :jruby
@@ -8,9 +8,7 @@ require "thread" # for safe queueing
8
8
  require "uri" # for escaping user input
9
9
  require 'json' # for converting event object to JSON for upload
10
10
 
11
- require 'net/http'
12
- require 'net/http/persistent'
13
- require 'net/https'
11
+ require 'manticore'
14
12
  require 'rbzip2'
15
13
  require 'zlib'
16
14
  require 'stringio'
@@ -27,9 +25,7 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
27
25
 
28
26
  config_name "scalyr"
29
27
 
30
- # For correctness reasons we need to limit this plugin to a single worker, a single worker will be single concurrency
31
- # anyway but we should be explicit.
32
- concurrency :single
28
+ concurrency :shared
33
29
 
34
30
  # The Scalyr API write token, these are available at https://www.scalyr.com/keys. This is the only compulsory configuration field required for proper upload
35
31
  config :api_write_token, :validate => :string, :required => true
@@ -37,12 +33,6 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
37
33
  # If you have an EU-based Scalyr account, please use https://eu.scalyr.com/
38
34
  config :scalyr_server, :validate => :string, :default => "https://agent.scalyr.com/"
39
35
 
40
- # Path to SSL bundle file.
41
- config :ssl_ca_bundle_path, :validate => :string, :default => "/etc/ssl/certs/ca-bundle.crt"
42
-
43
- # If we should append our built-in Scalyr cert to the one we find at `ssl_ca_bundle_path`.
44
- config :append_builtin_cert, :validate => :boolean, :default => true
45
-
46
36
  # server_attributes is a dictionary of key value pairs that represents/identifies the logstash aggregator server
47
37
  # (where this plugin is running). Keys are arbitrary except for the 'serverHost' key which holds special meaning to
48
38
  # Scalyr and is given special treatment in the Scalyr UI. All of these attributes are optional (not required for logs
@@ -91,9 +81,14 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
91
81
  # Set max interval in seconds between bulk retries.
92
82
  config :retry_max_interval, :validate => :number, :default => 64
93
83
 
94
- # The following two settings pertain to preventing Man-in-the-middle (MITM) attacks # echee TODO: eliminate?
84
+ # Whether or not to verify the connection to Scalyr, only set to false for debugging.
95
85
  config :ssl_verify_peer, :validate => :boolean, :default => true
96
- config :ssl_verify_depth, :validate => :number, :default => 5
86
+
87
+ # Path to SSL bundle file.
88
+ config :ssl_ca_bundle_path, :validate => :string, :default => "/etc/ssl/certs/ca-bundle.crt"
89
+
90
+ # If we should append our built-in Scalyr cert to the one we find at `ssl_ca_bundle_path`.
91
+ config :append_builtin_cert, :validate => :boolean, :default => true
97
92
 
98
93
  config :max_request_buffer, :validate => :number, :default => 5500000 # echee TODO: eliminate?
99
94
  config :force_message_encoding, :validate => :string, :default => nil
@@ -132,6 +127,24 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
132
127
  # Whether or not to create fresh quantile estimators after a status send. Depending on what you want to gather from
133
128
  # these stas this might be wanted or not.
134
129
  config :flush_quantile_estimates_on_status_send, :validate => :boolean, :default => false
130
+
131
+ # Causes this plugin to act as if it successfully uploaded the logs, while actually returning as quickly as possible
132
+ # after no work being done.
133
+ config :noop_mode, :validate => :boolean, :default => false
134
+
135
+ # Manticore related options
136
+ config :http_connect_timeout, :validate => :number, :default => 10
137
+ config :http_socket_timeout, :validate => :number, :default => 10
138
+ config :http_request_timeout, :validate => :number, :default => 60
139
+ config :http_pool_max, :validate => :number, :default => 50
140
+ config :http_pool_max_per_route, :validate => :number, :default => 25
141
+
142
+ def initialize(*params)
143
+ super
144
+ # Request statistics are accumulated across multiple threads and must be accessed through a mutex
145
+ @stats_lock = Mutex.new
146
+ @send_stats = Mutex.new
147
+ end
135
148
 
136
149
  def close
137
150
  @running = false
@@ -140,6 +153,8 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
140
153
 
141
154
  public
142
155
  def register
156
+ # This prng is used exclusively to determine when to sample statistics and no security related purpose, for this
157
+ # reason we do not ensure thread safety for it.
143
158
  @prng = Random.new
144
159
 
145
160
  if @event_metrics_sample_rate < 0 or @event_metrics_sample_rate > 1
@@ -223,9 +238,9 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
223
238
  @running = true
224
239
  @client_session = Scalyr::Common::Client::ClientSession.new(
225
240
  @logger, @add_events_uri,
226
- @compression_type, @compression_level,
227
- @ssl_verify_peer, @ssl_ca_bundle_path, @ssl_verify_depth,
228
- @append_builtin_cert, @record_stats_for_status, @flush_quantile_estimates_on_status_send
241
+ @compression_type, @compression_level, @ssl_verify_peer, @ssl_ca_bundle_path, @append_builtin_cert,
242
+ @record_stats_for_status, @flush_quantile_estimates_on_status_send,
243
+ @http_connect_timeout, @http_socket_timeout, @http_request_timeout, @http_pool_max, @http_pool_max_per_route
229
244
  )
230
245
 
231
246
  @logger.info("Started Scalyr output plugin", :class => self.class.name)
@@ -257,6 +272,9 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
257
272
  #
258
273
  public
259
274
  def multi_receive(events)
275
+ # Just return and pretend we did something if running in noop mode
276
+ return events if @noop_mode
277
+
260
278
  start_time = Time.now.to_f
261
279
 
262
280
  multi_event_request_array = build_multi_event_request_array(events)
@@ -276,6 +294,7 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
276
294
  # ignore these.
277
295
  if !multi_event_request.nil?
278
296
  @client_session.post_add_events(multi_event_request[:body], false, multi_event_request[:serialization_duration])
297
+
279
298
  sleep_interval = 0
280
299
  result.push(multi_event_request)
281
300
  end
@@ -306,7 +325,7 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
306
325
  exc_data[:payload] = "\tSample payload: #{request[:body][0,1024]}..." if @logger.debug?
307
326
  if e.is_commonly_retried?
308
327
  # well-known retriable errors should be debug
309
- @logger.debug(message, exc_data)
328
+ @logger.error(message, exc_data)
310
329
  else
311
330
  # all other failed uploads should be errors
312
331
  @logger.error(message, exc_data)
@@ -328,9 +347,11 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
328
347
  end
329
348
 
330
349
  if records_count > 0
331
- @multi_receive_statistics[:total_multi_receive_secs] += (Time.now.to_f - start_time)
332
- @plugin_metrics[:multi_receive_duration_secs].observe(Time.now.to_f - start_time)
333
- @plugin_metrics[:multi_receive_event_count].observe(records_count)
350
+ @stats_lock.synchronize do
351
+ @multi_receive_statistics[:total_multi_receive_secs] += (Time.now.to_f - start_time)
352
+ @plugin_metrics[:multi_receive_duration_secs].observe(Time.now.to_f - start_time)
353
+ @plugin_metrics[:multi_receive_event_count].observe(records_count)
354
+ end
334
355
  end
335
356
 
336
357
  send_status
@@ -501,10 +522,12 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
501
522
  end
502
523
 
503
524
  if should_sample_event_metrics
504
- @plugin_metrics[:event_attributes_count].observe(record.count)
525
+ @stats_lock.synchronize do
526
+ @plugin_metrics[:event_attributes_count].observe(record.count)
505
527
 
506
- if @flatten_nested_values
507
- @plugin_metrics[:flatten_values_duration_secs].observe(flatten_nested_values_duration)
528
+ if @flatten_nested_values
529
+ @plugin_metrics[:flatten_values_duration_secs].observe(flatten_nested_values_duration)
530
+ end
508
531
  end
509
532
  end
510
533
 
@@ -598,7 +621,7 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
598
621
  def create_multi_event_request(scalyr_events, current_threads, current_logs)
599
622
 
600
623
  body = {
601
- :session => @session_id,
624
+ :session => @session_id + Thread.current.object_id.to_s,
602
625
  :token => @api_write_token,
603
626
  :events => scalyr_events,
604
627
  }
@@ -638,33 +661,35 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
638
661
 
639
662
  # Retrieve batch and other event level metric values
640
663
  def get_stats
641
- current_stats = @multi_receive_statistics.clone
664
+ @stats_lock.synchronize do
665
+ current_stats = @multi_receive_statistics.clone
642
666
 
643
- current_stats[:multi_receive_duration_p50] = @plugin_metrics[:multi_receive_duration_secs].query(0.5)
644
- current_stats[:multi_receive_duration_p90] = @plugin_metrics[:multi_receive_duration_secs].query(0.9)
645
- current_stats[:multi_receive_duration_p99] = @plugin_metrics[:multi_receive_duration_secs].query(0.99)
667
+ current_stats[:multi_receive_duration_p50] = @plugin_metrics[:multi_receive_duration_secs].query(0.5)
668
+ current_stats[:multi_receive_duration_p90] = @plugin_metrics[:multi_receive_duration_secs].query(0.9)
669
+ current_stats[:multi_receive_duration_p99] = @plugin_metrics[:multi_receive_duration_secs].query(0.99)
646
670
 
647
- current_stats[:multi_receive_event_count_p50] = @plugin_metrics[:multi_receive_event_count].query(0.5)
648
- current_stats[:multi_receive_event_count_p90] = @plugin_metrics[:multi_receive_event_count].query(0.9)
649
- current_stats[:multi_receive_event_count_p99] = @plugin_metrics[:multi_receive_event_count].query(0.99)
671
+ current_stats[:multi_receive_event_count_p50] = @plugin_metrics[:multi_receive_event_count].query(0.5)
672
+ current_stats[:multi_receive_event_count_p90] = @plugin_metrics[:multi_receive_event_count].query(0.9)
673
+ current_stats[:multi_receive_event_count_p99] = @plugin_metrics[:multi_receive_event_count].query(0.99)
650
674
 
651
- current_stats[:event_attributes_count_p50] = @plugin_metrics[:event_attributes_count].query(0.5)
652
- current_stats[:event_attributes_count_p90] = @plugin_metrics[:event_attributes_count].query(0.9)
653
- current_stats[:event_attributes_count_p99] = @plugin_metrics[:event_attributes_count].query(0.99)
675
+ current_stats[:event_attributes_count_p50] = @plugin_metrics[:event_attributes_count].query(0.5)
676
+ current_stats[:event_attributes_count_p90] = @plugin_metrics[:event_attributes_count].query(0.9)
677
+ current_stats[:event_attributes_count_p99] = @plugin_metrics[:event_attributes_count].query(0.99)
654
678
 
655
- if @flatten_nested_values
656
- # We only return those metrics in case flattening is enabled
657
- current_stats[:flatten_values_duration_secs_p50] = @plugin_metrics[:flatten_values_duration_secs].query(0.5)
658
- current_stats[:flatten_values_duration_secs_p90] = @plugin_metrics[:flatten_values_duration_secs].query(0.9)
659
- current_stats[:flatten_values_duration_secs_p99] = @plugin_metrics[:flatten_values_duration_secs].query(0.99)
660
- end
679
+ if @flatten_nested_values
680
+ # We only return those metrics in case flattening is enabled
681
+ current_stats[:flatten_values_duration_secs_p50] = @plugin_metrics[:flatten_values_duration_secs].query(0.5)
682
+ current_stats[:flatten_values_duration_secs_p90] = @plugin_metrics[:flatten_values_duration_secs].query(0.9)
683
+ current_stats[:flatten_values_duration_secs_p99] = @plugin_metrics[:flatten_values_duration_secs].query(0.99)
684
+ end
661
685
 
662
- if @flush_quantile_estimates_on_status_send
663
- @logger.debug "Recreating / reseting quantile estimator classes for plugin metrics"
664
- @plugin_metrics = get_new_metrics
665
- end
686
+ if @flush_quantile_estimates_on_status_send
687
+ @logger.debug "Recreating / reseting quantile estimator classes for plugin metrics"
688
+ @plugin_metrics = get_new_metrics
689
+ end
666
690
 
667
- current_stats
691
+ current_stats
692
+ end
668
693
  end
669
694
 
670
695
 
@@ -683,33 +708,34 @@ class LogStash::Outputs::Scalyr < LogStash::Outputs::Base
683
708
  'plugin_id' => self.id,
684
709
  }
685
710
  }
686
-
687
- if !@last_status_transmit_time
688
- status_event[:attrs]['message'] = "Started Scalyr LogStash output plugin."
689
- status_event[:attrs]['serverHost'] = @node_hostname
690
- else
691
- cur_time = Time.now()
692
- return if (cur_time.to_i - @last_status_transmit_time.to_i) < @status_report_interval
693
- # echee TODO: get instance stats from session and create a status log line
694
- msg = 'plugin_status: '
695
- cnt = 0
696
- @client_session.get_stats.each do |k, v|
697
- val = v.instance_of?(Float) ? sprintf("%.4f", v) : v
698
- val = val.nil? ? 0 : val
699
- msg << ' ' if cnt > 0
700
- msg << "#{k.to_s}=#{val}"
701
- cnt += 1
702
- end
703
- get_stats.each do |k, v|
704
- val = v.instance_of?(Float) ? sprintf("%.4f", v) : v
705
- val = val.nil? ? 0 : val
706
- msg << ' ' if cnt > 0
707
- msg << "#{k.to_s}=#{val}"
708
- cnt += 1
711
+ @send_stats.synchronize do
712
+ if !@last_status_transmit_time
713
+ status_event[:attrs]['message'] = "Started Scalyr LogStash output plugin."
714
+ status_event[:attrs]['serverHost'] = @node_hostname
715
+ else
716
+ cur_time = Time.now()
717
+ return if (cur_time.to_i - @last_status_transmit_time.to_i) < @status_report_interval
718
+ # echee TODO: get instance stats from session and create a status log line
719
+ msg = 'plugin_status: '
720
+ cnt = 0
721
+ @client_session.get_stats.each do |k, v|
722
+ val = v.instance_of?(Float) ? sprintf("%.4f", v) : v
723
+ val = val.nil? ? 0 : val
724
+ msg << ' ' if cnt > 0
725
+ msg << "#{k.to_s}=#{val}"
726
+ cnt += 1
727
+ end
728
+ get_stats.each do |k, v|
729
+ val = v.instance_of?(Float) ? sprintf("%.4f", v) : v
730
+ val = val.nil? ? 0 : val
731
+ msg << ' ' if cnt > 0
732
+ msg << "#{k.to_s}=#{val}"
733
+ cnt += 1
734
+ end
735
+ status_event[:attrs]['message'] = msg
736
+ status_event[:attrs]['serverHost'] = @node_hostname
737
+ status_event[:attrs]['parser'] = @status_parser
709
738
  end
710
- status_event[:attrs]['message'] = msg
711
- status_event[:attrs]['serverHost'] = @node_hostname
712
- status_event[:attrs]['parser'] = @status_parser
713
739
  end
714
740
  multi_event_request = create_multi_event_request([status_event], nil, nil)
715
741
  @client_session.post_add_events(multi_event_request[:body], true, 0)
@@ -52,8 +52,9 @@ end
52
52
  class ClientSession
53
53
 
54
54
  def initialize(logger, add_events_uri, compression_type, compression_level,
55
- ssl_verify_peer, ssl_ca_bundle_path, ssl_verify_depth, append_builtin_cert,
56
- record_stats_for_status, flush_quantile_estimates_on_status_send)
55
+ ssl_verify_peer, ssl_ca_bundle_path, append_builtin_cert,
56
+ record_stats_for_status, flush_quantile_estimates_on_status_send,
57
+ connect_timeout, socket_timeout, request_timeout, pool_max, pool_max_per_route)
57
58
  @logger = logger
58
59
  @add_events_uri = add_events_uri # typically /addEvents
59
60
  @compression_type = compression_type
@@ -61,9 +62,13 @@ class ClientSession
61
62
  @ssl_verify_peer = ssl_verify_peer
62
63
  @ssl_ca_bundle_path = ssl_ca_bundle_path
63
64
  @append_builtin_cert = append_builtin_cert
64
- @ssl_verify_depth = ssl_verify_depth
65
65
  @record_stats_for_status = record_stats_for_status
66
66
  @flush_quantile_estimates_on_status_send = flush_quantile_estimates_on_status_send
67
+ @connect_timeout = connect_timeout
68
+ @socket_timeout = socket_timeout
69
+ @request_timeout = request_timeout
70
+ @pool_max = pool_max
71
+ @pool_max_per_route = pool_max_per_route
67
72
 
68
73
  # A cert to use by default to avoid issues caused by the OpenSSL library not validating certs according to standard
69
74
  @cert_string = "" \
@@ -126,11 +131,29 @@ class ClientSession
126
131
  :compression_type => @compression_type,
127
132
  :compression_level => @compression_level,
128
133
  }
134
+ end # def initialize
129
135
 
130
- @http = Net::HTTP::Persistent.new
136
+ def client_config
137
+ # TODO: Eventually expose some more of these as config options, though nothing here really needs tuning normally
138
+ # besides SSL
139
+ c = {
140
+ connect_timeout: @connect_timeout,
141
+ socket_timeout: @socket_timeout,
142
+ request_timeout: @request_timeout,
143
+ follow_redirects: true,
144
+ automatic_retries: 1,
145
+ retry_non_idempotent: false,
146
+ check_connection_timeout: 200,
147
+ pool_max: @pool_max,
148
+ pool_max_per_route: @pool_max_per_route,
149
+ cookies: true,
150
+ keepalive: true,
151
+ ssl: {}
152
+ }
131
153
 
132
154
  # verify peers to prevent potential MITM attacks
133
155
  if @ssl_verify_peer
156
+ c[:ssl][:verify] = :strict
134
157
  @ca_cert = Tempfile.new("ca_cert")
135
158
  if File.file?(@ssl_ca_bundle_path)
136
159
  @ca_cert.write(File.read(@ssl_ca_bundle_path))
@@ -142,13 +165,17 @@ class ClientSession
142
165
  end
143
166
  end
144
167
  @ca_cert.flush
145
- @http.ca_file = @ca_cert.path
146
- @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
147
- @http.verify_depth = @ssl_verify_depth
168
+ c[:ssl][:ca_file] = @ca_cert.path
148
169
  else
149
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
170
+ c[:ssl][:verify] = :disable
150
171
  end
151
- end # def initialize
172
+
173
+ c
174
+ end
175
+
176
+ def client
177
+ @client ||= Manticore::Client.new(client_config)
178
+ end
152
179
 
153
180
  # Convenience method to create a fresh quantile estimator
154
181
  def get_new_latency_stats
@@ -163,60 +190,56 @@ class ClientSession
163
190
 
164
191
  # Get a clone of current statistics hash and calculate percentiles
165
192
  def get_stats
166
- current_stats = @stats.clone
167
-
168
- current_stats[:request_latency_p50] = @latency_stats[:request_latency_secs].query(0.5)
169
- current_stats[:request_latency_p90] = @latency_stats[:request_latency_secs].query(0.9)
170
- current_stats[:request_latency_p99] = @latency_stats[:request_latency_secs].query(0.99)
171
- current_stats[:serialization_duration_secs_p50] = @latency_stats[:serialization_duration_secs].query(0.5)
172
- current_stats[:serialization_duration_secs_p90] = @latency_stats[:serialization_duration_secs].query(0.9)
173
- current_stats[:serialization_duration_secs_p99] = @latency_stats[:serialization_duration_secs].query(0.99)
174
- current_stats[:compression_duration_secs_p50] = @latency_stats[:compression_duration_secs].query(0.5)
175
- current_stats[:compression_duration_secs_p90] = @latency_stats[:compression_duration_secs].query(0.9)
176
- current_stats[:compression_duration_secs_p99] = @latency_stats[:compression_duration_secs].query(0.99)
177
- current_stats[:bytes_sent_p50] = @latency_stats[:bytes_sent].query(0.5)
178
- current_stats[:bytes_sent_p90] = @latency_stats[:bytes_sent].query(0.9)
179
- current_stats[:bytes_sent_p99] = @latency_stats[:bytes_sent].query(0.99)
180
-
181
- if @flush_quantile_estimates_on_status_send
182
- @logger.debug "Recreating / reseting quantile estimator classes for plugin metrics"
183
- @latency_stats = get_new_latency_stats
193
+ @stats_lock.synchronize do
194
+ current_stats = @stats.clone
195
+
196
+ current_stats[:request_latency_p50] = @latency_stats[:request_latency_secs].query(0.5)
197
+ current_stats[:request_latency_p90] = @latency_stats[:request_latency_secs].query(0.9)
198
+ current_stats[:request_latency_p99] = @latency_stats[:request_latency_secs].query(0.99)
199
+ current_stats[:serialization_duration_secs_p50] = @latency_stats[:serialization_duration_secs].query(0.5)
200
+ current_stats[:serialization_duration_secs_p90] = @latency_stats[:serialization_duration_secs].query(0.9)
201
+ current_stats[:serialization_duration_secs_p99] = @latency_stats[:serialization_duration_secs].query(0.99)
202
+ current_stats[:compression_duration_secs_p50] = @latency_stats[:compression_duration_secs].query(0.5)
203
+ current_stats[:compression_duration_secs_p90] = @latency_stats[:compression_duration_secs].query(0.9)
204
+ current_stats[:compression_duration_secs_p99] = @latency_stats[:compression_duration_secs].query(0.99)
205
+ current_stats[:bytes_sent_p50] = @latency_stats[:bytes_sent].query(0.5)
206
+ current_stats[:bytes_sent_p90] = @latency_stats[:bytes_sent].query(0.9)
207
+ current_stats[:bytes_sent_p99] = @latency_stats[:bytes_sent].query(0.99)
208
+
209
+ if @flush_quantile_estimates_on_status_send
210
+ @logger.debug "Recreating / reseting quantile estimator classes for plugin metrics"
211
+ @latency_stats = get_new_latency_stats
212
+ end
213
+ current_stats
184
214
  end
185
- current_stats
186
215
  end
187
216
 
188
217
 
189
218
 
190
219
  # Upload data to Scalyr. Assumes that the body size complies with Scalyr limits
191
220
  def post_add_events(body, is_status, body_serialization_duration = 0)
192
- post, compression_duration = prepare_post_object @add_events_uri.path, body
221
+ post_body, post_headers, compression_duration = prepare_post_object @add_events_uri.path, body
193
222
  fail_count = 1 # putative assume failure
194
223
  start_time = Time.now
195
224
  uncompressed_bytes_sent = 0
196
225
  compressed_bytes_sent = 0
197
226
  bytes_received = 0
198
227
  begin
199
- response = @http.request(@add_events_uri, post)
228
+ response = client.send(:post, @add_events_uri, body: post_body, headers: post_headers)
200
229
  handle_response(response)
201
230
 
202
231
  fail_count -= 1 # success means we negate the putative failure
203
232
  uncompressed_bytes_sent = (body.bytesize + @add_events_uri.path.bytesize)
204
- compressed_bytes_sent = (post.body.bytesize + @add_events_uri.path.bytesize)
233
+ compressed_bytes_sent = (post_body.bytesize + @add_events_uri.path.bytesize)
205
234
  bytes_received = response.body.bytesize # echee: double check
206
235
  # echee TODO add more statistics
207
236
 
237
+ # TODO: Manticore doesn't raise SSL errors as this but as "UnknownExceptions", need to dig in and see if there is a
238
+ # way to detect that it is from SSL.
208
239
  rescue OpenSSL::SSL::SSLError => e
209
- if @ssl_verify_peer and @ssl_ca_bundle_path.nil? and !File.file?(@ca_cert.path)
210
- @ca_cert = Tempfile.new("ca_cert")
211
- @ca_cert.write(@cert_string)
212
- @ca_cert.flush
213
- @http.ca_file = @ca_cert.path
214
- raise ClientError.new("Packaged certificate appears to have been deleted, writing a new one.", @add_events_uri)
215
- else
216
- raise e
217
- end
240
+ raise e
218
241
 
219
- rescue Net::HTTP::Persistent::Error => e
242
+ rescue Manticore::ManticoreException => e
220
243
  # The underlying persistent-connection library automatically retries when there are network-related errors.
221
244
  # Eventually, it will give up and raise this generic error, at which time, we convert it to a ClientError
222
245
  raise ClientError.new(e.message, @add_events_uri)
@@ -245,7 +268,6 @@ class ClientSession
245
268
 
246
269
 
247
270
  def close
248
- @http.shutdown
249
271
  end # def close
250
272
 
251
273
 
@@ -273,18 +295,20 @@ class ClientSession
273
295
  compression_duration = end_time - start_time
274
296
  end
275
297
 
276
- post = Net::HTTP::Post.new uri_path
277
- post.add_field('Content-Type', 'application/json')
278
- version = 'output-logstash-scalyr 0.1.9'
279
- post.add_field('User-Agent', version + ';' + RUBY_VERSION + ';' + RUBY_PLATFORM)
298
+ version = 'output-logstash-scalyr 0.1.10.beta'
299
+ post_headers = {
300
+ 'Content-Type': 'application/json',
301
+ 'User-Agent': version + ';' + RUBY_VERSION + ';' + RUBY_PLATFORM
302
+ }
280
303
 
304
+ post_body = nil
281
305
  if not encoding.nil?
282
- post.add_field('Content-Encoding', encoding)
283
- post.body = compressed_body
306
+ post_headers['Content-Encoding'] = encoding
307
+ post_body = compressed_body
284
308
  else
285
- post.body = body
309
+ post_body = body
286
310
  end
287
- return post, compression_duration
311
+ return post_body, post_headers, compression_duration
288
312
  end # def prepare_post_object
289
313
 
290
314
 
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-output-scalyr'
3
- s.version = '0.1.9'
3
+ s.version = '0.1.10.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)"
@@ -24,7 +24,7 @@ describe LogStash::Outputs::Scalyr do
24
24
  plugin = LogStash::Outputs::Scalyr.new({'api_write_token' => '1234', 'ssl_ca_bundle_path' => '/fakepath/nocerts', 'append_builtin_cert' => false})
25
25
  plugin.register
26
26
  plugin.multi_receive(sample_events)
27
- }.to raise_error(OpenSSL::SSL::SSLError, "certificate verify failed")
27
+ }.to raise_error(Scalyr::Common::Client::ClientError, "Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty")
28
28
  end
29
29
  end
30
30
 
@@ -38,7 +38,7 @@ describe LogStash::Outputs::Scalyr do
38
38
  plugin = LogStash::Outputs::Scalyr.new({'api_write_token' => '1234', 'append_builtin_cert' => false})
39
39
  plugin.register
40
40
  plugin.multi_receive(sample_events)
41
- }.to raise_error(OpenSSL::SSL::SSLError, "certificate verify failed")
41
+ }.to raise_error(Scalyr::Common::Client::ClientError, "Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty")
42
42
  end
43
43
  ensure
44
44
  `sudo mv /tmp/system_certs #{OpenSSL::X509::DEFAULT_CERT_DIR}`
@@ -65,7 +65,7 @@ describe LogStash::Outputs::Scalyr do
65
65
  plugin = LogStash::Outputs::Scalyr.new({'api_write_token' => '1234', 'scalyr_server' => 'https://invalid.mitm.should.fail.test.agent.scalyr.com:443'})
66
66
  plugin.register
67
67
  plugin.multi_receive(sample_events)
68
- }.to raise_error(OpenSSL::SSL::SSLError, "hostname \"invalid.mitm.should.fail.test.agent.scalyr.com\" does not match the server certificate")
68
+ }.to raise_error(Scalyr::Common::Client::ClientError, "Host name 'invalid.mitm.should.fail.test.agent.scalyr.com' does not match the certificate subject provided by the peer (CN=*.scalyr.com)")
69
69
  ensure
70
70
  # Clean up the hosts file
71
71
  `sudo truncate -s 0 /etc/hosts`
@@ -78,6 +78,7 @@ describe LogStash::Outputs::Scalyr do
78
78
  mock_client_session = MockClientSession.new
79
79
  plugin1.instance_variable_set(:@last_status_transmit_time, 100)
80
80
  plugin1.instance_variable_set(:@client_session, mock_client_session)
81
+ plugin1.instance_variable_set(:@session_id, "some_session_id")
81
82
  plugin1.instance_variable_set(:@plugin_metrics, {
82
83
  :multi_receive_duration_secs => Quantile::Estimator.new,
83
84
  :multi_receive_event_count => Quantile::Estimator.new,
@@ -95,6 +96,7 @@ describe LogStash::Outputs::Scalyr do
95
96
  # 1. Initial send
96
97
  plugin.instance_variable_set(:@last_status_transmit_time, nil)
97
98
  plugin.instance_variable_set(:@client_session, mock_client_session)
99
+ plugin.instance_variable_set(:@session_id, "some_session_id")
98
100
  status_event = plugin.send_status
99
101
  expect(status_event[:attrs]["message"]).to eq("Started Scalyr LogStash output plugin.")
100
102
 
@@ -138,6 +140,7 @@ describe LogStash::Outputs::Scalyr do
138
140
  # 1. Initial send
139
141
  plugin.instance_variable_set(:@last_status_transmit_time, nil)
140
142
  plugin.instance_variable_set(:@client_session, mock_client_session)
143
+ plugin.instance_variable_set(:@session_id, "some_session_id")
141
144
  expect(mock_client_session).to receive(:post_add_events).with(anything, true, anything)
142
145
  plugin.send_status
143
146
 
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.9
4
+ version: 0.1.10.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-06-07 00:00:00.000000000 Z
11
+ date: 2021-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -3745,9 +3745,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
3745
3745
  version: '0'
3746
3746
  required_rubygems_version: !ruby/object:Gem::Requirement
3747
3747
  requirements:
3748
- - - ">="
3748
+ - - ">"
3749
3749
  - !ruby/object:Gem::Version
3750
- version: '0'
3750
+ version: 1.3.1
3751
3751
  requirements: []
3752
3752
  rubyforge_project:
3753
3753
  rubygems_version: 2.7.10