rack-reqorder 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3e5ee5c3b78608fb23c33eccc40fad318829ee7a
4
- data.tar.gz: 878a3139311546392b38b6ab1d7efff7cd320db9
3
+ metadata.gz: d32d460562301fbe8a9891e17ec80ca6e77f35f2
4
+ data.tar.gz: 052269bec34d67bcf99b03cdb8856fc2c8c40412
5
5
  SHA512:
6
- metadata.gz: 608cd0e963785bc88d375272189556c062a1140b9b287ecd4ba4248e76487d44066244c317460734824eb3309397d78fa4d77be826aa56d738062921951b224d
7
- data.tar.gz: 317dbb6df532175889814c2dae12c83910ce81eadf9ec4ab6f97d1f04dc5ed593045743d9df02f17595b0e942e5d9a3a5b8f8c46caaef9d064eb5f029ace19f5
6
+ metadata.gz: 65255514d36099c7935d2043bdb2f518fcac3a0cc2f6d1ec6e08747220d58c152174119c58f7bd8d858e0359d8ccf012ebdc88e911e5499abdbacd1c7fa8898d
7
+ data.tar.gz: 2ddbddc83cbda7f5a7c2de83e092c30dff99e2f7cd77d64bce9248a6878e37160c9ffdecde52ef59377c64d5f7cc4431906125a8b13b39320f96e39c04b4f3d9
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ mongoid.yml
data/Gemfile CHANGED
@@ -1,5 +1,8 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'mongoid_hash_query', path: '../mongoid_hash_query'
4
+ gem 'pry'
5
+
3
6
  # Specify your gem's dependencies in rack-reqorder.gemspec
4
7
  gemspec
5
8
 
data/Rakefile CHANGED
@@ -1,2 +1,12 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rack/reqorder/api/api'
3
+
4
+ namespace :grape do
5
+ desc 'Print compiled grape routes'
6
+ task :routes do
7
+ Rack::Reqorder::Api.routes.each do |route|
8
+ puts route
9
+ end
10
+ end
11
+ end
2
12
 
@@ -0,0 +1,9 @@
1
+ require_relative '../reqorder'
2
+
3
+ Rack::Reqorder.configure do |config|
4
+ config.mongoid_yml = "#{File.dirname(__FILE__)}/../../../mongoid.yml"
5
+ end
6
+
7
+ Rack::Reqorder.boot!
8
+
9
+ run Rack::Reqorder::Monitor::Api
@@ -1,47 +1,100 @@
1
- module Rack
2
- module Reqorder
3
- class Logger
4
- include Rack::Reqorder::Models
1
+ module Rack::Reqorder
2
+ class Logger
3
+ include Rack::Reqorder::Models
5
4
 
6
- def initialize(app)
7
- @app = app
8
- end
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def call(environment)
10
+ http_request = save_http_request(environment)
9
11
 
10
- def call(environment)
11
- #api_session = ApiSession.create(created_at: Time.now)
12
- request = Rack::Request.new(environment)
13
- #api_session.request
14
-
15
- HttpRequest.create(
16
- path: request.path,
17
- full_path: request.fullpath,
18
- headers: extract_all_headers(request),
19
- parameters: request.params,
20
- )
21
-
22
- status, headers, body = @app.call(request.env)
23
-
24
- response = Rack::Response.new(body, status, headers)
25
-
26
- HttpResponse.create(
27
- headers: response.headers,
28
- #body: response.body.first,
29
- status: response.status.to_i
30
- )
31
- =begin
32
- response.finish
33
- =end
34
- return [status, headers, body]
12
+ begin
13
+ status, headers, body = @app.call(environment)
14
+ rescue => exception
15
+ log_exception(exception, http_request)
16
+ raise exception
35
17
  end
36
18
 
