turnstile-rb 2.0.1 → 3.0.2

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.
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
@@ -1,86 +0,0 @@
1
- module Turnstile
2
- module Collector
3
- class Updater
4
-
5
- class Session < ::Struct.new(:uid, :platform, :ip); end
6
-
7
- attr_accessor :queue, :semaphore, :cache, :tracker, :buffer_interval, :flush_interval
8
-
9
- def initialize(queue, buffer_interval = 5, flush_interval = 6)
10
- @queue = queue
11
- @semaphore = Mutex.new
12
- @cache = Hash.new(0)
13
- @tracker = Turnstile::Tracker.new
14
- @buffer_interval = buffer_interval
15
- @buffer_interval = 6 if @buffer_interval <= 0
16
- @flush_interval = flush_interval
17
- @flush_interval = 5 if @flush_interval <= 0
18
- end
19
-
20
- def run
21
- run_queue_popper
22
- run_flusher
23
- end
24
-
25
- private
26
-
27
- def run_flusher
28
- Thread.new do
29
- Thread.current[:name] = 'updater:flush'
30
- loop do
31
- semaphore.synchronize {
32
- unless cache.empty?
33
- Turnstile::Logger.logging "flushing cache with [#{cache.keys.size}] keys" do
34
- cache.keys.each do |key|
35
- session = parse(key)
36
- if session.uid && !session.uid.empty?
37
- tracker.track(session.uid, session.platform, session.ip)
38
- end
39
- end
40
- reset_cache
41
- end
42
- else
43
- Turnstile::Logger.log "nothing to flush, sleeping #{flush_interval}s.."
44
- end
45
- }
46
- sleep flush_interval
47
- end
48
- end
49
- end
50
-
51
- def run_queue_popper
52
- Thread.new do
53
- Thread.current[:name] = 'updater:queue'
54
- loop do
55
- unless queue.empty?
56
- Turnstile::Logger.logging "caching [#{queue.size}] keys locally" do
57
- while !queue.empty?
58
- semaphore.synchronize {
59
- add(queue.pop)
60
- }
61
- end
62
- end
63
- else
64
- Turnstile::Logger.log "nothing in the queue, sleeping #{buffer_interval}s..."
65
- end
66
- sleep buffer_interval
67
- end
68
- end
69
- end
70
-
71
- def add(token)
72
- cache[token] = 1
73
- end
74
-
75
- def parse(token)
76
- a = token.split(':')
77
- Session.new(a[2], a[0], a[1])
78
- end
79
-
80
- def reset_cache
81
- @cache.clear
82
- GC.start
83
- end
84
- end
85
- end
86
- end
@@ -1,107 +0,0 @@
1
- require 'optparse'
2
- require 'colored2'
3
- require 'forwardable'
4
-
5
- require 'turnstile/version'
6
-
7
- module Turnstile
8
- class Parser
9
- extend Forwardable
10
- def_delegators :@system, :stdout, :stdin, :stderr
11
-
12
- attr_accessor :options, :argv, :system
13
-
14
- def initialize(argv, system)
15
- self.system = system
16
- self.argv = argv.dup
17
- self.options = Hashie::Mash.new
18
- self.argv << '-h' if argv.empty?
19
- end
20
-
21
- def parse
22
- OptionParser.new do |opts|
23
- opts.banner = "Usage:\n".bold.magenta +
24
- " turnstile -f <file> [ --daemon ] [ options ]\n".yellow +
25
- " turnstile -s [ json | csv | nad ] [ options ]\n".yellow +
26
- " turnstile -a 'platform:ip:user' [ options ]\n".yellow
27
-
28
- opts.separator 'Description:'.bold.magenta
29
- opts.separator ' ' + ::Turnstile::DESCRIPTION.gsub(/\n/, "\n ")
30
-
31
- opts.separator 'Log File Specification:'.bold.magenta
32
- opts.on('-f', '--file FILE', 'File to monitor') do |file|
33
- options[:file] = file
34
- end
35
- opts.on('-t', '--file-type TYPE',
36
- 'Either: json_formatted, pipe_delimited,',
37
- 'or comma_delimited (default).') do |type|
38
- options[:filetype] = type
39
- end
40
- opts.on('-D', '--delimiter CHAR',
41
- 'Forces "delimited" file type, and uses ',
42
- 'the character in the argument as the delimiter') do |v|
43
- options[:delimiter] = v
44
- end
45
- opts.separator "\nRedis Server:".bold.magenta
46
- opts.on('-r', '--redis-url URL', 'Redis server URL') do |host|
47
- Turnstile.config.redis_url = host
48
- end
49
- opts.on('--redis-host HOST', 'Redis server host') do |host|
50
- Turnstile.config.redis_host = host
51
- end
52
- opts.on('--redis-port PORT', 'Redis server port') do |port|
53
- Turnstile.config.redis_port = port
54
- end
55
- opts.on('--redis-db DB', 'Redis server db') do |db|
56
- Turnstile.config.redis_db = db
57
- end
58
- opts.separator "\nMode of Operation:".bold.magenta
59
- opts.on('-d', '--daemonize', 'Daemonize to watch the logs') do |v|
60
- options[:daemonize] = true
61
- end
62
- opts.on('-s', '--summary [FORMAT]',
63
- 'Print current stats and exit. Optional format can be',
64
- 'json (default), nad, yaml, or csv') do |v|
65
- options[:summary] = true
66
- options[:summary_format] = (v || 'json').to_sym
67
- end
68
- opts.on('-a', '--add TOKEN',
69
- 'Registers an event from the token, such as ',
70
- '"ios:123.4.4.4:32442". Use -d to customize delimiter.') do |v|
71
- options[:add] = v
72
- end
73
- opts.separator "\nTiming Adjustments:".bold.magenta
74
- opts.on('-b', '--buffer-interval INTERVAL', 'Buffer for this many seconds') do |v|
75
- options[:buffer_interval] = v.to_i
76
- end
77
- opts.on('-i', '--flush-interval INTERVAL', 'Flush then sleep for this many seconds') do |v|
78
- options[:flush_interval] = v.to_i
79
- end
80
- opts.separator "\nMiscellaneous:".bold.magenta
81
- opts.on('-v', '--verbose', 'Print status to stdout') do |v|
82
- options[:debug] = true
83
- end
84
- opts.on_tail('-h', '--help', 'Show this message') do
85
- puts opts
86
- return
87
- end
88
- end.parse!(argv)
89
-
90
- if options[:summary]
91
- Turnstile::Summary.print(options[:summary_format] || :json, options[:delimiter])
92
- elsif options[:add]
93
- Turnstile::Tracker.new.add_token(options[:add], options[:delimiter] || ':')
94
- Turnstile::Summary.print(options[:summary_format] || :json)
95
- else
96
- Turnstile::Collector::Runner.new(options).run
97
- end
98
-
99
- rescue OptionParser::MissingArgument => e
100
- STDERR.puts e.message.bold.red
101
- rescue Exception => e
102
- STDERR.puts e.message.bold.red
103
- end
104
- end
105
- end
106
-
107
-
@@ -1,54 +0,0 @@
1
- require 'optparse'
2
- require 'colored2'
3
-
4
- require 'turnstile/version'
5
- require 'turnstile/parser'
6
-
7
- module Turnstile
8
-
9
-
10
- class Runner
11
- attr_reader :argv, :stdin, :stdout, :stderr, :kernel
12
-
13
- # Allow everything fun to be injected from the outside while defaulting to normal implementations.
14
- def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel)
15
- @argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
16
- end
17
-
18
- def execute!
19
- exit_code = begin
20
-
21
- Colored2.disable! unless stdout.tty?
22
-
23
- $stderr = stderr
24
- $stdin = stdin
25
- $stdout = stdout
26
-
27
- Turnstile::Parser.new(argv, self).parse
28
-
29
- # Thor::Base#start does not have a return value, assume success if no exception is raised.
30
- 0
31
- rescue StandardError => e
32
- # The ruby interpreter would pipe this to STDERR and exit 1 in the case of an unhandled exception
33
- b = e.backtrace
34
- @stderr.puts("#{b.shift}: #{e.message} (#{e.class})")
35
- @stderr.puts(b.map { |s| "\tfrom #{s}" }.join("\n"))
36
- 1
37
- rescue SystemExit => e
38
- e.status
39
- ensure
40
- $stderr = STDERR
41
- $stdin = STDIN
42
- $stdout = STDOUT
43
- end
44
-
45
- # Proxy our exit code back to the injected kernel.
46
- @kernel.exit(exit_code)
47
- end
48
- end
49
- end
50
-
51
- module Turnstile
52
-
53
- end
54
-
@@ -1,57 +0,0 @@
1
- require 'csv'
2
- module Turnstile
3
- class Summary
4
-
5
- class << self
6
- def print(method, delimiter = nil)
7
- summary = new
8
- if summary.respond_to?(method)
9
- STDOUT.puts summary.send(method, delimiter)
10
- else
11
- STDERR.puts "don't know how to use format '#{method}'mac"
12
- end
13
- end
14
- end
15
-
16
- def json(*)
17
- out = "{\n"
18
- first = true
19
- aggregate.each_pair do |key, value|
20
- out << ",\n" unless first
21
- first = false
22
- out << json_row(key, value)
23
- end
24
- out << "\n}"
25
- out
26
- end
27
-
28
- def nad(*)
29
- out = ''
30
- aggregate.each_pair do |key, value|
31
- out << nad_row(key, value)
32
- end
33
- out
34
- end
35
-
36
- def csv(delimiter = nil)
37
- out = CSV.generate do |csv|
38
- aggregate.each_pair do |key, value|
39
- csv << [key, value]
40
- end
41
- end
42
- delimiter ? out.gsub(/,/m, delimiter) : out
43
- end
44
-
45
- def nad_row(key, value)
46
- %Q(turnstile:#{key}#{"\tn\t"}#{value}\n)
47
- end
48
-
49
- def json_row(key, value)
50
- %Q( "#{key}": #{value})
51
- end
52
-
53
- def aggregate
54
- Turnstile::Adapter.new.aggregate
55
- end
56
- end
57
- end
@@ -1,41 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Turnstile::Summary do
4
- subject { described_class.new }
5
-
6
- let(:aggregate) {
7
- {
8
- 'android' => 3,
9
- 'ios' => 2,
10
- 'total' => 5
11
- }
12
- }
13
-
14
- before { expect(subject).to receive(:aggregate).once.and_return(aggregate) }
15
-
16
- describe '#nad' do
17
- context 'have some data' do
18
- let(:expected_string) {
19
- <<-EOF
20
- turnstile:android#{"\t"}n#{"\t"}3
21
- turnstile:ios#{"\t"}n#{"\t"}2
22
- turnstile:total#{"\t"}n#{"\t"}5
23
- EOF
24
- }
25
-
26
- it 'return data in NAD format' do
27
- expect(subject.nad).to eql(expected_string)
28
- end
29
- end
30
- end
31
-
32
- describe '#json' do
33
- context 'have some data' do
34
- let(:json) { subject.json }
35
- let(:hash) { JSON.load(json) }
36
- it 'return data in NAD format' do
37
- expect(hash).to eql(aggregate)
38
- end
39
- end
40
- end
41
- end