turnstile-rb 2.0.1 → 3.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.travis.yml +2 -2
  4. data/LICENSE.txt +2 -1
  5. data/README.md +200 -11
  6. data/Rakefile +1 -0
  7. data/bin/turnstile +4 -6
  8. data/example/custom_csv_matcher.rb +22 -0
  9. data/lib/turnstile.rb +37 -8
  10. data/lib/turnstile/cli/launcher.rb +83 -0
  11. data/lib/turnstile/cli/parser.rb +166 -0
  12. data/lib/turnstile/cli/runner.rb +58 -0
  13. data/lib/turnstile/collector.rb +2 -2
  14. data/lib/turnstile/collector/actor.rb +81 -0
  15. data/lib/turnstile/collector/controller.rb +121 -0
  16. data/lib/turnstile/collector/flusher.rb +36 -0
  17. data/lib/turnstile/collector/formats.rb +7 -34
  18. data/lib/turnstile/collector/formats/custom_matcher.rb +19 -0
  19. data/lib/turnstile/collector/formats/delimited_matcher.rb +30 -0
  20. data/lib/turnstile/collector/formats/json_matcher.rb +30 -0
  21. data/lib/turnstile/collector/log_reader.rb +84 -46
  22. data/lib/turnstile/collector/{matcher.rb → regexp_matcher.rb} +4 -5
  23. data/lib/turnstile/collector/session.rb +7 -0
  24. data/lib/turnstile/commands.rb +20 -0
  25. data/lib/turnstile/commands/base.rb +20 -0
  26. data/lib/turnstile/commands/flushdb.rb +21 -0
  27. data/lib/turnstile/commands/print_keys.rb +19 -0
  28. data/lib/turnstile/commands/show.rb +89 -0
  29. data/lib/turnstile/configuration.rb +23 -0
  30. data/lib/turnstile/dependencies.rb +31 -0
  31. data/lib/turnstile/logger.rb +9 -40
  32. data/lib/turnstile/logger/helper.rb +42 -0
  33. data/lib/turnstile/logger/provider.rb +74 -0
  34. data/lib/turnstile/observer.rb +5 -9
  35. data/lib/turnstile/redis/adapter.rb +97 -0
  36. data/lib/turnstile/redis/connection.rb +116 -0
  37. data/lib/turnstile/redis/spy.rb +42 -0
  38. data/lib/turnstile/sampler.rb +9 -2
  39. data/lib/turnstile/tracker.rb +14 -14
  40. data/lib/turnstile/version.rb +51 -12
  41. data/lib/turnstile/web_app.rb +29 -0
  42. data/spec/spec_helper.rb +18 -3
  43. data/spec/support/logging.rb +17 -0
  44. data/spec/turnstile/adapter_spec.rb +59 -46
  45. data/spec/turnstile/collector/flusher_spec.rb +16 -0
  46. data/spec/turnstile/collector/log_reader_spec.rb +127 -77
  47. data/spec/turnstile/commands/show_spec.rb +40 -0
  48. data/spec/turnstile/tracker_spec.rb +21 -7
  49. data/spec/turnstile_spec.rb +3 -0
  50. data/turnstile-rb.gemspec +5 -2
  51. metadata +89 -22
  52. data/Gemfile +0 -6
  53. data/lib/turnstile/adapter.rb +0 -61
  54. data/lib/turnstile/collector/runner.rb +0 -72
  55. data/lib/turnstile/collector/updater.rb +0 -86
  56. data/lib/turnstile/parser.rb +0 -107
  57. data/lib/turnstile/runner.rb +0 -54
  58. data/lib/turnstile/summary.rb +0 -57
  59. data/spec/turnstile/summary_spec.rb +0 -41
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'turnstile/commands/show'
3
+
4
+ EXPECTED_NAD_RESPONSE = <<-EOF
5
+ turnstile:android#{"\t"}n#{"\t"}3
6
+ turnstile:ios#{"\t"}n#{"\t"}2
7
+ turnstile:total#{"\t"}n#{"\t"}5
8
+ EOF
9
+ .strip
10
+
11
+ module Turnstile
12
+ module Commands
13
+ RSpec.describe Show do
14
+ let(:options) { { summary: true }}
15
+ subject { described_class.new(options) }
16
+
17
+ let(:aggregate) {
18
+ {
19
+ 'android' => 3,
20
+ 'ios' => 2,
21
+ 'total' => 5
22
+ }
23
+ }
24
+
25
+ describe '#execute' do
26
+ it 'return data in NAD format' do
27
+ expect(subject.nad(aggregate)).to eql(EXPECTED_NAD_RESPONSE)
28
+ end
29
+ end
30
+
31
+ describe '#json' do
32
+ let(:json) { subject.json(aggregate) }
33
+ let(:hash) { JSON.load(json) }
34
+ it 'return data in JSON format' do
35
+ expect(hash).to eql(aggregate)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Turnstile::Tracker do
4
4
 
