libhoney 1.12.1 → 1.13.0
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/.rubocop.yml +4 -0
- data/lib/libhoney/response.rb +2 -2
- data/lib/libhoney/transmission.rb +145 -45
- data/lib/libhoney/version.rb +1 -1
- data/libhoney.gemspec +3 -1
- metadata +32 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '085a461457305c6c626057ffa084b66bb3d364d3bf538a66116de45c8195b78e'
|
4
|
+
data.tar.gz: 25cdef56d6c38db3b91f411763049acf5f536514002c1ca4ad519c406cb4309c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5bd46c22d978845d9b124fda903632186ab69003f8a319db662718e222da0d18ce2215a9f54c71e413856a112b32fb6871a5a9d5f1539121120da2ea5476b30e
|
7
|
+
data.tar.gz: 8982666d9a862224d7ce2200ac79f0876ded8ecc05a6b010c7ff57e77cb90e978c8ffb64fa1edbc3b1951a2b0ab4c1ba2722c9bacc1a24ee980347535f7caf93
|
data/.rubocop.yml
CHANGED
data/lib/libhoney/response.rb
CHANGED
@@ -5,11 +5,11 @@ module Libhoney
|
|
5
5
|
attr_accessor :duration, :status_code, :metadata, :error
|
6
6
|
|
7
7
|
def initialize(duration: 0,
|
8
|
-
status_code:
|
8
|
+
status_code: 0,
|
9
9
|
metadata: nil,
|
10
10
|
error: nil)
|
11
11
|
@duration = duration
|
12
|
-
@status_code = status_code
|
12
|
+
@status_code = HTTP::Response::Status.new(status_code)
|
13
13
|
@metadata = metadata
|
14
14
|
@error = error
|
15
15
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'timeout'
|
1
3
|
require 'libhoney/response'
|
2
4
|
|
3
5
|
module Libhoney
|
@@ -17,16 +19,19 @@ module Libhoney
|
|
17
19
|
@block_on_send = block_on_send
|
18
20
|
@block_on_responses = block_on_responses
|
19
21
|
@max_batch_size = max_batch_size
|
20
|
-
|
22
|
+
# convert to seconds
|
23
|
+
@send_frequency = send_frequency.fdiv(1000)
|
21
24
|
@max_concurrent_batches = max_concurrent_batches
|
22
25
|
@pending_work_capacity = pending_work_capacity
|
23
26
|
@send_timeout = send_timeout
|
24
27
|
@user_agent = build_user_agent(user_agent_addition).freeze
|
25
28
|
|
26
|
-
|
27
|
-
@
|
28
|
-
@
|
29
|
-
@
|
29
|
+
@send_queue = Queue.new
|
30
|
+
@threads = []
|
31
|
+
@lock = Mutex.new
|
32
|
+
# use a SizedQueue so the producer will block on adding to the batch_queue when @block_on_send is true
|
33
|
+
@batch_queue = SizedQueue.new(@pending_work_capacity)
|
34
|
+
@batch_thread = nil
|
30
35
|
end
|
31
36
|
|
32
37
|
def add(event)
|
@@ -35,7 +40,7 @@ module Libhoney
|
|
35
40
|
raise ArgumentError, "No Dataset for Honeycomb. Can't send datasetless." if event.dataset == ''
|
36
41
|
|
37
42
|
begin
|
38
|
-
@
|
43
|
+
@batch_queue.enq(event, !@block_on_send)
|
39
44
|
rescue ThreadError
|
40
45
|
# happens if the queue was full and block_on_send = false.
|
41
46
|
end
|
@@ -44,57 +49,53 @@ module Libhoney
|
|
44
49
|
end
|
45
50
|
|
46
51
|
def send_loop
|
47
|
-
http_clients =
|
48
|
-
h[api_host] = HTTP.timeout(connect: @send_timeout, write: @send_timeout, read: @send_timeout)
|
49
|
-
.persistent(api_host)
|
50
|
-
.headers(
|
51
|
-
'User-Agent' => @user_agent,
|
52
|
-
'Content-Type' => 'application/json'
|
53
|
-
)
|
54
|
-
end
|
52
|
+
http_clients = build_http_clients
|
55
53
|
|
56
54
|
# eat events until we run out
|
57
55
|
loop do
|
58
|
-
|
59
|
-
break if
|
56
|
+
api_host, writekey, dataset, batch = @send_queue.pop
|
57
|
+
break if batch.nil?
|
60
58
|
|
61
59
|
before = Time.now
|
62
60
|
|
63
61
|
begin
|
64
|
-
http = http_clients[
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
rescue Exception => error
|
62
|
+
http = http_clients[api_host]
|
63
|
+
body = serialize_batch(batch)
|
64
|
+
next if body.nil?
|
65
|
+
|
66
|
+
headers = {
|
67
|
+
'Content-Type' => 'application/json',
|
68
|
+
'X-Honeycomb-Team' => writekey
|
69
|
+
}
|
70
|
+
|
71
|
+
response = http.post(
|
72
|
+
"/1/batch/#{Addressable::URI.escape(dataset)}",
|
73
|
+
body: body,
|
74
|
+
headers: headers
|
75
|
+
)
|
76
|
+
process_response(response, before, batch)
|
77
|
+
rescue Exception => e
|
81
78
|
# catch a broader swath of exceptions than is usually good practice,
|
82
79
|
# because this is effectively the top-level exception handler for the
|
83
80
|
# sender threads, and we don't want those threads to die (leaving
|
84
81
|
# nothing consuming the queue).
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
82
|
+
begin
|
83
|
+
batch.each do |event|
|
84
|
+
# nil events in the batch should already have had an error
|
85
|
+
# response enqueued in #serialize_batch
|
86
|
+
next if event.nil?
|
87
|
+
|
88
|
+
Response.new(error: e).tap do |error_response|
|
89
|
+
error_response.metadata = event.metadata
|
90
|
+
begin
|
91
|
+
@responses.enq(error_response, !@block_on_responses)
|
92
|
+
rescue ThreadError
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
rescue ThreadError
|
90
97
|
end
|
91
98
|
end
|
92
|
-
|
93
|
-
begin
|
94
|
-
@responses.enq(response, !@block_on_responses) if response
|
95
|
-
rescue ThreadError
|
96
|
-
# happens if the queue was full and block_on_send = false.
|
97
|
-
end
|
98
99
|
end
|
99
100
|
ensure
|
100
101
|
http_clients.each do |_, http|
|
@@ -108,7 +109,13 @@ module Libhoney
|
|
108
109
|
|
109
110
|
def close(drain)
|
110
111
|
# if drain is false, clear the remaining unprocessed events from the queue
|
111
|
-
|
112
|
+
unless drain
|
113
|
+
@batch_queue.clear
|
114
|
+
@send_queue.clear
|
115
|
+
end
|
116
|
+
|
117
|
+
@batch_queue.enq(nil)
|
118
|
+
@batch_thread.join
|
112
119
|
|
113
120
|
# send @threads.length number of nils so each thread will fall out of send_loop
|
114
121
|
@threads.length.times { @send_queue << nil }
|
@@ -121,8 +128,78 @@ module Libhoney
|
|
121
128
|
0
|
122
129
|
end
|
123
130
|
|
131
|
+
def batch_loop
|
132
|
+
next_send_time = Time.now + @send_frequency
|
133
|
+
batched_events = Hash.new do |h, key|
|
134
|
+
h[key] = []
|
135
|
+
end
|
136
|
+
|
137
|
+
loop do
|
138
|
+
begin
|
139
|
+
while (event = Timeout.timeout(@send_frequency) { @batch_queue.pop })
|
140
|
+
key = [event.api_host, event.writekey, event.dataset]
|
141
|
+
batched_events[key] << event
|
142
|
+
end
|
143
|
+
|
144
|
+
break
|
145
|
+
rescue Exception
|
146
|
+
ensure
|
147
|
+
next_send_time = flush_batched_events(batched_events) if Time.now > next_send_time
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
flush_batched_events(batched_events)
|
152
|
+
end
|
153
|
+
|
124
154
|
private
|
125
155
|
|
156
|
+
def process_response(http_response, before, batch)
|
157
|
+
index = 0
|
158
|
+
http_response.parse.each do |event|
|
159
|
+
index += 1 while batch[index].nil? && index < batch.size
|
160
|
+
break unless (batched_event = batch[index])
|
161
|
+
|
162
|
+
Response.new(status_code: event['status']).tap do |response|
|
163
|
+
response.duration = Time.now - before
|
164
|
+
response.metadata = batched_event.metadata
|
165
|
+
begin
|
166
|
+
@responses.enq(response, !@block_on_responses)
|
167
|
+
rescue ThreadError
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def serialize_batch(batch)
|
174
|
+
payload = []
|
175
|
+
batch.map! do |event|
|
176
|
+
begin
|
177
|
+
e = {
|
178
|
+
time: event.timestamp.iso8601(3),
|
179
|
+
samplerate: event.sample_rate,
|
180
|
+
data: event.data
|
181
|
+
}
|
182
|
+
payload << JSON.generate(e)
|
183
|
+
|
184
|
+
event
|
185
|
+
rescue StandardError => e
|
186
|
+
Response.new(error: e).tap do |response|
|
187
|
+
response.metadata = event.metadata
|
188
|
+
begin
|
189
|
+
@responses.enq(response, !@block_on_responses)
|
190
|
+
rescue ThreadError
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
nil
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
return if payload.empty?
|
199
|
+
|
200
|
+
"[#{payload.join(',')}]"
|
201
|
+
end
|
202
|
+
|
126
203
|
def build_user_agent(user_agent_addition)
|
127
204
|
ua = "libhoney-rb/#{VERSION}"
|
128
205
|
ua << " #{user_agent_addition}" if user_agent_addition
|
@@ -131,9 +208,32 @@ module Libhoney
|
|
131
208
|
|
132
209
|
def ensure_threads_running
|
133
210
|
@lock.synchronize do
|
211
|
+
@batch_thread = Thread.new { batch_loop } unless @batch_thread && @batch_thread.alive?
|
134
212
|
@threads.select!(&:alive?)
|
135
213
|
@threads << Thread.new { send_loop } while @threads.length < @max_concurrent_batches
|
136
214
|
end
|
137
215
|
end
|
216
|
+
|
217
|
+
def flush_batched_events(batched_events)
|
218
|
+
batched_events.each do |(api_host, writekey, dataset), events|
|
219
|
+
events.each_slice(@max_batch_size) do |batch|
|
220
|
+
@send_queue << [api_host, writekey, dataset, batch]
|
221
|
+
end
|
222
|
+
end
|
223
|
+
batched_events.clear
|
224
|
+
|
225
|
+
Time.now + @send_frequency
|
226
|
+
end
|
227
|
+
|
228
|
+
def build_http_clients
|
229
|
+
Hash.new do |h, api_host|
|
230
|
+
h[api_host] = HTTP.timeout(connect: @send_timeout, write: @send_timeout, read: @send_timeout)
|
231
|
+
.persistent(api_host)
|
232
|
+
.headers(
|
233
|
+
'User-Agent' => @user_agent,
|
234
|
+
'Content-Type' => 'application/json'
|
235
|
+
)
|
236
|
+
end
|
237
|
+
end
|
138
238
|
end
|
139
239
|
end
|
data/lib/libhoney/version.rb
CHANGED
data/libhoney.gemspec
CHANGED
@@ -26,7 +26,9 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency 'bundler'
|
27
27
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
28
28
|
spec.add_development_dependency 'rake', '~> 12.3'
|
29
|
-
spec.add_development_dependency 'rubocop'
|
29
|
+
spec.add_development_dependency 'rubocop', '< 0.69'
|
30
|
+
spec.add_development_dependency 'sinatra'
|
31
|
+
spec.add_development_dependency 'sinatra-contrib'
|
30
32
|
spec.add_development_dependency 'webmock', '~> 3.4'
|
31
33
|
spec.add_development_dependency 'yard'
|
32
34
|
spec.add_development_dependency 'yardstick', '~> 0.9'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: libhoney
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- The Honeycomb.io Team
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bump
|
@@ -68,6 +68,34 @@ dependencies:
|
|
68
68
|
version: '12.3'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "<"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.69'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "<"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.69'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sinatra
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sinatra-contrib
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
72
100
|
requirements:
|
73
101
|
- - ">="
|
@@ -206,7 +234,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
206
234
|
- !ruby/object:Gem::Version
|
207
235
|
version: '0'
|
208
236
|
requirements: []
|
209
|
-
|
237
|
+
rubyforge_project:
|
238
|
+
rubygems_version: 2.7.7
|
210
239
|
signing_key:
|
211
240
|
specification_version: 4
|
212
241
|
summary: send data to Honeycomb
|