log_spy 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a3109d5c0b67ca1205a4c268d3d00a2c14551823
4
+ data.tar.gz: 1ee795e98a6c2b25fa610fe74b7e6538127aa28b
5
+ SHA512:
6
+ metadata.gz: 739791004933ab3e1079d18a44813bb4765e0a517ef20f36243f2612bf69bef1a175fd0af392f6e953bf95192d1080f3ae6488433ced5829619d687d9469b2b2
7
+ data.tar.gz: 377744523bcb6bc2268ca28427836d3cf03eea916f39545bc685bdfe1d51fd71eab5f7c69007aaa9556c7ed652b890acede8427dd0aad0ddbad9c2d8556da4ce
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ *.swp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ -I ./spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in log_spy.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Yang-Hsing Lin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # LogSpy
2
+
3
+ LogSpy is a Rack middleware sending request log to [Amazon SQS](http://aws.amazon.com/sqs/).
4
+ After each request, `log_spy` opens a new thread and sends the request log payload as a `json` string onto [AWS SQS](http://aws.amazon.com/sqs/).
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'log_spy'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install log_spy
19
+
20
+ ## Usage
21
+
22
+ require and use the middleware:
23
+
24
+ - bare rack:
25
+
26
+ ```ruby
27
+ require 'log_spy'
28
+ use LogSpy::Spy, 'aws-sqs-url'
29
+ ```
30
+
31
+ - rails:
32
+ ```ruby
33
+ # application.rb
34
+ config.middleware.use LogSpy::Spy, 'aws-sqs-url', :reigon => 'ap-southeast-1'
35
+ ```
36
+
37
+ ## API Documents:
38
+
39
+ ### to `use` the middleware:
40
+ - usage: `use LogSpy::Spy, <aws-sqs-url>[, <options>]`
41
+ - params:
42
+ - `aws-sqs-url`(required): the [Queue URL](http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/ImportantIdentifiers.html) of SQS, which identifies the queue.
43
+ - `options`(optional): if given, `log_spy` would pass it to initialize [`AWS::SQS`](http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SQS.html)
44
+
45
+ ### the payload format sends to AWS SQS:
46
+
47
+ ```json
48
+ {
49
+ "path": "/the/request/path",
50
+ "status": 200,
51
+ "execution_time": 145.3, // in ms
52
+ "request": {
53
+ "content_type": "application/json",
54
+ "request_method": "post",
55
+ "ip": "123.1.1.1",
56
+ "query_string": "query-key=query-val&hello=world",
57
+ "body": "body-key=body-val"
58
+ },
59
+
60
+ // if got exception
61
+ "error": {
62
+ "message": "the exception message",
63
+ "backtrace": [ "exception back trace" ]
64
+ }
65
+ }
66
+ ```
67
+
68
+ - `error`: `error` would not be included in the payload if no exception was raised
69
+ - `request.body`: if the request `Content-Type` is of `multipart`, the body would be an empty string
70
+
71
+ ## Testing:
72
+ `$ bundle install`
73
+ `$ bundle exec rspec spec/`
74
+
75
+
76
+ ## Contributing
77
+
78
+ 1. Fork it ( https://github.com/[my-github-username]/log_spy/fork )
79
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
80
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
81
+ 4. Push to the branch (`git push origin my-new-feature`)
82
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,31 @@
1
+ require 'json'
2
+ class LogSpy::Payload
3
+ def initialize req, res, error = nil
4
+ @req = req
5
+ @res = res
6
+ @error = error
7
+ end
8
+
9
+ def to_json
10
+ req_body = @req.content_type =~ /multipart/ ? '' : @req.body.read
11
+
12
+ hash = {
13
+ :path => @req.path,
14
+ :status => @res.status,
15
+ :execution_time => @res.duration,
16
+ :request => {
17
+ :content_type => @req.content_type,
18
+ :request_method => @req.request_method,
19
+ :ip => @req.ip,
20
+ :query_string => @req.query_string,
21
+ :body => req_body
22
+ }
23
+ }
24
+
25
+ if @error
26
+ hash[:error] = { :message => @error.message, :backtrace => @error.backtrace }
27
+ end
28
+
29
+ hash.to_json
30
+ end
31
+ end
@@ -0,0 +1,43 @@
1
+ require 'aws-sdk'
2
+ require 'rack'
3
+ require 'log_spy/payload'
4
+ require 'json'
5
+ require 'ostruct'
6
+
7
+ class LogSpy::Spy
8
+ attr_reader :sqs_thread
9
+ def initialize(app, sqs_url, options = {})
10
+ @app = app
11
+ @sqs_url = sqs_url
12
+ @options = options
13
+ end
14
+
15
+ def call env
16
+ @start_time = Time.now.to_f
17
+ @req = Rack::Request.new env
18
+ @status, @header, @body = @app.call(env)
19
+
20
+ @sqs_thread = send_sqs_async
21
+ [ @status, @header, @body ]
22
+ rescue Exception => err
23
+ @sqs_thread = send_sqs_async(err)
24
+
25
+ raise err
26
+ end
27
+
28
+ def send_sqs_async(err = nil)
29
+ @sqs_thread = Thread.new do
30
+ status = err ? 500 : @status
31
+ sqs = AWS::SQS.new(@options)
32
+ duration = ( (Time.now.to_f - @start_time) * 1000 ).round(0)
33
+ res = OpenStruct.new({
34
+ :duration => duration,
35
+ :status => status
36
+ })
37
+ payload = ::LogSpy::Payload.new(@req, res, err)
38
+
39
+ sqs.queues[@sqs_url].send_message(payload.to_json)
40
+ end
41
+ end
42
+ private :send_sqs_async
43
+ end
@@ -0,0 +1,3 @@
1
+ module LogSpy
2
+ VERSION = "0.0.1"
3
+ end
data/lib/log_spy.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "log_spy/version"
2
+ require "log_spy/spy"
3
+
4
+ module LogSpy
5
+ # Your code goes here...
6
+ end
data/log_spy.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'log_spy/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "log_spy"
8
+ spec.version = LogSpy::VERSION
9
+ spec.authors = ["Yang-Hsing Lin"]
10
+ spec.email = ["yanghsing.lin@gmail.com"]
11
+ spec.summary = %q{ send rack application log to Amazon SQS }
12
+ spec.description = %q{ LogSpy is a rack middleware sending request log to Amazon SQS }
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "aws-sdk"
22
+ spec.add_dependency "rack"
23
+ spec.add_development_dependency "bundler", "~> 1.6"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+ require 'log_spy/payload'
3
+
4
+ describe LogSpy::Payload do
5
+ let(:path) { '/the/request/path' }
6
+ let(:request_method) { 'post' }
7
+ let(:ip) { '123.1.1.1' }
8
+ let(:query_string) { 'query-key=query-val' }
9
+ let(:status) { 200 }
10
+ let(:duration) { 335 }
11
+ let(:body) { double(:body, :read => 'the-raw-body') }
12
+ let(:content_type) { 'application/json' }
13
+
14
+ let(:request) do
15
+ double(:request,
16
+ :path => path,
17
+ :content_type => content_type,
18
+ :request_method => request_method,
19
+ :ip => ip,
20
+ :query_string => query_string,
21
+ :body => body)
22
+ end
23
+
24
+ let(:response) do
25
+ double(:response,
26
+ :status => status,
27
+ :duration => duration)
28
+ end
29
+
30
+ let(:error) { Exception.new 'the-message' }
31
+
32
+ describe '::new(request, response[, error = nil])' do
33
+ it 'takes a request, response and an optional error to init' do
34
+ payload = LogSpy::Payload.new request, response
35
+ payload_with_err = LogSpy::Payload.new(request, response, error)
36
+ end
37
+ end
38
+
39
+ describe '#to_json' do
40
+ let(:expected_hash) do
41
+ {
42
+ :path => path,
43
+ :status => status,
44
+ :execution_time => duration,
45
+ :request => {
46
+ :content_type => content_type,
47
+ :request_method => request_method,
48
+ :ip => ip,
49
+ :query_string => query_string,
50
+ :body => body.read
51
+ }
52
+ }
53
+ end
54
+
55
+ context "if no error" do
56
+ let(:payload) { LogSpy::Payload.new request, response }
57
+ it 'returns correct format' do
58
+ expect(payload.to_json).to eq(expected_hash.to_json)
59
+ end
60
+
61
+ it 'returns no body if request content_type is multipart' do
62
+ allow(request).to receive_messages(:content_type => 'multipart/form-data')
63
+ expected_hash[:request][:content_type] = 'multipart/form-data'
64
+ expected_hash[:request][:body] = ''
65
+ expect(payload.to_json).to eq(expected_hash.to_json)
66
+ end
67
+ end
68
+
69
+ context "if with error" do
70
+ let(:payload) { LogSpy::Payload.new request, response, error }
71
+ it 'returns error with message and backtrace' do
72
+ expected_hash[:error] = {
73
+ :message => error.message,
74
+ :backtrace => error.backtrace
75
+ }
76
+
77
+ expect(payload.to_json).to eq(expected_hash.to_json)
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,2 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
data/spec/spy_spec.rb ADDED
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+ require 'log_spy/spy'
3
+
4
+ describe LogSpy::Spy do
5
+ let(:app) { double(:app) }
6
+ let(:sqs_url) { 'the-sqs-url' }
7
+ let(:options) { { :reigon => 'ap-southeast-1' } }
8
+
9
+ describe '#new(app, sqs_url [, options = {}])' do
10
+ it 'takes an app, an sqs_url, and an optional options to init' do
11
+ middleware = LogSpy::Spy.new(app, sqs_url)
12
+ middleware_w_options = LogSpy::Spy.new(app, sqs_url, options)
13
+ end
14
+ end
15
+
16
+ describe 'call' do
17
+ let(:sqs) { double(:sqs, :queues => double(:[] => queue)) }
18
+ let(:queue) { double(:queue, :send_message => true) }
19
+ let(:call_result) { [200, { 'Content-Type' => 'application/json' }, [ 'body' ]] }
20
+ let(:env) { {} }
21
+
22
+ let(:request) { double(:request) }
23
+ let(:payload) { double(:payload, :to_json => {'key' => 'val'}.to_json) }
24
+
25
+ let(:middleware) { LogSpy::Spy.new(app, sqs_url, options) }
26
+ let(:duration) { 20.12345 }
27
+ let(:now) { Time.now }
28
+ let(:three_sec_later) { now + duration }
29
+
30
+ before :each do
31
+ allow(AWS::SQS).to receive_messages(:new => sqs)
32
+ allow(app).to receive_messages(:call => call_result)
33
+ allow(Rack::Request).to receive_messages(:new => request)
34
+ allow(LogSpy::Payload).to receive_messages(:new => payload)
35
+ allow(Time).to receive(:now).and_return(now, three_sec_later)
36
+ end
37
+
38
+ it 'config sqs with options' do
39
+ expect(AWS::SQS).to receive(:new).with(options)
40
+
41
+ middleware.call env
42
+ middleware.sqs_thread.join
43
+ end
44
+
45
+ it 'sends payload json json to sqs' do
46
+ expect(Rack::Request).to receive(:new).with(env)
47
+
48
+ expect(queue).to receive(:send_message).with(payload.to_json)
49
+ middleware.call env
50
+ middleware.sqs_thread.join
51
+ end
52
+
53
+ it 'builds payload with request, status, request_time' do
54
+ expect(LogSpy::Payload).to receive(:new) do |req, res|
55
+ expect(req).to be(request)
56
+ expect(res.status).to eq(200)
57
+ expect(res.duration).to eq((duration * 1000).round(0))
58
+ end
59
+
60
+ middleware.call env
61
+ middleware.sqs_thread.join
62
+ end
63
+
64
+ it 'returns original result' do
65
+ expect(middleware.call(env)).to eq(call_result)
66
+ middleware.sqs_thread.join
67
+ end
68
+
69
+ context "if `app.call raises`" do
70
+ let(:error) { Exception.new }
71
+
72
+ before :each do
73
+ allow(app).to receive(:call).and_raise(error)
74
+ end
75
+
76
+ it 'build payload with error' do
77
+ expect(LogSpy::Payload).to receive(:new) do |req, res, err|
78
+ expect(req).to be(request)
79
+ expect(res.status).to eq(500)
80
+ expect(res.duration).to eq(( duration * 1000 ).round(0))
81
+ expect(err).to eq(error)
82
+ end
83
+
84
+ begin
85
+ middleware.call(env)
86
+ rescue Exception => e
87
+ end
88
+
89
+ middleware.sqs_thread.join
90
+ end
91
+
92
+ it 'forward to error' do
93
+ expect {
94
+ middleware.call(env)
95
+ }.to raise_error(error)
96
+
97
+ middleware.sqs_thread.join
98
+ end
99
+ end
100
+
101
+ end
102
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: log_spy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Yang-Hsing Lin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.6'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: ' LogSpy is a rack middleware sending request log to Amazon SQS '
84
+ email:
85
+ - yanghsing.lin@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - .rspec
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - lib/log_spy.rb
97
+ - lib/log_spy/payload.rb
98
+ - lib/log_spy/spy.rb
99
+ - lib/log_spy/version.rb
100
+ - log_spy.gemspec
101
+ - spec/payload_spec.rb
102
+ - spec/spec_helper.rb
103
+ - spec/spy_spec.rb
104
+ homepage: ''
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.2.2
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: send rack application log to Amazon SQS
128
+ test_files:
129
+ - spec/payload_spec.rb
130
+ - spec/spec_helper.rb
131
+ - spec/spy_spec.rb