5
- subject { Turnstile::Tracker.new }
5
+ subject(:tracker) { Turnstile::Tracker.new }
6
6
 
7
7
  let(:adapter) { subject.send(:adapter) }
8
8
 
@@ -10,16 +10,30 @@ describe Turnstile::Tracker do
10
10
  let(:platform) { :ios }
11
11
  let(:ip) { '1.2.3.4' }
12
12
 
13
- describe "#track" do
14
- it "calls adapter with correct parameters" do
13
+ describe '#track_and_sample' do
14
+ it 'calls adapter with correct parameters' do
15
15
  expect(adapter).to receive(:add).once.with(uid, platform, ip)
16
- subject.track(uid, platform, ip)
16
+ subject.track_and_sample(uid, platform, ip)
17
17
  end
18
18
 
19
- it "does not track if sampler returns no" do
20
- allow_any_instance_of(Turnstile::Sampler).to receive(:sample).and_return false
19
+ it 'does not track if sampler returns no' do
20
+ allow(tracker).to receive(:should_track?).and_return false
21
21
  expect(adapter).to receive(:add).never
22
- subject.track(uid)
22
+ subject.track_and_sample(uid)
23
+ end
24
+ end
25
+
26
+ describe '#track_token' do
27
+ it 'calls adapter with correct parameters' do
28
+ expect(adapter).to receive(:add).once.with(uid.to_s, platform.to_s, ip)
29
+ subject.track_token("#{platform}:#{ip}:#{uid}")
30
+ end
31
+ end
32
+
33
+ describe '#track_all' do
34
+ it 'does not track if sampler returns no' do
35
+ expect(adapter).to receive(:add).once
36
+ subject.track_all(uid)
23
37
  end
24
38
  end
25
39
  end
@@ -4,6 +4,7 @@ describe Turnstile do
4
4
 
5
5
  let(:tracker) { Turnstile::Tracker.new }
6
6
  let(:observer) { Turnstile::Observer.new }
7
+ let(:adapter) { tracker.adapter }
7
8
 
8
9
  let(:uid_1) { 1238438 }
9
10
  let(:uid_2) { 1238439 }
@@ -21,6 +22,8 @@ describe Turnstile do
21
22
  let(:other_ip) { '4.3.2.1' }
22
23
 
23
24
  describe 'general tests' do
25
+ before { adapter.flushdb }
26
+
24
27
  let(:expected_stats) do