37
- def extract_all_headers(request)
38
- Hash[
39
- request.env.select{|k,v|
40
- k.start_with? 'HTTP_'
41
- }.map{|k,v|
42
- [k.gsub('HTTP_','').upcase, v]
43
- }
44
- ]
19
+ save_http_response(body, status, headers, http_request)
20
+
21
+ return [status, headers, body]
22
+ end
23
+
24
+ private
25
+
26
+ def extract_all_headers(request)
27
+ Hash[
28
+ request.env.select{|k,v|
29
+ k.start_with? 'HTTP_'
30
+ }.map{|k,v|
31
+ [k.gsub('HTTP_','').upcase, v]
32
+ }.select{|k,v|
33
+ k != 'COOKIE'
34
+ }
35
+ ]
36
+ end
37
+
38
+ def save_http_request(environment)
39
+ request = Rack::Request.new(environment)
40
+
41
+ return HttpRequest.create(
42
+ ip: request.ip,
43
+ url: request.url,
44
+ scheme: request.scheme,
45
+ base_url: request.base_url,
46
+ port: request.port,
47
+ path: request.path,
48
+ full_path: request.fullpath,
49
+ http_method: request.request_method,
50
+ headers: extract_all_headers(request),
51
+ params: request.params,
52
+ ssl: request.ssl?,
53
+ xhr: request.xhr?
54
+ )
55
+ end
56
+
57
+ def save_http_response(body, status, headers, http_request)
58
+ response = Rack::Response.new(body, status, headers)
59
+
60
+ HttpResponse.create(
61
+ headers: response.headers,
62
+ #body: response.body.first,
63
+ status: response.status.to_i,
64
+ http_request: http_request
65
+ )
66
+ end
67
+
68
+ def log_exception(exception, http_request)
69
+ bc = BacktraceCleaner.new
70
+ bc.add_filter { |line| line.gsub(Rails.root.to_s, '') }
71
+ bc.add_silencer { |line| line =~ /gems/ }
72
+
73
+ application_trace = bc.clean(exception.backtrace)
74
+
75
+ path, line, _ = application_trace.first.split(':')
76
+
77
+ AppException.create(
78
+ message: exception.message,
79
+ application_trace: application_trace,
80
+ full_trace: exception.backtrace,
81
+ line: line.to_i,
82
+ path: path[1..-1],
83
+ source_extract: source_fragment(path[1..-1], line.to_i),
84
+ http_request: http_request
85
+ )
86
+ end
87
+
88
+ def source_fragment(path, line)
89
+ return unless Rails.respond_to?(:root) && Rails.root
90
+
91
+ full_path = Rails.root.join(path)
92
+ if File.exist?(full_path)
93
+ File.open(full_path, "r") do |file|
94
+ start = [line - 3, 0].max
95
+ lines = file.each_line.drop(start).take(6)
96
+ Hash[*(start+1..(lines.count+start)).zip(lines).flatten]
97
+ end
45
98
  end
46
99
  end
47
100
  end
@@ -0,0 +1,19 @@
1
+ #Heavy influenced by ActionDispatch::DebugExceptions
2
+
3
+ module Rack::Reqorder::Models
4
+ class AppException
5
+ include ::Mongoid::Document
6
+ include ::Kaminari::MongoidExtension::Document
7
+
8
+ field :message, type: String
9
+ field :application_trace, type: Array
10
+ #field :framework_trace, type: Array
11
+ field :full_trace, type: Array
12
+ field :line, type: Integer
13
+ field :path, type: String
14
+ field :source_extract, type: Hash
15
+ field :created_at, type: Time, default: ->{ Time.now }
16
+
17
+ belongs_to :http_request
18
+ end
19
+ end
@@ -1,20 +1,24 @@
1
1
  module Rack::Reqorder::Models
2
2
  class HttpRequest
3
3
  include ::Mongoid::Document
4
+ include ::Kaminari::MongoidExtension::Document
4
5
 
6
+ field :ip, type: String
7
+ field :url, type: String
8
+ field :scheme, type: String
9
+ field :base_url, type: String
10
+ field :port, type: Integer
5
11
  field :path, type: String
6
12
  field :full_path, type: String
13
+ field :http_method, type: String
7
14
  field :headers, type: Hash
8
- field :parameters, type: Hash
9
- field :created_at, type: DateTime
15
+ field :params, type: Hash
16
+ field :ssl, type: Boolean
17
+ field :xhr, type: Boolean
18
+ field :created_at, type: Time, default: ->{ Time.now }
10
19
 
11
20
  has_one :http_response
