passenger 4.0.45 → 4.0.46

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (94) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/.editorconfig +19 -0
  5. data/CHANGELOG +47 -0
  6. data/CONTRIBUTING.md +9 -1
  7. data/CONTRIBUTORS +4 -0
  8. data/Vagrantfile +7 -3
  9. data/build/agents.rb +1 -0
  10. data/build/misc.rb +6 -4
  11. data/dev/vagrant/bashrc +2 -0
  12. data/doc/Design and Architecture.txt +9 -7
  13. data/doc/Users guide Apache.idmap.txt +2 -0
  14. data/doc/Users guide Apache.txt +24 -4
  15. data/doc/Users guide Nginx.idmap.txt +4 -0
  16. data/doc/Users guide Nginx.txt +23 -4
  17. data/doc/images/code_walkthrough.jpg +0 -0
  18. data/doc/users_guide_snippets/installation.txt +38 -0
  19. data/ext/common/AgentsStarter.h +6 -1
  20. data/ext/common/ApplicationPool2/Common.h +17 -2
  21. data/ext/common/ApplicationPool2/DirectSpawner.h +5 -11
  22. data/ext/common/ApplicationPool2/DummySpawner.h +2 -4
  23. data/ext/common/ApplicationPool2/ErrorRenderer.h +119 -0
  24. data/ext/common/ApplicationPool2/Implementation.cpp +159 -11
  25. data/ext/common/ApplicationPool2/Options.h +16 -7
  26. data/ext/common/ApplicationPool2/Pool.h +28 -24
  27. data/ext/common/ApplicationPool2/Process.h +1 -9
  28. data/ext/common/ApplicationPool2/SmartSpawner.h +15 -18
  29. data/ext/common/ApplicationPool2/Spawner.h +18 -14
  30. data/ext/common/ApplicationPool2/SpawnerFactory.h +12 -30
  31. data/ext/common/Constants.h +1 -1
  32. data/ext/common/Exceptions.h +15 -2
  33. data/ext/common/UnionStation/Core.h +9 -0
  34. data/ext/common/Utils/JsonUtils.h +53 -0
  35. data/ext/common/Utils/ProcessMetricsCollector.h +1 -1
  36. data/ext/common/Utils/SpeedMeter.h +7 -3
  37. data/ext/common/Utils/SystemMetricsCollector.h +8 -6
  38. data/ext/common/agents/HelperAgent/Main.cpp +4 -4
  39. data/ext/common/agents/HelperAgent/RequestHandler.h +115 -56
  40. data/ext/nginx/ConfigurationCommands.c +1 -1
  41. data/ext/nginx/ConfigurationCommands.c.erb +6 -1
  42. data/ext/nginx/ContentHandler.c +2 -1
  43. data/ext/nginx/config +1 -1
  44. data/helper-scripts/node-loader.js +23 -0
  45. data/helper-scripts/wsgi-loader.py +12 -4
  46. data/lib/phusion_passenger.rb +1 -1
  47. data/lib/phusion_passenger/active_support3_extensions/init.rb +39 -78
  48. data/lib/phusion_passenger/constants.rb +3 -1
  49. data/lib/phusion_passenger/loader_shared_helpers.rb +10 -5
  50. data/lib/phusion_passenger/nginx/config_options.rb +3 -1
  51. data/lib/phusion_passenger/packaging.rb +1 -0
  52. data/lib/phusion_passenger/public_api.rb +108 -16
  53. data/lib/phusion_passenger/rack/thread_handler_extension.rb +1 -0
  54. data/lib/phusion_passenger/request_handler.rb +2 -2
  55. data/lib/phusion_passenger/request_handler/thread_handler.rb +28 -46
  56. data/lib/phusion_passenger/standalone/command.rb +8 -1
  57. data/lib/phusion_passenger/standalone/main.rb +0 -1
  58. data/lib/phusion_passenger/standalone/start_command.rb +4 -0
  59. data/lib/phusion_passenger/union_station/connection.rb +67 -0
  60. data/lib/phusion_passenger/{analytics_logger.rb → union_station/core.rb} +55 -256
  61. data/lib/phusion_passenger/union_station/transaction.rb +168 -0
  62. data/lib/phusion_passenger/utils.rb +4 -0
  63. data/lib/phusion_passenger/utils/lock.rb +62 -0
  64. data/resources/mime.types +1 -0
  65. data/resources/templates/error_layout.html.template +2 -0
  66. data/resources/templates/standalone/config.erb +1 -0
  67. data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +5 -3
  68. data/test/cxx/ApplicationPool2/PoolTest.cpp +13 -3
  69. data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +16 -13
  70. data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +6 -0
  71. data/test/cxx/FileBackedPipeTest.cpp +1 -1
  72. data/test/cxx/RequestHandlerTest.cpp +158 -2
  73. data/test/cxx/ServerInstanceDirTest.cpp +2 -0
  74. data/test/cxx/TestSupport.h +21 -2
  75. data/test/cxx/UtilsTest.cpp +1 -0
  76. data/test/ruby/classic_rails/loader_spec.rb +0 -1
  77. data/test/ruby/classic_rails/preloader_spec.rb +0 -1
  78. data/test/ruby/rails3.0/loader_spec.rb +2 -2
  79. data/test/ruby/rails3.0/preloader_spec.rb +2 -2
  80. data/test/ruby/rails3.1/loader_spec.rb +2 -2
  81. data/test/ruby/rails3.1/preloader_spec.rb +2 -2
  82. data/test/ruby/rails3.2/loader_spec.rb +2 -2
  83. data/test/ruby/rails3.2/preloader_spec.rb +2 -2
  84. data/test/ruby/rails4.0/loader_spec.rb +2 -2
  85. data/test/ruby/rails4.0/preloader_spec.rb +2 -2
  86. data/test/ruby/request_handler_spec.rb +8 -8
  87. data/test/ruby/shared/rails/{analytics_logging_extensions_sharedspec.rb → union_station_extensions_sharedspec.rb} +5 -4
  88. data/test/ruby/union_station_spec.rb +283 -0
  89. data/test/stub/wsgi/passenger_wsgi.py +41 -5
  90. metadata +12 -7
  91. metadata.gz.asc +7 -7
  92. data/helper-scripts/wsgi-preloader.py +0 -1
  93. data/lib/phusion_passenger/standalone/package_runtime_command.rb +0 -105
  94. data/test/ruby/analytics_logger_spec.rb +0 -283
