logstash-output-honeycomb_json_batch 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c90c885174ae440377b402eac734980422d3192d
4
- data.tar.gz: c6e29760a336290dece1dd18bbc33f10b2b2fd2d
3
+ metadata.gz: d0021f21a46eb6dfb4685f88f6a3350e8c32e7e9
4
+ data.tar.gz: b1838276379b83acb24f4b6c13836922ebb8028c
5
5
  SHA512:
6
- metadata.gz: b9c755b2655e7218d4141c822a0a784cc1fc6006f943c891c1d7d2a993ce2f1c7436717dca1c860a9e016ce8cde6355f4a4c8f98647240d4951b855fca7986d1
7
- data.tar.gz: 6584d584c082375794988dd6cbe94c4410acc89d478c760ff62c03de8f5817421b4e358f6a6d4956285ada7c8da390441f0d2f09c880aab910fcf168cea49866
6
+ metadata.gz: f01e1628b5af907e299762afbf2fb2911beabbda20dd1a37db185af9b80e641a5f211d521a8a211a80394c3b682fc7748d3bf5e725cd08690030b89cb4256637
7
+ data.tar.gz: 786eeb856cafd6e6e5e30ab31faeb00748650d58081835f4fb7337ed2bfa4b14f0914bf8522d44a392a5e637bd72e20e77bd10a1823bb50d35339e8646a3ba20
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # logstash-output-honeycomb_json_batch
1
+ # logstash-output-honeycomb_json_batch [![Gem Version](https://badge.fury.io/rb/logstash-output-honeycomb_json_batch.svg)](https://badge.fury.io/rb/logstash-output-honeycomb_json_batch)
2
2
 