12
-
13
- before_create :set_created_at
14
-
15
- def set_created_at
16
- self.created_at = DateTime.now.utc
17
- end
21
+ has_one :app_exception
18
22
 
19
23
  end
20
24
  end
@@ -1,16 +1,21 @@
1
1
  module Rack::Reqorder::Models
2
2
  class HttpResponse
3
3
  include ::Mongoid::Document
4
+ include ::Kaminari::MongoidExtension::Document
4
5
 
5
6
  field :headers, type: Hash
6
7
  field :status, type: Integer
7
8
  #field :body, type: String
8
- field :created_at, type: DateTime
9
+ field :created_at, type: Time, default: ->{ Time.now }, pre_processed: true
10
+ field :response_time, type: Float
9
11
 
10
- before_create :set_created_at
12
+ belongs_to :http_request
11
13
 
12
- def set_created_at
13
- self.created_at = DateTime.now.utc
14
+ before_create :set_response_time
15
+
16
+ private
17
+ def set_response_time
18
+ self.response_time = self.created_at - self.http_request.created_at
14
19
  end
15
20
  end
16
21
  end
@@ -0,0 +1,74 @@
1
+ module Rack::Reqorder::Monitor
2
+ module Entities
3
+ class BaseEntity < Grape::Entity
4
+ expose :_id, documentation: { type: 'String', desc: 'BSON::ObjectId String' }, :format_with => :to_string, as: :id
5
+ format_with(:to_string) { |foo| foo.to_s }
6
+ format_with(:iso_timestamp) { |dt| dt.utc.iso8601 if dt }
7
+
8
+ format_with(:association_id) {|a| a.id.to_s if a}
9
+ end
10
+
11
+ class RequestEntity < BaseEntity
12
+ root :requests, :request
13
+
14
+ expose :ip
15
+ expose :url
16
+ expose :scheme
17
+ expose :base_url
18
+ expose :port
19
+ expose :path
20
+ expose :full_path
21
+ expose :http_method
22
+ expose :headers
23
+ expose :params
24
+ expose :ssl
25
+ expose :xhr
26
+
27
+ with_options(format_with: :iso_timestamp) do
28
+ expose :created_at
29
+ end
30
+
31
+ with_options(format_with: :association_id) do
32
+ expose :http_response, as: :response_id
33
+ end
34
+
35
+ end
36
+
37
+ class ResponseEntity < BaseEntity
38
+ root :responses, :response
39
+
40
+ expose :headers
41
+ expose :status
42
+ expose :response_time
43
+
44
+ with_options(format_with: :iso_timestamp) do
45
+ expose :created_at
46
+ end
47
+
48
+ with_options(format_with: :association_id) do
49
+ expose :http_request, as: :request_id
50
+ end
51
+ end
52
+
53
+
54
+ class ExceptionEntity < BaseEntity
55
+ root :exceptions, :exception
56
+
57
+ expose :message
58
+ expose :application_trace
59
+ expose :full_trace
60
+ expose :line
61
+ expose :path
62
+ expose :source_extract
63
+
64
+ with_options(format_with: :iso_timestamp) do
65
+ expose :created_at
66
+ end
67
+
68
+ with_options(format_with: :association_id) do
69
+ expose :http_request, as: :request_id
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,120 @@
1
+ require 'grape'
2
+ require 'grape-entity'
3
+ require 'rack/reqorder/monitor/entities'
4
+ require 'mongoid_hash_query'
5
+ require 'pry'
6
+
7
+ module Rack::Reqorder
8
+ module Monitor
9
+ end
10
+ end
11
+
12
+ module Rack::Reqorder::Monitor
13
+ class Api < Grape::API
14
+ include Rack::Reqorder::Models
15
+ include Rack::Reqorder::Monitor::Entities
16
+
17
+ helpers do
18
+ include MongoidHashQuery
19
+ end
20
+
21
+ version 'v1', using: :path, vendor: 'foobar'
22
+ format :json
23
+ prefix :api
24
+
25
+ #collection routes
26
+ resource :requests do
27
+ get do
28
+ requests = apply_filters(HttpRequest.all, params)
29
+
30
+ meta_aggregations = aggregations(requests, params)
31
+
32
+ requests = paginate(requests, params)
33
+
34
+ present_with_meta(
35
+ requests,
36
+ present(requests, with: RequestEntity),
37
+ meta_aggregations
38
+ )
39
+ end
40
+
41
+ #element routes
42
+ route_param :id do
43
+ get do
44
+ present(HttpRequest.find(params[:id]), with: RequestEntity)
45
+ end
46
+ end
47
+ end
48
+
49
+ #collection routes
50
+ resource :responses do
51
+ get do
52
+ responses = HttpResponse.all
53
+
54
+ responses = apply_filters(responses, params)
55
+
56
+ meta_aggregations = aggregations(exceptions, params)
57
+
58
+ responses = paginate(responses, params)
59
+
60
+ present_with_meta(
61
+ responses,
62
+ present(responses, with: ResponseEntity),
63
+ meta_aggregations
64
+ )
65
+ end
66
+
67
+ #element routes
68
+ route_param :id do
69
+ get do
70
+ present(HttpResponse.find(params[:id]), with: ResponseEntity)
71
+ end
72
+ end
73
+ end
74
+
75
+ #collection routes
76
+ resource :exceptions do
77
+ get do
78
+ exceptions = AppException.all
79
+
80
+ exceptions = apply_filters(exceptions, params)
81
+
82
+ meta_aggregations = aggregations(exceptions, params)
83
+
84
+ exceptions = paginate(exceptions, params)
85
+
86
+ present_with_meta(
87
+ exceptions,
88
+ present(exceptions, with: ExceptionEntity),
89
+ meta_aggregations
90
+ )
91
+ end
92
+
93
+ #element routes
94
+ route_param :id do
95
+ get do
96
+ present(AppException.find(params[:id]), with: ExceptionEntity)
97
+ end
98
+ end
99
+ end
100
+
101
+ helpers do
102
+ def present_with_meta(object, hash, extra_meta)
103
+ hash[:meta] = {
104
+ current_page: object.current_page,
105
+ next_page: object.next_page,
106
+ prev_page: object.prev_page,
107
+ total_pages: object.total_pages,
108
+ total_count: object.total_count
109
+ }.merge(extra_meta)
110
+
111
+ return hash
112
+ end
113
+
114
+ def paginate(object, params)
115
+ return object.page(params[:page] || 1).per(params[:per_page] || 30)
116
+ end
117
+ end
118
+
119
+ end
120
+ end
@@ -0,0 +1,76 @@
1
+ module Rack::Reqorder
2
+ class BacktraceCleaner
3
+ def initialize
4
+ @filters, @silencers = [], []
5
+ end
6
+
7
+ # Returns the backtrace after all filters and silencers have been run
8
+ # against it. Filters run first, then silencers.
9
+ def clean(backtrace, kind = :silent)
10
+ filtered = filter_backtrace(backtrace)
11
+
12
+ case kind
13
+ when :silent
14
+ silence(filtered)
15
+ when :noise
16
+ noise(filtered)
17
+ else
18
+ filtered
19
+ end
20
+ end
21
+ alias :filter :clean
22
+
23
+ # Adds a filter from the block provided. Each line in the backtrace will be
24
+ # mapped against this filter.
25
+ #
26
+ # # Will turn "/my/rails/root/app/models/person.rb" into "/app/models/person.rb"
27
+ # backtrace_cleaner.add_filter { |line| line.gsub(Rails.root, '') }
28
+ def add_filter(&block)
29
+ @filters << block
30
+ end
31
+
32
+ # Adds a silencer from the block provided. If the silencer returns +true+
33
+ # for a given line, it will be excluded from the clean backtrace.
34
+ #
35
+ # # Will reject all lines that include the word "mongrel", like "/gems/mongrel/server.rb" or "/app/my_mongrel_server/rb"
36
+ # backtrace_cleaner.add_silencer { |line| line =~ /mongrel/ }
37
+ def add_silencer(&block)
38
+ @silencers << block
39
+ end
40
+
41
+ # Removes all silencers, but leaves in the filters. Useful if your
42
+ # context of debugging suddenly expands as you suspect a bug in one of
43
+ # the libraries you use.
44
+ def remove_silencers!
45
+ @silencers = []
46
+ end
47
+
48
+ # Removes all filters, but leaves in the silencers. Useful if you suddenly
49
+ # need to see entire filepaths in the backtrace that you had already
50
+ # filtered out.
51
+ def remove_filters!
52
+ @filters = []
53
+ end
54
+
55
+ private
56
+ def filter_backtrace(backtrace)
57
+ @filters.each do |f|
58
+ backtrace = backtrace.map { |line| f.call(line) }
59
+ end
60
+
61
+ backtrace
62
+ end
63
+
64
+ def silence(backtrace)
65
+ @silencers.each do |s|
66
+ backtrace = backtrace.reject { |line| s.call(line) }
67
+ end
68
+
69
+ backtrace
70
+ end
71
+
72
+ def noise(backtrace)
73
+ backtrace - silence(backtrace)
74
+ end
75
+ end
76
+ end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  module Reqorder
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
data/lib/rack/reqorder.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'rack/reqorder/version'
2
2
  require 'active_support/inflector'
