libhoney 1.16.0 → 1.19.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/.circleci/config.yml +4 -2
- data/.github/CODEOWNERS +1 -1
- data/.github/dependabot.yml +15 -0
- data/.github/workflows/add-to-project.yml +14 -0
- data/.github/workflows/apply-labels.yml +10 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +4 -2
- data/CHANGELOG.md +38 -0
- data/lib/libhoney/client.rb +97 -32
- data/lib/libhoney/experimental_transmission.rb +109 -0
- data/lib/libhoney/mock_transmission.rb +1 -1
- data/lib/libhoney/null_transmission.rb +2 -0
- data/lib/libhoney/queueing.rb +1 -0
- data/lib/libhoney/queueing/LICENSE.txt +23 -0
- data/lib/libhoney/queueing/sized_queue_with_timeout.rb +176 -0
- data/lib/libhoney/response.rb +10 -2
- data/lib/libhoney/transmission.rb +122 -53
- data/lib/libhoney/version.rb +1 -1
- data/libhoney.gemspec +3 -0
- metadata +52 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40d54151fc103bf8020f14ff8b7a9e6e0298366869e795ce3ddf0438af066e16
|
4
|
+
data.tar.gz: c2b834d39609a73bce6e5e6e19caf4d2499903498895d32fe22b55041db7e59b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24d9d44cb0f1f5ad7549b75067dd6cbc2eb2264b8cad1c1c8cf523c19d955e41b91feb86d6cde74104885fc61ee4c4f5309b3ed46edfc5751483ba282d5e841e
|
7
|
+
data.tar.gz: 1b1690d32daf8f44a19761d2ccc26a5a099d1daec623ecd481776f34ea439506278c267ff8b319c0ccb380ec497ca1de4b6e9c2a7eee677a1584d9d1cfe1ec87
|
data/.circleci/config.yml
CHANGED
@@ -97,6 +97,8 @@ jobs:
|
|
97
97
|
- run:
|
98
98
|
name: run tests
|
99
99
|
command: bundle exec rake test
|
100
|
+
- store_test_results:
|
101
|
+
path: test/reports
|
100
102
|
|
101
103
|
build_artifacts:
|
102
104
|
executor:
|
@@ -147,11 +149,11 @@ workflows:
|
|
147
149
|
- test
|
148
150
|
- publish_github:
|
149
151
|
<<: *filters_publish
|
150
|
-
context: Honeycomb Secrets
|
152
|
+
context: Honeycomb Secrets for Public Repos
|
151
153
|
requires:
|
152
154
|
- build_artifacts
|
153
155
|
- publish_rubygems:
|
154
156
|
<<: *filters_publish
|
155
|
-
context: Honeycomb Secrets
|
157
|
+
context: Honeycomb Secrets for Public Repos
|
156
158
|
requires:
|
157
159
|
- build_artifacts
|
data/.github/CODEOWNERS
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
3
|
+
# Please see the documentation for all configuration options:
|
4
|
+
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
5
|
+
|
6
|
+
version: 2
|
7
|
+
updates:
|
8
|
+
- package-ecosystem: "bundler" # See documentation for possible values
|
9
|
+
directory: "/" # Location of package manifests
|
10
|
+
schedule:
|
11
|
+
interval: "weekly"
|
12
|
+
labels:
|
13
|
+
- "type: dependencies"
|
14
|
+
reviewers:
|
15
|
+
- "honeycombio/telemetry-team"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
name: Apply project management flow
|
2
|
+
on:
|
3
|
+
issues:
|
4
|
+
types: [opened]
|
5
|
+
pull_request_target:
|
6
|
+
types: [opened]
|
7
|
+
jobs:
|
8
|
+
project-management:
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
name: Apply project management flow
|
11
|
+
steps:
|
12
|
+
- uses: honeycombio/oss-management-actions/projects@v1
|
13
|
+
with:
|
14
|
+
ghprojects-token: ${{ secrets.GHPROJECTS_TOKEN }}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
name: Apply project labels
|
2
|
+
on: [issues, pull_request, label]
|
3
|
+
jobs:
|
4
|
+
apply-labels:
|
5
|
+
runs-on: ubuntu-latest
|
6
|
+
name: Apply common project labels
|
7
|
+
steps:
|
8
|
+
- uses: honeycombio/oss-management-actions/labels@v1
|
9
|
+
with:
|
10
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -9,17 +9,19 @@ Style/Documentation:
|
|
9
9
|
Lint/RescueException:
|
10
10
|
Exclude:
|
11
11
|
- 'lib/libhoney/transmission.rb'
|
12
|
+
- 'lib/libhoney/experimental_transmission.rb'
|
12
13
|
|
13
14
|
Metrics/BlockLength:
|
14
15
|
Max: 35
|
15
16
|
|
16
17
|
Metrics/ClassLength:
|
17
|
-
Max:
|
18
|
+
Max: 300
|
18
19
|
Exclude:
|
20
|
+
- lib/libhoney/transmission.rb # Should this remain so large?
|
19
21
|
- test/*
|
20
22
|
|
21
23
|
Metrics/MethodLength:
|
22
|
-
Max:
|
24
|
+
Max: 45
|
23
25
|
Exclude:
|
24
26
|
- lib/libhoney/transmission.rb
|
25
27
|
- test/*
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,43 @@
|
|
1
1
|
# libhoney-rb changelog
|
2
2
|
|
3
|
+
## changes pending release
|
4
|
+
|
5
|
+
## 1.19.0
|
6
|
+
|
7
|
+
### Improvements
|
8
|
+
|
9
|
+
- add a test_helper, Minitest reporters, & store test results in CI (#88)
|
10
|
+
- add experimental transmission with new sized-and-timed queue (#87)
|
11
|
+
|
12
|
+
### Fixes
|
13
|
+
|
14
|
+
- Process single-error responses from the Batch API (#89)
|
15
|
+
|
16
|
+
## 1.18.0
|
17
|
+
|
18
|
+
### Improvements
|
19
|
+
|
20
|
+
- replace HTTP client library to reduce external dependencies (#81)
|
21
|
+
|
22
|
+
### Deprecations
|
23
|
+
|
24
|
+
- `Libhoney::Client.new(proxy_config: _)`: the `proxy_config` parameter for client
|
25
|
+
creation will no longer accept an Array in the next major version. The recommended
|
26
|
+
way to configure the client for operation behind forwarding web proxies is to set
|
27
|
+
http/https/no_proxy environment variables appropriately.
|
28
|
+
|
29
|
+
## 1.17.0
|
30
|
+
|
31
|
+
### Fixes:
|
32
|
+
|
33
|
+
- Allow Ruby 3.0.0 (removes overly-pessimistic exception) (#79)
|
34
|
+
|
35
|
+
## 1.16.1
|
36
|
+
|
37
|
+
### Fixes:
|
38
|
+
|
39
|
+
- Fix closing down the client when no threads have been started. (#74 & #76)
|
40
|
+
|
3
41
|
## 1.16.0
|
4
42
|
|
5
43
|
### Fixes:
|
data/lib/libhoney/client.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
+
require 'addressable/uri'
|
1
2
|
require 'time'
|
2
3
|
require 'json'
|
3
|
-
require 'http'
|
4
4
|
require 'forwardable'
|
5
5
|
|
6
6
|
require 'libhoney/null_transmission'
|
@@ -46,11 +46,22 @@ module Libhoney
|
|
46
46
|
# Honeycomb query engine will interpret it as representative of 10 events)
|
47
47
|
# @param api_host [String] defaults to +API_HOST+, override to change the
|
48
48
|
# destination for these Honeycomb events.
|
49
|
-
# @param transmission [Object] transport used to actually send events. If nil (the default),
|
49
|
+
# @param transmission [Class, Object, nil] transport used to actually send events. If nil (the default),
|
50
|
+
# will be initialized with a {TransmissionClient}. If Class--for example, {MockTransmissionClient}--will attempt
|
51
|
+
# to create a new instance of that class with {TransmissionClient}'s usual parameters. If Object, checks
|
52
|
+
# that the instance passed in implements the Transmission interface (responds to :add and :close). If the
|
53
|
+
# check does not succeed, a no-op NullTransmission will be used instead.
|
50
54
|
# @param block_on_send [Boolean] if more than pending_work_capacity events are written, block sending further events
|
51
55
|
# @param block_on_responses [Boolean] if true, block if there is no thread reading from the response queue
|
52
56
|
# @param pending_work_capacity [Fixnum] defaults to 1000. If the queue of
|
53
57
|
# pending events exceeds 1000, this client will start dropping events.
|
58
|
+
# @param proxy_config [String, Array, nil] proxy connection information
|
59
|
+
# nil: (default, recommended) connection proxying will be determined from any http_proxy, https_proxy, and no_proxy environment
|
60
|
+
# variables set for the process.
|
61
|
+
# String: the value must be the URI for connecting to a forwarding web proxy. Must be parsable by stdlib URI.
|
62
|
+
# Array: (deprecated, removal in v2.0) the value must have one and at most four elements: e.g. ['host', port, 'username', 'password'].
|
63
|
+
# The assumption is that the TCP connection will be tunneled via HTTP, so the assumed scheme is 'http://'
|
64
|
+
# 'host' is required. 'port' is optional (default:80), unless a 'username' is included. 'password' is optional.
|
54
65
|
# rubocop:disable Metrics/ParameterLists
|
55
66
|
def initialize(writekey: nil,
|
56
67
|
dataset: nil,
|
@@ -70,7 +81,7 @@ module Libhoney
|
|
70
81
|
raise Exception, 'libhoney: max_concurrent_batches must be greater than 0' if max_concurrent_batches < 1
|
71
82
|
raise Exception, 'libhoney: sample rate must be greater than 0' if sample_rate < 1
|
72
83
|
|
73
|
-
unless Gem::Dependency.new('ruby', '
|
84
|
+
unless Gem::Dependency.new('ruby', '>= 2.2').match?('ruby', RUBY_VERSION)
|
74
85
|
raise Exception, 'libhoney: Ruby versions < 2.2 are not supported'
|
75
86
|
end
|
76
87
|
|
@@ -81,17 +92,6 @@ module Libhoney
|
|
81
92
|
@builder.sample_rate = sample_rate
|
82
93
|
@builder.api_host = api_host
|
83
94
|
|
84
|
-
@transmission = transmission
|
85
|
-
if !@transmission && !(writekey && dataset)
|
86
|
-
# if no writekey or dataset are configured, and we didn't override the
|
87
|
-
# transmission (e.g. to a MockTransmissionClient), that's almost
|
88
|
-
# certainly a misconfiguration, even though it's possible to override
|
89
|
-
# them on a per-event basis. So let's handle the misconfiguration
|
90
|
-
# early rather than potentially throwing thousands of exceptions at runtime.
|
91
|
-
warn "#{self.class.name}: no #{writekey ? 'dataset' : 'writekey'} configured, disabling sending events"
|
92
|
-
@transmission = NullTransmissionClient.new
|
93
|
-
end
|
94
|
-
|
95
95
|
@user_agent_addition = user_agent_addition
|
96
96
|
|
97
97
|
@block_on_send = block_on_send
|
@@ -101,8 +101,8 @@ module Libhoney
|
|
101
101
|
@max_concurrent_batches = max_concurrent_batches
|
102
102
|
@pending_work_capacity = pending_work_capacity
|
103
103
|
@responses = SizedQueue.new(2 * @pending_work_capacity)
|
104
|
-
@
|
105
|
-
@
|
104
|
+
@proxy_config = parse_proxy_config(proxy_config)
|
105
|
+
@transmission = setup_transmission(transmission, writekey, dataset)
|
106
106
|
end
|
107
107
|
|
108
108
|
attr_reader :block_on_send, :block_on_responses, :max_batch_size,
|
@@ -190,22 +190,6 @@ module Libhoney
|
|
190
190
|
# @param event [Event] the event to send to honeycomb
|
191
191
|
# @api private
|
192
192
|
def send_event(event)
|
193
|
-
@lock.synchronize do
|
194
|
-
transmission_client_params = {
|
195
|
-
max_batch_size: @max_batch_size,
|
196
|
-
send_frequency: @send_frequency,
|
197
|
-
max_concurrent_batches: @max_concurrent_batches,
|
198
|
-
pending_work_capacity: @pending_work_capacity,
|
199
|
-
responses: @responses,
|
200
|
-
block_on_send: @block_on_send,
|
201
|
-
block_on_responses: @block_on_responses,
|
202
|
-
user_agent_addition: @user_agent_addition,
|
203
|
-
proxy_config: @proxy_config
|
204
|
-
}
|
205
|
-
|
206
|
-
@transmission ||= TransmissionClient.new(**transmission_client_params)
|
207
|
-
end
|
208
|
-
|
209
193
|
@transmission.add(event)
|
210
194
|
end
|
211
195
|
|
@@ -224,5 +208,86 @@ module Libhoney
|
|
224
208
|
def should_drop(sample_rate)
|
225
209
|
rand(1..sample_rate) != 1
|
226
210
|
end
|
211
|
+
|
212
|
+
private
|
213
|
+
|
214
|
+
##
|
215
|
+
# Parameters to pass to a transmission based on client config.
|
216
|
+
#
|
217
|
+
def transmission_client_params
|
218
|
+
{
|
219
|
+
max_batch_size: @max_batch_size,
|
220
|
+
send_frequency: @send_frequency,
|
221
|
+
max_concurrent_batches: @max_concurrent_batches,
|
222
|
+
pending_work_capacity: @pending_work_capacity,
|
223
|
+
responses: @responses,
|
224
|
+
block_on_send: @block_on_send,
|
225
|
+
block_on_responses: @block_on_responses,
|
226
|
+
user_agent_addition: @user_agent_addition,
|
227
|
+
proxy_config: @proxy_config
|
228
|
+
}
|
229
|
+
end
|
230
|
+
|
231
|
+
def setup_transmission(transmission, writekey, dataset)
|
232
|
+
# if a provided transmission can add and close, we'll assume the user
|
233
|
+
# has provided a working transmission with a customized configuration
|
234
|
+
return transmission if quacks_like_a_transmission?(transmission)
|
235
|
+
|
236
|
+
if !(writekey && dataset) # rubocop:disable Style/NegatedIf
|
237
|
+
# if no writekey or dataset are configured, and we didn't override the
|
238
|
+
# transmission (e.g. to a MockTransmissionClient), that's almost
|
239
|
+
# certainly a misconfiguration, even though it's possible to override
|
240
|
+
# them on a per-event basis. So let's handle the misconfiguration
|
241
|
+
# early rather than potentially throwing thousands of exceptions at runtime.
|
242
|
+
warn "#{self.class.name}: no #{writekey ? 'dataset' : 'writekey'} configured, disabling sending events"
|
243
|
+
return NullTransmissionClient.new
|
244
|
+
end
|
245
|
+
|
246
|
+
case transmission
|
247
|
+
when NilClass # the default value for new clients
|
248
|
+
return TransmissionClient.new(**transmission_client_params)
|
249
|
+
when Class
|
250
|
+
# if a class has been provided, attempt to instantiate it with parameters given to the client
|
251
|
+
t = transmission.new(**transmission_client_params)
|
252
|
+
if quacks_like_a_transmission?(t) # rubocop:disable Style/GuardClause
|
253
|
+
return t
|
254
|
+
else
|
255
|
+
warn "#{t.class.name}: does not appear to behave like a transmission, disabling sending events"
|
256
|
+
return NullTransmissionClient.new
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def quacks_like_a_transmission?(transmission)
|
262
|
+
transmission.respond_to?(:add) && transmission.respond_to?(:close)
|
263
|
+
end
|
264
|
+
|
265
|
+
# @api private
|
266
|
+
def parse_proxy_config(config)
|
267
|
+
case config
|
268
|
+
when nil then nil
|
269
|
+
when String
|
270
|
+
URI.parse(config)
|
271
|
+
when Array
|
272
|
+
warn <<-WARNING
|
273
|
+
DEPRECATION WARNING: #{self.class.name} the proxy_config parameter will require a String value, not an Array in libhoney 2.0.
|
274
|
+
To resolve:
|
275
|
+
+ recommended: set http/https_proxy environment variables, which take precedence over any option set here, then remove proxy_config parameter from client initialization
|
276
|
+
+ set proxy_config to a String containing the forwarding proxy URI (only used if http/https_proxy are not set)
|
277
|
+
WARNING
|
278
|
+
host, port, user, password = config
|
279
|
+
|
280
|
+
parsed_config = URI::HTTP.build(host: host, port: port).tap do |uri|
|
281
|
+
uri.userinfo = "#{user}:#{password}" if user
|
282
|
+
end
|
283
|
+
redacted_config = parsed_config.dup.tap do |uri|
|
284
|
+
uri.password = 'REDACTED' unless uri.password.nil? || uri.password.empty?
|
285
|
+
end
|
286
|
+
warn "The array config given has been assumed to mean: #{redacted_config}"
|
287
|
+
parsed_config
|
288
|
+
end
|
289
|
+
rescue URI::Error => e
|
290
|
+
warn "#{self.class.name}: unable to parse proxy_config. Detail: #{e.class}: #{e.message}"
|
291
|
+
end
|
227
292
|
end
|
228
293
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'libhoney/queueing'
|
2
|
+
require 'libhoney/transmission'
|
3
|
+
|
4
|
+
module Libhoney
|
5
|
+
##
|
6
|
+
# An experimental variant of the standard {TransmissionClient} that uses
|
7
|
+
# a custom implementation of a sized queue whose pop/push methods support
|
8
|
+
# a timeout internally.
|
9
|
+
#
|
10
|
+
# @example Use this transmission with the Ruby Beeline
|
11
|
+
# require 'libhoney/experimental_transmission'
|
12
|
+
#
|
13
|
+
# Honeycomb.configure do |config|
|
14
|
+
# config.client = Libhoney::Client.new(
|
15
|
+
# writekey: ENV["HONEYCOMB_WRITE_KEY"],
|
16
|
+
# dataset: ENV.fetch("HONEYCOMB_DATASET", "awesome_sauce"),
|
17
|
+
# transmission: Libhoney::ExperimentalTransmissionClient
|
18
|
+
# )
|
19
|
+
# ...
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
#
|
24
|
+
class ExperimentalTransmissionClient < TransmissionClient
|
25
|
+
def add(event)
|
26
|
+
return unless event_valid(event)
|
27
|
+
|
28
|
+
begin
|
29
|
+
# if block_on_send is true, never timeout the wait to enqueue an event
|
30
|
+
# otherwise, timeout the wait immediately and if the queue is full, we'll
|
31
|
+
# have a ThreadError raised because we could not add to the queue.
|
32
|
+
timeout = @block_on_send ? :never : 0
|
33
|
+
@batch_queue.enq(event, timeout)
|
34
|
+
rescue Libhoney::Queueing::SizedQueueWithTimeout::PushTimedOut
|
35
|
+
# happens if the queue was full and block_on_send = false.
|
36
|
+
warn "#{self.class.name}: batch queue full, dropping event." if %w[debug trace].include?(ENV['LOG_LEVEL'])
|
37
|
+
end
|
38
|
+
|
39
|
+
ensure_threads_running
|
40
|
+
end
|
41
|
+
|
42
|
+
def batch_loop
|
43
|
+
next_send_time = Time.now + @send_frequency
|
44
|
+
batched_events = Hash.new do |h, key|
|
45
|
+
h[key] = []
|
46
|
+
end
|
47
|
+
|
48
|
+
loop do
|
49
|
+
begin
|
50
|
+
# an event on the batch_queue
|
51
|
+
# 1. pops out and is truthy
|
52
|
+
# 2. gets included in the current batch
|
53
|
+
# 3. while waits for another event
|
54
|
+
while (event = @batch_queue.pop(@send_frequency))
|
55
|
+
key = [event.api_host, event.writekey, event.dataset]
|
56
|
+
batched_events[key] << event
|
57
|
+
end
|
58
|
+
|
59
|
+
# a nil on the batch_queue
|
60
|
+
# 1. pops out and is falsy
|
61
|
+
# 2. ends the event-popping while do..end
|
62
|
+
# 3. breaks the loop
|
63
|
+
# 4. flushes the current batch
|
64
|
+
# 5. ends the batch_loop
|
65
|
+
break
|
66
|
+
|
67
|
+
# a timeout expiration waiting for an event
|
68
|
+
# 1. skips the break and is rescued
|
69
|
+
# 2. triggers the ensure to flush the current batch
|
70
|
+
# 3. begins the loop again with an updated next_send_time
|
71
|
+
rescue Libhoney::Queueing::SizedQueueWithTimeout::PopTimedOut => e
|
72
|
+
warn "#{self.class.name}: ⏱ " + e.message if %w[trace].include?(ENV['LOG_LEVEL'])
|
73
|
+
|
74
|
+
# any exception occurring in this loop should not take down the actual
|
75
|
+
# instrumented Ruby process, so handle here and log that there is trouble
|
76
|
+
rescue Exception => e
|
77
|
+
warn "#{self.class.name}: 💥 " + e.message if %w[debug trace].include?(ENV['LOG_LEVEL'])
|
78
|
+
warn e.backtrace.join("\n").to_s if ['trace'].include?(ENV['LOG_LEVEL'])
|
79
|
+
|
80
|
+
# regardless of the exception, figure out whether enough time has passed to
|
81
|
+
# send the current batched events, if so, send them and figure out the next send time
|
82
|
+
# before going back to the top of the loop
|
83
|
+
ensure
|
84
|
+
next_send_time = flush_batched_events(batched_events) if Time.now > next_send_time
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# don't need to capture the next_send_time here because the batch_loop is exiting
|
89
|
+
# for some reason (probably transmission.close)
|
90
|
+
flush_batched_events(batched_events)
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def setup_batch_queue
|
96
|
+
# override super()'s @batch_queue = SizedQueue.new(); use our SizedQueueWithTimeout:
|
97
|
+
# + block on adding events to the batch_queue when queue is full and @block_on_send is true
|
98
|
+
# + the queue knows how to limit size and how to time-out pushes and pops
|
99
|
+
@batch_queue = Libhoney::Queueing::SizedQueueWithTimeout.new(@pending_work_capacity)
|
100
|
+
warn "⚠️🐆 #{self.class.name} in use! It may drop data, consume all your memory, or cause skin irritation."
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_user_agent(user_agent_addition)
|
104
|
+
ua = "libhoney-rb/#{VERSION} (exp-transmission)"
|
105
|
+
ua << " #{user_agent_addition}" if user_agent_addition
|
106
|
+
ua
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'libhoney/queueing/sized_queue_with_timeout'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2021 Honeycomb
|
2
|
+
Copyright (c) 2013 Avdi Grimm
|
3
|
+
|
4
|
+
MIT License
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,176 @@
|
|
1
|
+
##
|
2
|
+
# SizedQueueWithTimeout is copyright and licensed per the LICENSE.txt in
|
3
|
+
# its containing subdirectory of this codebase.
|
4
|
+
#
|
5
|
+
module Libhoney
|
6
|
+
module Queueing
|
7
|
+
##
|
8
|
+
# A queue implementation with optional size limit and optional timeouts on pop and push
|
9
|
+
# operations. Heavily influenced / liberally mimicking Avdi Grimm's
|
10
|
+
# {Tapas::Queue}[https://github.com/avdi/tapas-queue].
|
11
|
+
#
|
12
|
+
class SizedQueueWithTimeout
|
13
|
+
class PushTimedOut < ThreadError; end
|
14
|
+
class PopTimedOut < ThreadError; end
|
15
|
+
|
16
|
+
##
|
17
|
+
# @param max_size [Integer, Float::INFINITY] the size limit for this queue
|
18
|
+
# @param options [Hash] optional dependencies to inject, primarily for testing
|
19
|
+
# @option options [QLock, Mutex] :lock the lock for synchronizing queue state change
|
20
|
+
# @option options [QCondition] :space_available_condition the condition variable
|
21
|
+
# to wait/signal on for space being available in the queue; when provided, must
|
22
|
+
# be accompanied by an +:item_available_condition+ and the shared +:lock+
|
23
|
+
# @option options [QCondition] :item_available_condition the condition variable
|
24
|
+
# to wait/signal on for an item being added to the queue; when provided, must
|
25
|
+
# be accompanied by an +:space_available_condition+ and the shared +:lock+
|
26
|
+
def initialize(max_size = Float::INFINITY, options = {})
|
27
|
+
@items = []
|
28
|
+
@max_size = max_size
|
29
|
+
@lock = options.fetch(:lock) { QLock.new }
|
30
|
+
@space_available = options.fetch(:space_available_condition) { QCondition.new(@lock) }
|
31
|
+
@item_available = options.fetch(:item_available_condition) { QCondition.new(@lock) }
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Push something onto the queue.
|
36
|
+
#
|
37
|
+
# @param obj [Object] the thing to add to the queue
|
38
|
+
# @param timeout [Numeric, :never] how long in seconds to wait for the queue to have space available or
|
39
|
+
# +:never+ to wait "forever"
|
40
|
+
# @param timeout_policy [#call] defaults to +-> { raise PushTimedOut }+ - a lambda/Proc/callable, what to do
|
41
|
+
# when the timeout expires
|
42
|
+
#
|
43
|
+
# @raise {PushTimedOut}
|
44
|
+
def push(obj, timeout = :never, &timeout_policy)
|
45
|
+
timeout_policy ||= -> { raise PushTimedOut }
|
46
|
+
|
47
|
+
wait_for_condition(@space_available, -> { !full? }, timeout, timeout_policy) do
|
48
|
+
@items.push(obj)
|
49
|
+
@item_available.signal
|
50
|
+
end
|
51
|
+
end
|
52
|
+
alias enq push
|
53
|
+
alias << push
|
54
|
+
|
55
|
+
##
|
56
|
+
# Pop something off the queue.
|
57
|
+
#
|
58
|
+
# @param timeout [Numeric, :never] how long in seconds to wait for the queue to have an item available or
|
59
|
+
# +:never+ to wait "forever"
|
60
|
+
# @param timeout_policy [#call] defaults to +-> { raise PopTimedOut }+ - a lambda/Proc/callable, what to do
|
61
|
+
# when the timeout expires
|
62
|
+
#
|
63
|
+
# @return [Object]
|
64
|
+
# @raise {PopTimedOut}
|
65
|
+
def pop(timeout = :never, &timeout_policy)
|
66
|
+
timeout_policy ||= -> { raise PopTimedOut }
|
67
|
+
|
68
|
+
wait_for_condition(@item_available, -> { !empty? }, timeout, timeout_policy) do
|
69
|
+
item = @items.shift
|
70
|
+
@space_available.signal unless full?
|
71
|
+
item
|
72
|
+
end
|
73
|
+
end
|
74
|
+
alias deq pop
|
75
|
+
alias shift pop
|
76
|
+
|
77
|
+
##
|
78
|
+
# Removes all objects from the queue. They are cast into the abyss never to be seen again.
|
79
|
+
#
|
80
|
+
def clear
|
81
|
+
@lock.synchronize do
|
82
|
+
@items = []
|
83
|
+
@space_available.signal unless full?
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
##
|
90
|
+
# Whether the queue is at capacity. Must be called with the queue's lock
|
91
|
+
# or the answer won't matter if you try to change state based on it.
|
92
|
+
#
|
93
|
+
# @return [true/false]
|
94
|
+
# @api private
|
95
|
+
def full?
|
96
|
+
@max_size <= @items.size
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Whether the queue is empty. Must be called with the queue's lock or the
|
101
|
+
# answer won't matter if you try to change state based on it.
|
102
|
+
#
|
103
|
+
# @return [true/false]
|
104
|
+
# @api private
|
105
|
+
def empty?
|
106
|
+
@items.empty?
|
107
|
+
end
|
108
|
+
|
109
|
+
# a generic conditional variable wait with a timeout loop
|
110
|
+
#
|
111
|
+
# @param condition [#wait] a condition variable to wait upon.
|
112
|
+
# @param condition_predicate [#call] a callable (i.e. lambda or proc) that returns true/false to act
|
113
|
+
# as a state tester (i.e. "is the queue currently empty?") to check on whether to keep waiting;
|
114
|
+
# used to handle spurious wake ups occurring before the timeout has elapsed
|
115
|
+
# @param timeout [:never, Numeric] the amount of time in (seconds?) to wait, or :never to wait forever
|
116
|
+
# @param timeout_policy [#call] a callable, what to do when a timeout occurs? Return a default? Raise an
|
117
|
+
# exception? You decide.
|
118
|
+
def wait_for_condition(condition, condition_predicate, timeout = :never, timeout_policy = -> { nil })
|
119
|
+
deadline = timeout == :never ? :never : trustworthy_current_time + timeout
|
120
|
+
@lock.synchronize do
|
121
|
+
loop do
|
122
|
+
time_remaining = timeout == :never ? nil : deadline - trustworthy_current_time
|
123
|
+
|
124
|
+
if !condition_predicate.call && time_remaining.to_f >= 0 # rubocop:disable Style/IfUnlessModifier
|
125
|
+
condition.wait(time_remaining)
|
126
|
+
end
|
127
|
+
|
128
|
+
if condition_predicate.call # rubocop:disable Style/GuardClause
|
129
|
+
return yield
|
130
|
+
elsif deadline == :never || deadline > trustworthy_current_time
|
131
|
+
next
|
132
|
+
else
|
133
|
+
return timeout_policy.call
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Within the context of the current process, return time from a
|
140
|
+
# monotonically increasing clock because for timeouts we care about
|
141
|
+
# elapsed time within the process, not human time.
|
142
|
+
#
|
143
|
+
# @return [Numeric]
|
144
|
+
def trustworthy_current_time
|
145
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class QCondition
|
150
|
+
def initialize(lock)
|
151
|
+
@lock = lock
|
152
|
+
@cv = ConditionVariable.new
|
153
|
+
end
|
154
|
+
|
155
|
+
def wait(timeout = nil)
|
156
|
+
@cv.wait(@lock.mutex, timeout)
|
157
|
+
end
|
158
|
+
|
159
|
+
def signal
|
160
|
+
@cv.signal
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class QLock
|
165
|
+
attr_reader :mutex
|
166
|
+
|
167
|
+
def initialize
|
168
|
+
@mutex = Mutex.new
|
169
|
+
end
|
170
|
+
|
171
|
+
def synchronize(&block)
|
172
|
+
@mutex.synchronize(&block)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
data/lib/libhoney/response.rb
CHANGED
@@ -1,7 +1,15 @@
|
|
1
|
-
require 'http'
|
1
|
+
require 'http/response/status'
|
2
2
|
|
3
3
|
module Libhoney
|
4
4
|
class Response
|
5
|
+
# The response status from HTTP calls to a Honeycomb API endpoint.
|
6
|
+
#
|
7
|
+
# For most of the life of this client, this response object has been
|
8
|
+
# a pass-through to the underlying HTTP library's response object.
|
9
|
+
# This class in the Libhoney namespace now owns the interface for
|
10
|
+
# API responses.
|
11
|
+
class Status < HTTP::Response::Status; end
|
12
|
+
|
5
13
|
attr_accessor :duration, :status_code, :metadata, :error
|
6
14
|
|
7
15
|
def initialize(duration: 0,
|
@@ -9,7 +17,7 @@ module Libhoney
|
|
9
17
|
metadata: nil,
|
10
18
|
error: nil)
|
11
19
|
@duration = duration
|
12
|
-
@status_code =
|
20
|
+
@status_code = Status.new(status_code)
|
13
21
|
@metadata = metadata
|
14
22
|
@error = error
|
15
23
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
require 'excon'
|
1
3
|
require 'json'
|
2
4
|
require 'timeout'
|
3
5
|
require 'libhoney/response'
|
@@ -34,9 +36,9 @@ module Libhoney
|
|
34
36
|
@send_queue = Queue.new
|
35
37
|
@threads = []
|
36
38
|
@lock = Mutex.new
|
37
|
-
# use a SizedQueue so the producer will block on adding to the batch_queue when @block_on_send is true
|
38
|
-
@batch_queue = SizedQueue.new(@pending_work_capacity)
|
39
39
|
@batch_thread = nil
|
40
|
+
|
41
|
+
setup_batch_queue
|
40
42
|
end
|
41
43
|
|
42
44
|
def add(event)
|
@@ -51,26 +53,6 @@ module Libhoney
|
|
51
53
|
ensure_threads_running
|
52
54
|
end
|
53
55
|
|
54
|
-
def event_valid(event)
|
55
|
-
invalid = []
|
56
|
-
invalid.push('api host') if event.api_host.nil? || event.api_host.empty?
|
57
|
-
invalid.push('write key') if event.writekey.nil? || event.writekey.empty?
|
58
|
-
invalid.push('dataset') if event.dataset.nil? || event.dataset.empty?
|
59
|
-
|
60
|
-
unless invalid.empty?
|
61
|
-
e = StandardError.new("#{self.class.name}: nil or empty required fields (#{invalid.join(', ')})"\
|
62
|
-
'. Will not attempt to send.')
|
63
|
-
Response.new(error: e).tap do |error_response|
|
64
|
-
error_response.metadata = event.metadata
|
65
|
-
enqueue_response(error_response)
|
66
|
-
end
|
67
|
-
|
68
|
-
return false
|
69
|
-
end
|
70
|
-
|
71
|
-
true
|
72
|
-
end
|
73
|
-
|
74
56
|
def send_loop
|
75
57
|
http_clients = build_http_clients
|
76
58
|
|
@@ -93,7 +75,7 @@ module Libhoney
|
|
93
75
|
}
|
94
76
|
|
95
77
|
response = http.post(
|
96
|
-
"/1/batch/#{Addressable::URI.escape(dataset)}",
|
78
|
+
path: "/1/batch/#{Addressable::URI.escape(dataset)}",
|
97
79
|
body: body,
|
98
80
|
headers: headers
|
99
81
|
)
|
@@ -103,6 +85,8 @@ module Libhoney
|
|
103
85
|
# because this is effectively the top-level exception handler for the
|
104
86
|
# sender threads, and we don't want those threads to die (leaving
|
105
87
|
# nothing consuming the queue).
|
88
|
+
warn "#{self.class.name}: 💥 " + e.message if %w[debug trace].include?(ENV['LOG_LEVEL'])
|
89
|
+
warn e.backtrace.join("\n").to_s if ['trace'].include?(ENV['LOG_LEVEL'])
|
106
90
|
begin
|
107
91
|
batch.each do |event|
|
108
92
|
# nil events in the batch should already have had an error
|
@@ -129,23 +113,31 @@ module Libhoney
|
|
129
113
|
end
|
130
114
|
|
131
115
|
def close(drain)
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
116
|
+
@lock.synchronize do
|
117
|
+
# if drain is false, clear the remaining unprocessed events from the queue
|
118
|
+
if drain
|
119
|
+
warn "#{self.class.name} - close: draining events" if %w[debug trace].include?(ENV['LOG_LEVEL'])
|
120
|
+
else
|
121
|
+
warn "#{self.class.name} - close: deleting unsent events" if %w[debug trace].include?(ENV['LOG_LEVEL'])
|
122
|
+
@batch_queue.clear
|
123
|
+
@send_queue.clear
|
124
|
+
end
|
137
125
|
|
138
|
-
|
139
|
-
|
126
|
+
@batch_queue.enq(nil)
|
127
|
+
if @batch_thread.nil?
|
128
|
+
else
|
129
|
+
@batch_thread.join(1.0) # limit the amount of time we'll wait for the thread to end
|
130
|
+
end
|
140
131
|
|
141
|
-
|
142
|
-
|
132
|
+
# send @threads.length number of nils so each thread will fall out of send_loop
|
133
|
+
@threads.length.times { @send_queue << nil }
|
143
134
|
|
144
|
-
|
145
|
-
|
135
|
+
@threads.each(&:join)
|
136
|
+
@threads = []
|
137
|
+
end
|
146
138
|
|
147
139
|
enqueue_response(nil)
|
148
|
-
|
140
|
+
warn "#{self.class.name} - close: close complete" if %w[debug trace].include?(ENV['LOG_LEVEL'])
|
149
141
|
0
|
150
142
|
end
|
151
143
|
|
@@ -157,25 +149,76 @@ module Libhoney
|
|
157
149
|
|
158
150
|
loop do
|
159
151
|
begin
|
152
|
+
# a timeout expiration waiting for an event
|
153
|
+
# 1. interrupts only when thread is in a blocking state (waiting for pop)
|
154
|
+
# 2. exception skips the break and is rescued
|
155
|
+
# 3. triggers the ensure to flush the current batch
|
156
|
+
# 3. begins the loop again with an updated next_send_time
|
160
157
|
Thread.handle_interrupt(Timeout::Error => :on_blocking) do
|
158
|
+
# an event on the batch_queue
|
159
|
+
# 1. pops out and is truthy
|
160
|
+
# 2. gets included in the current batch
|
161
|
+
# 3. while waits for another event
|
161
162
|
while (event = Timeout.timeout(@send_frequency) { @batch_queue.pop })
|
162
163
|
key = [event.api_host, event.writekey, event.dataset]
|
163
164
|
batched_events[key] << event
|
164
165
|
end
|
165
166
|
end
|
166
167
|
|
168
|
+
# a nil on the batch_queue
|
169
|
+
# 1. pops out and is falsy
|
170
|
+
# 2. ends the event-popping while do..end
|
171
|
+
# 3. breaks the loop
|
172
|
+
# 4. flushes the current batch
|
173
|
+
# 5. ends the batch_loop
|
167
174
|
break
|
168
|
-
rescue Exception
|
175
|
+
rescue Exception => e
|
176
|
+
warn "#{self.class.name}: 💥 " + e.message if %w[debug trace].include?(ENV['LOG_LEVEL'])
|
177
|
+
warn e.backtrace.join("\n").to_s if ['trace'].include?(ENV['LOG_LEVEL'])
|
178
|
+
|
179
|
+
# regardless of the exception, figure out whether enough time has passed to
|
180
|
+
# send the current batched events, if so, send them and figure out the next send time
|
181
|
+
# before going back to the top of the loop
|
169
182
|
ensure
|
170
183
|
next_send_time = flush_batched_events(batched_events) if Time.now > next_send_time
|
171
184
|
end
|
172
185
|
end
|
173
186
|
|
187
|
+
# don't need to capture the next_send_time here because the batch_loop is exiting
|
188
|
+
# for some reason (probably transmission.close)
|
174
189
|
flush_batched_events(batched_events)
|
175
190
|
end
|
176
191
|
|
177
192
|
private
|
178
193
|
|
194
|
+
def setup_batch_queue
|
195
|
+
# use a SizedQueue so the producer will block on adding to the batch_queue when @block_on_send is true
|
196
|
+
@batch_queue = SizedQueue.new(@pending_work_capacity)
|
197
|
+
end
|
198
|
+
|
199
|
+
REQUIRED_EVENT_FIELDS = %i[api_host writekey dataset].freeze
|
200
|
+
|
201
|
+
def event_valid(event)
|
202
|
+
missing_required_fields = REQUIRED_EVENT_FIELDS.select do |required_field|
|
203
|
+
event.public_send(required_field).nil? || event.public_send(required_field).empty?
|
204
|
+
end
|
205
|
+
|
206
|
+
if missing_required_fields.empty?
|
207
|
+
true
|
208
|
+
else
|
209
|
+
enqueue_response(
|
210
|
+
Response.new(
|
211
|
+
metadata: event.metadata,
|
212
|
+
error: StandardError.new(
|
213
|
+
"#{self.class.name}: nil or empty required fields (#{missing_required_fields.join(', ')})"\
|
214
|
+
'. Will not attempt to send.'
|
215
|
+
)
|
216
|
+
)
|
217
|
+
)
|
218
|
+
false
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
179
222
|
##
|
180
223
|
# Enqueues a response to the responses queue suppressing ThreadError when
|
181
224
|
# there is no space left on the queue and we are not blocking on response
|
@@ -186,15 +229,36 @@ module Libhoney
|
|
186
229
|
end
|
187
230
|
|
188
231
|
def process_response(http_response, before, batch)
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
232
|
+
if http_response.status == 200
|
233
|
+
index = 0
|
234
|
+
JSON.parse(http_response.body).each do |event|
|
235
|
+
index += 1 while batch[index].nil? && index < batch.size
|
236
|
+
break unless (batched_event = batch[index])
|
237
|
+
|
238
|
+
enqueue_response(
|
239
|
+
Response.new(
|
240
|
+
status_code: event['status'],
|
241
|
+
duration: (Time.now - before),
|
242
|
+
metadata: batched_event.metadata
|
243
|
+
)
|
244
|
+
)
|
245
|
+
end
|
246
|
+
else
|
247
|
+
error = JSON.parse(http_response.body)['error']
|
248
|
+
if %w[debug trace].include?(ENV['LOG_LEVEL'])
|
249
|
+
warn "#{self.class.name}: error sending data to Honeycomb - #{http_response.status} #{error}"
|
250
|
+
end
|
251
|
+
batch.each do |batched_event|
|
252
|
+
next unless batched_event # skip nils enqueued from serialization errors
|
253
|
+
|
254
|
+
enqueue_response(
|
255
|
+
Response.new(
|
256
|
+
status_code: http_response.status, # single error from API applied to all events sent in batch
|
257
|
+
duration: (Time.now - before),
|
258
|
+
metadata: batched_event.metadata,
|
259
|
+
error: RuntimeError.new(error)
|
260
|
+
)
|
261
|
+
)
|
198
262
|
end
|
199
263
|
end
|
200
264
|
end
|
@@ -256,14 +320,19 @@ module Libhoney
|
|
256
320
|
|
257
321
|
def build_http_clients
|
258
322
|
Hash.new do |h, api_host|
|
259
|
-
client =
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
323
|
+
client = ::Excon.new(
|
324
|
+
api_host,
|
325
|
+
persistent: true,
|
326
|
+
read_timeout: @send_timeout,
|
327
|
+
write_timeout: @send_timeout,
|
328
|
+
connect_timeout: @send_timeout,
|
329
|
+
proxy: @proxy_config,
|
330
|
+
headers: {
|
331
|
+
'User-Agent' => @user_agent,
|
332
|
+
'Content-Type' => 'application/json'
|
333
|
+
}
|
334
|
+
)
|
335
|
+
|
267
336
|
h[api_host] = client
|
268
337
|
end
|
269
338
|
end
|
data/lib/libhoney/version.rb
CHANGED
data/libhoney.gemspec
CHANGED
@@ -24,7 +24,9 @@ Gem::Specification.new do |spec|
|
|
24
24
|
|
25
25
|
spec.add_development_dependency 'bump', '~> 0.5'
|
26
26
|
spec.add_development_dependency 'bundler'
|
27
|
+
spec.add_development_dependency 'lockstep'
|
27
28
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
29
|
+
spec.add_development_dependency 'minitest-reporters'
|
28
30
|
spec.add_development_dependency 'rake', '~> 12.3'
|
29
31
|
spec.add_development_dependency 'rubocop', '< 0.69'
|
30
32
|
spec.add_development_dependency 'sinatra'
|
@@ -34,5 +36,6 @@ Gem::Specification.new do |spec|
|
|
34
36
|
spec.add_development_dependency 'yard'
|
35
37
|
spec.add_development_dependency 'yardstick', '~> 0.9'
|
36
38
|
spec.add_dependency 'addressable', '~> 2.0'
|
39
|
+
spec.add_dependency 'excon'
|
37
40
|
spec.add_dependency 'http', '>= 2.0', '< 5.0'
|
38
41
|
end
|
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.19.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:
|
11
|
+
date: 2021-07-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bump
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: lockstep
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: minitest
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +66,20 @@ dependencies:
|
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '5.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest-reporters
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: rake
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -178,6 +206,20 @@ dependencies:
|
|
178
206
|
- - "~>"
|
179
207
|
- !ruby/object:Gem::Version
|
180
208
|
version: '2.0'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: excon
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - ">="
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '0'
|
216
|
+
type: :runtime
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
181
223
|
- !ruby/object:Gem::Dependency
|
182
224
|
name: http
|
183
225
|
requirement: !ruby/object:Gem::Requirement
|
@@ -208,6 +250,9 @@ files:
|
|
208
250
|
- ".circleci/setup-rubygems.sh"
|
209
251
|
- ".editorconfig"
|
210
252
|
- ".github/CODEOWNERS"
|
253
|
+
- ".github/dependabot.yml"
|
254
|
+
- ".github/workflows/add-to-project.yml"
|
255
|
+
- ".github/workflows/apply-labels.yml"
|
211
256
|
- ".gitignore"
|
212
257
|
- ".rubocop.yml"
|
213
258
|
- ".rubocop_todo.yml"
|
@@ -224,11 +269,15 @@ files:
|
|
224
269
|
- lib/libhoney/cleaner.rb
|
225
270
|
- lib/libhoney/client.rb
|
226
271
|
- lib/libhoney/event.rb
|
272
|
+
- lib/libhoney/experimental_transmission.rb
|
227
273
|
- lib/libhoney/log_client.rb
|
228
274
|
- lib/libhoney/log_transmission.rb
|
229
275
|
- lib/libhoney/mock_transmission.rb
|
230
276
|
- lib/libhoney/null_client.rb
|
231
277
|
- lib/libhoney/null_transmission.rb
|
278
|
+
- lib/libhoney/queueing.rb
|
279
|
+
- lib/libhoney/queueing/LICENSE.txt
|
280
|
+
- lib/libhoney/queueing/sized_queue_with_timeout.rb
|
232
281
|
- lib/libhoney/response.rb
|
233
282
|
- lib/libhoney/test_client.rb
|
234
283
|
- lib/libhoney/transmission.rb
|
@@ -253,7 +302,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
253
302
|
- !ruby/object:Gem::Version
|
254
303
|
version: '0'
|
255
304
|
requirements: []
|
256
|
-
rubygems_version: 3.1.
|
305
|
+
rubygems_version: 3.1.6
|
257
306
|
signing_key:
|
258
307
|
specification_version: 4
|
259
308
|
summary: send data to Honeycomb
|