@@ -266,7 +266,7 @@
266
266
  ngx_conf_set_bitmask_slot,
267
267
  NGX_HTTP_LOC_CONF_OFFSET,
268
268
  offsetof(passenger_loc_conf_t, upstream_config.ignore_headers),
269
- NULL
269
+ &ngx_http_upstream_ignore_headers_masks
270
270
  },
271
271
 
272
272
  {
@@ -123,6 +123,11 @@ def struct_field_for(option)
123
123
  return "offsetof(#{struct_type_for(option)}, #{field})"
124
124
  end
125
125
  end
126
+
127
+ def post_for(option)
128
+ return option[:post] if option[:post]
129
+ return "NULL"
130
+ end
126
131
  %>
127
132
 
128
133
  <% for option in LOCATION_CONFIGURATION_OPTIONS %>
@@ -133,6 +138,6 @@ end
133
138
  <%= setter_function_for(option) %>,
134
139
  <%= struct_for(option) %>,
135
140
  <%= struct_field_for(option) %>,
136
- NULL
141
+ <%= post_for(option) %>
137
142
  },
138
143
  <% end %>
@@ -533,7 +533,8 @@ create_request(ngx_http_request_t *r)
533
533
  b->last = ngx_copy(b->last, "CONTENT_LENGTH",
534
534
  sizeof("CONTENT_LENGTH"));