3
3
  require 'mongoid'
4
+ require 'kaminari'
5
+ require 'kaminari/models/mongoid_extension'
4
6
 
5
7
  module Rack
6
8
  module Reqorder
@@ -39,6 +41,20 @@ module Rack
39
41
  end
40
42
  end
41
43
 
44
+ Kaminari.configure do |config|
45
+ # config.default_per_page = 25
46
+ # config.max_per_page = nil
47
+ # config.window = 4
48
+ # config.outer_window = 0
49
+ # config.left = 0
50
+ # config.right = 0
51
+ # config.page_method_name = :page
52
+ # config.param_name = :page
53
+ end
54
+
42
55
  require 'rack/reqorder/models/http_request'
43
56
  require 'rack/reqorder/models/http_response'
57
+ require 'rack/reqorder/models/app_exception'
58
+ require 'rack/reqorder/services/backtrace_cleaner'
44
59
  require 'rack/reqorder/logger'
60
+ require 'rack/reqorder/monitor'
@@ -22,4 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency "rake", "~> 10.0"
23
23
  spec.add_dependency "mongoid", "~> 4.0.0"
24
24
  spec.add_dependency "activesupport", "~> 4.1.6"
25
+ spec.add_dependency "grape"
26
+ spec.add_dependency "grape-entity"
27
+ spec.add_dependency "kaminari"
25
28
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-reqorder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Filippos Vasilakis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-28 00:00:00.000000000 Z
11
+ date: 2015-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,48 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 4.1.6
69
+ - !ruby/object:Gem::Dependency
70
+ name: grape
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: grape-entity
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: kaminari
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
69
111
  description: Request recorder and analyzer for rack apps
