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.
- 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
|