535
535
 
536
- b->last = ngx_snprintf(b->last, 10, "%ui", r->headers_in.content_length_n);
536
+ b->last = ngx_snprintf(b->last, 10, "%O",
537
+ r->headers_in.content_length_n);
537
538
  *b->last++ = (u_char) 0;
538
539
  }
539
540
 
@@ -87,7 +87,7 @@ if [ $ngx_found = yes ]; then
87
87
  CORE_LIBS="$CORE_LIBS -lrt"
88
88
  fi
89
89
 
90
- nginx_version=`grep 'NGINX_VERSION ' src/core/nginx.h | awk '{ print $3 }' | sed 's/"//g'`
90
+ nginx_version=`grep 'NGINX_VERSION ' src/core/nginx.h | awk '{ print $3 }' | sed 's/"//g' | head -n1`
91
91
 
92
92
  nginx_major_version=`echo "$nginx_version" | cut -d . -f 1`
93
93
  have=PASSENGER_NGINX_MAJOR_VERSION value="$nginx_major_version"
@@ -128,12 +128,35 @@ function generateServerSocketPath() {
128
128
  + process.pid + "." + ((Math.random() * 0xFFFFFFFF) & 0xFFFFFFF);
129
129
  }
130
130
 
131
+ function addListenerAtBeginning(emitter, event, callback) {
132
+ var listeners = emitter.listeners(event);
133
+ var i;
134
+
135
+ emitter.removeAllListeners(event);
136
+ emitter.on(event, callback);
137
+ for (i = 0; i < listeners.length; i++) {
138
+ emitter.on(event, listeners[i]);
139
+ }
140
+ }
141
+
131
142
  function installServer() {
132
143
  var server = this;
133
144
  if (!PhusionPassenger._appInstalled) {
134
145
  PhusionPassenger._appInstalled = true;
135
146
  PhusionPassenger._server = server;
136
147
 
148
+ // Ensure that req.connection.remoteAddress and remotePort return something
149
+ // instead of undefined. Apps like Etherpad expect it.
150
+ // See https://github.com/phusion/passenger/issues/1224
151
+ addListenerAtBeginning(server, 'request', function(req) {
152
+ req.connection.__defineGetter__('remoteAddress', function() {
153
+ return '127.0.0.1';
154
+ });
155
+ req.connection.__defineGetter__('remotePort', function() {
156
+ return 0;
157
+ });
158
+ });
159
+
137
160
  var listenTries = 0;
138
161
  doListen(extractCallback(arguments));
139
162
 
@@ -53,7 +53,11 @@ def handshake_and_read_startup_request():
53
53
  line = readline()
54
54
 
55
55
  def load_app():
56
- return imp.load_source('passenger_wsgi', 'passenger_wsgi.py')
56
+ global options
57
+
58
+ sys.path.insert(0, os.getcwd())
59
+ startup_file = options.get('startup_file', 'passenger_wsgi.py')
60
+ return imp.load_source('passenger_wsgi', startup_file)
57
61
 
58
62
  def create_server_socket():
59
63
  global options
@@ -256,9 +260,13 @@ class RequestHandler:
256
260
  headers_set[:] = [status, response_headers]
257
261
  return write
258
262
 
259
- def hijack():
260
- env['passenger.hijacked_socket'] = output_stream
261
- return output_stream
263
+ # Django's django.template.base module goes through all WSGI
264
+ # environment values, and calls each value that is a callable.
265
+ # No idea why, but we work around that with the `do_it` parameter.
266
+ def hijack(do_it = False):
267
+ if do_it:
268
+ env['passenger.hijacked_socket'] = output_stream
269
+ return output_stream
262
270
 
263
271
  env['passenger.hijack'] = hijack
264
272
 
@@ -30,7 +30,7 @@ module PhusionPassenger
30
30
 
31
31
  PACKAGE_NAME = 'passenger'
32
32
  # Run 'rake ext/common/Constants.h' after changing this number.
33
- VERSION_STRING = '4.0.45'
33
+ VERSION_STRING = '4.0.46'
34
34
 
35
35
  PREFERRED_NGINX_VERSION = '1.6.0'
36
36
  NGINX_SHA256_CHECKSUM = '943ad757a1c3e8b3df2d5c4ddacc508861922e36fa10ea6f8e3a348fc9abfc1a'
@@ -28,31 +28,31 @@ module PhusionPassenger
28
28
 
29
29
  module ActiveSupport3Extensions
30
30
  def self.init!(options, user_options = {})
31
- if !AnalyticsLogging.install!(options, user_options)
31
+ if !UnionStationExtension.install!(options, user_options)
32
32
  # Remove code to save memory.
33
- PhusionPassenger::ActiveSupport3Extensions.send(:remove_const, :AnalyticsLogging)
33
+ PhusionPassenger::ActiveSupport3Extensions.send(:remove_const, :UnionStationExtension)
34
34
  PhusionPassenger.send(:remove_const, :ActiveSupport3Extensions)
35
35
  end
36
36
  end
37
37
  end
38
38
 
39
39
  module ActiveSupport3Extensions
40
- class AnalyticsLogging < ActiveSupport::LogSubscriber
40
+ class UnionStationExtension < ActiveSupport::LogSubscriber
41
41
  def self.install!(options, user_options)
42
- analytics_logger = options["analytics_logger"]
42
+ union_station_core = options["union_station_core"]
43
43
  app_group_name = options["app_group_name"]
44
- return false if !analytics_logger || !options["analytics"]
44
+ return false if !union_station_core || !options["analytics"]
45
45
 
46
46
  # If the Ruby interpreter supports GC statistics then turn it on
47
47
  # so that the info can be logged.
48
48
  GC.enable_stats if GC.respond_to?(:enable_stats)
49
49
 
50
50
  subscriber = self.new(user_options)
51
- AnalyticsLogging.attach_to(:action_controller, subscriber)
52
- AnalyticsLogging.attach_to(:active_record, subscriber)
51
+ UnionStationExtension.attach_to(:action_controller, subscriber)
52
+ UnionStationExtension.attach_to(:active_record, subscriber)
53
53
  if defined?(ActiveSupport::Cache::Store)
54
54
  ActiveSupport::Cache::Store.instrument = true
55
- AnalyticsLogging.attach_to(:active_support, subscriber)
55
+ UnionStationExtension.attach_to(:active_support, subscriber)
56
56
  end
57
57
  PhusionPassenger.on_event(:starting_request_handler_thread) do
58
58
  if defined?(ActiveSupport::Cache::Store)
@@ -70,7 +70,9 @@ class AnalyticsLogging < ActiveSupport::LogSubscriber
70
70
  if defined?(Rails)
71
71
  Rails.application.middleware.insert_after(
72
72
  exceptions_middleware,
73
- ExceptionLogger, analytics_logger, app_group_name)
73
+ ExceptionLogger,
74
+ union_station_core,
75
+ app_group_name)
74
76
  end
75
77
  end
76
78
 
@@ -95,21 +97,18 @@ class AnalyticsLogging < ActiveSupport::LogSubscriber
95
97
  end
96
98
 
97
99
  def process_action(event)
98
- log = Thread.current[PASSENGER_ANALYTICS_WEB_LOG]
99
- if log
100
- view_runtime = event.payload[:view_runtime]
101
- log.message("View rendering time: #{(view_runtime * 1000).to_i}") if view_runtime
100
+ view_runtime = event.payload[:view_runtime]
101
+ if view_runtime
102
+ PhusionPassenger.log_total_view_rendering_time(nil, (view_runtime * 1000).to_i)
102
103
  end
103
104
  end
104
105
 
105
106
  def sql(event)
106
- if log = Thread.current[PASSENGER_ANALYTICS_WEB_LOG]
107
- name = event.payload[:name] || "SQL"
108
- sql = event.payload[:sql]
109
- digest = Digest::MD5.hexdigest("#{name}\0#{sql}\0#{rand}")
110
- log.measured_time_points("DB BENCHMARK: #{digest}",
111
- event.time, event.end, "#{name}\n#{sql}")
112
- end
107
+ PhusionPassenger.log_database_query(nil,
108
+ event.payload[:name] || "SQL",
109
+ event.time,
110
+ event.end,
111
+ event.payload[:sql])
113
112
  end
114
113
 
115
114
  def cache_read(event)
@@ -130,76 +129,45 @@ class AnalyticsLogging < ActiveSupport::LogSubscriber
130
129
  end
131
130
 
132
131
  class ExceptionLogger
133
- def initialize(app, analytics_logger, app_group_name)
132
+ def initialize(app, union_station_core, app_group_name)
134
133
  @app = app
135
- @analytics_logger = analytics_logger
134
+ @union_station_core = union_station_core
136
135
  @app_group_name = app_group_name
137
136
  end
138
137
 
139
138
  def call(env)
140
139
  @app.call(env)
141
140
  rescue Exception => e
142
- log_analytics_exception(env, e) if env[PASSENGER_TXN_ID]
141
+ log_union_station_exception(env, e) if env[PASSENGER_TXN_ID]
143
142
  raise e
144
143
  end
145
144
 
146
145
  private
147
- def log_analytics_exception(env, exception)
148
- log = @analytics_logger.new_transaction(
149
- @app_group_name,
150
- :exceptions,
151
- env[PASSENGER_UNION_STATION_KEY])
152
- begin
153
- request = ActionDispatch::Request.new(env)
154
- if request.parameters['controller']
155
- controller = request.parameters['controller'].humanize + "Controller"
156
- action = request.parameters['action']
157
- end
158
-
159
- request_txn_id = env[PASSENGER_TXN_ID]
160
- message = exception.message
161
- message = exception.to_s if message.empty?
162
- message = [message].pack('m')
163
- message.gsub!("\n", "")
164
- backtrace_string = [exception.backtrace.join("\n")].pack('m')
165
- backtrace_string.gsub!("\n", "")
166
- if action && controller
167
- controller_action = "#{controller}##{action}"
168
- else
169
- controller_action = controller
170
- end
171
-
172
- log.message("Request transaction ID: #{request_txn_id}")
173
- log.message("Message: #{message}")
174
- log.message("Class: #{exception.class.name}")
175
- log.message("Backtrace: #{backtrace_string}")
176
- log.message("Controller action: #{controller_action}") if controller_action
177
- ensure
178
- log.close
146
+ def log_union_station_exception(env, exception)
147
+ options = {}
148
+ request = ActionDispatch::Request.new(env)
149
+ if request.parameters['controller']
150
+ options[:controller_name] = request.parameters['controller'].humanize + "Controller"
151
+ options[:action_name] = request.parameters['action']
179
152
  end
153
+ PhusionPassenger.log_request_exception(env, exception, options)
180
154
  end
181
155
  end
182
156
 
183
157
  module ACExtension
184
158
  def process_action(action, *args)
185
- log = request.env[PASSENGER_ANALYTICS_WEB_LOG]
186
- if log
187
- log.message("Controller action: #{self.class.name}##{action_name}")
188
- log.measure("framework request processing") do
189
- super
190
- end
191
- else
159
+ options = {
160
+ :controller_name => self.class.name,
161
+ :action_name => action_name,
162
+ :method => request.request_method
163
+ }
164
+ PhusionPassenger.log_controller_action(request.env, options) do
192
165
  super
193
166
  end
194
167
  end
195
168
 
196
169
  def render(*args)
197
- log = request.env[PASSENGER_ANALYTICS_WEB_LOG]
198
- if log
199
- log.measure("view rendering") do
200
- super
201
- end
202
- else
170
+ PhusionPassenger.log_view_rendering(request.env) do
203
171
  super
204
172
  end
205
173
  end
@@ -207,14 +175,7 @@ class AnalyticsLogging < ActiveSupport::LogSubscriber
207
175
 
208
176
  module ASBenchmarkableExtension
209
177
  def benchmark_with_passenger(message = "Benchmarking", *args)
210
- log = Thread.current[PASSENGER_ANALYTICS_WEB_LOG]
211
- if log
212
- log.measure("BENCHMARK: #{message}") do
213
- benchmark_without_passenger(message, *args) do
214
- yield
215
- end
216
- end
217
- else
178
+ PhusionPassenger.benchmark(nil, message) do
218
179
  benchmark_without_passenger(message, *args) do
219
180
  yield
220
181
  end
@@ -222,7 +183,7 @@ class AnalyticsLogging < ActiveSupport::LogSubscriber
222
183
  end
223
184
  end
224
185
 
225
- private
186
+ private
226
187
  def install_event_preprocessor(event_preprocessor)
227
188
  public_methods(false).each do |name|
228
189
  singleton = class << self; self end
@@ -232,7 +193,7 @@ class AnalyticsLogging < ActiveSupport::LogSubscriber
232
193
  end)
