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.
- checksums.yaml +5 -5
- data/.gitignore +3 -0
- data/.travis.yml +2 -2
- data/LICENSE.txt +2 -1
- data/README.md +200 -11
- data/Rakefile +1 -0
- data/bin/turnstile +4 -6
- data/example/custom_csv_matcher.rb +22 -0
- data/lib/turnstile.rb +37 -8
- data/lib/turnstile/cli/launcher.rb +83 -0
- data/lib/turnstile/cli/parser.rb +166 -0
- data/lib/turnstile/cli/runner.rb +58 -0
- data/lib/turnstile/collector.rb +2 -2
- data/lib/turnstile/collector/actor.rb +81 -0
- data/lib/turnstile/collector/controller.rb +121 -0
- data/lib/turnstile/collector/flusher.rb +36 -0
- data/lib/turnstile/collector/formats.rb +7 -34
- data/lib/turnstile/collector/formats/custom_matcher.rb +19 -0
- data/lib/turnstile/collector/formats/delimited_matcher.rb +30 -0
- data/lib/turnstile/collector/formats/json_matcher.rb +30 -0
- data/lib/turnstile/collector/log_reader.rb +84 -46
- data/lib/turnstile/collector/{matcher.rb → regexp_matcher.rb} +4 -5
- data/lib/turnstile/collector/session.rb +7 -0
- data/lib/turnstile/commands.rb +20 -0
- data/lib/turnstile/commands/base.rb +20 -0
- data/lib/turnstile/commands/flushdb.rb +21 -0
- data/lib/turnstile/commands/print_keys.rb +19 -0
- data/lib/turnstile/commands/show.rb +89 -0
- data/lib/turnstile/configuration.rb +23 -0
- data/lib/turnstile/dependencies.rb +31 -0
- data/lib/turnstile/logger.rb +9 -40
- data/lib/turnstile/logger/helper.rb +42 -0
- data/lib/turnstile/logger/provider.rb +74 -0
- data/lib/turnstile/observer.rb +5 -9
- data/lib/turnstile/redis/adapter.rb +97 -0
- data/lib/turnstile/redis/connection.rb +116 -0
- data/lib/turnstile/redis/spy.rb +42 -0
- data/lib/turnstile/sampler.rb +9 -2
- data/lib/turnstile/tracker.rb +14 -14
- data/lib/turnstile/version.rb +51 -12
- data/lib/turnstile/web_app.rb +29 -0
- data/spec/spec_helper.rb +18 -3
- data/spec/support/logging.rb +17 -0
- data/spec/turnstile/adapter_spec.rb +59 -46
- data/spec/turnstile/collector/flusher_spec.rb +16 -0
- data/spec/turnstile/collector/log_reader_spec.rb +127 -77
- data/spec/turnstile/commands/show_spec.rb +40 -0
- data/spec/turnstile/tracker_spec.rb +21 -7
- data/spec/turnstile_spec.rb +3 -0
- data/turnstile-rb.gemspec +5 -2
- metadata +89 -22
- data/Gemfile +0 -6
- data/lib/turnstile/adapter.rb +0 -61
- data/lib/turnstile/collector/runner.rb +0 -72
- data/lib/turnstile/collector/updater.rb +0 -86
- data/lib/turnstile/parser.rb +0 -107
- data/lib/turnstile/runner.rb +0 -54
- data/lib/turnstile/summary.rb +0 -57
- 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
|
data/lib/turnstile/parser.rb
DELETED
@@ -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
|
-
|
data/lib/turnstile/runner.rb
DELETED
@@ -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
|
-
|
data/lib/turnstile/summary.rb
DELETED
@@ -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
|