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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0da02f1388bebec96709fa3da4b237960a3bc83a9ef8320921836933b9e1e540
|
4
|
+
data.tar.gz: 31193b223254facb6ee7863dfe21811da38dc0874cccfc7d624d1b1aea38d968
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0ea0957e804292a8cab5c788afefb7a8eb5299448fcc71894383dc3d6e8c03ae01af1b4f0cde1010a57e2b04a0a8c7930d2c4c4a311f89af01e389598be005f
|
7
|
+
data.tar.gz: 6ec94cb380a8860f2b952adc95d87abbd716deea8823c2e4d7fa2299f82975de90ceb6eb86ce3e130df3b5d468b532a90867d7a73b57caefc0a173c35c3a4a7e
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -5,30 +5,175 @@
|
|
5
5
|
|
6
6
|
# Turnstile
|
7
7
|
|
8
|
-
The goal of this gem is to provide near real time tracking and reporting on the number of users currently online and accessing given application. It requires that the reporting layer is able to uniquely identify each user
|
8
|
+
The goal of this gem is to provide near real time tracking and reporting on the number of users currently online and accessing given application. It requires that the reporting layer is able to uniquely identify each user. You may also add one another dimension to the tracking, such as i.e. a _platform_ — a coded device or device type the user is using.
|
9
9
|
|
10
|
-
|
10
|
+
For example, you might support platforms: `ios`, `android`, `macos`, `windows`, etc.
|
11
|
+
|
12
|
+
The gem uses [Redis](http://redis.io/) in order to keep track of the data, and can operate in either the **online* mode** (tracking users from a Rack Middleware) or **offline mode**, by taling an application file log file and searching for a particular pattern.
|
11
13
|
|
12
14
|
## Installation
|
13
15
|
|
14
16
|
Add this line to your application's Gemfile:
|
15
17
|
|
16
18
|
$ gem install turnstile-rb
|
19
|
+
$ turnstile --help
|
17
20
|
|
18
21
|
## Usage
|
19
22
|
|
20
|
-
|
23
|
+
The gem provides command line interface shown below:
|
24
|
+
|
25
|
+
|
26
|
+
### Description
|
27
|
+
|
28
|
+
Turnstile is a Redis-based library that can accurately track total number
|
29
|
+
of concurrent users accessing a web/API based server application. It can
|
30
|
+
break it down by "platform" or a device type, and returns data in JSON,
|
31
|
+
CSV of NAD formats. While user tracking may happen synchronously using a
|
32
|
+
Rack middleware, another method is provided that is based on log file
|
33
|
+
analysis, and can therefore be performed outside web server process.
|
34
|
+
|
35
|
+
### Usage
|
36
|
+
|
37
|
+
```
|
38
|
+
# Tail the log file as a proper daemon, and optionally HTTP end point
|
39
|
+
turnstile -f <file> [ --daemon ] [ --web PORT ] [ options ]
|
40
|
+
|
41
|
+
# Add a single item and exit
|
42
|
+
turnstile -a 'platform:ip:user' [ options ]
|
43
|
+
|
44
|
+
# Print the summary stats and exit
|
45
|
+
turnstile -s [ json | csv | nad ] [ options ]
|
46
|
+
```
|
47
|
+
|
48
|
+
### Details
|
49
|
+
|
50
|
+
Turnstile can be run as a daemon, in which case it watches a given log
|
51
|
+
file. Or, you can run turnstile to print the current aggregated stats in
|
52
|
+
several supported formats, such as JSON.
|
53
|
+
|
54
|
+
When Turnstile is used to tail the log files, ideally you should start
|
55
|
+
turnstile daemon on each app sever that's generating log file, or be
|
56
|
+
content with the effects of sampling.
|
57
|
+
|
58
|
+
Note that the IP address is not required to track uniqueness. Only
|
59
|
+
platform and UID are used. Also note that custom formatter can be
|
60
|
+
specified in a config file to parse arbitrary complex log lines.
|
61
|
+
|
62
|
+
|
63
|
+
For tailing a log files, Turnstile must first match a log line expected to
|
64
|
+
contain the tokens, and then extract is using one of the matchers. You can
|
65
|
+
specify which matcher to use depending on whether you can add Turnstile's
|
66
|
+
tokens to your log or not. If you can, great! If not, implement your own
|
67
|
+
custom matcher and great again.
|
68
|
+
|
69
|
+
The following matchers are available, and can be selected with -F:
|
70
|
+
|
71
|
+
1. Format named `delimited`, which expects the following token in the
|
72
|
+
log file:
|
73
|
+
|
74
|
+
x-turnstile:platform:ip:user-identifier
|
75
|
+
|
76
|
+
Where ':' (the delimiter) can be set via -l option, OR you can use one
|
77
|
+
of the following formats: `json_formatted`, `pipe_formatted`,
|
78
|
+
`comma_formatted`, `colon_formatted` (the default). The match is
|
79
|
+
performed on a string `x-turnstile`, other log lines are skipped.
|
80
|
+
|
81
|
+
2. Format `json_delimited`, which expects to find a single-line JSON
|
82
|
+
Hash, containing keys `platform`, `ip_address`, and `user_id`. The match
|
83
|
+
is performed on any log line containing string '`ip_address`', other
|
84
|
+
lines are skipped.
|
85
|
+
|
86
|
+
3. Format `custom` requires passing an additional flag `-c/--config
|
87
|
+
file.rb`, which will be required, and which can define a matcher and
|
88
|
+
assign it to the `Turnstile.config.custom_matcher` config variable.
|
89
|
+
|
90
|
+
Custom matcher is any object that responds to two methods:
|
91
|
+
|
92
|
+
* `matches?(line)` — must return true if the line is to be used in token extraction, and
|
93
|
+
* `tokenlize(line)` — must return a string of the form `platform:ip:user-id` as a result of parsing the `line`.
|
94
|
+
|
95
|
+
### Command Line Options
|
96
|
+
|
97
|
+
The following flags are available:
|
98
|
+
|
99
|
+
```
|
100
|
+
Mode of Operation:
|
101
|
+
-f, --file FILE Starts Turnstile in a file-tailing mode
|
102
|
+
|
103
|
+
-s, --show [FORMAT] Print current stats and exit. Optional
|
104
|
+
format can be "json" (default), "nad",
|
105
|
+
"yaml", or "csv"
|
106
|
+
|
107
|
+
-w, --web [PORT] Starts a Sinatra app on a given port
|
108
|
+
offering /turnstile/<json|yaml> end point.
|
109
|
+
Can be used with file tailing mode, or
|
110
|
+
standalone. Default port is 9090
|
111
|
+
|
112
|
+
-a, --add TOKEN Registers an event from the token, such as
|
113
|
+
"ios:123.4.4.4:32442". Use -l to customize
|
114
|
+
the delimiter
|
115
|
+
|
116
|
+
-p, --print-keys Prints all Turnstile keys in Redis
|
117
|
+
|
118
|
+
--flushdb Wipes Redis database, and exit
|
119
|
+
|
120
|
+
Tailing log file:
|
121
|
+
-d, --daemonize Daemonize to watch the logs
|
122
|
+
|
123
|
+
-b, --read-backwards [LINES] Like tail, read last LINES lines
|
124
|
+
instead of tailing from the end
|
125
|
+
|
126
|
+
-F, --format FORMAT Log file format (see above)
|
127
|
+
|
128
|
+
-l, --delimiter CHAR Forces "delimited" file type, and
|
129
|
+
uses CHAR as the delimiter
|
130
|
+
|
131
|
+
-c, --config FILE Ruby config file that can define the
|
132
|
+
custom matcher, supporting arbitrary
|
133
|
+
complex logs
|
134
|
+
|
135
|
+
Redis Server:
|
136
|
+
-r, --redis-url URL Redis server URL
|
137
|
+
--redis-host HOST Redis server host
|
138
|
+
--redis-port PORT Redis server port
|
139
|
+
--redis-db DB Redis server db
|
140
|
+
--hiredis Use hiredis high performance library
|
141
|
+
|
142
|
+
Miscellaneous:
|
143
|
+
-i, --idle-sleep SECONDS When no work was detected, pause the
|
144
|
+
threads for several seconds.
|
145
|
+
-v, --verbose Print status to stdout
|
146
|
+
-t, --trace Enable trace mode
|
147
|
+
-h, --help Show this message
|
148
|
+
|
149
|
+
```
|
150
|
+
|
151
|
+
To summarize, you can run `turnstile` in order to:
|
152
|
+
|
153
|
+
* start a daemon to tail a log file
|
154
|
+
* optionally start a Sinatra web server on port 9090, that returns aggregated results to `/turnstile/json`
|
155
|
+
* to print results
|
156
|
+
* to print all redis keys
|
157
|
+
* to reset all data
|
158
|
+
* to add new data
|
159
|
+
|
160
|
+
### Data Collection and Reporting
|
21
161
|
|
22
162
|
Turnstile contains two primary parts: data collection and reporting.
|
23
163
|
|
24
164
|
Data collection may happen in two way:
|
25
165
|
|
26
|
-
1. Synchronously — in real time — i.e. from a web request
|
27
|
-
2. Or asynchronously — by "tailing" the logs on your servers
|
166
|
+
1. Synchronously — in real time — i.e. from a web request
|
167
|
+
2. Or asynchronously — by "tailing" the logs on your servers
|
168
|
+
|
169
|
+
*Synchronous tracking* is accurate, supports sampling, but introduces a run-time dependency into your application middleware stack that might not be desirable. This is how `Rack::Attack` and other similar middleware operates. Luckily, there is a better way.
|
28
170
|
|
29
|
-
|
171
|
+
*Asynchronous tracking* has a slight initial setup overhead and it requires a consistently formatted log file that includes lines with the platform, IP and user ID for ALL users at least some of the time. This method introduces zero run-time overhead, as the data collection happens outside of the web request in a standalone daemon, that simply tails your log files.
|
172
|
+
|
173
|
+
On high performance web applications it is highly recommended to employ the *asynchronous tracking* via the log file, and possibly — a custom matcher.
|
174
|
+
|
175
|
+
> NOTE: as of Turnstile version 3, you can define a [custom matcher](#custom-matcher) in a ruby-syntax configuration file. Coupled with `-F custom` you are then able to parse arbitrary complex log files and extract the three tokens needed by Turnstile: the `platform` enum type, `IP addresss`, and `User ID`.
|
30
176
|
|
31
|
-
Asynchronous tracking has a slight initial setup overhead, but has zero run-time overhead, as the data collection happens outside of the web request.
|
32
177
|
|
33
178
|
#### Real Time Tracking API
|
34
179
|
|
@@ -90,8 +235,8 @@ Turnstile supports two primary formats:
|
|
90
235
|
{ "user_id" :17344742,
|
91
236
|
"platform" :"iphone",
|
92
237
|
"session_id" :"4eKMZJ4nggzvkix29zpS",
|
93
|
-
"ip_address" :"70.210.128.241"
|
94
|
-
|
238
|
+
"ip_address" :"70.210.128.241"
|
239
|
+
}
|
95
240
|
```
|
96
241
|
|
97
242
|
2. Plain text format, where lines are space delimited, and the token is one of the fields of your log line, itself delimited using a configurable character.
|
@@ -113,6 +258,51 @@ You can also pass the token delimiter on the command line, `-D | --delimiter ","
|
|
113
258
|
|
114
259
|
> NOTE: Default format is **`pipe_delimited`**.
|
115
260
|
|
261
|
+
<a name="custom-matcher"></a>
|
262
|
+
|
263
|
+
### Custom Log Matchers
|
264
|
+
|
265
|
+
> This feature is only available in Turnstile version 3.0 or later.
|
266
|
+
|
267
|
+
To be able to tail a structured log file in any format, create a ruby config file, and pass it with `-c <file>`.
|
268
|
+
|
269
|
+
For example, below we'll define a custom matcher that extracts our token from a CSV log file.
|
270
|
+
|
271
|
+
```
|
272
|
+
# config/custom_csv_matcher.rb
|
273
|
+
|
274
|
+
# This matcher extracts platform, UID and IP from the following CSV string:
|
275
|
+
# 2018-05-02 21:51:44.031,25928,3997,th-M4wDQM4w0,web,j5v-dzg0J,69.181.72.240,e2b1be795372c385c92a7df420752992
|
276
|
+
|
277
|
+
class CSVMatcher
|
278
|
+
def tokenize(line)
|
279
|
+
words = line.split(',')
|
280
|
+
platform, ip, uid = [ words[4], web[6], web[7] ]
|
281
|
+
[platform, ip, uid].join(':')
|
282
|
+
end
|
283
|
+
|
284
|
+
def matches?(line)
|
285
|
+
line =~ /x-rqst/
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
Turnstile.config.custom_matcher = CSVMatcher.new
|
290
|
+
```
|
291
|
+
|
292
|
+
The above matcher defines a very simple block that receives a string as input, and returns a string which is a combined value from:
|
293
|
+
|
294
|
+
```ruby
|
295
|
+
"#{platform}:#{ip}:#{user_id}"
|
296
|
+
```
|
297
|
+
|
298
|
+
For example, `ios:1.4.54.25:fg7988798779` is a valid token. Note that IP addresses are not necessary for the purposes of tracking, and can be omitted.
|
299
|
+
|
300
|
+
With the above file defined, we would start turnstile's collector process as follows, tailing the CSV log file:
|
301
|
+
|
302
|
+
```bash
|
303
|
+
turnstile -f log/production_log.csv -F custom -c config/custom_csv_matcher.rb
|
304
|
+
```
|
305
|
+
|
116
306
|
### Examples
|
117
307
|
|
118
308
|
For example:
|
@@ -137,8 +327,7 @@ For example:
|
|
137
327
|
^Ctrl-C
|
138
328
|
```
|
139
329
|
|
140
|
-
Note that ideally you should run ```turnstile``` on all app servers, for completeness, and because
|
141
|
-
this does not incur any additional cost for the application (as user tracking is happening outside web request).
|
330
|
+
Note that ideally you should run ```turnstile``` on all app servers, for completeness, and because this does not incur any additional cost for the application (as user tracking is happening outside web request).
|
142
331
|
|
143
332
|
### Reporting
|
144
333
|
|
data/Rakefile
CHANGED
data/bin/turnstile
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
require 'turnstile'
|
3
|
+
lib_path = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH << lib_path if File.exist?(lib_path) && !$LOAD_PATH.include?(lib_path)
|
7
5
|
|
8
|
-
require 'turnstile
|
6
|
+
require 'turnstile'
|
9
7
|
|
10
|
-
Turnstile::Runner.new(ARGV).execute!
|
8
|
+
Turnstile::CLI::Runner.new(ARGV).execute!
|
11
9
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# For a log file of the following format:
|
2
|
+
#
|
3
|
+
# I│2018-05-02 07:47:58.770 │ 25887 ⤶ 3997 │ th-M4wDQDbBB ❯❯❯ time: 73.8ms ✔ │ x-rqst │ src=android │ uid=0xFFFFD978998789 │ ip=47.23.6.197 │ sess=BGHSsdCsX5VvsN1jLsAR │ REQ GET │ 200 │ /api/v3/dashboard
|
4
|
+
# I│2018-05-02 21:51:43.925 │ 29122 ⤶ 3997 │ th-7lxrgeJZA ❯❯❯ time: 80.6ms ✔ │ x-rqst │ src=android │ uid=0xF9909808f90809 │ ip=47.21.6.197 │ sess=BGHSsdCsX5VvsN1jLsAR │ REQ GET │ 200 │ /api/v4/stuff
|
5
|
+
#
|
6
|
+
module Application
|
7
|
+
class CSVLogFormat
|
8
|
+
def tokenize(line)
|
9
|
+
platform = line.scan(/src=(\w*)/).flatten.first
|
10
|
+
ip = line.scan(/ip=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/).flatten.first
|
11
|
+
uid = line.scan(/sess=(\w*)/).flatten.first
|
12
|
+
|
13
|
+
[platform, ip, uid].join(':')
|
14
|
+
end
|
15
|
+
|
16
|
+
def matches?(line)
|
17
|
+
line =~ /x-rqst/
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Turnstile.config.custom_matcher = Application::CSVLogFormat.new
|
data/lib/turnstile.rb
CHANGED
@@ -1,19 +1,48 @@
|
|
1
1
|
require 'turnstile/version'
|
2
2
|
require 'turnstile/configuration'
|
3
|
+
require 'turnstile/commands'
|
4
|
+
require 'turnstile/logger'
|
5
|
+
require 'turnstile/dependencies'
|
3
6
|
require 'turnstile/sampler'
|
4
|
-
require 'turnstile/adapter'
|
5
7
|
require 'turnstile/tracker'
|
6
8
|
require 'turnstile/observer'
|
7
|
-
require 'turnstile/
|
9
|
+
require 'turnstile/redis/adapter'
|
8
10
|
require 'turnstile/collector'
|
9
|
-
|
11
|
+
|
12
|
+
require 'turnstile/cli/runner'
|
10
13
|
|
11
14
|
module Turnstile
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
+
class CommandNotFoundError < StandardError; end
|
16
|
+
class ConfigFileError < StandardError; end
|
17
|
+
class HiredisDriverNotFound < StandardError; end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
attr_accessor :debug
|
21
|
+
|
22
|
+
def debug?
|
23
|
+
self.debug
|
24
|
+
end
|
25
|
+
|
26
|
+
def configure(&block)
|
27
|
+
@configuration = create_config.configure(&block)
|
28
|
+
end
|
15
29
|
|
16
|
-
|
17
|
-
|
30
|
+
def config
|
31
|
+
@configuration ||= create_config
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def create_config
|
37
|
+
::Turnstile::Configuration.new
|
38
|
+
end
|
18
39
|
end
|
19
40
|
end
|
41
|
+
|
42
|
+
Kernel.send(:define_method, :tdb) do |msg, io = STDOUT|
|
43
|
+
io.puts ''.green + ' debug '.black.on.green+ ''.green + ' —— ' + msg
|
44
|
+
end
|
45
|
+
|
46
|
+
Kernel.send(:define_method, :terr) do |msg, io = STDERR|
|
47
|
+
io.puts ''.bold.red + ' error '.bold.white.on.red + ''.red + ' —— ' + msg
|
48
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'turnstile/dependencies'
|
2
|
+
|
3
|
+
module Turnstile
|
4
|
+
module CLI
|
5
|
+
class Launcher
|
6
|
+
include Dependencies
|
7
|
+
|
8
|
+
attr_reader :stdin, :stdout, :stderr, :sinatra_thread
|
9
|
+
attr_accessor :options
|
10
|
+
|
11
|
+
def initialize(options, stdin = STDIN, stdout = STDOUT, stderr = STDERR)
|
12
|
+
self.options = options
|
13
|
+
@stdin, @stdout, @stderr= stdin, stdout, stderr
|
14
|
+
end
|
15
|
+
|
16
|
+
def launch
|
17
|
+
launch_sinatra_app if options[:web]
|
18
|
+
launch_signal_handler
|
19
|
+
|
20
|
+
tdb "config: #{config.to_h}" if Turnstile.config.trace
|
21
|
+
result = if options[:show]
|
22
|
+
command(:show).execute(options[:show_format] || :json, options[:delimiter])
|
23
|
+
|
24
|
+
elsif options[:token]
|
25
|
+
tracker.track_token(options[:token], options[:delimiter])
|
26
|
+
|
27
|
+
elsif options[:flushdb]
|
28
|
+
command(:flushdb).execute
|
29
|
+
|
30
|
+
elsif options[:print_keys]
|
31
|
+
command(:print_keys).execute
|
32
|
+
|
33
|
+
elsif options[:file]
|
34
|
+
controller.start
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
puts result if result && !result.empty?
|
39
|
+
rescue SystemExit, SignalException
|
40
|
+
exit 6
|
41
|
+
rescue Exception => e
|
42
|
+
handle_error('Error', e)
|
43
|
+
ensure
|
44
|
+
sinatra_thread.join if sinatra_thread
|
45
|
+
end
|
46
|
+
|
47
|
+
def launch_signal_handler
|
48
|
+
Signal.trap('INT') { sleep 1; Kernel.exit(5) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def launch_sinatra_app
|
52
|
+
@sinatra_thread = Thread.new do
|
53
|
+
require_relative '../web_app'
|
54
|
+
Kernel.exit(0)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def command(name)
|
59
|
+
::Turnstile::Commands.command(name).new(options)
|
60
|
+
end
|
61
|
+
|
62
|
+
def handle_error(title, e)
|
63
|
+
if options[:trace]
|
64
|
+
trace = e.backtrace.reverse
|
65
|
+
last = trace.pop
|
66
|
+
stderr.puts trace.join("\n")
|
67
|
+
stderr.puts last.bold.red
|
68
|
+
end
|
69
|
+
stderr.puts
|
70
|
+
stderr.puts title.bold.yellow
|
71
|
+
stderr.puts "\t" + e.message.red
|
72
|
+
stderr.puts
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def controller
|
78
|
+
@controller ||= Turnstile::Collector::Controller.new(options)
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|