libhoney 1.12.1 → 1.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|