sms-logparser 0.3.0 → 0.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9f0b662e2f8fc572e36fb90d70772df4719bd74e
4
- data.tar.gz: b689ed872012488f135a78ddd38e3583025a02e5
3
+ metadata.gz: 7d8d1676ea63d17f63d196dd955d52bcd822c28b
4
+ data.tar.gz: 6a0a0341ed6a5050cefa8871835247122f2a2934
5
5
  SHA512:
6
- metadata.gz: 141e69433bd1a57508777f58f6f21f72c7d6456c56bf13b284ac84e85fd75045ab04df0e47aa608db8f0ade2136c9a44e14bcca9c268988d5e7e4acffe50616f
7
- data.tar.gz: 73fd2b7ddef48779755558005dbe50ee4bf53fc7c4d7f3e0c2f4f5d3dfd3f3d271bed72fd15e83c2f4e4f9dcaf1ef09d46cdf2ca61474f5c0641305c9dea0bd1
6
+ metadata.gz: dabe200a09f9dd8f508726c6b8ba8471847c713ae0e142c0121ea8eafb526321cc023972c6c0d830996de5107158981fafa987c53f44074af772947fe9a9bae8
7
+ data.tar.gz: b5e7aa89a019a2582fbf0296fc38a3350da2a7aba1cc41c03b7fed1f01948b768f570807fce987b81a9cf6842b7fb770c68ce363946ce55f4a1ed3a14c19825e
data/README.md CHANGED
@@ -1,46 +1,60 @@
1
- # SmsLogparser
1
+ # SMS Logparser
2
2
 
3
- sms-logparser - DB-Logparser for Simplex Media Server (SMS). Reads access logs stored in a MySQL database (coming from the SWISS TXT CDN) and pushes them to the SMS API.
3
+ sms-logparser - Logparser for Simplex Media Server (SMS). Reads access logs stored in a MySQL database (coming from the SWISS TXT CDN) and sends them to the SMS API.
4
4
 