233
194
  end
234
195
  end
235
- end # class AnalyticsLogging
196
+ end # class UnionStationExtension
236
197
  end # module ActiveSupport3Extensions
237
198
 
238
199
  end # module PhusionPassenger
@@ -22,8 +22,10 @@
22
22
  # THE SOFTWARE.
23
23
 
24
24
  module PhusionPassenger
25
- PASSENGER_ANALYTICS_WEB_LOG = "PASSENGER_ANALYTICS_WEB_LOG".freeze
25
+ UNION_STATION_CORE = "UNION_STATION_CORE".freeze
26
+ UNION_STATION_REQUEST_TRANSACTION = "UNION_STATION_REQUEST_TRANSACTION".freeze
26
27
  PASSENGER_TXN_ID = "PASSENGER_TXN_ID".freeze
28
+ PASSENGER_APP_GROUP_NAME = "PASSENGER_APP_GROUP_NAME".freeze
27
29
  PASSENGER_UNION_STATION_KEY = "UNION_STATION_KEY".freeze
28
30
  RACK_HIJACK_IO = "rack.hijack_io".freeze
29
31
 
@@ -128,6 +128,11 @@ module LoaderSharedHelpers
128
128
  if defined?(Gem)
129
129
  File.open("#{dir}/ruby_info", "a") do |f|
