union_station_hooks_core 1.0.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.
@@ -0,0 +1,187 @@
1
+ # Union Station - https://www.unionstationapp.com/
2
+ # Copyright (c) 2010-2015 Phusion Holding B.V.
3
+ #
4
+ # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V.
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+
24
+ UnionStationHooks.require_lib 'utils'
25
+
26
+ module UnionStationHooks
27
+ class RequestReporter
28
+ ###### Logging controller-related information ######
29
+
30
+ # Logs that you are calling a web framework controller action. Of course,
31
+ # you should only call this if your web framework has the concept of
32
+ # controller actions. For example Rails does, but Sinatra and Grape
33
+ # don't.
34
+ #
35
+ # This form takes an options hash as well as a block. You can pass
36
+ # additional information about the web framework controller action, which
37
+ # will be logged. The block is expected to perform the actual request
38
+ # handling. When the block returns, timing information about the block is
39
+ # automatically logged.
40
+ #
41
+ # See also {#log_controller_action} for a form that doesn't
42
+ # expect a block.
43
+ #
44
+ # The `union_station_hooks_rails` gem automatically calls this for you
45
+ # if your application is a Rails app.
46
+ #
47
+ # @yield The given block is expected to perform request handling.
48
+ # @param [Hash] options Information about the controller action.
49
+ # All options are optional.
50
+ # @option options [String] :controller_name
51
+ # The controller's name, e.g. `PostsController`.
52
+ # @option options [String] :action_name
53
+ # The controller action's name, e.g. `create`.
54
+ # @option options [String] :method
55
+ # The HTTP method that the web framework thinks this request should have,
56
+ # e.g. `GET` and `PUT`. The main use case for this option is to support
57
+ # Rails's HTTP verb emulation. Rails uses a parameter named
58
+ # [`_method`](http://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-patch-put-or-delete-methods-work-questionmark)
59
+ # to emulate HTTP verbs besides GET and POST. Other web frameworks may
60
+ # have a similar mechanism.
61
+ # @return The return value of the block.
62
+ #
63
+ # @example Rails example
64
+ # # This example shows what to put inside a Rails controller action
65
+ # # method. Note that all of this is automatically done for you if you
66
+ # # use the union_station_hooks_rails gem.
67
+ # options = {
68
+ # :controller_name => self.class.name,
69
+ # :action_name => action_name,
70
+ # :method => request.request_method
71
+ # }
72
+ # reporter.log_controller_action_block(options) do
73
+ # do_some_request_processing_here
74
+ # end
75
+ def log_controller_action_block(options = {})
76
+ if null?
77
+ do_nothing_on_null(:log_controller_action_block)
78
+ yield
79
+ else
80
+ build_full_controller_action_string(options)
81
+ has_error = true
82
+ begin_time = UnionStationHooks.now
83
+ begin
84
+ result = yield
85
+ has_error = false
86
+ result
87
+ ensure
88
+ log_controller_action(
89
+ options.merge(
90
+ :begin_time => begin_time,
91
+ :end_time => UnionStationHooks.now,
92
+ :has_error => has_error
93
+ )
94
+ )
95
+ end
96
+ end
97
+ end
98
+
99
+ # Logs that you are calling a web framework controller action. Of course,
100
+ # you should only call this if your web framework has the concept of
101
+ # controller actions. For example Rails does, but Sinatra and Grape
102
+ # don't.
103
+ #
104
+ # You can pass additional information about the web framework controller
105
+ # action, which will be logged.
106
+ #
107
+ # Unlike {#log_controller_action_block}, this form does not expect a block.
108
+ # However, you are expected to pass timing information to the options
109
+ # hash.
110
+ #
111
+ # The `union_station_hooks_rails` gem automatically calls
112
+ # {#log_controller_action_block} for you if your application is a Rails
113
+ # app.
114
+ #
115
+ # @param [Hash] options Information about the controller action.
116
+ # @option options [String] :controller_name (optional)
117
+ # The controller's name, e.g. `PostsController`.
118
+ # @option options [String] :action_name (optional if :controller_name
119
+ # isn't set) The controller action's name, e.g. `create`.
120
+ # @option options [String] :method (optional)
121
+ # The HTTP method that the web framework thinks this request should have,
122
+ # e.g. `GET` and `PUT`. The main use case for this option is to support
123
+ # Rails's HTTP verb emulation. Rails uses a parameter named
124
+ # [`_method`](http://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-patch-put-or-delete-methods-work-questionmark)
125
+ # to emulate HTTP verbs besides GET and POST. Other web frameworks may
126
+ # have a similar mechanism.
127
+ # @option options [TimePoint or Time] :begin_time The time at which the
128
+ # controller action begun. See {UnionStationHooks.now} to learn more.
129
+ # @option options [TimePoint or Time] :end_time The time at which the
130
+ # controller action ended. See {UnionStationHooks.now} to learn more.
131
+ # @option options [Boolean] :has_error (optional) Whether an uncaught
132
+ # exception occurred during the request. Default: false.
133
+ #
134
+ # @example
135
+ # # This example shows what to put inside a Rails controller action
136
+ # # method. Note that all of this is automatically done for you if you
137
+ # # use the union_station_hooks_rails gem.
138
+ # options = {
139
+ # :controller_name => self.class.name,
140
+ # :action_name => action_name,
141
+ # :method => request.request_method,
142
+ # :begin_time => UnionStationHooks.now
143
+ # }
144
+ # begin
145
+ # do_some_request_processing_here
146
+ # rescue Exception
147
+ # options[:has_error] = true
148
+ # raise
149
+ # ensure
150
+ # options[:end_time] = UnionStationHooks.now
151
+ # reporter.log_controller_action(options)
152
+ # end
153
+ def log_controller_action(options)
154
+ return do_nothing_on_null(:log_controller_action) if null?
155
+ Utils.require_key(options, :begin_time)
156
+ Utils.require_key(options, :end_time)
157
+
158
+ if options[:controller_name]
159
+ build_full_controller_action_string(options)
160
+ @transaction.message("Controller action: #{@controller_action}")
161
+ end
162
+ if options[:method]
163
+ @transaction.message("Application request method: #{options[:method]}")
164
+ end
165
+ @transaction.log_activity('framework request processing',
166
+ options[:begin_time], options[:end_time], nil, options[:has_error])
167
+ end
168
+
169
+ # Returns whether {#log_controller_action_block} or
170
+ # {#log_controller_action} has been called during this request.
171
+ #
172
+ # @return [Boolean]
173
+ def controller_action_logged?
174
+ !!@controller_action
175
+ end
176
+
177
+ private
178
+
179
+ def build_full_controller_action_string(options)
180
+ if options[:controller_name]
181
+ Utils.require_key(options, :action_name)
182
+ @controller_action = "#{options[:controller_name]}#" \
183
+ "#{options[:action_name]}"
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,218 @@
1
+ # Union Station - https://www.unionstationapp.com/
2
+ # Copyright (c) 2010-2015 Phusion Holding B.V.
3
+ #
4
+ # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V.
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+
24
+ require 'digest/md5'
25
+ UnionStationHooks.require_lib 'utils'
26
+
27
+ module UnionStationHooks
28
+ class RequestReporter
29
+ ###### Logging miscellaneous other information ######
30
+
31
+ # Logs a user-defined activity, for display in the activity timeline.
32
+ #
33
+ # An activity is a block in the activity timeline in the Union Station
34
+ # user interace. It has a name, a begin time and an end time.
35
+ #
36
+ # This form takes a block. Before and after the block runs, the time
37
+ # is measured.
38
+ #
39
+ # @param name The name that should show up in the activity timeline.
40
+ # It can be any arbitrary name but may not contain newlines.
41
+ # @return The return value of the block.
42
+ # @yield The block is expected to perform the activity.
43
+ # @example
44
+ # reporter.log_activity_block('Preheat cache') do
45
+ # calculate_preheat_values.each_pair do |key, value|
46
+ # Rails.cache.write(key, value)
47
+ # end
48
+ # end
49
+ def log_activity_block(name, &block)
50
+ if null?
51
+ do_nothing_on_null(:log_activity_block)
52
+ yield
53
+ else
54
+ @transaction.log_activity_block(name, &block)
55
+ end
56
+ end
57
+
58
+ # Logs a benchmarking activity, for display in the activity timeline.
59
+ #
60
+ # An activity is a block in the activity timeline in the Union Station
61
+ # user interace. It has a name, a begin time and an end time.
62
+ #
63
+ # The primary use case of this method is to integrate with Rails's
64
+ # benchmarking API (`ActiveSupport::Benchmarkable`). The Rails benchmarking
65
+ # API allows you to run a block and to log how long that block has taken.
66
+ # But you can also use it to integrate with the Ruby standard library's
67
+ # `Benchmark` class.
68
+ #
69
+ # You can wrap a benchmark call in a
70
+ # `UnionStationHooks.log_benchmark_block` call, so that an entry for it is
71
+ # displayed in the acitivity timeline. This method measures the time before
72
+ # and after the block runs.
73
+ #
74
+ # The difference between this method and {#log_activity_block} is that this
75
+ # method generates timeline blocks of a different color, as to
76
+ # differentiate user-defined activities from benchmark activities.
77
+ #
78
+ # If your app is a Rails app, then the `union_station_hooks_rails` gem
79
+ # automatically calls this for you every time
80
+ # `ActiveSupport::Benchmarkable#benchmark` is called. This includes
81
+ # `benchmark` calls from controllers and from views.
82
+ #
83
+ # @param title A title for this benchmark. It can be any arbitrary name but
84
+ # may not contain newlines.
85
+ # @return The return value of the block.
86
+ # @yield The block is expected to perform the benchmarking activity.
87
+ # @example Rails example
88
+ # # This example shows what to put inside a Rails controller action
89
+ # # method. Note that the `log_benchmark_block` call is automatically done
90
+ # # for you if you use the union_station_hooks_rails gem.
91
+ # UnionStationHooks.log_benchmark_block('Process data files') do
92
+ # benchmark('Process data files') do
93
+ # expensive_files_operation
94
+ # end
95
+ # end
96
+ def log_benchmark_block(title = 'Benchmarking', &block)
97
+ if null?
98
+ do_nothing_on_null(:log_benchmark_block)
99
+ yield
100
+ else
101
+ log_activity_block("BENCHMARK: #{title}", &block)
102
+ end
103
+ end
104
+
105
+ # Logs an exception that occurred during a request.
106
+ #
107
+ # If {#log_controller_action} or {#log_controller_action_happened}
108
+ # was called during the same request, then the information passed to
109
+ # those methods will be included in the exception report.
110
+ #
111
+ # @param [Exception] exception
112
+ def log_exception(exception)
113
+ transaction = @context.new_transaction(
114
+ @app_group_name,
115
+ :exceptions,
116
+ @key)
117
+ begin
118
+ return do_nothing_on_null(:log_exception) if transaction.null?
119
+
120
+ base64_message = exception.message
121
+ base64_message = exception.to_s if base64_message.empty?
122
+ base64_message = Utils.base64(base64_message)
123
+ base64_backtrace = Utils.base64(exception.backtrace.join("\n"))
124
+
125
+ if controller_action_logged?
126
+ transaction.message("Controller action: #{@controller_action}")
127
+ end
128
+ transaction.message("Request transaction ID: #{@txn_id}")
129
+ transaction.message("Message: #{base64_message}")
130
+ transaction.message("Class: #{exception.class.name}")
131
+ transaction.message("Backtrace: #{base64_backtrace}")
132
+ ensure
133
+ transaction.close
134
+ end
135
+ end
136
+
137
+ # Logs a database query that was performed during the request.
138
+ #
139
+ # @option options [String] :name (optional) A name for this database
140
+ # query activity. Default: "SQL"
141
+ # @option options [TimePoint or Time] :begin_time The time at which this
142
+ # database query begun. See {UnionStationHooks.now} to learn more.
143
+ # @option options [TimePoint or Time] :end_time The time at which this
144
+ # database query ended. See {UnionStationHooks.now} to learn more.
145
+ # @option options [String] :query The database query string.
146
+ def log_database_query(options)
147
+ return do_nothing_on_null(:log_database_query) if null?
148
+ Utils.require_key(options, :begin_time)
149
+ Utils.require_key(options, :end_time)
150
+ Utils.require_non_empty_key(options, :query)
151
+
152
+ name = options[:name] || 'SQL'
153
+ begin_time = options[:begin_time]
154
+ end_time = options[:end_time]
155
+ query = options[:query]
156
+
157
+ digest = Digest::MD5.hexdigest("#{name}\0#{query}\0#{rand}")
158
+ @transaction.log_activity("DB BENCHMARK: #{digest}",
159
+ begin_time, end_time, "#{name}\n#{query}")
160
+ end
161
+
162
+ # Logs that something was successfully retrieved from a cache.
163
+ # This can be any cache, be it an in-memory Hash, Redis, Memcached, a
164
+ # flat file or whatever.
165
+ #
166
+ # There is just one exception. You should not use this method to log cache
167
+ # hits in the ActiveRecord SQL cache or similar mechanisms.
168
+ # Database-related timing should be logged with {#log_database_query}.
169
+ #
170
+ # If your app is a Rails app, then the `union_station_hooks_rails` gem
171
+ # automatically calls this for you every time an `ActiveSupport::Cache`
172
+ # `#fetch` or `#read` call success. This includes calls to
173
+ # `Rails.cache.fetch` or `Rails.cache.read`, because `Rails.cache` is
174
+ # an instance of `ActiveSupport::Cache`.
175
+ #
176
+ # @param [String] name A unique name for this cache hit event. The cache
177
+ # key is a good value to use.
178
+ # @note At present (30 September 2015), logged cache hit/miss information
179
+ # isn't shown in the Union Station interface. We may implement this
180
+ # feature in the near future.
181
+ def log_cache_hit(name)
182
+ return do_nothing_on_null(:log_cache_hit) if null?
183
+ @transaction.message("Cache hit: #{name}")
184
+ end
185
+
186
+ # Logs the failure to retrieve something from a cache.
187
+ # This can be any cache, be it an in-memory Hash, Redis, Memcached, a
188
+ # flat file or whatever.
189
+ #
190
+ # There is just one exception. You should not use this method to log cache
191
+ # misses in the ActiveRecord SQL cache or similar mechanisms.
192
+ # Database-related timing should be logged with {#log_database_query}.
193
+ #
194
+ # If your app is a Rails app, then the `union_station_hooks_rails` gem
195
+ # automatically calls this for you every time an `ActiveSupport::Cache`
196
+ # `#fetch` or `#read` call success. This includes calls to
197
+ # `Rails.cache.fetch` or `Rails.cache.read`, because `Rails.cache` is
198
+ # an instance of `ActiveSupport::Cache`.
199
+ #
200
+ # @param [String] name A unique name for this cache miss event. The cache
201
+ # key is a good value to use.
202
+ # @param [Numeric] miss_cost_duration The amount of time that was spent in
203
+ # calculating or processing something, as a result of this cache miss.
204
+ # This time is in **microseconds**.
205
+ # @note At present (30 September 2015), logged cache hit/miss information
206
+ # isn't shown in the Union Station interface. We may implement this
207
+ # feature in the near future.
208
+ def log_cache_miss(name, miss_cost_duration = nil)
209
+ return do_nothing_on_null(:log_cache_miss) if null?
210
+ if miss_cost_duration
211
+ @transaction.message("Cache miss (#{miss_cost_duration.to_i} usec): " \
212
+ "#{name}")
213
+ else
214
+ @transaction.message("Cache miss: #{name}")
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,76 @@
1
+ # Union Station - https://www.unionstationapp.com/
2
+ # Copyright (c) 2010-2015 Phusion Holding B.V.
3
+ #
4
+ # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V.
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+
24
+ UnionStationHooks.require_lib 'utils'
25
+
26
+ module UnionStationHooks
27
+ class RequestReporter
28
+ ###### Logging view rendering-related information ######
29
+
30
+ # Logs timing information about the rendering of a single view, template or
31
+ # partial. This form expects a block, which is to perform the
32
+ # view/template/partial rendering. Timing information is collected before
33
+ # and after the block returns.
34
+ #
35
+ # The `union_station_hooks_rails` gem automatically calls this for you
36
+ # if your application is a Rails app. It will call this on every view
37
+ # or partial rendering.
38
+ #
39
+ # @yield The given block is expected to perform the actual view rendering.
40
+ # @return The return value of the block.
41
+ def log_view_rendering_block(&block)
42
+ if null?
43
+ do_nothing_on_null(:log_view_rendering_block)
44
+ yield
45
+ else
46
+ log_activity_block('view rendering', &block)
47
+ end
48
+ end
49
+
50
+ # Logs timing information about the rendering of a single view, template or
51
+ # partial.
52
+ #
53
+ # Unlike {#log_view_rendering_block}, this form does not expect a block.
54
+ # However, you are expected to pass timing information to the options
55
+ # hash.
56
+ #
57
+ # The `union_station_hooks_rails` gem automatically calls
58
+ # {#log_view_rendering_block} for you if your application is a Rails app.
59
+ # It will call this on every view or partial rendering.
60
+ #
61
+ # @option options [TimePoint or Time] :begin_time The time at which this
62
+ # view rendering begun. See {UnionStationHooks.now} to learn more.
63
+ # @option options [TimePoint or Time] :end_time The time at which this view
64
+ # rendering ended. See {UnionStationHooks.now} to learn more.
65
+ # @option options [Boolean] :has_error (optional) Whether an uncaught
66
+ # exception occurred during the view rendering. Default: false.
67
+ def log_view_rendering(options)
68
+ return do_nothing_on_null(:log_view_rendering) if null?
69
+ Utils.require_key(options, :begin_time)
70
+ Utils.require_key(options, :end_time)
71
+
72
+ @transaction.log_activity('view rendering', options[:begin_time],
73
+ options[:end_time], nil, options[:has_error])
74
+ end
75
+ end
76
+ end