5
5
  [![Gem Version](https://badge.fury.io/rb/sms-logparser.png)](http://badge.fury.io/rb/sms-logparser)
6
6
 
7
7
  ## Installation
8
8
 
9
- Add this line to your application's Gemfile:
9
+ Install the sms-logpaser gem:
10
10
 
11
- gem 'sms-logparser'
12
-
13
- And then execute:
14
-
15
- $ bundle
16
-
17
- Or install it yourself as:
18
-
19
- $ gem install sms-logparser
11
+ ```bash
12
+ $ gem install sms-logparser
13
+ ```
20
14
 
21
15
  ## Setup
22
16
 
23
17
  Create the database table to track which logs have been parsed:
24
18
 
25
- $ sms-logparser setup
19
+ ```bash
20
+ $ sms-logparser setup
21
+ ```
26
22
 
27
23
  Make a test run:
28
24
 
29
- $ sms-logparser parse --simulate --verbose
25
+ ```bash
26
+ $ sms-logparser parse --simulate --verbose
27
+ ```
30
28
 
31
29
  ## Usage
32
30
 
33
31
  See available commands:
34
32
 
35
- $ sms-logparser help
33
+ ```bash
34
+ $ sms-logparser help
35
+ ```
36
36
 
37
37
  Parse logs from database and send them to the API
38
38
 
39
- $ sms-logparser parse
39
+ ```bash
40
+ $ sms-logparser parse
41
+ ```
40
42
 
41
43
  Show the last parser runs:
42
44
 
43
- $ sms-logparser last_runs
45
+ ```bash
46
+ $ sms-logparser history
47
+ ``
48
+
49
+ ## Configuration file
50
+
51
+ sms-logparser tries to read default options from a yaml file named '.sms-logparser.yml' placed in your home directory. Using the "-c/--config" flag you can adapt the path to the configuration file.
52
+
53
+ An configuration for adapting the default MySQL password could look like this:
54
+
55
+ ```yaml
56
+ :mysql_password: "my!secret"
57
+ ```
44
58
 
45
59
  ## Development
46
60
 
@@ -6,12 +6,17 @@ module SmsLogparser
6
6
  end
7
7
 
8
8
  def send(data)
9
+ urls = []
9
10
  base_url = "#{@options[:api_base_path]}/"
10
11
  base_url += "#{data[:customer_id]}/"
11
12
  base_url += "#{data[:author_id]}/"
12
13
  base_url += "#{data[:project_id]}"
13
- urls = ["#{base_url}/#{data[:traffic_type]}/#{data[:bytes]}"]
14
- urls << "#{base_url}/#{data[:visitor_type]}/1" if data[:visitor_type]
14
+ unless data[:file] =~ /.*\.m3u8$/
15
+ urls = ["#{base_url}/#{data[:traffic_type]}/#{data[:bytes]}"]
16
+ end
17
+ if data[:visitor_type]
18
+ urls << "#{base_url}/#{data[:visitor_type]}/1"
19
+ end
15
20
  unless @options[:simulate]
16
21
  urls.each do |url|
17
22
  begin
@@ -1,22 +1,56 @@
1
1
  module SmsLogparser
2
2
  class Cli < Thor
3
+ require 'yaml'
3
4
 
4
- class_option :mysql_host, :default => 'localhost', aliases: %w(-h)
5
- class_option :mysql_user, :default => 'root', aliases: %w(-u)
6
- class_option :mysql_password, aliases: %w(-p)
7
- class_option :mysql_db, :default => 'Syslog', aliases: %w(-d)
5
+ STATUS = {
6
+ :ok => 0,
7
+ :api_error => 1
8
+ }
8
9
 
9
- desc "version", "print cloudstack-cli version number"
10
+ class_option :config,
11
+ :default => File.join(Dir.home, '.sms-logparser.yml'),
12
+ :aliases => %w(-c),
13
+ :desc => "Configuration file for default options"
14
+
15
+ class_option :mysql_host,
16
+ :default => 'localhost',
17
+ :aliases => %w(-h),
18
+ :desc => "MySQL host"
19
+
20
+ class_option :mysql_user,
21
+ :default => 'root',
22
+ :aliases => %w(-u),
23
+ :desc => "MySQL user"
24
+
25
+ class_option :mysql_password,
26
+ :aliases => %w(-p),
27
+ :desc => "MySQL password"
28
+
29
+ class_option :mysql_db,
30
+ :default => 'Syslog',
31
+ :aliases => %w(-d),
32
+ :desc => "MySQL database"
33
+
34
+ desc "version", "Print cloudstack-cli version number"
10
35
  def version
11
36
  say "sms-logparser version #{SmsLogparser::VERSION}"
12
37
  end
13
38
  map %w(-v --version) => :version
14
39
 
15
- desc "parse", "Check the database for pcache logs and send them to SMS"
16
- option :api_base_path, :default => 'http://dev.simplex.tv/creator/rest', aliases: %w(-a)
17
- option :api_key, aliases: %w(-k)
18
- option :simulate, :type => :boolean, :default => false, aliases: %w(-s)
19
- option :verbose, :type => :boolean, :default => false, aliases: %w(-v)
40
+ desc "parse", "Check the database for pcache logs and send them to the SMS-API"
41
+ option :api_base_path,
42
+ :default => 'http://dev.simplex.tv/creator/rest',
43
+ :aliases => %w(-a)
44
+ option :api_key,
45
+ :aliases => %w(-k)
46
+ option :simulate,
47
+ :type => :boolean,
48
+ :default => false,
49
+ :aliases => %w(-s)
50
+ option :verbose,
51
+ :type => :boolean,
52
+ :default => false,
53
+ :aliases => %w(-v)
20
54
  def parse
21
55
  start_time = Time.now
22
56
  count = 0
@@ -25,44 +59,61 @@ module SmsLogparser
25
59
  entries = mysql.get_entries
26
60
  api = Api.new(options)
27
61
  last_id = mysql.get_last_parse_id
62
+ status = STATUS[:ok]
28
63
  entries.each do |entry|
29
- if Parser.match(entry['Message'])
64
+ if Parser.match?(entry['Message'])
30
65
  data = Parser.extract_data_from_msg(entry['Message'])
31
66
  begin
32
67
  urls = api.send(data)
33
68
  rescue => e
34
69
  say "Error: #{e.message}", :red
35
70
  say "Aborting parser run...", :red
71
+ status = STATUS[:api_error]
36
72
  break
37
73
  end
38
74
  last_id = entry['ID']
39
75
  count += 1
40
- verbose_parser_output(data, urls, entry) if options[:verbose]
76
+ if options[:verbose]
77
+ verbose_parser_output(data, urls, entry)
78
+ end
41
79
  end
42
80
  end
43
- mysql.write_parse_result(last_id, count) unless options[:simulate]
81
+ unless options[:simulate]
82
+ mysql.write_parse_result(last_id, count, status)
83
+ end
44
84
  say "Started:\t", :cyan
45
85
  say start_time.strftime('%d.%d.%Y %T')
46
86
  say "Runtime:\t", :cyan
47
87
  say "#{(Time.now - start_time).round(2)}s"
48
- options[:simulate] ? say("Events found:\t", :cyan) : say("Events sent:\t", :cyan)
88
+ if options[:simulate]
89
+ say("Events found:\t", :cyan)
90
+ else
91
+ say("Events sent:\t", :cyan)
92
+ end
49
93
  say count
50
94
  rescue => e
51
95
  say "Error: #{e.message}", :red
52
96
  end
53
97
  end
54
98
 
55
- desc "last_runs", "List the last paser runs"
56
- def last_runs
99
+ desc "history", "List the last paser runs"
100
+ option :results,
101
+ :type => :numeric,
102
+ :default => 10,
103
+ :aliases => %w(-n),
104
+ :desc => "Number of results to display"
105
+ def history
106
+ puts options
57
107
  begin
58
- runs = Mysql.new(options).last_runs
108
+ runs = Mysql.new(options).last_runs(options[:results])
59
109
  if runs.size > 0
60
- table = [%w(RunAt #Events LastEventID)]
61
- runs.each do |run|
110
+ table = [%w(RunAt #Events LastEventID Status)]
111
+ runs.to_a.reverse.each do |run|
62
112
  table << [
63
113
  run['RunAt'],
64
114
  run['EventsFound'],
65
- run['LastEventID']
115
+ run['LastEventID'],
116
+ STATUS.index(run['Status']).upcase
66
117
  ]
67
118
  end
68
119
  print_table table
@@ -75,26 +126,46 @@ module SmsLogparser
75
126
  end
76
127
 
77
128
  desc "setup", "Create the parser table to track the last logs parsed"
129
+ option :force,
130
+ :type => :boolean,
131
+ :default => false,
132
+ :aliases => %w(-f),
133
+ :desc => "Drop an existing table if it exists"
78
134
  def setup
79
135
  begin
80
- Mysql.new(options).create_parser_table
81
- say "OK", :green
136
+ case Mysql.new(options).create_parser_table(options[:force])
137
+ when 0
138
+ say "OK, table created.", :green
139
+ when 1
140
+ say "Table already exists.", :yellow
141
+ end
82
142
  rescue => e
83
143
  say "Error: #{e.message}", :red
84
144
  end
85
145
  end
86
146
 
87
147
  no_commands do
148
+
88
149
  def verbose_parser_output(data, urls, entry)
89
150
  say "ID:\t", :cyan
90
151
  say entry['ID']
91
152
  say "URL:\t", :cyan
92
153
  say urls.join("\n\t")
93
154
  say "Data:\t", :cyan
94
- say data.map{|k,v| "#{k}:\t#{v}"}.join("\n\t")
155
+ say data.map{|k,v| "#{k}:\t#{v}"}.join("\n\t") || "\n"
156
+ puts
95
157
  puts "-" * 100
96
158
  puts
97
159
  end
160
+
161
+ def options
162
+ original_options = super
163
+ filename = original_options[:config] || File.join(Dir.home, '.sms-logparser.yml')
164
+ return original_options unless File.exists?(filename)
165
+ defaults = ::YAML::load_file(filename) || {}
166
+ defaults.merge(original_options)
167
+ end
168
+
98
169
  end
99
170
 
100
171
  end
@@ -14,27 +14,54 @@ module SmsLogparser
14
14
  )
15
15
  end
16
16
 
17
- def last_runs
17
+ def last_runs(results = 10)
18
18
  begin
19
19
  runs = client.query(
20
- "SELECT * FROM SmsParserRuns ORDER BY ID ASC LIMIT 10"
20
+ "SELECT * FROM SmsParserRuns ORDER BY ID DESC LIMIT #{results}"
21
21
  )
22
22
  rescue Mysql2::Error => e
23
23
  raise e
24
24
  end
25
25
  end
26
26
 
27
- def create_parser_table
27
+ def parser_table_exists?
28
28
  begin
29
29
  return client.query(
30
- "CREATE TABLE IF NOT EXISTS\
31
- SmsParserRuns(\
32
- ID INT PRIMARY KEY AUTO_INCREMENT,\
33
- RunAt datetime DEFAULT NULL,\
34
- LastEventID INT DEFAULT NULL,\
35
- EventsFound INT DEFAULT 0,\
36
- INDEX `LastEventID_I1` (`LastEventID`)
37
- )"
30
+ "SHOW TABLES LIKE 'SmsParserRuns'"
31
+ ).size > 0
32
+ rescue Mysql2::Error => e
33
+ raise e
34
+ end
35
+ end
36
+
37
+ def create_parser_table(force = false)
38
+ if force
39
+ drop_parser_table
40
+ elsif parser_table_exists?
41
+ return 1
42
+ end
43
+ begin
44
+ client.query(
45
+ "CREATE TABLE SmsParserRuns(\
46
+ ID SERIAL PRIMARY KEY AUTO_INCREMENT,\
47
+ RunAt datetime DEFAULT NULL,\
48
+ LastEventID BIGINT(20) UNSIGNED DEFAULT 0,\
49
+ EventsFound INT DEFAULT 0,\
50
+ Status TINYINT UNSIGNED DEFAULT 0,\
51
+ INDEX `LastEventID_I1` (`LastEventID`)
52
+ )"
53
+ )
54
+ rescue Mysql2::Error => e
55
+ raise e
56
+ end
57
+ return 0
58
+ end
59
+
60
+ def drop_parser_table
61
+ return nil unless parser_table_exists?
62
+ begin
63
+ return client.query(
64
+ "DROP TABLE SmsParserRuns"
38
65
  )
39
66
  rescue Mysql2::Error => e
40
67
  raise e
@@ -53,12 +80,13 @@ module SmsLogparser
53
80
  end
54
81
  end
55
82
 
56
- def write_parse_result(id, count)
57
- client.query("INSERT INTO SmsParserRuns(RunAt, LastEventID, EventsFound)\
83
+ def write_parse_result(id, count, status)
84
+ client.query("INSERT INTO SmsParserRuns(RunAt, LastEventID, EventsFound, Status)\
58
85
  VALUES(\
59
86
  '#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}',\
60
87
  #{id},\
61
- #{count}
88
+ #{count},\
89
+ #{status}
62
90
  )"
63
91
  )
64
92
  end
@@ -18,8 +18,14 @@ module SmsLogparser
18
18
  }
19
19
  end
20
20
 
21
- def self.match(message)
22
- message =~ /\/content\/.*\.(f4v|flv|mp4|mp3|ts|m3u8) .* (200|206)/
21
+ def self.match?(message)
22
+ match = message.match(
23
+ /\/content\/.+\/(\w+\.(f4v|flv|mp4|mp3|ts|m3u8)) .+ (200|206)/i
24
+ )
25
+ if match
26
+ return true unless match[1] =~ /detect.mp4|index.m3u8/i
27
+ end
28
+ false
23
29
  end
24
30
 
25
31
  # see https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent
@@ -1,3 +1,3 @@
1
1
  module SmsLogparser
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.required_ruby_version = '>= 1.9.3'
22
+
21
23
  spec.add_development_dependency "bundler", "~> 1.3"
22
24
  spec.add_development_dependency "rake"
23
25
 
data/spec/api_spec.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe SmsLogparser::Api do
4
+
5
+ before do
6
+ @api = SmsLogparser::Api.new(
7
+ simulate: true,
8
+ api_base_path: "http://myapi.com/rest/"
9
+ )
10
+ end
11
+
12
+ it "sends the correct information to the api" do
13
+ data = {
14
+ :customer_id => 1,
15
+ :author_id => 2,
16
+ :project_id => 3,
17
+ :file => 'myfile.mp4',
18
+ :bytes => 128,
19
+ :user_agent => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko)",
20
+ :traffic_type => 'TRAFFIC_WEBCAST',
21
+ :visitor_type => 'VISITORS_WEBCAST'
22
+ }
23
+ urls = @api.send(data)
24
+ urls.size.must_equal 2
25
+ urls[0].must_match /.+\/1\/2\/3\/TRAFFIC_WEBCAST\/128$/
26
+ urls[1].must_match /.+\/1\/2\/3\/VISITORS_WEBCAST\/1$/
27
+ end
28
+
29
+ it "does not send traffic for m3u8 files" do
30
+ data = {
31
+ :customer_id => 100,
32
+ :author_id => 200,
33
+ :project_id => 300,
34
+ :file => 'myfile.m3u8',
35
+ :bytes => 512,
36
+ :user_agent => 'Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0',
37
+ :traffic_type => 'TRAFFIC_MOBILE',
38
+ :visitor_type => 'VISITORS_MOBILE'
39
+ }
40
+ urls = @api.send(data)
41
+ urls.size.must_equal 1
42
+ urls[0].must_match /.+\/100\/200\/300\/VISITORS_MOBILE\/1$/
43
+ end
44
+
45
+ it "does not send visitor info if no visitor_type" do
46
+ data = {
47
+ :customer_id => 101,
48
+ :author_id => 202,
49
+ :project_id => 303,
50
+ :file => 'myfile.mp4',
51
+ :bytes => 48,
52
+ :user_agent => 'Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0',
53
+ :traffic_type => 'TRAFFIC_MOBILE',
54
+ }
55
+ urls = @api.send(data)
56
+ urls.size.must_equal 1
57
+ urls[0].must_match /.+\/101\/202\/303\/TRAFFIC_MOBILE\/48$/
58
+ end
59
+
60
+ end
data/spec/cli_spec.rb CHANGED
@@ -14,7 +14,7 @@ describe SmsLogparser::Cli do
14
14
  out, err = capture_io do
15
15
  TestHelper.sms_logparser.setup
16
16
  end
17
- out.must_match /OK/
17
+ out.must_match /OK.*/
18
18
  end
19
19
 
20
20
  it "can parse a log database and find matches" do
@@ -40,15 +40,15 @@ describe SmsLogparser::Cli do
40
40
  out.must_match /\s+0$/
41
41
  end
42
42
 
43
- it "lists parser runs" do
43
+ it "can show the parser history" do
44
44
  TestHelper.seed_db(1)
45
45
  parser = TestHelper.sms_logparser
46
46
  parser.options[:api_base_path] = 'http://devnull-as-a-service.com/dev/null/'
47
47
  out, err = capture_io do
48
48
  TestHelper.sms_logparser.setup
49
49
  parser.parse
50
- parser.last_runs
50
+ parser.history
51
51
  end
52
- assert_equal(err, "")
52
+ err.must_equal ""
53
53
  end
54
54
  end
data/spec/parser_spec.rb CHANGED
@@ -4,41 +4,49 @@ describe SmsLogparser::Parser do
4
4
 
5
5
  %w(f4v flv mp4 mp3 ts m3u8).each do |extension|
6
6
  it "matches #{extension} files" do
7
- SmsLogparser::Parser.match(
7
+ SmsLogparser::Parser.match?(
8
8
  "GET /content/2/719/54986/file.#{extension} HTTP/1.1\" 200 6741309 "
9
- ).wont_be_nil
9
+ ).must_equal true
10
10
  end
11
11
  end
12
12
 
13
13
  %w(jpg js css m4a docx).each do |extension|
14
14
  it "does not matche #{extension} files" do
15
- SmsLogparser::Parser.match(
15
+ SmsLogparser::Parser.match?(
16
16
  "GET /content/2/719/54986/file.#{extension} HTTP/1.1\" 200 6741309 "
17
- ).must_be_nil
17
+ ).must_equal false
18
18
  end
19
19
  end
20
20
 
21
21
  %w(200 206).each do |status|
22
22
  it "does match status code #{status}" do
23
- SmsLogparser::Parser.match(
23
+ SmsLogparser::Parser.match?(
24
24
  "GET /content/2/719/54986/file.mp4 HTTP/1.1\" #{status} 50000 "
25
- ).wont_be_nil
25
+ ).must_equal true
26
26
  end
27
27
  end
28
28
 
29
29
  %w(404 500 304).each do |status|
30
30
  it "does not match status code #{status}" do
31
- SmsLogparser::Parser.match(
31
+ SmsLogparser::Parser.match?(
32
32
  "GET /content/2/719/54986/file.mp4 HTTP/1.1\" #{status} 50000 "
33
- ).must_be_nil
33
+ ).must_equal false
34
34
  end
35
35
  end
36
36
 
37
- %w(contents public index CONTENT).each do |dir|
37
+ %w(contents public index assets).each do |dir|
38
38
  it "does not match directories other than /content" do
39
- SmsLogparser::Parser.match(
39
+ SmsLogparser::Parser.match?(
40
40
  "GET /#{dir}/2/719/54986/file.mp4 HTTP/1.1\" 200 50000 "
41
- ).must_be_nil
41
+ ).must_equal false
42
+ end
43
+ end
44
+
45
+ %w(detect.mp4 index.m3u8).each do |file|
46
+ it "does not match excluded files" do
47
+ SmsLogparser::Parser.match?(
48
+ "GET /content/2/719/54986/#{file} HTTP/1.1\" 200 128 "
49
+ ).must_equal false
42
50
  end
43
51
  end
44
52
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sms-logparser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - niwo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-25 00:00:00.000000000 Z
11
+ date: 2014-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -101,6 +101,7 @@ files:
101
101
  - lib/sms-logparser/parser.rb
102
102
  - lib/sms-logparser/version.rb
103
103
  - sms-logparser.gemspec
104
+ - spec/api_spec.rb
104
105
  - spec/cli_spec.rb
105
106
  - spec/parser_spec.rb
106
107
  - spec/spec_helper.rb
@@ -116,7 +117,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
116
117
  requirements:
117
118
  - - '>='
118
119
  - !ruby/object:Gem::Version
119
- version: '0'
120
+ version: 1.9.3
120
121
  required_rubygems_version: !ruby/object:Gem::Requirement
121
122
  requirements:
122
123
  - - '>='
@@ -129,6 +130,7 @@ signing_key:
129
130
  specification_version: 4
130
131
  summary: SMS Logparser
131
132
  test_files:
133
+ - spec/api_spec.rb
132
134
  - spec/cli_spec.rb
133
135
  - spec/parser_spec.rb
134
136
  - spec/spec_helper.rb