70
112
  email:
71
113
  - vasilakisfil@gmail.com
@@ -84,10 +126,14 @@ files:
84
126
  - bin/console
85
127
  - bin/setup
86
128
  - lib/rack/reqorder.rb
129
+ - lib/rack/reqorder/config.ru
87
130
  - lib/rack/reqorder/logger.rb
131
+ - lib/rack/reqorder/models/app_exception.rb
88
132
  - lib/rack/reqorder/models/http_request.rb
89
133
  - lib/rack/reqorder/models/http_response.rb
90
- - lib/rack/reqorder/services/request_saver.rb
134
+ - lib/rack/reqorder/monitor.rb
135
+ - lib/rack/reqorder/monitor/entities.rb
136
+ - lib/rack/reqorder/services/backtrace_cleaner.rb
91
137
  - lib/rack/reqorder/version.rb
92
138
  - rack-reqorder.gemspec
93
139
  homepage: ''
@@ -110,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
156
  version: '0'
111
157
  requirements: []
112
158
  rubyforge_project:
113
- rubygems_version: 2.4.5
159
+ rubygems_version: 2.4.8
114
160
  signing_key:
115
161
  specification_version: 4
116
162
  summary: Request recorder and analyzer for rack apps
@@ -1,6 +0,0 @@
1
- class NewRequestService
2
- class << self
3
- def save(request)
4
- end
5
- end
6
- end