130
130
  f.puts "RubyGems version = #{Gem::VERSION}"
131
+ if Gem.respond_to?(:path)
132
+ f.puts "RubyGems paths = #{Gem.path.inspect}"
133
+ else
134
+ f.puts "RubyGems paths = unknown; incompatible RubyGems API"
135
+ end
131
136
  end
132
137
  File.open("#{dir}/activated_gems", "wb") do |f|
133
138
  if Gem.respond_to?(:loaded_specs)
@@ -190,9 +195,9 @@ module LoaderSharedHelpers
190
195
  def before_loading_app_code_step1(startup_file, options)
191
196
  DebugLogging.log_level = options["log_level"] if options["log_level"]
192
197
 
193
- # Instantiate the analytics logger if requested. Can be nil.
194
- PhusionPassenger.require_passenger_lib 'analytics_logger'
195
- options["analytics_logger"] = AnalyticsLogger.new_from_options(options)
198
+ # Instantiate the Union Station core if requested. Can be nil.
199
+ PhusionPassenger.require_passenger_lib 'union_station/core'
200
+ options["union_station_core"] = UnionStation::Core.new_from_options(options)
196
201
  end
197
202
 
198
203
  def run_load_path_setup_code(options)
@@ -320,8 +325,8 @@ module LoaderSharedHelpers
320
325
  $0 = options["process_title"] + ": " + options["app_group_name"]
