rack-reqorder 0.1.0 → 0.2.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: 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