rails_band 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +54 -0
- data/Rakefile +15 -0
- data/lib/rails_band/action_controller/event/exist_fragment.rb +14 -0
- data/lib/rails_band/action_controller/event/expire_fragment.rb +14 -0
- data/lib/rails_band/action_controller/event/halted_callback.rb +14 -0
- data/lib/rails_band/action_controller/event/process_action.rb +72 -0
- data/lib/rails_band/action_controller/event/read_fragment.rb +14 -0
- data/lib/rails_band/action_controller/event/redirect_to.rb +24 -0
- data/lib/rails_band/action_controller/event/send_data.rb +34 -0
- data/lib/rails_band/action_controller/event/send_file.rb +44 -0
- data/lib/rails_band/action_controller/event/start_processing.rb +44 -0
- data/lib/rails_band/action_controller/event/unpermitted_parameters.rb +42 -0
- data/lib/rails_band/action_controller/event/write_fragment.rb +14 -0
- data/lib/rails_band/action_controller/log_subscriber.rb +76 -0
- data/lib/rails_band/action_view/event/render_collection.rb +34 -0
- data/lib/rails_band/action_view/event/render_partial.rb +28 -0
- data/lib/rails_band/action_view/event/render_template.rb +24 -0
- data/lib/rails_band/action_view/from_views.rb +18 -0
- data/lib/rails_band/action_view/log_subscriber.rb +32 -0
- data/lib/rails_band/active_record/event/instantiation.rb +18 -0
- data/lib/rails_band/active_record/event/sql.rb +49 -0
- data/lib/rails_band/active_record/event/strict_loading_violation.rb +18 -0
- data/lib/rails_band/active_record/log_subscriber.rb +32 -0
- data/lib/rails_band/base_event.rb +44 -0
- data/lib/rails_band/configuration.rb +55 -0
- data/lib/rails_band/railtie.rb +29 -0
- data/lib/rails_band/version.rb +5 -0
- data/lib/rails_band.rb +22 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 97783d31ff40a28fa21181d954f365769bfcaf03a08557572363d90399e32347
|
4
|
+
data.tar.gz: 9e32469800f4a9b9e818e928283278975fd50408f24bee7d0241a7018e86e1f6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e63696203569e2126281fa79e7ec42ad4c8a09fdd04597eb41ae73cc1fd98b977776513e4f701304a8eb58a1597cb5940f2f920b3fe683c8df7262bc40adfffe
|
7
|
+
data.tar.gz: f267534e31f135a5a216eab5e41a0298f4b59c9ba17f4a683fb212bb1d10bf8eb2e024646fae669e16b315399a0e412f814dbd7d432222b60a2e6345b44fa0c5
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2021 Yutaka Kamei
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# rails_band
|
2
|
+
|
3
|
+
Easy-to-use Rails Instrumentation API.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'rails_band'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
```console
|
16
|
+
bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```console
|
21
|
+
gem install rails_band
|
22
|
+
```
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
rails_band automatically replaces each `LogSubscriber` with its own ones after it's loaded as a gem.
|
27
|
+
And then, you should configure how to consume Instrumentation hooks from core Rails implementations like this:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
Rails.application.config.rails_band.consumers = ->(event) { Rails.logger.info(event.to_h) }
|
31
|
+
```
|
32
|
+
|
33
|
+
You can also configure it by specifying event names:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
Rails.application.config.rails_band.consumers = {
|
37
|
+
default: ->(event) { Rails.logger.info(event.to_h) },
|
38
|
+
action_controller: ->(event) { Rails.logger.info(event.slice(:name, :method, :path, :status, :controller, :action)) },
|
39
|
+
'sql.active_record': ->(event) { Rails.logger.debug("#{event.sql_name}: #{event.sql}") },
|
40
|
+
}
|
41
|
+
```
|
42
|
+
|
43
|
+
Note `:default` is the fallback of other non-specific event names. Other events will be ignored without `:default`.
|
44
|
+
In other words, you can consume only events that you want to really consume without `:default`.
|
45
|
+
|
46
|
+
rails_band does not limit you only to use logging purposes. Enjoy with Rails Instrumentation hooks!
|
47
|
+
|
48
|
+
## Contributing
|
49
|
+
|
50
|
+
Contributing is welcome 😄 Please open a pull request!
|
51
|
+
|
52
|
+
## License
|
53
|
+
|
54
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
|
5
|
+
require 'bundler/gem_tasks'
|
6
|
+
|
7
|
+
require 'rake/testtask'
|
8
|
+
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'test'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = false
|
13
|
+
end
|
14
|
+
|
15
|
+
task default: :test
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActionController
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `exist_fragment?.action_controller`.
|
7
|
+
class ExistFragment < BaseEvent
|
8
|
+
def key
|
9
|
+
@key ||= @event.payload.fetch(:key)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActionController
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `expire_fragment.action_controller`.
|
7
|
+
class ExpireFragment < BaseEvent
|
8
|
+
def key
|
9
|
+
@key ||= @event.payload.fetch(:key)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActionController
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `halted_callback.action_controller`.
|
7
|
+
class HaltedCallback < BaseEvent
|
8
|
+
def filter
|
9
|
+
@filter ||= @event.payload.fetch(:filter)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActionController
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `process_action.action_controller`.
|
7
|
+
class ProcessAction < BaseEvent
|
8
|
+
def controller
|
9
|
+
@controller ||= @event.payload.fetch(:controller)
|
10
|
+
end
|
11
|
+
|
12
|
+
def action
|
13
|
+
@action ||= @event.payload.fetch(:action)
|
14
|
+
end
|
15
|
+
|
16
|
+
def params
|
17
|
+
@params ||= @event.payload.fetch(:params).except(*INTERNAL_PARAMS)
|
18
|
+
end
|
19
|
+
|
20
|
+
def headers
|
21
|
+
@headers ||= @event.payload.fetch(:headers)
|
22
|
+
end
|
23
|
+
|
24
|
+
def format
|
25
|
+
@format ||= @event.payload.fetch(:format)
|
26
|
+
end
|
27
|
+
|
28
|
+
def method
|
29
|
+
@method ||= @event.payload.fetch(:method)
|
30
|
+
end
|
31
|
+
|
32
|
+
def path
|
33
|
+
@path ||= @event.payload.fetch(:path).split('?', 2).first
|
34
|
+
end
|
35
|
+
|
36
|
+
def status
|
37
|
+
@status ||=
|
38
|
+
begin
|
39
|
+
status = @event.payload[:status]
|
40
|
+
|
41
|
+
if status.nil? && (exception_class_name = @event.payload[:exception]&.first)
|
42
|
+
status = ::ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
|
43
|
+
end
|
44
|
+
status
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def view_runtime
|
49
|
+
@view_runtime ||= @event.payload.fetch(:view_runtime)
|
50
|
+
end
|
51
|
+
|
52
|
+
if Gem::Version.new(Rails.version) >= Gem::Version.new('6.1')
|
53
|
+
define_method(:request) do
|
54
|
+
@request ||= @event.payload[:request]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if Gem::Version.new(Rails.version) >= Gem::Version.new('6.1')
|
59
|
+
define_method(:response) do
|
60
|
+
@response ||= @event.payload[:response]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def db_runtime
|
65
|
+
return @db_runtime if defined? @db_runtime
|
66
|
+
|
67
|
+
@db_runtime = @event.payload[:db_runtime]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActionController
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `read_fragment.action_controller`.
|
7
|
+
class ReadFragment < BaseEvent
|
8
|
+
def key
|
9
|
+
@key ||= @event.payload.fetch(:key)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActionController
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `redirect_to.action_controller`.
|
7
|
+
class RedirectTo < BaseEvent
|
8
|
+
def status
|
9
|
+
@status ||= @event.payload.fetch(:status)
|
10
|
+
end
|
11
|
+
|
12
|
+
def location
|
13
|
+
@location ||= @event.payload.fetch(:location)
|
14
|
+
end
|
15
|
+
|
16
|
+
if Gem::Version.new(Rails.version) >= Gem::Version.new('6.1')
|
17
|
+
define_method(:request) do
|
18
|
+
@request ||= @event.payload[:request]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActionController
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `send_data.action_controller`.
|
7
|
+
class SendData < BaseEvent
|
8
|
+
def filename
|
9
|
+
return @filename if defined? @filename
|
10
|
+
|
11
|
+
@filename = @event.payload[:filename]
|
12
|
+
end
|
13
|
+
|
14
|
+
def type
|
15
|
+
return @type if defined? @type
|
16
|
+
|
17
|
+
@type = @event.payload[:type]
|
18
|
+
end
|
19
|
+
|
20
|
+
def disposition
|
21
|
+
return @disposition if defined? @disposition
|
22
|
+
|
23
|
+
@disposition = @event.payload[:disposition]
|
24
|
+
end
|
25
|
+
|
26
|
+
def status
|
27
|
+
return @status if defined? @status
|
28
|
+
|
29
|
+
@status = @event.payload[:status]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActionController
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `send_file.action_controller`.
|
7
|
+
class SendFile < BaseEvent
|
8
|
+
def path
|
9
|
+
@path ||= @event.payload.fetch(:path)
|
10
|
+
end
|
11
|
+
|
12
|
+
def filename
|
13
|
+
return @filename if defined? @filename
|
14
|
+
|
15
|
+
@filename = @event.payload[:filename]
|
16
|
+
end
|
17
|
+
|
18
|
+
def type
|
19
|
+
return @type if defined? @type
|
20
|
+
|
21
|
+
@type = @event.payload[:type]
|
22
|
+
end
|
23
|
+
|
24
|
+
def disposition
|
25
|
+
return @disposition if defined? @disposition
|
26
|
+
|
27
|
+
@disposition = @event.payload[:disposition]
|
28
|
+
end
|
29
|
+
|
30
|
+
def status
|
31
|
+
return @status if defined? @status
|
32
|
+
|
33
|
+
@status = @event.payload[:status]
|
34
|
+
end
|
35
|
+
|
36
|
+
def url_based_filename
|
37
|
+
return @url_based_filename if defined? @url_based_filename
|
38
|
+
|
39
|
+
@url_based_filename = @event.payload[:url_based_filename]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActionController
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `start_processing.action_controller`.
|
7
|
+
class StartProcessing < BaseEvent
|
8
|
+
def controller
|
9
|
+
@controller ||= @event.payload.fetch(:controller)
|
10
|
+
end
|
11
|
+
|
12
|
+
def action
|
13
|
+
@action ||= @event.payload.fetch(:action)
|
14
|
+
end
|
15
|
+
|
16
|
+
def params
|
17
|
+
@params ||= @event.payload.fetch(:params).except(*INTERNAL_PARAMS)
|
18
|
+
end
|
19
|
+
|
20
|
+
def headers
|
21
|
+
@headers ||= @event.payload.fetch(:headers)
|
22
|
+
end
|
23
|
+
|
24
|
+
def format
|
25
|
+
@format ||= @event.payload.fetch(:format)
|
26
|
+
end
|
27
|
+
|
28
|
+
def method
|
29
|
+
@method ||= @event.payload.fetch(:method)
|
30
|
+
end
|
31
|
+
|
32
|
+
def path
|
33
|
+
@path ||= @event.payload.fetch(:path).split('?', 2).first
|
34
|
+
end
|
35
|
+
|
36
|
+
if Gem::Version.new(Rails.version) >= Gem::Version.new('6.1')
|
37
|
+
define_method(:request) do
|
38
|
+
@request ||= @event.payload[:request]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActionController
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `unpermitted_parameters.action_controller`.
|
7
|
+
class UnpermittedParameters < BaseEvent
|
8
|
+
def keys
|
9
|
+
@keys ||= @event.payload.fetch(:keys)
|
10
|
+
end
|
11
|
+
|
12
|
+
if Gem::Version.new(Rails.version) >= Gem::Version.new('7.0')
|
13
|
+
# @see https://github.com/rails/rails/pull/41809
|
14
|
+
define_method(:controller) do
|
15
|
+
@controller ||= @event.payload.dig(:context, :controller)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
if Gem::Version.new(Rails.version) >= Gem::Version.new('7.0')
|
20
|
+
# @see https://github.com/rails/rails/pull/41809
|
21
|
+
define_method(:action) do
|
22
|
+
@action ||= @event.payload.dig(:context, :action)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if Gem::Version.new(Rails.version) >= Gem::Version.new('7.0')
|
27
|
+
# @see https://github.com/rails/rails/pull/41809
|
28
|
+
define_method(:request) do
|
29
|
+
@request ||= @event.payload.dig(:context, :request)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
if Gem::Version.new(Rails.version) >= Gem::Version.new('7.0')
|
34
|
+
# @see https://github.com/rails/rails/pull/41809
|
35
|
+
define_method(:params) do
|
36
|
+
@params ||= @event.payload.dig(:context, :params)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActionController
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `write_fragment.action_controller`.
|
7
|
+
class WriteFragment < BaseEvent
|
8
|
+
def key
|
9
|
+
@key ||= @event.payload.fetch(:key)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_band/action_controller/event/write_fragment'
|
4
|
+
require 'rails_band/action_controller/event/read_fragment'
|
5
|
+
require 'rails_band/action_controller/event/expire_fragment'
|
6
|
+
require 'rails_band/action_controller/event/exist_fragment'
|
7
|
+
require 'rails_band/action_controller/event/start_processing'
|
8
|
+
require 'rails_band/action_controller/event/process_action'
|
9
|
+
require 'rails_band/action_controller/event/send_file'
|
10
|
+
require 'rails_band/action_controller/event/send_data'
|
11
|
+
require 'rails_band/action_controller/event/redirect_to'
|
12
|
+
require 'rails_band/action_controller/event/halted_callback'
|
13
|
+
require 'rails_band/action_controller/event/unpermitted_parameters'
|
14
|
+
|
15
|
+
module RailsBand
|
16
|
+
module ActionController
|
17
|
+
# This comes from ::ActionController::LogSubscriber
|
18
|
+
# @see https://github.com/rails/rails/blob/53000f3a2df5c59252d019bbb8d46728b291ec74/actionpack/lib/action_controller/log_subscriber.rb#L5
|
19
|
+
INTERNAL_PARAMS = %w[controller action format _method only_path].freeze
|
20
|
+
|
21
|
+
# The custom LogSubscriber for ActionController.
|
22
|
+
class LogSubscriber < ::ActiveSupport::LogSubscriber
|
23
|
+
mattr_accessor :consumers
|
24
|
+
|
25
|
+
def write_fragment(event)
|
26
|
+
consumer_of(__method__)&.call(Event::WriteFragment.new(event))
|
27
|
+
end
|
28
|
+
|
29
|
+
def read_fragment(event)
|
30
|
+
consumer_of(__method__)&.call(Event::ReadFragment.new(event))
|
31
|
+
end
|
32
|
+
|
33
|
+
def expire_fragment(event)
|
34
|
+
consumer_of(__method__)&.call(Event::ExpireFragment.new(event))
|
35
|
+
end
|
36
|
+
|
37
|
+
def exist_fragment?(event)
|
38
|
+
consumer_of(__method__)&.call(Event::ExistFragment.new(event))
|
39
|
+
end
|
40
|
+
|
41
|
+
def start_processing(event)
|
42
|
+
consumer_of(__method__)&.call(Event::StartProcessing.new(event))
|
43
|
+
end
|
44
|
+
|
45
|
+
def process_action(event)
|
46
|
+
consumer_of(__method__)&.call(Event::ProcessAction.new(event))
|
47
|
+
end
|
48
|
+
|
49
|
+
def send_file(event)
|
50
|
+
consumer_of(__method__)&.call(Event::SendFile.new(event))
|
51
|
+
end
|
52
|
+
|
53
|
+
def send_data(event)
|
54
|
+
consumer_of(__method__)&.call(Event::SendData.new(event))
|
55
|
+
end
|
56
|
+
|
57
|
+
def redirect_to(event)
|
58
|
+
consumer_of(__method__)&.call(Event::RedirectTo.new(event))
|
59
|
+
end
|
60
|
+
|
61
|
+
def halted_callback(event)
|
62
|
+
consumer_of(__method__)&.call(Event::HaltedCallback.new(event))
|
63
|
+
end
|
64
|
+
|
65
|
+
def unpermitted_parameters(event)
|
66
|
+
consumer_of(__method__)&.call(Event::UnpermittedParameters.new(event))
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def consumer_of(sub_event)
|
72
|
+
consumers[:"#{sub_event}.action_controller"] || consumers[:action_controller] || consumers[:default]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_band/action_view/from_views'
|
4
|
+
|
5
|
+
module RailsBand
|
6
|
+
module ActionView
|
7
|
+
module Event
|
8
|
+
# A wrapper for the event that is passed to `render_collection.action_view`.
|
9
|
+
class RenderCollection < BaseEvent
|
10
|
+
include FromViews
|
11
|
+
|
12
|
+
def identifier
|
13
|
+
@identifier ||= from_views(@event.payload.fetch(:identifier))
|
14
|
+
end
|
15
|
+
|
16
|
+
def layout
|
17
|
+
return @layout if defined? @layout
|
18
|
+
|
19
|
+
@layout = @event.payload[:layout]&.yield_self { |layout| from_views(layout) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def count
|
23
|
+
@count ||= @event.payload.fetch(:count)
|
24
|
+
end
|
25
|
+
|
26
|
+
def cache_hits
|
27
|
+
return @cache_hits if defined? @cache_hits
|
28
|
+
|
29
|
+
@cache_hits = @event.payload[:cache_hits]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_band/action_view/from_views'
|
4
|
+
|
5
|
+
module RailsBand
|
6
|
+
module ActionView
|
7
|
+
module Event
|
8
|
+
# A wrapper for the event that is passed to `render_partial.action_view`.
|
9
|
+
class RenderPartial < BaseEvent
|
10
|
+
include FromViews
|
11
|
+
|
12
|
+
def identifier
|
13
|
+
@identifier ||= from_views(@event.payload.fetch(:identifier))
|
14
|
+
end
|
15
|
+
|
16
|
+
def layout
|
17
|
+
return @layout if defined? @layout
|
18
|
+
|
19
|
+
@layout = @event.payload[:layout]&.yield_self { |layout| from_views(layout) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def cache_hit
|
23
|
+
@cache_hit ||= @event.payload.fetch(:cache_hit)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_band/action_view/from_views'
|
4
|
+
|
5
|
+
module RailsBand
|
6
|
+
module ActionView
|
7
|
+
module Event
|
8
|
+
# A wrapper for the event that is passed to `render_template.action_view`.
|
9
|
+
class RenderTemplate < BaseEvent
|
10
|
+
include FromViews
|
11
|
+
|
12
|
+
def identifier
|
13
|
+
@identifier ||= from_views(@event.payload.fetch(:identifier))
|
14
|
+
end
|
15
|
+
|
16
|
+
def layout
|
17
|
+
return @layout if defined? @layout
|
18
|
+
|
19
|
+
@layout = @event.payload[:layout]&.yield_self { |layout| from_views(layout) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActionView
|
5
|
+
# FromViews provides a sole method #from_views to delete the prefix of 'app/views/' from the passed string.
|
6
|
+
module FromViews
|
7
|
+
private
|
8
|
+
|
9
|
+
def from_views(string)
|
10
|
+
string.delete_prefix(views_prefix)
|
11
|
+
end
|
12
|
+
|
13
|
+
def views_prefix
|
14
|
+
@views_prefix ||= Rails.root.join('app/views/').to_path
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_band/action_view/event/render_template'
|
4
|
+
require 'rails_band/action_view/event/render_partial'
|
5
|
+
require 'rails_band/action_view/event/render_collection'
|
6
|
+
|
7
|
+
module RailsBand
|
8
|
+
module ActionView
|
9
|
+
# The custom LogSubscriber for ActionView.
|
10
|
+
class LogSubscriber < ::ActiveSupport::LogSubscriber
|
11
|
+
mattr_accessor :consumers
|
12
|
+
|
13
|
+
def render_template(event)
|
14
|
+
consumer_of(__method__)&.call(Event::RenderTemplate.new(event))
|
15
|
+
end
|
16
|
+
|
17
|
+
def render_partial(event)
|
18
|
+
consumer_of(__method__)&.call(Event::RenderPartial.new(event))
|
19
|
+
end
|
20
|
+
|
21
|
+
def render_collection(event)
|
22
|
+
consumer_of(__method__)&.call(Event::RenderCollection.new(event))
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def consumer_of(sub_event)
|
28
|
+
consumers[:"#{sub_event}.action_view"] || consumers[:action_view] || consumers[:default]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActiveRecord
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `instantiation.active_record`.
|
7
|
+
class Instantiation < BaseEvent
|
8
|
+
def record_count
|
9
|
+
@record_count ||= @event.payload.fetch(:record_count)
|
10
|
+
end
|
11
|
+
|
12
|
+
def class_name
|
13
|
+
@class_name ||= @event.payload.fetch(:class_name)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActiveRecord
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `sql.active_record`.
|
7
|
+
class Sql < BaseEvent
|
8
|
+
def sql
|
9
|
+
@sql ||= @event.payload.fetch(:sql)
|
10
|
+
end
|
11
|
+
|
12
|
+
# @note This method is renamed in order to avoid conflicts with BaseEvent#name.
|
13
|
+
def sql_name
|
14
|
+
@sql_name ||= @event.payload.fetch(:name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def binds
|
18
|
+
@binds ||= @event.payload.fetch(:binds)
|
19
|
+
end
|
20
|
+
|
21
|
+
def type_casted_binds
|
22
|
+
@type_casted_binds ||= @event.payload.fetch(:type_casted_binds)
|
23
|
+
end
|
24
|
+
|
25
|
+
def connection
|
26
|
+
@connection ||= @event.payload.fetch(:connection)
|
27
|
+
end
|
28
|
+
|
29
|
+
def statement_name
|
30
|
+
return @statement_name if defined? @statement_name
|
31
|
+
|
32
|
+
@statement_name = @event.payload[:statement_name]
|
33
|
+
end
|
34
|
+
|
35
|
+
def async
|
36
|
+
return @async if defined? @async
|
37
|
+
|
38
|
+
@async = @event.payload[:async]
|
39
|
+
end
|
40
|
+
|
41
|
+
def cached
|
42
|
+
return @cached if defined? @cached
|
43
|
+
|
44
|
+
@cached = @event.payload[:cached]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
module ActiveRecord
|
5
|
+
module Event
|
6
|
+
# A wrapper for the event that is passed to `strict_loading_violation.active_record`.
|
7
|
+
class StrictLoadingViolation < BaseEvent
|
8
|
+
def owner
|
9
|
+
@owner ||= @event.payload.fetch(:owner)
|
10
|
+
end
|
11
|
+
|
12
|
+
def reflection
|
13
|
+
@reflection ||= @event.payload.fetch(:reflection)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_band/active_record/event/sql'
|
4
|
+
require 'rails_band/active_record/event/instantiation'
|
5
|
+
require 'rails_band/active_record/event/strict_loading_violation'
|
6
|
+
|
7
|
+
module RailsBand
|
8
|
+
module ActiveRecord
|
9
|
+
# The custom LogSubscriber for ActiveRecord.
|
10
|
+
class LogSubscriber < ::ActiveSupport::LogSubscriber
|
11
|
+
mattr_accessor :consumers
|
12
|
+
|
13
|
+
def strict_loading_violation(event)
|
14
|
+
consumer_of(__method__)&.call(Event::StrictLoadingViolation.new(event))
|
15
|
+
end
|
16
|
+
|
17
|
+
def sql(event)
|
18
|
+
consumer_of(__method__)&.call(Event::Sql.new(event))
|
19
|
+
end
|
20
|
+
|
21
|
+
def instantiation(event)
|
22
|
+
consumer_of(__method__)&.call(Event::Instantiation.new(event))
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def consumer_of(sub_event)
|
28
|
+
consumers[:"#{sub_event}.active_record"] || consumers[:active_record] || consumers[:default]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
# The base class of each Event class.
|
5
|
+
class BaseEvent
|
6
|
+
attr_reader :name, :time, :end, :transaction_id, :children,
|
7
|
+
:cpu_time, :idle_time, :allocations, :duration
|
8
|
+
|
9
|
+
# @param event [ActiveSupport::Notifications::Event]
|
10
|
+
def initialize(event)
|
11
|
+
@event = event
|
12
|
+
@name = event.name
|
13
|
+
@time = event.time
|
14
|
+
@end = event.end
|
15
|
+
@transaction_id = event.transaction_id
|
16
|
+
@children = event.children
|
17
|
+
@cpu_time = event.cpu_time
|
18
|
+
@idle_time = event.idle_time
|
19
|
+
@allocations = event.allocations
|
20
|
+
@duration = event.duration
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_h
|
24
|
+
@to_h ||= {
|
25
|
+
name: @name, time: @time, end: @end, transaction_id: @transaction_id, children: @children,
|
26
|
+
cpu_time: @cpu_time, idle_time: @idle_time, allocations: @allocations, duration: @duration
|
27
|
+
}.merge!(
|
28
|
+
public_methods(false).reject { |meth| non_hash_keys.include?(meth) }.each_with_object({}) do |meth, h|
|
29
|
+
h[meth] = public_send(meth)
|
30
|
+
end
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def slice(*args)
|
35
|
+
to_h.slice(*args)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def non_hash_keys
|
41
|
+
@non_hash_keys ||= []
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBand
|
4
|
+
# RailsBand::Configuration is responsible for storing user-specified configuration.
|
5
|
+
class Configuration
|
6
|
+
# Consumers is a wrapper of ActiveSupport::HashWithIndifferentAccess, which validates the value on #[]=.
|
7
|
+
class Consumers < ActiveSupport::HashWithIndifferentAccess
|
8
|
+
def []=(key, value)
|
9
|
+
unless value.respond_to?(:call)
|
10
|
+
raise ArgumentError, "The value for `#{key.inspect}` must have #call: the passed one is `#{value.inspect}`"
|
11
|
+
end
|
12
|
+
|
13
|
+
super(key, value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Consumers]
|
18
|
+
attr_reader :consumers
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@consumers = Consumers.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param value [Hash, #call]
|
25
|
+
# Consumer(s) to be called when instrumentation APIs are dispatched. If you pass a single value that has `#call`
|
26
|
+
# method, the value is going to be always called for any instrumentation API events. You can also specify
|
27
|
+
# consumers, assigning a Hash, the keys of which are instrumentation API event names. When the value is a Hash,
|
28
|
+
# you are able to set a key, named `:default`, which is called for the rest of instrumentation API events
|
29
|
+
# you don't assign.
|
30
|
+
#
|
31
|
+
# Instrumentation API event names can be full event names or just namespaces, such as `:action_controller`.
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# config = RailsBand::Configuration.new
|
35
|
+
# config.consumers = ->(e) { Rails.logger.info(e) }
|
36
|
+
# config.consumers = {
|
37
|
+
# default: ->(e) { Rails.logger.info(e) }
|
38
|
+
# action_controller: ->(e) { Rails.logger.info("ActionController! #{e}") }
|
39
|
+
# 'render_template.action_view': ->(e) { Rails.logger.debug("RenderTemplate! #{e}") }
|
40
|
+
# }
|
41
|
+
#
|
42
|
+
# @see https://guides.rubyonrails.org/active_support_instrumentation.html
|
43
|
+
def consumers=(value)
|
44
|
+
@consumers =
|
45
|
+
case value
|
46
|
+
when Hash
|
47
|
+
value.each_with_object(Consumers.new) do |(k, v), c|
|
48
|
+
c[k] = v
|
49
|
+
end
|
50
|
+
else
|
51
|
+
Consumers.new.tap { |c| c[:default] = value }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_controller/log_subscriber'
|
4
|
+
require 'action_view/log_subscriber'
|
5
|
+
|
6
|
+
module RailsBand
|
7
|
+
# RailsBand::Railtie is responsible for preparing its configuration and accepting user-specified configs.
|
8
|
+
class Railtie < ::Rails::Railtie
|
9
|
+
config.rails_band = Configuration.new
|
10
|
+
|
11
|
+
config.after_initialize do |app|
|
12
|
+
consumers = app.config.rails_band.consumers
|
13
|
+
|
14
|
+
swap = lambda { |old_class, new_class, namespace|
|
15
|
+
old_class.detach_from namespace
|
16
|
+
new_class.consumers = consumers
|
17
|
+
new_class.attach_to namespace
|
18
|
+
}
|
19
|
+
|
20
|
+
swap.call(::ActionController::LogSubscriber, RailsBand::ActionController::LogSubscriber, :action_controller)
|
21
|
+
swap.call(::ActionView::LogSubscriber, RailsBand::ActionView::LogSubscriber, :action_view)
|
22
|
+
|
23
|
+
if defined?(::ActiveRecord)
|
24
|
+
require 'active_record/log_subscriber'
|
25
|
+
swap.call(::ActiveRecord::LogSubscriber, RailsBand::ActiveRecord::LogSubscriber, :active_record)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/rails_band.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_band/version'
|
4
|
+
require 'rails_band/configuration'
|
5
|
+
require 'rails_band/base_event'
|
6
|
+
require 'rails_band/railtie'
|
7
|
+
|
8
|
+
# Rails::Band unsubscribes all default LogSubscribers from Rails Instrumentation API,
|
9
|
+
# and it subscribes our own custom LogSubscribers to make it easy to access Rails Instrumentation API.
|
10
|
+
module RailsBand
|
11
|
+
module ActionController
|
12
|
+
autoload :LogSubscriber, 'rails_band/action_controller/log_subscriber'
|
13
|
+
end
|
14
|
+
|
15
|
+
module ActionView
|
16
|
+
autoload :LogSubscriber, 'rails_band/action_view/log_subscriber'
|
17
|
+
end
|
18
|
+
|
19
|
+
module ActiveRecord
|
20
|
+
autoload :LogSubscriber, 'rails_band/active_record/log_subscriber'
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_band
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yutaka Kamei
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-10-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.0'
|
27
|
+
description: A Rails plugin to facilitate the use of Rails Instrumentation API.
|
28
|
+
email:
|
29
|
+
- kamei@yykamei.me
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- LICENSE
|
35
|
+
- README.md
|
36
|
+
- Rakefile
|
37
|
+
- lib/rails_band.rb
|
38
|
+
- lib/rails_band/action_controller/event/exist_fragment.rb
|
39
|
+
- lib/rails_band/action_controller/event/expire_fragment.rb
|
40
|
+
- lib/rails_band/action_controller/event/halted_callback.rb
|
41
|
+
- lib/rails_band/action_controller/event/process_action.rb
|
42
|
+
- lib/rails_band/action_controller/event/read_fragment.rb
|
43
|
+
- lib/rails_band/action_controller/event/redirect_to.rb
|
44
|
+
- lib/rails_band/action_controller/event/send_data.rb
|
45
|
+
- lib/rails_band/action_controller/event/send_file.rb
|
46
|
+
- lib/rails_band/action_controller/event/start_processing.rb
|
47
|
+
- lib/rails_band/action_controller/event/unpermitted_parameters.rb
|
48
|
+
- lib/rails_band/action_controller/event/write_fragment.rb
|
49
|
+
- lib/rails_band/action_controller/log_subscriber.rb
|
50
|
+
- lib/rails_band/action_view/event/render_collection.rb
|
51
|
+
- lib/rails_band/action_view/event/render_partial.rb
|
52
|
+
- lib/rails_band/action_view/event/render_template.rb
|
53
|
+
- lib/rails_band/action_view/from_views.rb
|
54
|
+
- lib/rails_band/action_view/log_subscriber.rb
|
55
|
+
- lib/rails_band/active_record/event/instantiation.rb
|
56
|
+
- lib/rails_band/active_record/event/sql.rb
|
57
|
+
- lib/rails_band/active_record/event/strict_loading_violation.rb
|
58
|
+
- lib/rails_band/active_record/log_subscriber.rb
|
59
|
+
- lib/rails_band/base_event.rb
|
60
|
+
- lib/rails_band/configuration.rb
|
61
|
+
- lib/rails_band/railtie.rb
|
62
|
+
- lib/rails_band/version.rb
|
63
|
+
homepage: https://github.com/yykamei/rails_band
|
64
|
+
licenses:
|
65
|
+
- MIT
|
66
|
+
metadata:
|
67
|
+
homepage_uri: https://github.com/yykamei/rails_band
|
68
|
+
source_code_uri: https://github.com/yykamei/rails_band
|
69
|
+
changelog_uri: https://github.com/yykamei/rails_band/blob/main/CHANGELOG.md
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '2.6'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubygems_version: 3.2.22
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Easy-to-use Rails Instrumentation API
|
89
|
+
test_files: []
|