analytics-ruby 2.2.5 → 2.2.6.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +12 -0
- data/Rakefile +11 -1
- data/analytics-ruby.gemspec +4 -0
- data/lib/segment/analytics/logging.rb +34 -7
- data/lib/segment/analytics/message_batch.rb +9 -2
- data/lib/segment/analytics/request.rb +5 -5
- data/lib/segment/analytics/version.rb +1 -1
- data/lib/segment/analytics/worker.rb +4 -6
- data/spec/isolated/json_example.rb +9 -0
- data/spec/isolated/with_active_support.rb +11 -0
- data/spec/isolated/with_active_support_and_oj.rb +16 -0
- data/spec/isolated/with_oj.rb +13 -0
- data/spec/segment/analytics/e2e_spec.rb +6 -0
- data/spec/segment/analytics/message_batch_spec.rb +9 -8
- metadata +23 -7
- data/lib/segment/analytics/message.rb +0 -26
- data/spec/segment/analytics/message_spec.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16a399d60ac370a68cbb28f5887da70711403133e683d7acfe5db4b6eb8fb78d
|
4
|
+
data.tar.gz: 02c96de2188fb9b79b4d567c36a61ddf2ce0b62610adb6848a777ad327e47535
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69d989c8e9483dea80c707ddd4ca456d0d7891cd8b5624056a2131bd006dca153b00e46a22c582e26b0ad94978bd6beca4036a13b9921b1511b4d3ffb76b1339
|
7
|
+
data.tar.gz: 2c9a86a62efe64cbe7baea4faa9b605e32394b207eac8bd277bf1b3ca420aea6f9e34c7da4d476996f38907d1ca7a0d9e4d3abf2bd5088bd397d94a1db6641cc
|
data/History.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
2.2.6.pre / 2018-06-27
|
2
|
+
==================
|
3
|
+
|
4
|
+
* [Fix](https://github.com/segmentio/analytics-ruby/pull/168): Revert 'reuse
|
5
|
+
TCP connections' to fix EMFILE errors
|
6
|
+
* [Fix](https://github.com/segmentio/analytics-ruby/pull/166): Fix oj/rails
|
7
|
+
conflict
|
8
|
+
* [Fix](https://github.com/segmentio/analytics-ruby/pull/162): Add missing
|
9
|
+
'Forwardable' requirement
|
10
|
+
* [Improvement](https://github.com/segmentio/analytics-ruby/pull/163): Better
|
11
|
+
logging
|
12
|
+
|
1
13
|
2.2.5 / 2018-05-01
|
2
14
|
==================
|
3
15
|
|
data/Rakefile
CHANGED
@@ -3,12 +3,22 @@ require 'rspec/core/rake_task'
|
|
3
3
|
default_tasks = []
|
4
4
|
|
5
5
|
RSpec::Core::RakeTask.new(:spec) do |spec|
|
6
|
-
spec.pattern = 'spec/**/*_spec.rb'
|
6
|
+
spec.pattern = 'spec/segment/**/*_spec.rb'
|
7
7
|
spec.rspec_opts = "--tag ~e2e" if ENV["RUN_E2E_TESTS"] != "true"
|
8
8
|
end
|
9
9
|
|
10
10
|
default_tasks << :spec
|
11
11
|
|
12
|
+
# Isolated tests are run as separate rake tasks so that gem conflicts can be
|
13
|
+
# tests in different processes
|
14
|
+
Dir.glob('spec/isolated/**/*.rb').each do |isolated_test_path|
|
15
|
+
RSpec::Core::RakeTask.new(isolated_test_path) do |spec|
|
16
|
+
spec.pattern = isolated_test_path
|
17
|
+
end
|
18
|
+
|
19
|
+
default_tasks << isolated_test_path
|
20
|
+
end
|
21
|
+
|
12
22
|
# Rubocop doesn't support < 2.1
|
13
23
|
if RUBY_VERSION >= "2.1"
|
14
24
|
require 'rubocop/rake_task'
|
data/analytics-ruby.gemspec
CHANGED
@@ -25,6 +25,10 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_development_dependency 'faraday', '~> 0.13'
|
26
26
|
spec.add_development_dependency 'pmap', '~> 1.1'
|
27
27
|
|
28
|
+
if RUBY_VERSION >= '2.0' && RUBY_PLATFORM != 'java'
|
29
|
+
spec.add_development_dependency 'oj', '~> 3.6.2'
|
30
|
+
end
|
31
|
+
|
28
32
|
if RUBY_VERSION >= "2.1"
|
29
33
|
spec.add_development_dependency 'rubocop', '~> 0.51.0'
|
30
34
|
end
|
@@ -2,16 +2,43 @@ require 'logger'
|
|
2
2
|
|
3
3
|
module Segment
|
4
4
|
class Analytics
|
5
|
+
# Wraps an existing logger and adds a prefix to all messages
|
6
|
+
class PrefixedLogger
|
7
|
+
def initialize(logger, prefix)
|
8
|
+
@logger = logger
|
9
|
+
@prefix = prefix
|
10
|
+
end
|
11
|
+
|
12
|
+
def debug(msg)
|
13
|
+
@logger.debug("#{@prefix} #{msg}")
|
14
|
+
end
|
15
|
+
|
16
|
+
def info(msg)
|
17
|
+
@logger.info("#{@prefix} #{msg}")
|
18
|
+
end
|
19
|
+
|
20
|
+
def warn(msg)
|
21
|
+
@logger.warn("#{@prefix} #{msg}")
|
22
|
+
end
|
23
|
+
|
24
|
+
def error(msg)
|
25
|
+
@logger.error("#{@prefix} #{msg}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
5
29
|
module Logging
|
6
30
|
class << self
|
7
31
|
def logger
|
8
|
-
@logger
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
32
|
+
return @logger if @logger
|
33
|
+
|
34
|
+
base_logger = if defined?(Rails)
|
35
|
+
Rails.logger
|
36
|
+
else
|
37
|
+
logger = Logger.new STDOUT
|
38
|
+
logger.progname = 'Segment::Analytics'
|
39
|
+
logger
|
40
|
+
end
|
41
|
+
@logger = PrefixedLogger.new(base_logger, '[analytics-ruby]')
|
15
42
|
end
|
16
43
|
|
17
44
|
attr_writer :logger
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'forwardable'
|
1
2
|
require 'segment/analytics/logging'
|
2
3
|
|
3
4
|
module Segment
|
@@ -15,11 +16,13 @@ module Segment
|
|
15
16
|
end
|
16
17
|
|
17
18
|
def <<(message)
|
18
|
-
|
19
|
+
message_json_size = message.to_json.bytesize
|
20
|
+
|
21
|
+
if message_too_big?(message_json_size)
|
19
22
|
logger.error('a message exceeded the maximum allowed size')
|
20
23
|
else
|
21
24
|
@messages << message
|
22
|
-
@json_size +=
|
25
|
+
@json_size += message_json_size + 1 # One byte for the comma
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
@@ -42,6 +45,10 @@ module Segment
|
|
42
45
|
@messages.length >= @max_message_count
|
43
46
|
end
|
44
47
|
|
48
|
+
def message_too_big?(message_json_size)
|
49
|
+
message_json_size > Defaults::Message::MAX_BYTES
|
50
|
+
end
|
51
|
+
|
45
52
|
# We consider the max size here as just enough to leave room for one more
|
46
53
|
# message of the largest size possible. This is a shortcut that allows us
|
47
54
|
# to use a native Ruby `Queue` that doesn't allow peeking. The tradeoff
|
@@ -38,10 +38,14 @@ module Segment
|
|
38
38
|
#
|
39
39
|
# returns - Response of the status and error if it exists
|
40
40
|
def post(write_key, batch)
|
41
|
+
logger.debug("Sending request for #{batch.length} items")
|
42
|
+
|
41
43
|
last_response, exception = retry_with_backoff(@retries) do
|
42
44
|
status_code, body = send_request(write_key, batch)
|
43
45
|
error = JSON.parse(body)['error']
|
44
46
|
should_retry = should_retry_request?(status_code, body)
|
47
|
+
logger.debug("Response status code: #{status_code}")
|
48
|
+
logger.debug("Response error: #{error}") if error
|
45
49
|
|
46
50
|
[Response.new(status_code, error), should_retry]
|
47
51
|
end
|
@@ -90,6 +94,7 @@ module Segment
|
|
90
94
|
end
|
91
95
|
|
92
96
|
if should_retry && (retries_remaining > 1)
|
97
|
+
logger.debug("Retrying request, #{retries_remaining} retries left")
|
93
98
|
sleep(@backoff_policy.next_interval.to_f / 1000)
|
94
99
|
retry_with_backoff(retries_remaining - 1, &block)
|
95
100
|
else
|
@@ -112,11 +117,6 @@ module Segment
|
|
112
117
|
|
113
118
|
[200, '{}']
|
114
119
|
else
|
115
|
-
# If `start` is not called, Ruby adds a 'Connection: close' header to
|
116
|
-
# all requests, preventing us from reusing a connection for multiple
|
117
|
-
# HTTP requests
|
118
|
-
@http.start unless @http.started?
|
119
|
-
|
120
120
|
response = @http.request(request, payload)
|
121
121
|
[response.code.to_i, response.body]
|
122
122
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'segment/analytics/defaults'
|
2
|
-
require 'segment/analytics/message'
|
3
2
|
require 'segment/analytics/message_batch'
|
4
3
|
require 'segment/analytics/request'
|
5
4
|
require 'segment/analytics/utils'
|
@@ -9,6 +8,7 @@ module Segment
|
|
9
8
|
class Worker
|
10
9
|
include Segment::Analytics::Utils
|
11
10
|
include Segment::Analytics::Defaults
|
11
|
+
include Segment::Analytics::Logging
|
12
12
|
|
13
13
|
# public: Creates a new worker
|
14
14
|
#
|
@@ -29,7 +29,6 @@ module Segment
|
|
29
29
|
batch_size = options[:batch_size] || Defaults::MessageBatch::MAX_SIZE
|
30
30
|
@batch = MessageBatch.new(batch_size)
|
31
31
|
@lock = Mutex.new
|
32
|
-
@request = Request.new
|
33
32
|
end
|
34
33
|
|
35
34
|
# public: Continuously runs the loop to check for new events
|
@@ -39,12 +38,11 @@ module Segment
|
|
39
38
|
return if @queue.empty?
|
40
39
|
|
41
40
|
@lock.synchronize do
|
42
|
-
until @batch.full? || @queue.empty?
|
43
|
-
@batch << Message.new(@queue.pop)
|
44
|
-
end
|
41
|
+
@batch << @queue.pop until @batch.full? || @queue.empty?
|
45
42
|
end
|
46
43
|
|
47
|
-
res =
|
44
|
+
res = Request.new.post @write_key, @batch
|
45
|
+
|
48
46
|
@on_error.call(res.status, res.error) unless res.status == 200
|
49
47
|
|
50
48
|
@lock.synchronize { @batch.clear }
|
@@ -0,0 +1,9 @@
|
|
1
|
+
RSpec.shared_examples 'message_batch_json' do
|
2
|
+
it 'MessageBatch generates proper JSON' do
|
3
|
+
batch = Segment::Analytics::MessageBatch.new(100)
|
4
|
+
batch << { 'a' => 'b' }
|
5
|
+
batch << { 'c' => 'd' }
|
6
|
+
|
7
|
+
expect(JSON.generate(batch)).to eq('[{"a":"b"},{"c":"d"}]')
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'isolated/json_example'
|
3
|
+
|
4
|
+
if RUBY_VERSION >= '2.0' && RUBY_PLATFORM != 'java'
|
5
|
+
describe 'with active_support and oj' do
|
6
|
+
before do
|
7
|
+
require 'active_support'
|
8
|
+
require 'active_support/json'
|
9
|
+
|
10
|
+
require 'oj'
|
11
|
+
Oj.mimic_JSON
|
12
|
+
end
|
13
|
+
|
14
|
+
include_examples 'message_batch_json'
|
15
|
+
end
|
16
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
module Segment
|
@@ -19,6 +21,10 @@ module Segment
|
|
19
21
|
let(:runscope_client) { RunscopeClient.new(ENV.fetch('RUNSCOPE_TOKEN')) }
|
20
22
|
|
21
23
|
it 'tracks events' do
|
24
|
+
# Runscope inspector has shut down, disable for a while until we've
|
25
|
+
# found a replacement.
|
26
|
+
skip if Date.today < Date.new(2018, 7, 27)
|
27
|
+
|
22
28
|
id = SecureRandom.uuid
|
23
29
|
client.track(
|
24
30
|
user_id: 'dummy_user_id',
|
@@ -7,14 +7,13 @@ module Segment
|
|
7
7
|
|
8
8
|
describe '#<<' do
|
9
9
|
it 'appends messages' do
|
10
|
-
subject <<
|
10
|
+
subject << { 'a' => 'b' }
|
11
11
|
expect(subject.length).to eq(1)
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'rejects messages that exceed the maximum allowed size' do
|
15
15
|
max_bytes = Defaults::Message::MAX_BYTES
|
16
|
-
|
17
|
-
message = Message.new(hash)
|
16
|
+
message = { 'a' => 'b' * max_bytes }
|
18
17
|
|
19
18
|
subject << message
|
20
19
|
expect(subject.length).to eq(0)
|
@@ -23,21 +22,23 @@ module Segment
|
|
23
22
|
|
24
23
|
describe '#full?' do
|
25
24
|
it 'returns true once item count is exceeded' do
|
26
|
-
99.times { subject <<
|
25
|
+
99.times { subject << { a: 'b' } }
|
27
26
|
expect(subject.full?).to be(false)
|
28
27
|
|
29
|
-
subject <<
|
28
|
+
subject << { a: 'b' }
|
30
29
|
expect(subject.full?).to be(true)
|
31
30
|
end
|
32
31
|
|
33
32
|
it 'returns true once max size is almost exceeded' do
|
34
|
-
message =
|
33
|
+
message = { a: 'b' * (Defaults::Message::MAX_BYTES - 10) }
|
34
|
+
|
35
|
+
message_size = message.to_json.bytesize
|
35
36
|
|
36
37
|
# Each message is under the individual limit
|
37
|
-
expect(
|
38
|
+
expect(message_size).to be < Defaults::Message::MAX_BYTES
|
38
39
|
|
39
40
|
# Size of the batch is over the limit
|
40
|
-
expect(50 *
|
41
|
+
expect(50 * message_size).to be > Defaults::MessageBatch::MAX_BYTES
|
41
42
|
|
42
43
|
expect(subject.full?).to be(false)
|
43
44
|
50.times { subject << message }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: analytics-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.6.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Segment.io
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: commander
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '1.1'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: oj
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 3.6.2
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 3.6.2
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: rubocop
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -159,7 +173,6 @@ files:
|
|
159
173
|
- lib/segment/analytics/client.rb
|
160
174
|
- lib/segment/analytics/defaults.rb
|
161
175
|
- lib/segment/analytics/logging.rb
|
162
|
-
- lib/segment/analytics/message.rb
|
163
176
|
- lib/segment/analytics/message_batch.rb
|
164
177
|
- lib/segment/analytics/request.rb
|
165
178
|
- lib/segment/analytics/response.rb
|
@@ -167,11 +180,14 @@ files:
|
|
167
180
|
- lib/segment/analytics/version.rb
|
168
181
|
- lib/segment/analytics/worker.rb
|
169
182
|
- spec/helpers/runscope_client.rb
|
183
|
+
- spec/isolated/json_example.rb
|
184
|
+
- spec/isolated/with_active_support.rb
|
185
|
+
- spec/isolated/with_active_support_and_oj.rb
|
186
|
+
- spec/isolated/with_oj.rb
|
170
187
|
- spec/segment/analytics/backoff_policy_spec.rb
|
171
188
|
- spec/segment/analytics/client_spec.rb
|
172
189
|
- spec/segment/analytics/e2e_spec.rb
|
173
190
|
- spec/segment/analytics/message_batch_spec.rb
|
174
|
-
- spec/segment/analytics/message_spec.rb
|
175
191
|
- spec/segment/analytics/request_spec.rb
|
176
192
|
- spec/segment/analytics/response_spec.rb
|
177
193
|
- spec/segment/analytics/worker_spec.rb
|
@@ -192,12 +208,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
192
208
|
version: '0'
|
193
209
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
194
210
|
requirements:
|
195
|
-
- - "
|
211
|
+
- - ">"
|
196
212
|
- !ruby/object:Gem::Version
|
197
|
-
version:
|
213
|
+
version: 1.3.1
|
198
214
|
requirements: []
|
199
215
|
rubyforge_project:
|
200
|
-
rubygems_version: 2.7.
|
216
|
+
rubygems_version: 2.7.7
|
201
217
|
signing_key:
|
202
218
|
specification_version: 4
|
203
219
|
summary: Segment.io analytics library
|
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'segment/analytics/defaults'
|
2
|
-
|
3
|
-
module Segment
|
4
|
-
class Analytics
|
5
|
-
# Represents a message to be sent to the API
|
6
|
-
class Message
|
7
|
-
def initialize(hash)
|
8
|
-
@hash = hash
|
9
|
-
end
|
10
|
-
|
11
|
-
def too_big?
|
12
|
-
json_size > Defaults::Message::MAX_BYTES
|
13
|
-
end
|
14
|
-
|
15
|
-
def json_size
|
16
|
-
to_json.bytesize
|
17
|
-
end
|
18
|
-
|
19
|
-
# Since the hash is expected to not be modified (set at initialization),
|
20
|
-
# the JSON version can be cached after the first computation.
|
21
|
-
def to_json(*args)
|
22
|
-
@json ||= @hash.to_json(*args)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Segment
|
4
|
-
class Analytics
|
5
|
-
describe Message do
|
6
|
-
describe '#to_json' do
|
7
|
-
it 'caches JSON conversions' do
|
8
|
-
# Keeps track of the number of times to_json was called
|
9
|
-
nested_obj = Class.new do
|
10
|
-
attr_reader :to_json_call_count
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
@to_json_call_count = 0
|
14
|
-
end
|
15
|
-
|
16
|
-
def to_json(*_)
|
17
|
-
@to_json_call_count += 1
|
18
|
-
'{}'
|
19
|
-
end
|
20
|
-
end.new
|
21
|
-
|
22
|
-
message = Message.new('some_key' => nested_obj)
|
23
|
-
expect(nested_obj.to_json_call_count).to eq(0)
|
24
|
-
|
25
|
-
message.to_json
|
26
|
-
expect(nested_obj.to_json_call_count).to eq(1)
|
27
|
-
|
28
|
-
# When called a second time, the call count shouldn't increase
|
29
|
-
message.to_json
|
30
|
-
expect(nested_obj.to_json_call_count).to eq(1)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|