turnstile-rb 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.travis.yml +22 -0
- data/Gemfile +6 -0
- data/Guardfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +197 -0
- data/Rakefile +1 -0
- data/bin/turnstile +91 -0
- data/lib/turnstile.rb +19 -0
- data/lib/turnstile/adapter.rb +60 -0
- data/lib/turnstile/collector.rb +8 -0
- data/lib/turnstile/collector/formats.rb +46 -0
- data/lib/turnstile/collector/log_reader.rb +81 -0
- data/lib/turnstile/collector/matcher.rb +21 -0
- data/lib/turnstile/collector/runner.rb +71 -0
- data/lib/turnstile/collector/updater.rb +86 -0
- data/lib/turnstile/configuration.rb +38 -0
- data/lib/turnstile/logger.rb +47 -0
- data/lib/turnstile/nad.rb +16 -0
- data/lib/turnstile/observer.rb +33 -0
- data/lib/turnstile/rb.rb +1 -0
- data/lib/turnstile/sampler.rb +20 -0
- data/lib/turnstile/tracker.rb +17 -0
- data/lib/turnstile/version.rb +3 -0
- data/spec/fixtures/sample-production.log +4 -0
- data/spec/fixtures/sample-production.log.json +37 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/turnstile/adapter_spec.rb +71 -0
- data/spec/turnstile/collector/log_reader_spec.rb +114 -0
- data/spec/turnstile/configuration_spec.rb +78 -0
- data/spec/turnstile/nad_spec.rb +31 -0
- data/spec/turnstile/observer_spec.rb +65 -0
- data/spec/turnstile/sampler_spec.rb +35 -0
- data/spec/turnstile/tracker_spec.rb +25 -0
- data/spec/turnstile_spec.rb +68 -0
- data/turnstile-rb.gemspec +36 -0
- metadata +277 -0
data/lib/turnstile/rb.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative '../turnstile'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Turnstile
|
2
|
+
class Sampler
|
3
|
+
def extrapolate(n)
|
4
|
+
(n * 100.0 / sampling_rate).to_i
|
5
|
+
end
|
6
|
+
|
7
|
+
# this method uses a unique string to integer hashing (object->hash)
|
8
|
+
# sampling shifts depending on the day of the month so that sampling
|
9
|
+
# does not stick to the same people all the time
|
10
|
+
def sample(uid)
|
11
|
+
((uid.hash + Time.now.day) % 100) < sampling_rate
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def sampling_rate
|
17
|
+
Turnstile.config.sampling_rate
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Turnstile
|
2
|
+
class Tracker
|
3
|
+
def track(uid, platform = 'unknown', ip = nil)
|
4
|
+
adapter.add(uid, platform, ip) if sampler.sample(uid)
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def adapter
|
10
|
+
@adapter ||= Adapter.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def sampler
|
14
|
+
@sampler ||= Sampler.new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
{"method":"GET","path":"/api/v1/users/5462583","format":"json","controller":"Api::V1::UsersController","action":"show","status":200,"duration":17.69,"view":11.92,"db":3.15,"time":"2017-03-22 10:46:17.592 -0700","pid":42045,"user_id":5462583,"platform":"ipad","session_id":"U6Dx4zoASN5KYfh6qLyU","params":{"last_view":"WMagicFeedViewController","client_id":"101","time":"1490204776","auth_token":"U6Dx4zoASN5KYfh6qLyU","format":"json","controller":"api/v1/users","action":"show","id":"5462583","user":{}},"ip_address":"69.61.173.104","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
2
|
+
called users_with_profile_by_product with per=10
|
3
|
+
{"method":"GET","path":"/api/v1/search/suggestions","format":"json","controller":"Api::V1::SearchController","action":"autocomplete","status":200,"duration":33.92,"view":17.07,"db":7.85,"time":"2017-03-22 10:46:17.622 -0700","pid":42021,"user_id":20860276,"platform":"iphone","session_id":"SRj6ePyUU9ey3y7DrFZw","params":{"query":"crop","type":"","last_view":"WProductSearchResultsViewController","client_id":"101","time":"1490204776","auth_token":"SRj6ePyUU9ey3y7DrFZw","format":"json","controller":"api/v1/search","action":"autocomplete","search":{}},"ip_address":"107.152.104.81","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
4
|
+
{"method":"GET","path":"/api/v1/products/39862579/details","format":"json","controller":"Api::V1::ProductsController","action":"details","status":200,"duration":70.26,"view":50.62,"db":15.4,"time":"2017-03-22 10:46:17.606 -0700","pid":42025,"user_id":null,"platform":"android","session_id":null,"params":{"format":"json","controller":"api/v1/products","action":"details","id":"39862579"},"ip_address":"23.235.47.28","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
5
|
+
{"method":"GET","path":"/api/v1/themes/products/5","format":"json","controller":"Api::V1::ThemesController","action":"products","status":200,"duration":944.52,"view":489.56,"db":195.77,"time":"2017-03-22 10:46:16.746 -0700","pid":42042,"user_id":5445274,"platform":"iphone","session_id":"qRQCRidfy88bHs1MzfYP","params":{"theme_path":"women,dresses,casual","last_view":"theme_Casual","client_id":"101","time":"1490204775","auth_token":"qRQCRidfy88bHs1MzfYP","format":"json","controller":"api/v1/themes","action":"products","page":"5","theme":{}},"ip_address":"68.134.103.85","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
6
|
+
{"method":"GET","path":"/api/v1/search/products/4","format":"json","controller":"Api::V1::ProductSearchController","action":"products","status":200,"duration":4.21,"view":0.0,"db":0.0,"time":"2017-03-22 10:46:17.693 -0700","pid":42021,"user_id":21086925,"platform":"iphone","session_id":"JKiDpyhSB5jaxwV9tECh","params":{"last_view":"theme_On Sale Now","on_sale":"true","per_page":"60","query":"","preprocess_query":"true","user":"brittnaydickinson","client_id":"101","time":"1490204776","auth_token":"JKiDpyhSB5jaxwV9tECh","format":"json","controller":"api/v1/product_search","action":"products","page":"4","product_search":{}},"ip_address":"168.17.61.221","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
7
|
+
searching solr for products (grand AND theft AND auto).. (elapsed: 1202.27ms)
|
8
|
+
{"method":"GET","path":"/api/v1/users/20447675","format":"json","controller":"Api::V1::UsersController","action":"show","status":200,"duration":23.39,"view":17.63,"db":3.29,"time":"2017-03-22 10:46:17.783 -0700","pid":42042,"user_id":20447675,"platform":"android","session_id":"vZQ5pJiPTLTsg7honwV5","params":{"time":"1490204777","client_id":"android","auth_token":"vZQ5pJiPTLTsg7honwV5","format":"json","controller":"api/v1/users","action":"show","id":"20447675"},"ip_address":"174.218.137.85","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
9
|
+
{"method":"GET","path":"/api/v1/users/19981112","format":"json","controller":"Api::V1::UsersController","action":"show","status":200,"duration":20.27,"view":14.9,"db":2.53,"time":"2017-03-22 10:46:17.793 -0700","pid":42030,"user_id":19981112,"platform":"iphone","session_id":"yyKeorp4QD98sxN4zLBu","params":{"last_view":"WReviewPhotosViewController","client_id":"101","time":"1490204777","auth_token":"yyKeorp4QD98sxN4zLBu","format":"json","controller":"api/v1/users","action":"show","id":"19981112","user":{}},"ip_address":"73.194.214.138","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
10
|
+
{"method":"GET","path":"/api/v1/users/4970486/order_surveys/current","format":"json","controller":"Api::V1::OrderSurveysController","action":"current","status":200,"duration":24.9,"view":10.6,"db":6.78,"time":"2017-03-22 10:46:17.841 -0700","pid":42030,"user_id":4970486,"platform":"iphone","session_id":"5z68kWXoCmWrwLidCut1","params":{"survey_version":"2","last_view":"WGuestLandingViewController","client_id":"101","time":"1490204777","auth_token":"5z68kWXoCmWrwLidCut1","format":"json","controller":"api/v1/order_surveys","action":"current","id":"4970486","order_survey":{}},"ip_address":"66.87.112.30","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
11
|
+
{"method":"GET","path":"/api/v1/saves/4SB8U-1Am9u-4ixC5","format":"json","controller":"Api::V1::SavesController","action":"show","status":200,"duration":49.01,"view":0.3,"db":9.31,"time":"2017-03-22 10:46:17.873 -0700","pid":42042,"user_id":17344742,"platform":"iphone","session_id":"4eKMZJ4nggzvkix29zpS","params":{"last_view":"product","client_id":"101","time":"1490204776","auth_token":"4eKMZJ4nggzvkix29zpS","format":"json","controller":"api/v1/saves","action":"show","id":"4SB8U-1Am9u-4ixC5","save":{}},"ip_address":"70.210.128.241","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
12
|
+
{"method":"GET","path":"/api/v1/products/trending","format":"json","controller":"Api::V1::TrendingController","action":"show","status":200,"duration":45.76,"view":17.29,"db":2.34,"time":"2017-03-22 10:46:17.878 -0700","pid":42021,"user_id":4970486,"platform":"iphone","session_id":"5z68kWXoCmWrwLidCut1","params":{"last_view":"trending","ignore_price_filters":"true","type":"","product_categories_from_settings":"true","client_id":"101","time":"1490204777","auth_token":"5z68kWXoCmWrwLidCut1","format":"json","controller":"api/v1/trending","action":"show","trending":{}},"ip_address":"66.87.112.30","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
13
|
+
{"method":"GET","path":"/api/v1/products/26548478/reviews","format":"json","controller":"Api::V1::ReviewsController","action":"product_index","status":200,"duration":21.28,"view":5.36,"db":6.0,"time":"2017-03-22 10:46:17.950 -0700","pid":42021,"user_id":10768109,"platform":"iphone","session_id":"RcT9ekNbhPsqydG4Pv7V","params":{"last_view":"WProductReviewsTableViewController","client_id":"101","time":"1490204776","auth_token":"RcT9ekNbhPsqydG4Pv7V","format":"json","controller":"api/v1/reviews","action":"product_index","product_id":"26548478","review":{}},"ip_address":"12.7.40.147","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
14
|
+
{"method":"GET","path":"/api/v1/products/40701853/variants/with_availability","format":"json","controller":"Api::V1::ProductsController","action":"variants_with_availability","status":200,"duration":312.66,"view":3.71,"db":26.85,"time":"2017-03-22 10:46:17.700 -0700","pid":42045,"user_id":8554487,"platform":"iphone","session_id":"qVwqQKpoBEtpE5H7TFpd","params":{"last_view":"product","f_type":"sf_195_cents_markup","p_type":"default","client_id":"101","time":"1490204777","auth_token":"qVwqQKpoBEtpE5H7TFpd","format":"json","controller":"api/v1/products","action":"variants_with_availability","id":"40701853","product":{}},"ip_address":"199.79.169.163","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
15
|
+
called users_with_profile_by_product with per=10
|
16
|
+
{"method":"GET","path":"/api/v1/products/50891048/details","format":"json","controller":"Api::V1::ProductsController","action":"details","status":200,"duration":76.89,"view":58.04,"db":15.28,"time":"2017-03-22 10:46:18.020 -0700","pid":42021,"user_id":null,"platform":"iphone","session_id":null,"params":{"f_type":"sf_180_cents_markup","p_type":"","format":"json","controller":"api/v1/products","action":"details","id":"50891048","product":{}},"ip_address":"23.235.47.40","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
17
|
+
{"method":"GET","path":"/search","format":"html","controller":"SearchController","action":"products","status":200,"duration":1595.73,"view":346.95,"db":50.27,"time":"2017-03-22 10:46:16.513 -0700","pid":42077,"user_id":null,"platform":"desktop","session_id":"90bba1d374001aedfe5b31525a528513","params":{"ref":"gsl","query":"Grand Theft Auto","page":"2","controller":"search","action":"products"},"ip_address":"162.223.13.23","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":"6LZrqw6xsO3nEiyheu340L7ObsGeCFmM11BILUFqljQ=","verified_request":true}
|
18
|
+
{"method":"HEAD","path":"/shop/custom-timberland-boots-etsy","format":"html","controller":"SeoPhrasesController","action":"show","status":200,"duration":1167.48,"view":391.63,"db":389.24,"time":"2017-03-22 10:46:16.966 -0700","pid":42079,"user_id":null,"platform":"desktop","session_id":"c00000b340017143f8502d0a2520274e","params":{"wnl_page_type":"Shop Page","wnl_page_version":"1.0","controller":"seo_phrases","action":"show","slug":"custom-timberland-boots-etsy"},"ip_address":"52.90.73.229","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":"4x1n4QllLThOYpXmR8bJgb8k+akwy1tBznIJWAttsLs=","verified_request":true}
|
19
|
+
{"method":"GET","path":"/api/v1/users/me/activity_feed/unified/2","format":"json","controller":"Api::V1::MagicFeedController","action":"unified","status":200,"duration":168.39,"view":70.15,"db":41.48,"time":"2017-03-22 10:46:17.973 -0700","pid":42042,"user_id":19719275,"platform":"iphone","session_id":"ywEoKieAZSvU7yoEbeyV","params":{"type":"no_affiliate_in_mf","last_view":"WMagicFeedViewController","client_id":"101","time":"1490204777","auth_token":"ywEoKieAZSvU7yoEbeyV","format":"json","controller":"api/v1/magic_feed","action":"unified","page":"2","magic_feed":{}},"ip_address":"69.80.178.31","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
20
|
+
Can't verify CSRF token authenticity
|
21
|
+
called users_with_profile_by_product with per=10
|
22
|
+
{"method":"GET","path":"/api/v1/saves/48WAQ-FpJA-4hvZ5/comments","format":"json","controller":"Api::V1::CommentsController","action":"index","status":200,"duration":7.9,"view":0.12,"db":2.36,"time":"2017-03-22 10:46:18.272 -0700","pid":42042,"user_id":null,"platform":"iphone","session_id":null,"params":{"order":"least_recent_first","format":"json","type":"save","controller":"api/v1/comments","action":"index","save_id":"48WAQ-FpJA-4hvZ5","comment":{}},"ip_address":"23.235.47.22","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
23
|
+
{"method":"GET","path":"/api/v1/saves/aTqO-8IVC-ew1O","format":"json","controller":"Api::V1::SavesController","action":"show","status":200,"duration":78.78,"view":0.38,"db":11.27,"time":"2017-03-22 10:46:18.229 -0700","pid":42079,"user_id":1977750,"platform":"iphone","session_id":"kHg6giEpAAQykW8uM6nf","params":{"last_view":"product","client_id":"101","time":"1490204777","auth_token":"kHg6giEpAAQykW8uM6nf","format":"json","controller":"api/v1/saves","action":"show","id":"aTqO-8IVC-ew1O","save":{}},"ip_address":"70.209.144.195","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
24
|
+
{"method":"GET","path":"/api/v1/products/40479095/comments","format":"json","controller":"Api::V1::CommentsController","action":"index","status":200,"duration":87.47,"view":0.16,"db":20.6,"time":"2017-03-22 10:46:18.298 -0700","pid":42088,"user_id":null,"platform":"iphone","session_id":null,"params":{"paged":"1","order":"least_recent_first","format":"json","type":"product","controller":"api/v1/comments","action":"index","product_id":"40479095","comment":{}},"ip_address":"23.235.47.40","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
25
|
+
{"method":"GET","path":"/api/v1/products/18531328/details","format":"json","controller":"Api::V1::ProductsController","action":"details","status":200,"duration":177.23,"view":153.44,"db":20.25,"time":"2017-03-22 10:46:18.223 -0700","pid":42045,"user_id":null,"platform":"iphone","session_id":null,"params":{"f_type":"sf_195_cents_markup","p_type":"default","format":"json","controller":"api/v1/products","action":"details","id":"18531328","product":{}},"ip_address":"23.235.47.51","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
26
|
+
{"method":"GET","path":"/api/v1/suggestions/products/8694532","format":"json","controller":"Api::V1::SuggestionsController","action":"products","status":200,"duration":451.62,"view":0.18,"db":153.67,"time":"2017-03-22 10:46:17.975 -0700","pid":42026,"user_id":null,"platform":"iphone","session_id":null,"params":{"type":"","format":"json","controller":"api/v1/suggestions","action":"products","product_id":"8694532","suggestion":{}},"ip_address":"23.235.47.44","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
27
|
+
{"method":"GET","path":"/api/v1/search/suggestions","format":"json","controller":"Api::V1::SearchController","action":"autocomplete","status":200,"duration":34.19,"view":17.4,"db":5.94,"time":"2017-03-22 10:46:18.400 -0700","pid":42079,"user_id":20860276,"platform":"iphone","session_id":"SRj6ePyUU9ey3y7DrFZw","params":{"query":"crop ","type":"","last_view":"WProductSearchResultsViewController","client_id":"101","time":"1490204777","auth_token":"SRj6ePyUU9ey3y7DrFZw","format":"json","controller":"api/v1/search","action":"autocomplete","search":{}},"ip_address":"107.152.104.81","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
28
|
+
called users_with_profile_by_product with per=10
|
29
|
+
{"method":"POST","path":"/api/v1/orders","format":"json","controller":"Api::V1::OrdersController","action":"new","status":200,"duration":343.03,"view":66.05,"db":142.68,"time":"2017-03-22 10:46:18.170 -0700","pid":42077,"user_id":9675208,"platform":"iphone","session_id":"9m5w4vTrfj7FVie9EMyN","params":{"product_id":"53487642","f_type":"sf_195_cents_markup","merge_tax_and_service_fee":"true","p_type":"default","last_view":"product","client_id":"101","time":"1490204776","auth_token":"9m5w4vTrfj7FVie9EMyN","format":"json","controller":"api/v1/orders","action":"new","order":{}},"ip_address":"172.56.29.65","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
30
|
+
{"method":"GET","path":"/api/v1/products/42913240/details","format":"json","controller":"Api::V1::ProductsController","action":"details","status":200,"duration":70.92,"view":48.06,"db":19.16,"time":"2017-03-22 10:46:18.457 -0700","pid":42026,"user_id":null,"platform":"iphone","session_id":null,"params":{"f_type":"sf_195_cents_markup","p_type":"fbw115","format":"json","controller":"api/v1/products","action":"details","id":"42913240","product":{}},"ip_address":"23.235.47.43","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
31
|
+
{"method":"GET","path":"/api/v1/suggestions/products/39862579","format":"json","controller":"Api::V1::SuggestionsController","action":"products","status":200,"duration":430.93,"view":0.36,"db":75.16,"time":"2017-03-22 10:46:18.100 -0700","pid":42009,"user_id":null,"platform":"android","session_id":null,"params":{"type":"buyable_and_affiliate","format":"json","controller":"api/v1/suggestions","action":"products","product_id":"39862579"},"ip_address":"23.235.47.49","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
32
|
+
{"method":"GET","path":"/api/v1/users/678238/collections","format":"json","controller":"Api::V1::UsersController","action":"collections","status":200,"duration":12.89,"view":0.14,"db":4.59,"time":"2017-03-22 10:46:18.608 -0700","pid":42026,"user_id":678238,"platform":"iphone","session_id":"L4dgADorpsmQUWFv5vJ7","params":{"last_view":"WMagicFeedViewController","client_id":"101","time":"1490204777","auth_token":"L4dgADorpsmQUWFv5vJ7","format":"json","controller":"api/v1/users","action":"collections","id":"678238","user":{}},"ip_address":"132.189.82.11","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
33
|
+
{"method":"GET","path":"/api/v1/products/34552155/variants/with_availability","format":"json","controller":"Api::V1::ProductsController","action":"variants_with_availability","status":200,"duration":77.38,"view":0.67,"db":17.95,"time":"2017-03-22 10:46:18.595 -0700","pid":42009,"user_id":10151188,"platform":"android","session_id":"uK8wwYNZjqXnoZz1bpGm","params":{"time":"1490204777","client_id":"android","auth_token":"uK8wwYNZjqXnoZz1bpGm","format":"json","controller":"api/v1/products","action":"variants_with_availability","id":"34552155"},"ip_address":"172.58.67.103","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
34
|
+
{"method":"GET","path":"/api/v1/users/5754901/collections","format":"json","controller":"Api::V1::UsersController","action":"collections","status":200,"duration":7.72,"view":0.12,"db":1.02,"time":"2017-03-22 10:46:18.668 -0700","pid":42088,"user_id":5754901,"platform":"iphone","session_id":"zqsActyuTd8zXpBo92ct","params":{"last_view":"trending","client_id":"101","time":"1490204777","auth_token":"zqsActyuTd8zXpBo92ct","format":"json","controller":"api/v1/users","action":"collections","id":"5754901","user":{}},"ip_address":"70.210.64.69","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":null,"verified_request":null}
|
35
|
+
{"method":"POST","path":"/int/admin/stores/591600","format":"html","controller":"Admin::StoresController","action":"update","status":302,"duration":44.47,"view":0.0,"db":11.61,"location":"https://wanelo.co/int/admin/stores/591600","time":"2017-03-22 10:46:06.609 -0700","pid":75556,"user_id":14092896,"platform":"desktop","session_id":"2730608f1d862d6250f541b19ce8ffd3","params":{"_method":"put","authenticity_token":"t3A2nHFu5hyKmM66NPdEBUKBU4ePPlFGr0ufr64Qpzc=","store":{"category_id_with_event":"5"},"action":"update","controller":"admin/stores","id":"591600"},"ip_address":"182.255.42.49","hostname":null,"xhr":false,"authenticity_token":"t3A2nHFu5hyKmM66NPdEBUKBU4ePPlFGr0ufr64Qpzc=","authenticity_token_header":null,"expected_auth_token":"t3A2nHFu5hyKmM66NPdEBUKBU4ePPlFGr0ufr64Qpzc=","verified_request":true}
|
36
|
+
{"method":"GET","path":"/int/admin/stores/536402","format":"html","controller":"Admin::StoresController","action":"show","status":200,"duration":696.01,"view":267.24,"db":229.64,"time":"2017-03-22 10:46:05.977 -0700","pid":75568,"user_id":14092901,"platform":"desktop","session_id":"777dbb41f8d29008b1555630f1fb31a3","params":{"action":"show","controller":"admin/stores","id":"536402"},"ip_address":"103.196.139.175","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":"IV7BLkptllMtK+FAerenwT4bjFGaLymGD4jei9znUGw=","verified_request":true}
|
37
|
+
{"method":"GET","path":"/store/joyworkshoppe","format":"html","controller":"Legacy::StoresController","action":"show","status":200,"duration":469.7,"view":434.78,"db":16.0,"time":"2017-03-22 10:46:17.090 -0700","pid":42025,"user_id":null,"platform":"mobile_web","session_id":"5058494231f66532c08371587ed4eeb7","params":{"wnl_page_type":"Wanelo.com Store Page","wnl_page_version":"1.0","controller":"legacy/stores","action":"show","id":"joyworkshoppe"},"ip_address":"75.155.232.189","hostname":null,"xhr":false,"authenticity_token":null,"authenticity_token_header":null,"expected_auth_token":"Nf9dMhJlWzFHkzUouHnSLm1Cd+1EaSesoa75HhkJRkw=","verified_request":true}
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
|
8
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
9
|
+
|
10
|
+
require 'rubygems'
|
11
|
+
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
|
12
|
+
require 'simplecov'
|
13
|
+
|
14
|
+
SimpleCov.start
|
15
|
+
|
16
|
+
require 'turnstile'
|
17
|
+
require 'rspec/its'
|
18
|
+
|
19
|
+
RSpec.configure do |config|
|
20
|
+
config.order = 'random'
|
21
|
+
config.before :each do
|
22
|
+
Turnstile::Adapter.new.redis.flushdb
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Turnstile::Adapter do
|
4
|
+
|
5
|
+
subject { Turnstile::Adapter.new }
|
6
|
+
|
7
|
+
let(:redis) { subject.send(:redis) }
|
8
|
+
|
9
|
+
let(:uid) { 1238438 }
|
10
|
+
let(:other_uid) { 1238439 }
|
11
|
+
let(:another_uid) { 1238440 }
|
12
|
+
|
13
|
+
let(:ip) { '1.2.3.4' }
|
14
|
+
let(:another_ip) { '4.3.2.1' }
|
15
|
+
|
16
|
+
let(:platform) { :ios }
|
17
|
+
let(:another_platform) { :android }
|
18
|
+
|
19
|
+
describe '#add' do
|
20
|
+
it 'calls redis with the correct params' do
|
21
|
+
key = "t:#{uid}:#{platform}:#{ip}"
|
22
|
+
expect(redis).to receive(:setex).once.with(key, Turnstile.config.activity_interval, 1)
|
23
|
+
subject.add(uid, platform, ip)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#fetch' do
|
28
|
+
let(:sort_lambda) { ->(a, b) { a[:uid] <=> b[:uid] } }
|
29
|
+
let(:expected_hash) do
|
30
|
+
[
|
31
|
+
{ uid: uid.to_s, platform: platform.to_s, ip: ip },
|
32
|
+
{ uid: other_uid.to_s, platform: platform.to_s, ip: ip },
|
33
|
+
{ uid: another_uid.to_s, platform: another_platform.to_s, ip: another_ip },
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
before do
|
38
|
+
subject.add(uid, platform, ip)
|
39
|
+
subject.add(other_uid, platform, ip)
|
40
|
+
subject.add(another_uid, another_platform, another_ip)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'pulls the platform specific stats from redis' do
|
44
|
+
expect(subject.fetch.sort(&sort_lambda)).to eq(expected_hash.sort(&sort_lambda))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#aggregate' do
|
49
|
+
let(:expected_hash) do
|
50
|
+
{
|
51
|
+
'android' => 3,
|
52
|
+
'ios' => 2,
|
53
|
+
'total' => 5
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
before do
|
58
|
+
subject.add(123, :android, ip)
|
59
|
+
subject.add(124, :android, ip)
|
60
|
+
subject.add(125, :android, ip)
|
61
|
+
|
62
|
+
subject.add(200, :ios, ip)
|
63
|
+
subject.add(201, :ios, ip)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should calculated proper aggregation' do
|
67
|
+
expect(subject.aggregate).to eql expected_hash
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'file/tail'
|
3
|
+
require 'timeout'
|
4
|
+
require 'thread'
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
require 'turnstile/collector/log_reader'
|
8
|
+
|
9
|
+
describe Turnstile::Collector::LogReader do
|
10
|
+
include Timeout
|
11
|
+
|
12
|
+
def consume_file(reader, read_timeout)
|
13
|
+
hash = {}
|
14
|
+
counter = 0
|
15
|
+
|
16
|
+
run_reader(read_timeout) do
|
17
|
+
reader.read do |token|
|
18
|
+
counter += 1
|
19
|
+
hash[token] = 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
return counter, hash
|
23
|
+
end
|
24
|
+
|
25
|
+
def run_reader(read_timeout, &block)
|
26
|
+
t_reader = Thread.new do
|
27
|
+
begin
|
28
|
+
timeout(read_timeout) do
|
29
|
+
block.call
|
30
|
+
end
|
31
|
+
rescue Timeout::Error
|
32
|
+
end
|
33
|
+
end
|
34
|
+
t_reader.join
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:queue) { Queue.new }
|
38
|
+
let(:read_timeout) { 0.1 }
|
39
|
+
let(:consume_file_result) { consume_file(reader, read_timeout) }
|
40
|
+
let(:counter) { consume_file_result.first }
|
41
|
+
let(:hash) { consume_file_result.last }
|
42
|
+
|
43
|
+
before { reader.file.backward(1000) }
|
44
|
+
|
45
|
+
context 'json log file' do
|
46
|
+
let(:file) { 'spec/fixtures/sample-production.log.json' }
|
47
|
+
let(:reader) { Turnstile::Collector::LogReader.json_formatted(file, queue) }
|
48
|
+
|
49
|
+
let(:expected_uniques) { 28 }
|
50
|
+
let(:expected_total) { 31 }
|
51
|
+
let(:expected_key) { 'ipad:69.61.173.104:5462583' }
|
52
|
+
|
53
|
+
context '#read' do
|
54
|
+
it 'should be able to read and parse IPs from a static file' do
|
55
|
+
expect(counter).to eql(expected_total)
|
56
|
+
expect(hash.keys.size).to eql(expected_uniques)
|
57
|
+
expect(hash.keys).to include(expected_key)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context '#process!' do
|
62
|
+
it 'should read values into the queue' do
|
63
|
+
run_reader(read_timeout) { reader.process! }
|
64
|
+
expect(queue.size).to eql(31)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'pipe delimited file' do
|
70
|
+
let(:file) { 'spec/fixtures/sample-production.log' }
|
71
|
+
let(:reader) { Turnstile::Collector::LogReader.pipe_delimited(file, queue) }
|
72
|
+
|
73
|
+
let(:log_reader) { Turnstile::Collector::LogReader }
|
74
|
+
|
75
|
+
let(:expected_uniques) { 2 }
|
76
|
+
let(:expected_total) { 4 }
|
77
|
+
let(:expected_key) { 'desktop:124.5.4.3:AF39945f8f87F' }
|
78
|
+
|
79
|
+
context 'matcher' do
|
80
|
+
subject(:matcher) { log_reader.delimited_matcher }
|
81
|
+
|
82
|
+
its(:regexp) { should_not be_nil }
|
83
|
+
|
84
|
+
its(:extractor) { should_not be_nil }
|
85
|
+
its(:extractor) { should be_kind_of(Proc) }
|
86
|
+
|
87
|
+
it 'should match lines in the file' do
|
88
|
+
File.open(file).each do |line|
|
89
|
+
expect(line).to match(matcher.regexp)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
it 'should extract the token from file' do
|
93
|
+
File.open(file).each do |line|
|
94
|
+
expect(matcher.token_from(line)).to_not be_nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context '#read' do
|
100
|
+
it 'should be load all matching rows' do
|
101
|
+
expect(counter).to eql(expected_total)
|
102
|
+
expect(hash.keys.size).to eql(expected_uniques)
|
103
|
+
expect(hash.keys).to include(expected_key)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context '#process!' do
|
108
|
+
it 'should read values into the queue' do
|
109
|
+
run_reader(read_timeout) { reader.process! }
|
110
|
+
expect(queue.size).to eql(expected_total)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Turnstile::Configuration do
|
4
|
+
|
5
|
+
subject(:config) { Turnstile::Configuration.new }
|
6
|
+
|
7
|
+
let(:spec_ip) { '128.23.12.8' }
|
8
|
+
let(:spec_port) { '2134' }
|
9
|
+
let(:spec_db) { '13' }
|
10
|
+
let(:spec_timeout) { 0.087 }
|
11
|
+
let(:spec_activity_interval) { 30 }
|
12
|
+
let(:spec_sampling_rate) { 10 }
|
13
|
+
|
14
|
+
context 'using redis sub-config method missing' do
|
15
|
+
before do
|
16
|
+
config.configure do |c|
|
17
|
+
c.redis.configure do |r|
|
18
|
+
r.host = spec_ip
|
19
|
+
r.port = spec_port
|
20
|
+
r.db = spec_db
|
21
|
+
r.timeout = spec_timeout
|
22
|
+
end
|
23
|
+
c.activity_interval = spec_activity_interval
|
24
|
+
c.sampling_rate = spec_sampling_rate
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'main config' do
|
29
|
+
its(:activity_interval) { should eq spec_activity_interval }
|
30
|
+
its(:sampling_rate) { should eq spec_sampling_rate }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'redis sub-config' do
|
34
|
+
subject(:redis_config) { config.redis }
|
35
|
+
its(:host) { should eq spec_ip }
|
36
|
+
its(:port) { should eq spec_port.to_i }
|
37
|
+
its(:db) { should eq spec_db.to_i }
|
38
|
+
its(:timeout) { should eq spec_timeout.to_f }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'using redis_<property> method missing' do
|
43
|
+
before do
|
44
|
+
config.configure do |c|
|
45
|
+
c.redis_host = spec_ip
|
46
|
+
c.redis_port = spec_port
|
47
|
+
c.redis_db = spec_db
|
48
|
+
c.redis_timeout = spec_timeout
|
49
|
+
c.activity_interval = spec_activity_interval
|
50
|
+
c.sampling_rate = spec_sampling_rate
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
its(:redis_host) { should eq spec_ip }
|
55
|
+
its(:redis_port) { should eq spec_port.to_i }
|
56
|
+
its(:redis_db) { should eq spec_db.to_i }
|
57
|
+
its(:redis_timeout) { should eq spec_timeout.to_f }
|
58
|
+
its(:activity_interval) { should eq spec_activity_interval }
|
59
|
+
its(:sampling_rate) { should eq spec_sampling_rate }
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'without explicit assignment' do
|
63
|
+
let(:spec_ip) { '127.0.0.1' }
|
64
|
+
let(:spec_port) { 6379 }
|
65
|
+
let(:spec_db) { 1 }
|
66
|
+
let(:spec_timeout) { 0.05 }
|
67
|
+
let(:spec_activity_interval) { 60 }
|
68
|
+
let(:spec_sampling_rate) { 100 }
|
69
|
+
|
70
|
+
its(:redis_host) { should eq spec_ip }
|
71
|
+
its(:redis_port) { should eq spec_port }
|
72
|
+
its(:redis_db) { should eq spec_db }
|
73
|
+
its(:redis_timeout) { should eq spec_timeout }
|
74
|
+
its(:activity_interval) { should eq spec_activity_interval }
|
75
|
+
its(:sampling_rate) { should eq spec_sampling_rate }
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Turnstile::Nad' do
|
4
|
+
|
5
|
+
subject { Turnstile::Nad.new }
|
6
|
+
|
7
|
+
describe '#data' do
|
8
|
+
context 'have some data' do
|
9
|
+
let(:aggregate) { {
|
10
|
+
'android' => 3,
|
11
|
+
'ios' => 2,
|
12
|
+
'total' => 5
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
let(:expected_string) {
|
17
|
+
<<-EOF
|
18
|
+
turnstile:android#{"\t"}n#{"\t"}3
|
19
|
+
turnstile:ios#{"\t"}n#{"\t"}2
|
20
|
+
turnstile:total#{"\t"}n#{"\t"}5
|
21
|
+
EOF
|
22
|
+
}
|
23
|
+
|
24
|
+
it "return data in NAD tab dilimited format" do
|
25
|
+
expect(subject).to receive(:aggregate).once.and_return(aggregate)
|
26
|
+
expect(subject.data).to eql(expected_string)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Turnstile::Observer do
|
4
|
+
|
5
|
+
subject { Turnstile::Observer.new }
|
6
|
+
|
7
|
+
let(:adapter) { subject.send(:adapter) }
|
8
|
+
|
9
|
+
let(:uid) { 1238438 }
|
10
|
+
let(:platform) { :ios }
|
11
|
+
let(:ip) { '1.2.3.4' }
|
12
|
+
|
13
|
+
let(:sample_data) { [{uid: uid, platform: platform, ip: ip}] }
|
14
|
+
|
15
|
+
let(:expected_stats) do
|
16
|
+
{
|
17
|
+
stats: {
|
18
|
+
total: 1,
|
19
|
+
platforms: {
|
20
|
+
ios: 1
|
21
|
+
}
|
22
|
+
},
|
23
|
+
users: sample_data
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
let(:extrapolated_stats) do
|
28
|
+
{
|
29
|
+
stats: {
|
30
|
+
total: 20,
|
31
|
+
platforms: {
|
32
|
+
ios: 20
|
33
|
+
}
|
34
|
+
},
|
35
|
+
users: sample_data
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#stats' do
|
40
|
+
context 'when there are users' do
|
41
|
+
before do
|
42
|
+
expect(adapter).to receive(:fetch).once.and_return(sample_data)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'fetches data from adapter and aggregates it' do
|
46
|
+
expect(subject.stats).to eql(expected_stats)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'extrapolates numbers correctly' do
|
50
|
+
allow(Turnstile.config).to receive(:sampling_rate).and_return(5)
|
51
|
+
expect(subject.stats).to eql(extrapolated_stats)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when there are no users' do
|
56
|
+
before do
|
57
|
+
expect(adapter).to receive(:fetch).once.and_return([])
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'returns 0 for total' do
|
61
|
+
expect(subject.stats[:stats][:total]).to eql 0
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|