3
3
  A logstash plugin for interacting with [Honeycomb](https://honeycomb.io) at high volumes. (See here for more information about [using Honeycomb](https://honeycomb.io/intro/) and [its libraries](https://honeycomb.io/docs/send-data/sdks).)
4
4
 
@@ -14,11 +14,13 @@ The easiest way to use this plugin is by installing it through rubygems like any
14
14
  bin/logstash-plugin install logstash-output-honeycomb_json_batch
15
15
  ```
16
16
 
17
- ## Usage
17
+ ## Compatibility
18
+
19
+ This plugin requires Logstash 2.4 or greater. Please open an issue if you require support for older versions.
18
20
 
19
- The default batch size is 50, the default flush interval is 5 seconds, and each of those can be overridden via the plugin config.
21
+ ## Usage
20
22
 
21
- A simple config to test this might be:
23
+ A simple config is:
22
24
 
23
25
  ```
24
26
  input {
@@ -35,12 +37,7 @@ output {
35
37
  ```
36
38
 
37
39
  Additional arguments to `honeycomb_json_batch`:
38
-
39
- Consider these when tuning performance:
40
-
41
- - `flush_size`: Default batch size, defaults to 50
42
- - `idle_flush_time`: Default flush interval in seconds, defaults to 5
43
- - `pool_max`: Maximum number of requests to be run in parallel, defaults to 10
40
+ - `flush_size`: Maximum batch size, defaults to 75
44
41
  - `retry_individual`: On failed requests, whether to retry event sends individually, defaults to true
45
42
  - `api_host`: Allows you to override the Honeycomb host, defaults to https://api.honeycomb.io
46
43
 
@@ -1,14 +1,15 @@
1
1
  # encoding: utf-8
2
+ require "enumerator"
2
3
  require "logstash/outputs/base"
3
4
  require "logstash/namespace"
4
5
  require "logstash/json"
5
6
  require "uri"
6
- require "stud/buffer"
7
7
  require "logstash/plugin_mixins/http_client"
8
8
 
9
9
  class LogStash::Outputs::HoneycombJSONBatch < LogStash::Outputs::Base
10
10
  include LogStash::PluginMixins::HttpClient
11
- include Stud::Buffer
11
+
12
+ concurrency :shared
12
13
 
13
14
  config_name "honeycomb_json_batch"
14
15
 
@@ -18,24 +19,18 @@ class LogStash::Outputs::HoneycombJSONBatch < LogStash::Outputs::Base
18
19
 
19
20
  config :dataset, :validate => :string, :required => true
20
21
 
21
- config :flush_size, :validate => :number, :default => 50
22
+ config :retry_individual, :validate => :boolean, :default => true
22
23
 
23
- config :idle_flush_time, :validate => :number, :default => 5
24
+ config :flush_size, :validate => :number, :default => 75
24
25
 
25
- config :retry_individual, :validate => :boolean, :default => true
26
+ # The following configuration options are deprecated and do nothing.
27
+ config :idle_flush_time, :validate => :number, :default => 5
26
28
 
27
29
  config :pool_max, :validate => :number, :default => 10
28
30
 
29
31
  def register
30
- # We count outstanding requests with this queue
31
- # This queue tracks the requests to create backpressure
32
- # When this queue is empty no new requests may be sent,
33
- # tokens must be added back by the client on success
34
- @request_tokens = SizedQueue.new(@pool_max)
35
- @pool_max.times {|t| @request_tokens << true }
36
32
  @total = 0
37
33
  @total_failed = 0
38
- @requests = Array.new
39
34
  if @api_host.nil?
40
35
  @api_host = "https://api.honeycomb.io"
41
36
  elsif !@api_host.start_with? "http"
@@ -43,94 +38,65 @@ class LogStash::Outputs::HoneycombJSONBatch < LogStash::Outputs::Base
43
38
  end
44
39
  @api_host = @api_host.chomp
45
40
 
46
- buffer_initialize(
47
- :max_items => @flush_size,
48
- :max_interval => @idle_flush_time,
49
- :logger => @logger
50
- )
51
41
  logger.info("Initialized honeycomb_json_batch with settings",
52
- :flush_size => @flush_size,
53
- :idle_flush_time => @idle_flush_time,
54
- :request_tokens => @pool_max,
55
42
  :api_host => @api_host,
56
43
  :headers => request_headers,
57
44
  :retry_individual => @retry_individual)
58
-
59
- end
60
-
61
- # This module currently does not support parallel requests as that would circumvent the batching
62
- def receive(event, async_type=:background)
63
- buffer_receive(event)
64
45
  end
65
46
 
66
47
  def close
67
- buffer_flush(:final => true)
68
48
  client.close
69
49
  end
70
50
 
71
- public
72
- def flush(events, close=false)
73
- documents = [] #this is the array of hashes that we push to Fusion as documents
74
-
75
- events.each do |event|
76
- data = event.to_hash()
77
- timestamp = data.delete("@timestamp")
78
- doc = { "time" => timestamp, "data" => data }
79
- if samplerate = data.delete("@samplerate")
80
- doc["samplerate"] = samplerate.to_i
51
+ def multi_receive(events)
52
+ events.each_slice(@flush_size) do |chunk|
53
+ documents = []
54
+ chunk.each do |event|
55
+ data = event.to_hash()
56
+ timestamp = data.delete("@timestamp")
57
+ doc = { "time" => timestamp, "data" => data }
58
+ if samplerate = data.delete("@samplerate")
59
+ doc["samplerate"] = samplerate.to_i
60
+ end
61
+ documents.push(doc)
81
62
  end
82
- documents.push(doc)
63
+ make_request(documents)
83
64
  end
84
-
85
- make_request(documents)
86
- end
87
-
88
- def multi_receive(events)
89
- events.each {|event| buffer_receive(event)}
90
65
  end
91
66
 
92
67
  private
93
68
 
94
69
  def make_request(documents)
95
70
  body = LogStash::Json.dump({ @dataset => documents })
96
- # Block waiting for a token
97
- token = @request_tokens.pop
98
- @logger.debug("Got token", :tokens => @request_tokens.length)
99
71
 
100
-
101
- # Create an async request
102
- begin
103
- request = client.post("#{@api_host}/1/batch", {
104
- :body => body,
105
- :headers => request_headers,
106
- :async => true
107
- })
108
- rescue Exception => e
109
- @logger.warn("An error occurred while indexing: #{e.message}")
110
- end
111
-
112
- # attach handlers before performing request
113
- request.on_complete do
114
- # Make sure we return the token to the pool
115
- @request_tokens << token
116
- end
72
+ url = "#{@api_host}/1/batch"
73
+ request = client.post(url, {
74
+ :body => body,
75
+ :headers => request_headers
76
+ })
117
77
 
118
78
  request.on_success do |response|
119
79
  if response.code >= 200 && response.code < 300
120
80
  @total = @total + documents.length
121
- @logger.debug("Successfully submitted",
81
+ @logger.debug("Successfully submitted",
122
82
  :docs => documents.length,
123
83
  :response_code => response.code,
124
84
  :total => @total)
125
85
  else
126
86
  if documents.length > 1 && @retry_individual
127
87
  if statuses = JSON.parse(response.body).values.first
128
- status.each_with_index do |status, i|
129
- next if status >= 200 && status < 300
88
+ statuses.each_with_index do |status, i|
89
+ code = status["status"]
90
+ if code == nil
91
+ @logger.warn("Status code missing in response: #{status}")
92
+ next
93
+ elsif code >= 200 && code < 300
94
+ next
95
+ end
130
96
  make_request([documents[i]])
131
97
  end
132
98
  end
133
- else
99
+ else
134
100
  @total_failed += documents.length
135
101
  log_failure(
136
102
  "Encountered non-200 HTTP code #{response.code}",
@@ -150,7 +116,7 @@ class LogStash::Outputs::HoneycombJSONBatch < LogStash::Outputs::Base
150
116
  :url => url,
151
117
  :method => @http_method,
152
118
  :body => body,
153
- :headers => headers,
119
+ :headers => request_headers,
154
120
  :message => exception.message,
155
121
  :class => exception.class.name,
156
122
  :backtrace => exception.backtrace,
@@ -158,7 +124,8 @@ class LogStash::Outputs::HoneycombJSONBatch < LogStash::Outputs::Base
158
124
  )
159
125
  end
160
126
 
161
- client.execute!
127
+ request.call
128
+
162
129
  rescue Exception => e
163
130
  log_failure("Got totally unexpected exception #{e.message}", :docs => documents.length)
164
131
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-output-honeycomb_json_batch'
3
- s.version = '0.1.2'
3
+ s.version = '0.2.0'
4
4
  s.licenses = ['Apache-2.0']
5
5
  s.summary = "This output lets you `POST` batches of events to the Honeycomb.io API endpoint"
6
6
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -33,22 +33,11 @@ describe LogStash::Outputs::HoneycombJSONBatch do
33
33
 
34
34
  it "should receive a single post request" do
35
35
  expect(client).to receive(:post).
36
- with("#{ api_host }/1/batch", hash_including(:body, :headers, :async)).
36
+ with("#{ api_host }/1/batch", hash_including(:body, :headers)).
37
37
  once.
38
38
  and_call_original
39
39
 
40
- 5.times {|t| @honeycomb.receive(event)}
41
- @honeycomb.buffer_flush(:force => true)
42
- end
43
-
44
- it "should send batches based on the specified flush_size" do
45
- expect(client).to receive(:post).
46
- with("#{ api_host }/1/batch", hash_including(:body, :headers, :async)).
47
- twice.
48
- and_call_original
49
-
50
- (flush_size + 1).times {|t| @honeycomb.receive(event)}
51
- @honeycomb.buffer_flush(:force => true)
40
+ @honeycomb.multi_receive([event])
52
41
  end
53
42
 
54
43
  it "should attach the right headers for Honeycomb ingestion" do
@@ -59,11 +48,10 @@ describe LogStash::Outputs::HoneycombJSONBatch do
59
48
  })).once.
60
49
  and_call_original
61
50
 
62
- @honeycomb.receive(event)
63
- @honeycomb.buffer_flush(:force => true)
51
+ @honeycomb.multi_receive([event])
64
52
  end
65
53
 
66
- it "should wrap events in the right structure Honeycomb ingestion" do
54
+ it "should wrap events in the right structure for Honeycomb ingestion" do
67
55
  data = event.to_hash()
68
56
  data.delete("@timestamp")
69
57
  expect(client).to receive(:post).
@@ -71,25 +59,23 @@ describe LogStash::Outputs::HoneycombJSONBatch do
71
59
  DATASET => [ { "time" => event.timestamp.to_s, "data" => data } ]
72
60
  }))).once.
73
61
  and_call_original
74
-
75
- @honeycomb.receive(event)
76
- @honeycomb.buffer_flush(:force => true)
62
+ @honeycomb.multi_receive([event])
77
63
  end
78
64
 
79
65
  it "should extract timestamp and samplerate from the data" do
80
- with_samplerate = LogStash::Event.new("alpha" => 1.0, "@samplerate" => "17.5")
66
+ with_samplerate = LogStash::Event.new("alpha" => 1.0, "@samplerate" => "17.5",
67
+ "@timestamp" => "2014-11-17T20:37:17.223Z")
81
68
  data = with_samplerate.to_hash()
82
69
  data.delete("@timestamp")
83
70
  data.delete("@samplerate")
84
71
 
85
72
  expect(client).to receive(:post).
86
73
  with("#{ api_host }/1/batch", hash_including(:body => LogStash::Json.dump({
87
- DATASET => [ { "time" => event.timestamp.to_s, "data" => data, "samplerate" => 17 } ]
74
+ DATASET => [ { "time" => with_samplerate.timestamp.to_s, "data" => data, "samplerate" => 17 } ]
88
75
  }))).once.
89
76
  and_call_original
90
77
 
91
- @honeycomb.receive(with_samplerate)
92
- @honeycomb.buffer_flush(:force => true)
78
+ @honeycomb.multi_receive([with_samplerate])
93
79
  end
94
80
 
95
81
  it "should wrap multiple events up in the right structure" do
@@ -107,9 +93,16 @@ describe LogStash::Outputs::HoneycombJSONBatch do
107
93
  }))).once.
108
94
  and_call_original
109
95
 
110
- @honeycomb.receive(event1)
111
- @honeycomb.receive(event2)
112
- @honeycomb.receive(event3)
113
- @honeycomb.buffer_flush(:force => true)
96
+ @honeycomb.multi_receive([event1, event2, event3])
97
+ end
98
+
99
+ it "should chunk large batches" do
100
+ events = []
101
+ (1..3*@honeycomb.flush_size).each do |i|
102
+ events.push(LogStash::Event.new("index" => i))
103
+ end
104
+ expect(client).to receive(:post).exactly(3).times.
105
+ and_call_original
106
+ @honeycomb.multi_receive(events)
114
107
  end
115
108
  end
metadata CHANGED
@@ -1,16 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-output-honeycomb_json_batch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Honeycomb
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-04 00:00:00.000000000 Z
11
+ date: 2017-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
+ name: logstash-core-plugin-api
14
15
  requirement: !ruby/object:Gem::Requirement
15
16
  requirements:
16
17
  - - ">="
@@ -19,9 +20,8 @@ dependencies:
19
20
  - - "<="
20
21
  - !ruby/object:Gem::Version
21
22
  version: '2.99'
22
- name: logstash-core-plugin-api
23
- prerelease: false
24
23
  type: :runtime
24
+ prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
@@ -31,6 +31,7 @@ dependencies:
31
31
  - !ruby/object:Gem::Version
32
32
  version: '2.99'
33
33
  - !ruby/object:Gem::Dependency
34
+ name: logstash-mixin-http_client
34
35
  requirement: !ruby/object:Gem::Requirement
35
36
  requirements:
36
37
  - - ">="
@@ -39,9 +40,8 @@ dependencies:
39
40
  - - "<"
40
41
  - !ruby/object:Gem::Version
41
42
  version: 5.0.0
42
- name: logstash-mixin-http_client
43
- prerelease: false
44
43
  type: :runtime
44
+ prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
46
46
  requirements:
47
47
  - - ">="
@@ -51,20 +51,22 @@ dependencies:
51
51
  - !ruby/object:Gem::Version
52
52
  version: 5.0.0
53
53
  - !ruby/object:Gem::Dependency
54
+ name: logstash-devutils
54
55
  requirement: !ruby/object:Gem::Requirement
55
56
  requirements:
56
57
  - - ">="
57
58
  - !ruby/object:Gem::Version
58
59
  version: '0'
59
- name: logstash-devutils
60
- prerelease: false
61
60
  type: :development
61
+ prerelease: false
62
62
  version_requirements: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
66
  version: '0'
67
- description: This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program
67
+ description: This gem is a Logstash plugin required to be installed on top of the
68
+ Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This
69
+ gem is not a stand-alone program
68
70
  email: support@honeycomb.io
69
71
  executables: []
70
72
  extensions: []
@@ -82,7 +84,7 @@ licenses:
82
84
  metadata:
83
85
  logstash_plugin: 'true'
84
86
  logstash_group: output
85
- post_install_message:
87
+ post_install_message:
86
88
  rdoc_options: []
87
89
  require_paths:
88
90
  - lib
@@ -97,9 +99,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
99
  - !ruby/object:Gem::Version
98
100
  version: '0'
99
101
  requirements: []
100
- rubyforge_project:
101
- rubygems_version: 2.6.8
102
- signing_key:
102
+ rubyforge_project:
103
+ rubygems_version: 2.4.8
104
+ signing_key:
103
105
  specification_version: 4
104
106
  summary: This output lets you `POST` batches of events to the Honeycomb.io API endpoint
105
107
  test_files: