union_station_hooks_core 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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