25
28
  {
26
29
  stats: {
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
16
16
  spec.homepage = 'https://github.com/kigster/turnstile-rb'
17
17
  spec.license = 'MIT'
18
18
 
19
- spec.files = `git ls-files`.split($/)
19
+ spec.files = `git ls-files`.split($/).reject {|f| f =~ /Gemfile/ }
20
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
21
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
22
  spec.require_paths = ['lib']
@@ -24,13 +24,16 @@ Gem::Specification.new do |spec|
24
24
  spec.add_dependency 'redis'
25
25
  spec.add_dependency 'file-tail'
26
26
  spec.add_dependency 'daemons'
27
- spec.add_dependency 'json'
28
27
  spec.add_dependency 'hashie'
29
28
  spec.add_dependency 'colored2'
29
+ spec.add_dependency 'connection_pool'
30
+ spec.add_dependency 'activesupport'
31
+ spec.add_dependency 'sinatra'
30
32
 
31
33
  spec.add_development_dependency 'bundler'
32
34
  spec.add_development_dependency 'rake'
33
35
  spec.add_development_dependency 'yard'
36
+ spec.add_development_dependency 'irbtools'
34
37
 
35
38
  spec.add_development_dependency 'rspec'
36
39
  spec.add_development_dependency 'rspec-its'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turnstile-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 3.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Gredeskoul
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-04 00:00:00.000000000 Z
11
+ date: 2018-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: json
56
+ name: hashie
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: hashie
70
+ name: colored2
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -81,7 +81,35 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: colored2
84
+ name: connection_pool
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
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: activesupport
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: sinatra
85
113
  requirement: !ruby/object:Gem::Requirement
86
114
  requirements:
87
115
  - - ">="
@@ -136,6 +164,20 @@ dependencies:
136
164
  - - ">="
137
165
  - !ruby/object:Gem::Version
138
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: irbtools
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
139
181
  - !ruby/object:Gem::Dependency
140
182
  name: rspec
141
183
  requirement: !ruby/object:Gem::Requirement
@@ -178,12 +220,15 @@ dependencies:
178
220
  - - ">="
179
221
  - !ruby/object:Gem::Version
180
222
  version: '0'
181
- description: |2
182
- Turnstile is a Redis-based library that can accurately track total number of concurrent
183
- users accessing a web/API based server application. It can break it down by "platform"
184
- or a device type, and returns data in JSON, CSV of NAD formats. While user tracking
185
- may happen synchronously using a Rack middleware, another method is provided that is
186
- based on log file analysis, and can therefore be performed outside web server process.
223
+ description: |+
224
+ Turnstile is a Redis-based library that can accurately track total number
225
+ of concurrent users accessing a web/API based server application. It can
226
+ break it down by "platform" or a device type, and returns data in JSON,
227
+ CSV of NAD formats. While user tracking may happen synchronously using a
228
+ Rack middleware, another method is provided that is based on log file
229
+ analysis, and can therefore be performed outside web server process.
230
+
231
+
187
232
  email:
188
233
  - kigster@gmail.com
189
234
  executables:
@@ -194,37 +239,56 @@ files:
194
239
  - ".gitignore"
195
240
  - ".rspec"
196
241
  - ".travis.yml"
197
- - Gemfile
198
242
  - LICENSE.txt
199
243
  - README.md
200
244
  - Rakefile
201
245
  - bin/turnstile
246
+ - example/custom_csv_matcher.rb
202
247
  - lib/turnstile.rb
203
- - lib/turnstile/adapter.rb
248
+ - lib/turnstile/cli/launcher.rb
249
+ - lib/turnstile/cli/parser.rb
250
+ - lib/turnstile/cli/runner.rb
204
251
  - lib/turnstile/collector.rb
252
+ - lib/turnstile/collector/actor.rb
253
+ - lib/turnstile/collector/controller.rb
254
+ - lib/turnstile/collector/flusher.rb
205
255
  - lib/turnstile/collector/formats.rb
256
+ - lib/turnstile/collector/formats/custom_matcher.rb
257
+ - lib/turnstile/collector/formats/delimited_matcher.rb
258
+ - lib/turnstile/collector/formats/json_matcher.rb
206
259
  - lib/turnstile/collector/log_reader.rb
207
- - lib/turnstile/collector/matcher.rb
208
- - lib/turnstile/collector/runner.rb
209
- - lib/turnstile/collector/updater.rb
260
+ - lib/turnstile/collector/regexp_matcher.rb
261
+ - lib/turnstile/collector/session.rb
262
+ - lib/turnstile/commands.rb
263
+ - lib/turnstile/commands/base.rb
264
+ - lib/turnstile/commands/flushdb.rb
265
+ - lib/turnstile/commands/print_keys.rb
266
+ - lib/turnstile/commands/show.rb
210
267
  - lib/turnstile/configuration.rb
268
+ - lib/turnstile/dependencies.rb
211
269
  - lib/turnstile/logger.rb
270
+ - lib/turnstile/logger/helper.rb
271
+ - lib/turnstile/logger/provider.rb
212
272
  - lib/turnstile/observer.rb
213
- - lib/turnstile/parser.rb
214
- - lib/turnstile/runner.rb
273
+ - lib/turnstile/redis/adapter.rb
274
+ - lib/turnstile/redis/connection.rb
275
+ - lib/turnstile/redis/spy.rb
215
276
  - lib/turnstile/sampler.rb
216
- - lib/turnstile/summary.rb
217
277
  - lib/turnstile/tracker.rb
218
278
  - lib/turnstile/version.rb
279
+ - lib/turnstile/web_app.rb
280
+ - spec/fixtures/custom-production.log
219
281
  - spec/fixtures/sample-production.log
220
282
  - spec/fixtures/sample-production.log.json
221
283
  - spec/spec_helper.rb
284
+ - spec/support/logging.rb
222
285
  - spec/turnstile/adapter_spec.rb
286
+ - spec/turnstile/collector/flusher_spec.rb
223
287
  - spec/turnstile/collector/log_reader_spec.rb
288
+ - spec/turnstile/commands/show_spec.rb
224
289
  - spec/turnstile/configuration_spec.rb
225
290
  - spec/turnstile/observer_spec.rb
226
291
  - spec/turnstile/sampler_spec.rb
227
- - spec/turnstile/summary_spec.rb
228
292
  - spec/turnstile/tracker_spec.rb
229
293
  - spec/turnstile_spec.rb
230
294
  - turnstile-rb.gemspec
@@ -248,20 +312,23 @@ required_rubygems_version: !ruby/object:Gem::Requirement
248
312
  version: '0'
249
313
  requirements: []
250
314
  rubyforge_project:
251
- rubygems_version: 2.6.11
315
+ rubygems_version: 2.7.6
252
316
  signing_key:
253
317
  specification_version: 4
254
318
  summary: Asynchronous and non-invasive concurrent user tracking with Redis, by scanning
255
319
  application logs across all servers.
256
320
  test_files:
321
+ - spec/fixtures/custom-production.log
257
322
  - spec/fixtures/sample-production.log
258
323
  - spec/fixtures/sample-production.log.json
259
324
  - spec/spec_helper.rb
325
+ - spec/support/logging.rb
260
326
  - spec/turnstile/adapter_spec.rb
327
+ - spec/turnstile/collector/flusher_spec.rb
261
328
  - spec/turnstile/collector/log_reader_spec.rb
329
+ - spec/turnstile/commands/show_spec.rb
262
330
  - spec/turnstile/configuration_spec.rb
263
331
  - spec/turnstile/observer_spec.rb
264
332
  - spec/turnstile/sampler_spec.rb
265
- - spec/turnstile/summary_spec.rb
266
333
  - spec/turnstile/tracker_spec.rb
267
334
  - spec/turnstile_spec.rb
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- source 'https://rubygems.org'
4
-
5
- # Specify your gem's dependencies in turnstile-rb.gemspec
6
- gemspec
@@ -1,61 +0,0 @@
1
- require 'redis'
2
- require 'timeout'
3
-
4
- module Turnstile
5
- class Adapter
6
- attr_accessor :redis
7
- include Timeout
8
-
9
- def initialize
10
- self.redis = config.redis.url ?
11
- ::Redis.new(url: config.redis.url) :
12
- ::Redis.new(host: config.redis.host,
13
- port: config.redis.port,
14
- db: config.redis.db)
15
- end
16
-
17
- def add(uid, platform, ip)
18
- key = compose_key(uid, platform, ip)
19
- timeout(config.redis.timeout) do
20
- redis.setex(key, config.activity_interval, 1)
21
- end
22
- rescue StandardError => e
23
- Turnstile::Logger.log "exception while writing to redis: #{e.inspect}"
24
- end
25
-
26
- def fetch
27
- redis.keys('t:*').map do |key|
28
- fields = key.split(':')
29
- {
30
- uid: fields[1],
31
- platform: fields[2],
32
- ip: fields[3],
33
- }
34
- end
35
- end
36
-
37
- def aggregate
38
- redis.keys('t:*').inject({}) { |hash, key| increment_platform(hash, key) }.tap do |h|
39
- h['total'] = h.values.inject(&:+) || 0
40
- end
41
- end
42
-
43
- private
44
-
45
- def increment_platform(hash, key)
46
- platform = key.split(':')[2]
47
- hash[platform] ||= 0
48
- hash[platform] += 1
49
- hash
50
- end
51
-
52
- def config
53
- Turnstile.config
54
- end
55
-
56
- def compose_key(uid, platform = nil, ip = nil)
57
- "t:#{uid}:#{platform}:#{ip}"
58
- end
59
-
60
- end
61
- end
@@ -1,72 +0,0 @@
1
- require 'thread'
2
- require 'daemons/daemonize'
3
- require 'colored2'
4
-
5
-
6
- module Turnstile
7
- module Collector
8
- class Runner
9
- attr_accessor :config, :queue, :reader, :updater, :file
10
-
11
- def initialize(*args)
12
- @config = args.last.is_a?(Hash) ? args.pop : {}
13
- @file = config[:file]
14
- @queue = Queue.new
15
-
16
- config[:debug] ? Turnstile::Logger.enable : Turnstile::Logger.disable
17
-
18
- wait_for_file(@file)
19
-
20
- self.reader
21
- self.updater
22
-
23
- Daemonize.daemonize if config[:daemonize]
24
- STDOUT.sync = true if config[:debug]
25
- end
26
-
27
- def wait_for_file(file)
28
- sleep_period = 1
29
- while !File.exist?(file)
30
- STDERR.puts "File #{file.bold.yellow} does not exist, waiting for it to appear..."
31
- STDERR.puts 'Press Ctrl-C to abort.' if sleep_period == 1
32
-
33
- sleep sleep_period
34
- sleep_period *= 1.2
35
- end
36
- end
37
-
38
- def run
39
- threads = [reader, updater].map(&:run)
40
- threads.last.join
41
- end
42
-
43
- def updater
44
- @updater ||= Turnstile::Collector::Updater.new(queue,
45
- config[:buffer_interval] || 5,
46
- config[:flush_interval] || 6)
47
- end
48
-
49
- def log_reader_class
50
- Turnstile::Collector::LogReader
51
- end
52
-
53
- def reader
54
- args = [file, queue]
55
- matcher = :default
56
-
57
- if config[:delimiter]
58
- matcher = :delimited
59
- args << config[:delimiter]
60
- elsif config[:filetype]
61
- matcher = config[:filetype].to_sym
62
- end
63
-
64
- @reader ||= if log_reader_class.respond_to?(matcher)
65
- log_reader_class.send(matcher, *args)
66
- else
67
- raise ArgumentError, "Invalid matcher #{matcher}"
68
- end
69
- end
70
- end
71
- end
72
- end