321
326
  end
322
327
 
323
- if forked && options["analytics_logger"]
324
- options["analytics_logger"].clear_connection
328
+ if forked && options["union_station_core"]
329
+ options["union_station_core"].clear_connection
325
330
  end
326
331
 
327
332
  # If we were forked from a preloader process then clear or
@@ -66,6 +66,7 @@
66
66
  # a dot (.e.g `upstream_config.pass_headers`) then the structure field will
67
67
  # also not be auto-generated, because it is assumed to belong to an existing
68
68
  # structure field.
69
+ # * post - The extra information needed by function for post-processing.
69
70
  # * header - The name of the corresponding CGI header. By default CGI header
70
71
  # generation code is automatically generated, using the configuration
71
72
  # option's name in uppercase as the CGI header name.
@@ -193,7 +194,8 @@ LOCATION_CONFIGURATION_OPTIONS = [
193
194
  :name => 'passenger_ignore_headers',
194
195
  :take => 'NGX_CONF_1MORE',
195
196
  :function => 'ngx_conf_set_bitmask_slot',
196
- :field => 'upstream_config.ignore_headers'
197
+ :field => 'upstream_config.ignore_headers',
198
+ :post => '&ngx_http_upstream_ignore_headers_masks'
197
199
  },
198
200
  {
199
201
  :name => 'passenger_set_cgi_param',
@@ -68,6 +68,7 @@ module Packaging
68
68
  GLOB = [
69
69
  '.gitignore',
70
70
  '.travis.yml',
71
+ '.editorconfig',
71
72
  'configure',
72
73
  'Rakefile',
73
74
  'Vagrantfile',
@@ -57,21 +57,108 @@ class << self
57
57
  @@advertised_concurrency_level = value
58
58
  end
59
59
 
60
- def benchmark(env = nil, title = "Benchmarking")
61
- log = lookup_analytics_log(env)
62
- if log
63
- log.measure("BENCHMARK: #{title}") do
60
+ def measure_and_log_event(env, name)
61
+ transaction = lookup_union_station_web_transaction(env)
62
+ if transaction
63
+ transaction.measure(name) do
64
64
  yield
65
65
  end
66
66
  else
67
67
  yield
68
68
  end
69
69
  end
70
+
71
+ def benchmark(env = nil, title = "Benchmarking", &block)
72
+ measure_and_log_event(env, "BENCHMARK: #{title}", &block)
73
+ end
74
+
75
+ # Log an exception that occurred during a request.
76
+ def log_request_exception(env, exception, options = nil)
77
+ return if !env[PASSENGER_TXN_ID]
78
+ core = lookup_union_station_core(env)
79
+ if core
80
+ transaction = core.new_transaction(
81
+ env[PASSENGER_APP_GROUP_NAME],
82
+ :exceptions,
83
+ env[PASSENGER_UNION_STATION_KEY])
84
+ begin
85
+ request_txn_id = env[PASSENGER_TXN_ID]
86
+ message = exception.message
87
+ message = exception.to_s if message.empty?
88
+ message = [message].pack('m')
89
+ message.gsub!("\n", "")
90
+ backtrace_string = [exception.backtrace.join("\n")].pack('m')
91
+ backtrace_string.gsub!("\n", "")
92
+
93
+ transaction.message("Request transaction ID: #{request_txn_id}")
94
+ transaction.message("Message: #{message}")
95
+ transaction.message("Class: #{exception.class.name}")
96
+ transaction.message("Backtrace: #{backtrace_string}")
97
+
98
+ if options && options[:controller_name]
99
+ if options[:action_name]
100
+ controller_action = "#{options[:controller_name]}##{options[:action_name]}"
101
+ else
102
+ controller_action = controller_name
103
+ end
104
+ transaction.message("Controller action: #{controller_action}")
105
+ end
106
+ ensure
107
+ transaction.close
108
+ end
109
+ end
110
+ end
111
+
112
+ # Log a controller action invocation.
113
+ def log_controller_action(env, options)
114
+ transaction = lookup_union_station_web_transaction(env)
115
+ if transaction
116
+ if options[:controller_name]
117
+ if !options[:action_name]
118
+ raise ArgumentError, "The :action_name option must be set"
119
+ end
120
+ transaction.message("Controller action: #{options[:controller_name]}##{options[:action_name]}")
121
+ end
122
+ if options[:method]
123
+ transaction.message("Request method: #{options[:method]}")
124
+ end
125
+ transaction.measure("framework request processing") do
126
+ yield
127
+ end
128
+ else
129
+ yield
130
+ end
131
+ end
132
+
133
+ # Log the total view rendering time of a request.
134
+ def log_total_view_rendering_time(env, runtime)
135
+ transaction = lookup_union_station_web_transaction(env)
136
+ if transaction
137
+ transaction.message("View rendering time: #{(runtime).to_i}")
138
+ end
139
+ end
140
+
141
+ # Log a single view rendering.
142
+ def log_view_rendering(env = nil, &block)
143
+ measure_and_log_event(env, "view rendering", &block)
144
+ end
70
145
 
146
+ # Log a database query.
147
+ def log_database_query(env, name, begin_time, end_time, sql)
148
+ transaction = lookup_union_station_web_transaction(env)
149
+ if transaction
150
+ digest = Digest::MD5.hexdigest("#{name}\0#{sql}\0#{rand}")
151
+ transaction.measured_time_points("DB BENCHMARK: #{digest}",
152
+ begin_time,
153
+ end_time,
154
+ "#{name}\n#{sql}")
155
+ end
156
+ end
157
+
71
158
  def log_cache_hit(env, name)
72
- log = lookup_analytics_log(env)
73
- if log
74
- log.message("Cache hit: #{name}")
159
+ transaction = lookup_union_station_web_transaction(env)
160
+ if transaction
161
+ transaction.message("Cache hit: #{name}")
75
162
  return true
76
163
  else
77
164
  return false
@@ -79,12 +166,12 @@ class << self
79
166
  end
80
167
 
81
168
  def log_cache_miss(env, name, generation_time = nil)
82
- log = lookup_analytics_log(env)
83
- if log
169
+ transaction = lookup_union_station_web_transaction(env)
170
+ if transaction
84
171
  if generation_time
85
- log.message("Cache miss (#{generation_time.to_i}): #{name}")
172
+ transaction.message("Cache miss (#{generation_time.to_i}): #{name}")
86
173
  else
87
- log.message("Cache miss: #{name}")
174
+ transaction.message("Cache miss: #{name}")
88
175
  end
89
176
  return true
90
177
  else
@@ -111,14 +198,19 @@ private
111
198
  raise ArgumentError, "Unknown event name '#{name}'"
112
199
  end
113
200
  end
114
-
115
- def lookup_analytics_log(env)
201
+
202
+ def lookup_union_station_core(env = nil)
116
203
  if env
117
- return env[PASSENGER_ANALYTICS_WEB_LOG]
118
- else
119
- return Thread.current[PASSENGER_ANALYTICS_WEB_LOG]
204
+ result = env[UNION_STATION_CORE]
120
205
  end
206
+ return result || Thread.current[UNION_STATION_CORE]
121
207
  end
122
208
 
209
+ def lookup_union_station_web_transaction(env = nil)
210
+ if env
211
+ result = env[UNION_STATION_REQUEST_TRANSACTION]
212
+ end
213
+ return result || Thread.current[UNION_STATION_REQUEST_TRANSACTION]
214
+ end
123
215
  end
124
216
  end # module PhusionPassenger