meta_request 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 685c2846348d63d6afcda0e5b9ac26862830d471
4
+ data.tar.gz: 7bcf29fded9fba0b8a87c4bd06b5c7c1ca955560
5
+ SHA512:
6
+ metadata.gz: 6f56ed8974498e5aae4fcf4b96cac36f34524c9e9e1e8f60ce901cbe847aacc75e3db95444627b574b547316200ae9f1e8e75fb1523ad3dc0146bcef42c9f119
7
+ data.tar.gz: 26336064cfd832cbc3ba5b522d7e0daec80fcf39162bc6f07f1464c181cd0e71dcf933d59a56681f29a1550e260a2b54cafae9ad12ab82b416ee66c6f4a128f4
data/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # MetaRequest
2
+
3
+ Supporting gem for [Rails Panel (Google Chrome extension for Rails development)](https://github.com/dejan/rails_panel).
4
+
5
+ ## Installation
6
+
7
+ Add meta_request gem to development group in Gemfile:
8
+
9
+ ```ruby
10
+ group :development do
11
+ gem 'meta_request'
12
+ end
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ See [Rails Panel extension](https://github.com/dejan/rails_panel).
18
+
19
+ ## Compatibility Warnings
20
+
21
+ If you're using [LiveReload](http://livereload.com/) or
22
+ [Rack::LiveReload](https://github.com/johnbintz/rack-livereload) make sure to
23
+ exclude watching your tmp/ folder because meta_request writes a lot of data there
24
+ and your browser will refresh like a madman.
25
+
26
+ ## Licence
27
+
28
+ Copyright (c) 2012 Dejan Simic
29
+
30
+ MIT License
31
+
32
+ Permission is hereby granted, free of charge, to any person obtaining
33
+ a copy of this software and associated documentation files (the
34
+ "Software"), to deal in the Software without restriction, including
35
+ without limitation the rights to use, copy, modify, merge, publish,
36
+ distribute, sublicense, and/or sell copies of the Software, and to
37
+ permit persons to whom the Software is furnished to do so, subject to
38
+ the following conditions:
39
+
40
+ The above copyright notice and this permission notice shall be
41
+ included in all copies or substantial portions of the Software.
42
+
43
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
44
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
45
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
46
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
47
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
48
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
49
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,76 @@
1
+ module MetaRequest
2
+ class AppNotifications
3
+
4
+ # these are the specific keys in the cache payload that we display in the
5
+ # panel view
6
+ CACHE_KEY_COLUMNS = [:key, :hit, :options, :type]
7
+
8
+ # define this here so we can pass it in to all of our cache subscribe calls
9
+ CACHE_BLOCK = Proc.new {|*args|
10
+ name, start, ending, transaction_id, payload = args
11
+
12
+ # from http://edgeguides.rubyonrails.org/active_support_instrumentation.html#cache-fetch-hit-active-support
13
+ #
14
+ # :super_operation :fetch is added when a read is used with #fetch
15
+ #
16
+ # so if :super_operation is present, we'll use it for the type. otherwise
17
+ # strip (say) 'cache_delete.active_support' down to 'delete'
18
+ payload[:type] = payload.delete(:super_operation) || name.sub(/cache_(.*?)\..*$/, '\1')
19
+
20
+ # anything that isn't in CACHE_KEY_COLUMNS gets shoved into :options
21
+ # instead
22
+ payload[:options] = {}
23
+ payload.keys.each do |k|
24
+ payload[:options][k] = payload.delete(k) unless k.in? CACHE_KEY_COLUMNS
25
+ end
26
+
27
+ dev_caller = caller.detect { |c| c.include? MetaRequest.rails_root }
28
+ if dev_caller
29
+ c = Callsite.parse(dev_caller)
30
+ payload.merge!(:line => c.line, :filename => c.filename, :method => c.method)
31
+ end
32
+
33
+ Event.new(name, start, ending, transaction_id, payload)
34
+ }
35
+ # Subscribe to all events relevant to RailsPanel
36
+ #
37
+ def self.subscribe
38
+ new.
39
+ subscribe("meta_request.log").
40
+ subscribe("sql.active_record") do |*args|
41
+ name, start, ending, transaction_id, payload = args
42
+ dev_caller = caller.detect { |c| c.include? MetaRequest.rails_root }
43
+ if dev_caller
44
+ c = Callsite.parse(dev_caller)
45
+ payload.merge!(:line => c.line, :filename => c.filename, :method => c.method)
46
+ end
47
+ Event.new(name, start, ending, transaction_id, payload)
48
+ end.
49
+ subscribe("render_partial.action_view").
50
+ subscribe("render_template.action_view").
51
+ subscribe("process_action.action_controller.exception").
52
+ subscribe("process_action.action_controller") do |*args|
53
+ name, start, ending, transaction_id, payload = args
54
+ payload[:format] ||= (payload[:formats]||[]).first # Rails 3.0.x Support
55
+ payload[:status] = '500' if payload[:exception]
56
+ Event.new(name, start, ending, transaction_id, payload)
57
+ end.
58
+ subscribe("cache_read.active_support", &CACHE_BLOCK).
59
+ subscribe("cache_generate.active_support", &CACHE_BLOCK).
60
+ subscribe("cache_fetch_hit.active_support", &CACHE_BLOCK).
61
+ subscribe("cache_write.active_support", &CACHE_BLOCK).
62
+ subscribe("cache_delete.active_support", &CACHE_BLOCK).
63
+ subscribe("cache_exist?.active_support", &CACHE_BLOCK)
64
+ end
65
+
66
+ def subscribe(event_name)
67
+ ActiveSupport::Notifications.subscribe(event_name) do |*args|
68
+ event = block_given?? yield(*args) : Event.new(*args)
69
+ AppRequest.current.events << event if AppRequest.current
70
+ end
71
+ self
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -0,0 +1,19 @@
1
+ module MetaRequest
2
+ class AppRequest
3
+ attr_reader :id, :events
4
+
5
+ def initialize(id)
6
+ @id = id
7
+ @events = []
8
+ end
9
+
10
+ def self.current
11
+ Thread.current[:meta_request_id]
12
+ end
13
+
14
+ def current!
15
+ Thread.current[:meta_request_id] = self
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,64 @@
1
+ require 'active_support'
2
+ require 'active_support/json'
3
+ require 'active_support/core_ext'
4
+
5
+ module MetaRequest
6
+
7
+ # Subclass of ActiveSupport Event that is JSON encodable
8
+ #
9
+ class Event < ActiveSupport::Notifications::Event
10
+ attr_reader :duration
11
+
12
+ def initialize(name, start, ending, transaction_id, payload)
13
+ super(name, start, ending, transaction_id, json_encodable(payload))
14
+ @duration = 1000.0 * (ending - start)
15
+ end
16
+
17
+ def self.events_for_exception(exception_wrapper)
18
+ if defined?(ActionDispatch::ExceptionWrapper)
19
+ exception = exception_wrapper.exception
20
+ trace = exception_wrapper.application_trace
21
+ trace = exception_wrapper.framework_trace if trace.empty?
22
+ else
23
+ exception = exception_wrapper
24
+ trace = exception.backtrace
25
+ end
26
+ trace.unshift "#{exception.class} (#{exception.message})"
27
+ trace.map do |call|
28
+ Event.new('process_action.action_controller.exception', 0, 0, nil, {:call => call})
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def json_encodable(payload)
35
+ return {} unless payload.is_a?(Hash)
36
+ transform_hash(payload, :deep => true) { |hash, key, value|
37
+ begin
38
+ value.to_json(:methods => [:duration])
39
+ new_value = value
40
+ rescue
41
+ new_value = 'Not JSON Encodable'
42
+ end
43
+ hash[key] = new_value
44
+ }.with_indifferent_access
45
+ end
46
+
47
+ # https://gist.github.com/dbenhur/1070399
48
+ def transform_hash(original, options={}, &block)
49
+ options[:safe_descent] ||= {}
50
+ new_hash = {}
51
+ options[:safe_descent][original.object_id] = new_hash
52
+ original.inject(new_hash) { |result, (key,value)|
53
+ if (options[:deep] && Hash === value)
54
+ value = options[:safe_descent].fetch( value.object_id ) {
55
+ transform_hash(value, options, &block)
56
+ }
57
+ end
58
+ block.call(result,key,value)
59
+ result
60
+ }
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,49 @@
1
+ require 'callsite'
2
+
3
+ module MetaRequest
4
+ module LogInterceptor
5
+
6
+ def debug(message = nil, &block)
7
+ push_event(:debug, message)
8
+ super
9
+ end
10
+
11
+ def info(message = nil, &block)
12
+ push_event(:info, message)
13
+ super
14
+ end
15
+
16
+ def warn(message = nil, &block)
17
+ push_event(:warn, message)
18
+ super
19
+ end
20
+
21
+ def error(message = nil, &block)
22
+ push_event(:error, message)
23
+ super
24
+ end
25
+
26
+ def fatal(message = nil, &block)
27
+ push_event(:fatal, message)
28
+ super
29
+ end
30
+
31
+ def unknown(message = nil, &block)
32
+ push_event(:unknown, message)
33
+ super
34
+ end
35
+
36
+
37
+ private
38
+ def push_event(level, message)
39
+ dev_log = AppRequest.current && caller[1].include?(MetaRequest.rails_root)
40
+ if dev_log
41
+ c = Callsite.parse(caller[1])
42
+ payload = {:message => message, :level => level, :line => c.line, :filename => c.filename, :method => c.method}
43
+ AppRequest.current.events << Event.new('meta_request.log', 0, 0, 0, payload)
44
+ end
45
+ rescue Exception => e
46
+ MetaRequest.logger.fatal(e.message + "\n " + e.backtrace.join("\n "))
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,32 @@
1
+ require 'json'
2
+
3
+ module MetaRequest
4
+ module Middlewares
5
+ class AppRequestHandler
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ app_request = AppRequest.new env["action_dispatch.request_id"]
12
+ app_request.current!
13
+ @app.call(env)
14
+ rescue Exception => exception
15
+ if defined?(ActionDispatch::ExceptionWrapper)
16
+ wrapper = if ActionDispatch::ExceptionWrapper.method_defined? :env
17
+ ActionDispatch::ExceptionWrapper.new(env, exception)
18
+ else
19
+ ActionDispatch::ExceptionWrapper.new(env['action_dispatch.backtrace_cleaner'], exception)
20
+ end
21
+ app_request.events.push(*Event.events_for_exception(wrapper))
22
+ else
23
+ app_request.events.push(*Event.events_for_exception(exception))
24
+ end
25
+ raise
26
+ ensure
27
+ Storage.new(app_request.id).write(app_request.events.to_json) unless app_request.events.empty?
28
+ end
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,37 @@
1
+ require 'rack/contrib/response_headers'
2
+
3
+ module MetaRequest
4
+ module Middlewares
5
+ class Headers
6
+ def initialize(app, app_config)
7
+ @app = app
8
+ @app_config = app_config
9
+ end
10
+
11
+ def call(env)
12
+ request_path = env['PATH_INFO']
13
+ middleware = Rack::ResponseHeaders.new(@app) do |headers|
14
+ headers['X-Meta-Request-Version'] = MetaRequest::VERSION unless skip?(request_path)
15
+ end
16
+ middleware.call(env)
17
+ end
18
+
19
+ private
20
+
21
+ # returns true if path should be ignored (not handled by RailsPanel extension)
22
+ #
23
+ def skip?(path)
24
+ asset?(path) || path.start_with?('/__better_errors/')
25
+ end
26
+
27
+ def asset?(path)
28
+ @app_config.respond_to?(:assets) && path.start_with?(assets_prefix)
29
+ end
30
+
31
+ def assets_prefix
32
+ "/#{@app_config.assets.prefix[/\A\/?(.*?)\/?\z/, 1]}/"
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ module MetaRequest
2
+ module Middlewares
3
+ class MetaRequestHandler
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ request_id = env["PATH_INFO"][%r{/__meta_request/(.+)\.json$}, 1]
10
+ if request_id
11
+ events_json(request_id)
12
+ else
13
+ @app.call(env)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def events_json(request_id)
20
+ events_json = Storage.new(request_id).read
21
+ [200, { "Content-Type" => "text/plain; charset=utf-8" }, [events_json]]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ require 'securerandom'
2
+ require 'active_support/core_ext/string/access'
3
+ require 'active_support/core_ext/object/blank'
4
+
5
+ # Backported from Rails 3.2 (ActionDispatch::RequestId)
6
+ module MetaRequest
7
+ module Middlewares
8
+ # Makes a unique request id available to the action_dispatch.request_id env variable (which is then accessible through
9
+ # ActionDispatch::Request#uuid) and sends the same id to the client via the X-Request-Id header.
10
+ #
11
+ # The unique request id is either based off the X-Request-Id header in the request, which would typically be generated
12
+ # by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid. If the
13
+ # header is accepted from the outside world, we sanitize it to a max of 255 chars and alphanumeric and dashes only.
14
+ #
15
+ # The unique request id can be used to trace a request end-to-end and would typically end up being part of log files
16
+ # from multiple pieces of the stack.
17
+ class RequestId
18
+ def initialize(app)
19
+ @app = app
20
+ end
21
+
22
+ def call(env)
23
+ env["action_dispatch.request_id"] = external_request_id(env) || internal_request_id
24
+ status, headers, body = @app.call(env)
25
+
26
+ headers["X-Request-Id"] = env["action_dispatch.request_id"]
27
+ [ status, headers, body ]
28
+ end
29
+
30
+ private
31
+ def external_request_id(env)
32
+ if request_id = env["HTTP_X_REQUEST_ID"].presence
33
+ request_id.gsub(/[^\w\-]/, "").first(255)
34
+ end
35
+ end
36
+
37
+ def internal_request_id
38
+ SecureRandom.hex(16)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,8 @@
1
+ module MetaRequest
2
+ module Middlewares
3
+ autoload :Headers, "meta_request/middlewares/headers"
4
+ autoload :AppRequestHandler, "meta_request/middlewares/app_request_handler"
5
+ autoload :MetaRequestHandler, "meta_request/middlewares/meta_request_handler"
6
+ autoload :RequestId, "meta_request/middlewares/request_id"
7
+ end
8
+ end
@@ -0,0 +1,30 @@
1
+ require 'rails/railtie'
2
+
3
+ module MetaRequest
4
+ class Railtie < ::Rails::Railtie
5
+
6
+ initializer 'meta_request.inject_middlewares' do |app|
7
+ app.middleware.use Middlewares::RequestId unless defined?(ActionDispatch::RequestId)
8
+ app.middleware.use Middlewares::MetaRequestHandler
9
+
10
+ if defined? ActionDispatch::DebugExceptions
11
+ app.middleware.insert_before ActionDispatch::DebugExceptions, Middlewares::Headers, app.config
12
+ else
13
+ app.middleware.use Middlewares::Headers, app.config
14
+ end
15
+
16
+ app.middleware.use Middlewares::AppRequestHandler
17
+ end
18
+
19
+ initializer 'meta_request.log_interceptor' do
20
+ Rails.logger.extend(LogInterceptor) if Rails.logger
21
+ end
22
+
23
+ initializer 'meta_request.subscribe_to_notifications' do
24
+ AppNotifications.subscribe
25
+ end
26
+
27
+ end
28
+ end
29
+
30
+
@@ -0,0 +1,43 @@
1
+ module MetaRequest
2
+ class Storage
3
+ attr_reader :key
4
+
5
+ def initialize(key)
6
+ @key = key
7
+ end
8
+
9
+ def write(value)
10
+ FileUtils.mkdir_p dir_path
11
+ File.open(json_file, 'wb') { |file| file.write(value) }
12
+ maintain_file_pool(10)
13
+ end
14
+
15
+ def read
16
+ File.read(json_file)
17
+ end
18
+
19
+ private
20
+
21
+ def maintain_file_pool(size)
22
+ files = Dir["#{dir_path}/*.json"]
23
+ files = files.sort_by { |f| -file_ctime(f) }
24
+ (files[size..-1] || []).each do |file|
25
+ FileUtils.rm_f(file)
26
+ end
27
+ end
28
+
29
+ def file_ctime(file)
30
+ File.stat(file).ctime.to_i
31
+ rescue Errno::ENOENT
32
+ 0
33
+ end
34
+
35
+ def json_file
36
+ File.join(dir_path, "#{key}.json")
37
+ end
38
+
39
+ def dir_path
40
+ File.join(Rails.root, 'tmp', 'data', 'meta_request')
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module MetaRequest
2
+ VERSION = '0.4.0'
3
+ end
@@ -0,0 +1,21 @@
1
+ module MetaRequest
2
+ autoload :VERSION, "meta_request/version"
3
+ autoload :Event, "meta_request/event"
4
+ autoload :AppRequest, "meta_request/app_request"
5
+ autoload :Storage, "meta_request/storage"
6
+ autoload :Middlewares, "meta_request/middlewares"
7
+ autoload :LogInterceptor, "meta_request/log_interceptor"
8
+ autoload :AppNotifications, "meta_request/app_notifications"
9
+
10
+ def self.logger
11
+ @@logger ||= Logger.new(File.join(Rails.root, 'log', 'meta_request.log'))
12
+ end
13
+
14
+ # stash a frozen copy away so we're not allocating a new string over and over
15
+ # again in AppNotifications and LogInterceptor
16
+ def self.rails_root
17
+ @@rails_root ||= Rails.root.to_s.freeze
18
+ end
19
+ end
20
+
21
+ require "meta_request/railtie"
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: meta_request
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Dejan Simic
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: railties
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 3.0.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 5.1.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 3.0.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 5.1.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: rack-contrib
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.1'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.1'
47
+ - !ruby/object:Gem::Dependency
48
+ name: callsite
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.0'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 0.0.11
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '0.0'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 0.0.11
67
+ description: Supporting gem for Rails Panel (Google Chrome extension for Rails development)
68
+ email: desimic@gmail.com
69
+ executables: []
70
+ extensions: []
71
+ extra_rdoc_files: []
72
+ files:
73
+ - README.md
74
+ - lib/meta_request.rb
75
+ - lib/meta_request/app_notifications.rb
76
+ - lib/meta_request/app_request.rb
77
+ - lib/meta_request/event.rb
78
+ - lib/meta_request/log_interceptor.rb
79
+ - lib/meta_request/middlewares.rb
80
+ - lib/meta_request/middlewares/app_request_handler.rb
81
+ - lib/meta_request/middlewares/headers.rb
82
+ - lib/meta_request/middlewares/meta_request_handler.rb
83
+ - lib/meta_request/middlewares/request_id.rb
84
+ - lib/meta_request/railtie.rb
85
+ - lib/meta_request/storage.rb
86
+ - lib/meta_request/version.rb
87
+ homepage: https://github.com/dejan/rails_panel/tree/master/meta_request
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.4.5
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Request your Rails request
111
+ test_files: []