Class: UnionStationHooks::RequestReporter
- Inherits:
-
Object
- Object
- UnionStationHooks::RequestReporter
- Defined in:
- lib/union_station_hooks_core/request_reporter.rb,
lib/union_station_hooks_core/request_reporter/misc.rb,
lib/union_station_hooks_core/request_reporter/basics.rb,
lib/union_station_hooks_core/request_reporter/controllers.rb,
lib/union_station_hooks_core/request_reporter/view_rendering.rb
Overview
A RequestReporter object is used for logging request-specific information to Union Station. “Information” may include (and are not limited to):
* Web framework controller and action name.
* Exceptions raised during the request.
* Cache hits and misses.
* Database actions.
A unique RequestReporter is created by Passenger at the beginning of every request (by calling begin_rack_request). This object is closed at the end of the same request (after the Rack body object is closed).
As an application developer, the RequestReporter is the main class that you will be interfacing with. See the UnionStationHooks module description for an example of how you can use RequestReporter.
## Obtaining a RequestReporter
You are not supposed to create a RequestReporter object directly. You are supposed to obtain the RequestReporter object that Passenger creates for you. This is done through the ‘union_station_hooks` key in the Rack environment hash, as well as through the `:union_station_hooks` key in the current thread’s object:
env['union_station_hooks']
# => RequestReporter object or nil
Thread.current[:union_station_hooks]
# => RequestReporter object or nil
Note that Passenger may not have created such an object because of an error. At present, there are two error conditions that would cause a RequestReporter object not to be created. However, your code should take into account that in the future more error conditions may trigger this.
1. There is no transaction ID associated with the current request.
When Union Station support is enabled in Passenger, Passenger always
assigns a transaction ID. However, administrators can also
{https://www.phusionpassenger.com/library/admin/nginx/request_individual_processes.html
access Ruby processes directly} through process-private HTTP sockets,
bypassing Passenger's load balancing mechanism. In that case, no
transaction ID will be assigned.
2. An error occurred recently while sending data to the UstRouter, either
because the UstRouter crashed or because of some other kind of
communication error occurred. This error condition isn't cleared until
certain a timeout has passed.
The UstRouter is a Passenger process which runs locally and is
responsible for aggregating Union Station log data from multiple
processes, with the goal of sending the aggregate data over the network
to the Union Station service.
This kind of error is automatically recovered from after a certain
period of time.
## Null mode
The error condition 2 described above may also cause an existing RequestReporter object to enter the “null mode”. When this mode is entered, any further actions on the RequestReporter object will become no-ops. You can check whether the null mode is active by calling #null?.
Closing a RequestReporter also causes it to enter the null mode.
Constant Summary collapse
- GC_MUTEX =
A mutex for synchronizing GC stats reporting. We do this because in multithreaded situations we don’t want to interleave GC stats access with calls to ‘GC.clear_stats`. Not that GC stats are very helpful in multithreaded situations, but this is better than nothing.
Mutex.new
- OBJECT_SPACE_SUPPORTS_LIVE_OBJECTS =
ObjectSpace.respond_to?(:live_objects)
- OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS =
ObjectSpace.respond_to?(:allocated_objects)
- OBJECT_SPACE_SUPPORTS_COUNT_OBJECTS =
ObjectSpace.respond_to?(:count_objects)
- GC_SUPPORTS_TIME =
GC.respond_to?(:time)
- GC_SUPPORTS_CLEAR_STATS =
GC.respond_to?(:clear_stats)
Instance Method Summary collapse
-
#close ⇒ Object
private
Indicates that no further information will be logged for this request.
-
#controller_action_logged? ⇒ Boolean
Returns whether #log_controller_action_block or #log_controller_action has been called during this request.
-
#initialize(context, txn_id, app_group_name, key) ⇒ RequestReporter
constructor
private
Returns a new RequestReporter object.
-
#log_activity_block(name) { ... } ⇒ Object
Logs a user-defined activity, for display in the activity timeline.
-
#log_benchmark_block(title = 'Benchmarking') { ... } ⇒ Object
Logs a benchmarking activity, for display in the activity timeline.
-
#log_cache_hit(name) ⇒ Object
Logs that something was successfully retrieved from a cache.
-
#log_cache_miss(name, miss_cost_duration = nil) ⇒ Object
Logs the failure to retrieve something from a cache.
-
#log_controller_action(options) ⇒ Object
Logs that you are calling a web framework controller action.
-
#log_controller_action_block(options = {}) { ... } ⇒ Object
Logs that you are calling a web framework controller action.
-
#log_database_query(options) ⇒ Object
Logs a database query that was performed during the request.
-
#log_exception(exception) ⇒ Object
Logs an exception that occurred during a request.
- #log_gc_stats_on_request_begin ⇒ Object
- #log_gc_stats_on_request_end ⇒ Object
-
#log_request_begin ⇒ Object
Log the beginning of a Rack request.
-
#log_request_end(uncaught_exception_raised_during_request = false) ⇒ Object
Log the end of a Rack request.
-
#log_view_rendering(options) ⇒ Object
Logs timing information about the rendering of a single view, template or partial.
-
#log_view_rendering_block { ... } ⇒ Object
Logs timing information about the rendering of a single view, template or partial.
-
#null? ⇒ Boolean
Returns whether is this RequestReporter object is in null mode.
Constructor Details
#initialize(context, txn_id, app_group_name, key) ⇒ RequestReporter
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a new RequestReporter object. You should not call ‘RequestReporter.new` directly. See “Obtaining a RequestReporter” in the class description.
100 101 102 103 104 105 106 107 108 109 |
# File 'lib/union_station_hooks_core/request_reporter.rb', line 100 def initialize(context, txn_id, app_group_name, key) raise ArgumentError, 'Transaction ID must be given' if txn_id.nil? raise ArgumentError, 'App group name must be given' if app_group_name.nil? raise ArgumentError, 'Union Station key must be given' if key.nil? @context = context @txn_id = txn_id @app_group_name = app_group_name @key = key @transaction = continue_transaction end |
Instance Method Details
#close ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Indicates that no further information will be logged for this request.
115 116 117 |
# File 'lib/union_station_hooks_core/request_reporter.rb', line 115 def close @transaction.close end |
#controller_action_logged? ⇒ Boolean
Returns whether #log_controller_action_block or #log_controller_action has been called during this request.
173 174 175 |
# File 'lib/union_station_hooks_core/request_reporter/controllers.rb', line 173 def controller_action_logged? !!@controller_action end |
#log_activity_block(name) { ... } ⇒ Object
Logs a user-defined activity, for display in the activity timeline.
An activity is a block in the activity timeline in the Union Station user interace. It has a name, a begin time and an end time.
This form takes a block. Before and after the block runs, the time is measured.
49 50 51 52 53 54 55 56 |
# File 'lib/union_station_hooks_core/request_reporter/misc.rb', line 49 def log_activity_block(name, &block) if null? do_nothing_on_null(:log_activity_block) yield else @transaction.log_activity_block(name, &block) end end |
#log_benchmark_block(title = 'Benchmarking') { ... } ⇒ Object
Logs a benchmarking activity, for display in the activity timeline.
An activity is a block in the activity timeline in the Union Station user interace. It has a name, a begin time and an end time.
The primary use case of this method is to integrate with Rails’s benchmarking API (‘ActiveSupport::Benchmarkable`). The Rails benchmarking API allows you to run a block and to log how long that block has taken. But you can also use it to integrate with the Ruby standard library’s ‘Benchmark` class.
You can wrap a benchmark call in a ‘UnionStationHooks.log_benchmark_block` call, so that an entry for it is displayed in the acitivity timeline. This method measures the time before and after the block runs.
The difference between this method and #log_activity_block is that this method generates timeline blocks of a different color, as to differentiate user-defined activities from benchmark activities.
If your app is a Rails app, then the ‘union_station_hooks_rails` gem automatically calls this for you every time `ActiveSupport::Benchmarkable#benchmark` is called. This includes `benchmark` calls from controllers and from views.
96 97 98 99 100 101 102 103 |
# File 'lib/union_station_hooks_core/request_reporter/misc.rb', line 96 def log_benchmark_block(title = 'Benchmarking', &block) if null? do_nothing_on_null(:log_benchmark_block) yield else log_activity_block("BENCHMARK: #{title}", &block) end end |
#log_cache_hit(name) ⇒ Object
At present (30 September 2015), logged cache hit/miss information isn’t shown in the Union Station interface. We may implement this feature in the near future.
Logs that something was successfully retrieved from a cache. This can be any cache, be it an in-memory Hash, Redis, Memcached, a flat file or whatever.
There is just one exception. You should not use this method to log cache hits in the ActiveRecord SQL cache or similar mechanisms. Database-related timing should be logged with #log_database_query.
If your app is a Rails app, then the ‘union_station_hooks_rails` gem automatically calls this for you every time an `ActiveSupport::Cache` `#fetch` or `#read` call success. This includes calls to `Rails.cache.fetch` or `Rails.cache.read`, because `Rails.cache` is an instance of `ActiveSupport::Cache`.
181 182 183 184 |
# File 'lib/union_station_hooks_core/request_reporter/misc.rb', line 181 def log_cache_hit(name) return do_nothing_on_null(:log_cache_hit) if null? @transaction.("Cache hit: #{name}") end |
#log_cache_miss(name, miss_cost_duration = nil) ⇒ Object
At present (30 September 2015), logged cache hit/miss information isn’t shown in the Union Station interface. We may implement this feature in the near future.
Logs the failure to retrieve something from a cache. This can be any cache, be it an in-memory Hash, Redis, Memcached, a flat file or whatever.
There is just one exception. You should not use this method to log cache misses in the ActiveRecord SQL cache or similar mechanisms. Database-related timing should be logged with #log_database_query.
If your app is a Rails app, then the ‘union_station_hooks_rails` gem automatically calls this for you every time an `ActiveSupport::Cache` `#fetch` or `#read` call success. This includes calls to `Rails.cache.fetch` or `Rails.cache.read`, because `Rails.cache` is an instance of `ActiveSupport::Cache`.
208 209 210 211 212 213 214 215 216 |
# File 'lib/union_station_hooks_core/request_reporter/misc.rb', line 208 def log_cache_miss(name, miss_cost_duration = nil) return do_nothing_on_null(:log_cache_miss) if null? if miss_cost_duration @transaction.("Cache miss (#{miss_cost_duration.to_i} usec): " \ "#{name}") else @transaction.("Cache miss: #{name}") end end |
#log_controller_action(options) ⇒ Object
Logs that you are calling a web framework controller action. Of course, you should only call this if your web framework has the concept of controller actions. For example Rails does, but Sinatra and Grape don’t.
You can pass additional information about the web framework controller action, which will be logged.
Unlike #log_controller_action_block, this form does not expect a block. However, you are expected to pass timing information to the options hash.
The ‘union_station_hooks_rails` gem automatically calls #log_controller_action_block for you if your application is a Rails app.
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/union_station_hooks_core/request_reporter/controllers.rb', line 153 def log_controller_action() return do_nothing_on_null(:log_controller_action) if null? Utils.require_key(, :begin_time) Utils.require_key(, :end_time) if [:controller_name] build_full_controller_action_string() @transaction.("Controller action: #{@controller_action}") end if [:method] @transaction.("Application request method: #{[:method]}") end @transaction.log_activity('framework request processing', [:begin_time], [:end_time], nil, [:has_error]) end |
#log_controller_action_block(options = {}) { ... } ⇒ Object
Logs that you are calling a web framework controller action. Of course, you should only call this if your web framework has the concept of controller actions. For example Rails does, but Sinatra and Grape don’t.
This form takes an options hash as well as a block. You can pass additional information about the web framework controller action, which will be logged. The block is expected to perform the actual request handling. When the block returns, timing information about the block is automatically logged.
See also #log_controller_action for a form that doesn’t expect a block.
The ‘union_station_hooks_rails` gem automatically calls this for you if your application is a Rails app.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/union_station_hooks_core/request_reporter/controllers.rb', line 75 def log_controller_action_block( = {}) if null? do_nothing_on_null(:log_controller_action_block) yield else build_full_controller_action_string() has_error = true begin_time = UnionStationHooks.now begin result = yield has_error = false result ensure log_controller_action( .merge( :begin_time => begin_time, :end_time => UnionStationHooks.now, :has_error => has_error ) ) end end end |
#log_database_query(options) ⇒ Object
Logs a database query that was performed during the request.
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/union_station_hooks_core/request_reporter/misc.rb', line 146 def log_database_query() return do_nothing_on_null(:log_database_query) if null? Utils.require_key(, :begin_time) Utils.require_key(, :end_time) Utils.require_non_empty_key(, :query) name = [:name] || 'SQL' begin_time = [:begin_time] end_time = [:end_time] query = [:query] digest = Digest::MD5.hexdigest("#{name}\0#{query}\0#{rand}") @transaction.log_activity("DB BENCHMARK: #{digest}", begin_time, end_time, "#{name}\n#{query}") end |
#log_exception(exception) ⇒ Object
Logs an exception that occurred during a request.
If #log_controller_action or #log_controller_action_happened was called during the same request, then the information passed to those methods will be included in the exception report.
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/union_station_hooks_core/request_reporter/misc.rb', line 112 def log_exception(exception) transaction = @context.new_transaction( @app_group_name, :exceptions, @key) begin return do_nothing_on_null(:log_exception) if transaction.null? = exception. = exception.to_s if .empty? = Utils.base64() base64_backtrace = Utils.base64(exception.backtrace.join("\n")) if controller_action_logged? transaction.("Controller action: #{@controller_action}") end transaction.("Request transaction ID: #{@txn_id}") transaction.("Message: #{}") transaction.("Class: #{exception.class.name}") transaction.("Backtrace: #{base64_backtrace}") ensure transaction.close end end |
#log_gc_stats_on_request_begin ⇒ Object
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/union_station_hooks_core/request_reporter/basics.rb', line 79 def log_gc_stats_on_request_begin return do_nothing_on_null(:log_gc_stats_on_request_begin) if null? # See the docs for MUTEX on why we synchronize this. GC_MUTEX.synchronize do if OBJECT_SPACE_SUPPORTS_LIVE_OBJECTS @transaction.('Initial objects on heap: ' \ "#{ObjectSpace.live_objects}") end if OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS @transaction.('Initial objects allocated so far: ' \ "#{ObjectSpace.allocated_objects}") elsif OBJECT_SPACE_SUPPORTS_COUNT_OBJECTS count = ObjectSpace.count_objects @transaction.('Initial objects allocated so far: ' \ "#{count[:TOTAL] - count[:FREE]}") end if GC_SUPPORTS_TIME @transaction.("Initial GC time: #{GC.time}") end end end |
#log_gc_stats_on_request_end ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/union_station_hooks_core/request_reporter/basics.rb', line 103 def log_gc_stats_on_request_end return do_nothing_on_null(:log_gc_stats_on_request_end) if null? # See the docs for MUTEX on why we synchronize this. GC_MUTEX.synchronize do if OBJECT_SPACE_SUPPORTS_LIVE_OBJECTS @transaction.('Final objects on heap: ' \ "#{ObjectSpace.live_objects}") end if OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS @transaction.('Final objects allocated so far: ' \ "#{ObjectSpace.allocated_objects}") elsif OBJECT_SPACE_SUPPORTS_COUNT_OBJECTS count = ObjectSpace.count_objects @transaction.('Final objects allocated so far: ' \ "#{count[:TOTAL] - count[:FREE]}") end if GC_SUPPORTS_TIME @transaction.("Final GC time: #{GC.time}") end if GC_SUPPORTS_CLEAR_STATS # Clear statistics to void integer wraps. GC.clear_stats end end end |
#log_request_begin ⇒ Object
Log the beginning of a Rack request. This is automatically called from UnionStationHooks.begin_rack_request (and thus automatically from Passenger).
60 61 62 63 |
# File 'lib/union_station_hooks_core/request_reporter/basics.rb', line 60 def log_request_begin return do_nothing_on_null(:log_request_begin) if null? @transaction.log_activity_begin('app request handler processing') end |
#log_request_end(uncaught_exception_raised_during_request = false) ⇒ Object
Log the end of a Rack request. This is automatically called from UnionStationHooks.begin_rack_request (and thus automatically from Passenger).
70 71 72 73 74 |
# File 'lib/union_station_hooks_core/request_reporter/basics.rb', line 70 def log_request_end(uncaught_exception_raised_during_request = false) return do_nothing_on_null(:log_request_end) if null? @transaction.log_activity_end('app request handler processing', UnionStationHooks.now, uncaught_exception_raised_during_request) end |
#log_view_rendering(options) ⇒ Object
Logs timing information about the rendering of a single view, template or partial.
Unlike #log_view_rendering_block, this form does not expect a block. However, you are expected to pass timing information to the options hash.
The ‘union_station_hooks_rails` gem automatically calls #log_view_rendering_block for you if your application is a Rails app. It will call this on every view or partial rendering.
67 68 69 70 71 72 73 74 |
# File 'lib/union_station_hooks_core/request_reporter/view_rendering.rb', line 67 def log_view_rendering() return do_nothing_on_null(:log_view_rendering) if null? Utils.require_key(, :begin_time) Utils.require_key(, :end_time) @transaction.log_activity('view rendering', [:begin_time], [:end_time], nil, [:has_error]) end |
#log_view_rendering_block { ... } ⇒ Object
Logs timing information about the rendering of a single view, template or partial. This form expects a block, which is to perform the view/template/partial rendering. Timing information is collected before and after the block returns.
The ‘union_station_hooks_rails` gem automatically calls this for you if your application is a Rails app. It will call this on every view or partial rendering.
41 42 43 44 45 46 47 48 |
# File 'lib/union_station_hooks_core/request_reporter/view_rendering.rb', line 41 def log_view_rendering_block(&block) if null? do_nothing_on_null(:log_view_rendering_block) yield else log_activity_block('view rendering', &block) end end |
#null? ⇒ Boolean
Returns whether is this RequestReporter object is in null mode. See the class description for more information.
121 122 123 |
# File 'lib/union_station_hooks_core/request_reporter.rb', line 121 def null? @transaction.null? end |