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
@@ -80,6 +80,7 @@ module ThreadHandlerExtension
80
80
  # otherwise maliciously crafted responses can crash the app,
81
81
  # forcing it to be respawned, and thereby effectively DoSing it.
82
82
  print_exception("Rack application object", e)
83
+ PhusionPassenger.log_request_exception(env, e)
83
84
  end
84
85
  return false
85
86
  end
@@ -92,7 +92,7 @@ class RequestHandler
92
92
  "app_group_name",
93
93
  "connect_password",
94
94
  "detach_key",
95
- "analytics_logger",
95
+ "union_station_core",
96
96
  "pool_account_username"
97
97
  )
98
98
 
@@ -400,7 +400,7 @@ private
400
400
  :app => @app,
401
401
  :app_group_name => @app_group_name,
402
402
  :connect_password => @connect_password,
403
- :analytics_logger => @analytics_logger
403
+ :union_station_core => @union_station_core
404
404
  }
405
405
  main_socket_options = common_options.merge(
406
406
  :server_socket => @main_socket,
@@ -68,7 +68,7 @@ class ThreadHandler
68
68
  @app_group_name = Utils.require_option(options, :app_group_name)
69
69
  Utils.install_options_as_ivars(self, options,
70
70
  :app,
71
- :analytics_logger,
71
+ :union_station_core,
72
72
  :connect_password
73
73
  )
74
74
 
@@ -166,8 +166,8 @@ private
166
166
  print_exception("Passenger RequestHandler's client socket", e)
167
167
  end
168
168
  else
169
- if @analytics_logger && headers && headers[PASSENGER_TXN_ID]
170
- log_analytics_exception(headers, e)
169
+ if headers
170
+ PhusionPassenger.log_request_exception(headers, e)
171
171
  end
172
172
  raise e if should_reraise_error?(e)
173
173
  end
@@ -276,64 +276,69 @@ private
276
276
  # end
277
277
 
278
278
  def prepare_request(connection, headers)
279
- if @analytics_logger && headers[PASSENGER_TXN_ID]
279
+ if @union_station_core && headers[PASSENGER_TXN_ID]
280
280
  txn_id = headers[PASSENGER_TXN_ID]
281
281
  union_station_key = headers[PASSENGER_UNION_STATION_KEY]
282
- log = @analytics_logger.continue_transaction(txn_id,
282
+ transaction = @union_station_core.continue_transaction(txn_id,
283
283
  @app_group_name,
284
284
  :requests, union_station_key)
285
- headers[PASSENGER_ANALYTICS_WEB_LOG] = log
286
- Thread.current[PASSENGER_ANALYTICS_WEB_LOG] = log
285
+ headers[UNION_STATION_REQUEST_TRANSACTION] = transaction
286
+ headers[UNION_STATION_CORE] = @union_station_core
287
+ headers[PASSENGER_APP_GROUP_NAME] = @app_group_name
288
+ Thread.current[UNION_STATION_REQUEST_TRANSACTION] = transaction
289
+ Thread.current[UNION_STATION_CORE] = @union_station_core
287
290
  Thread.current[PASSENGER_TXN_ID] = txn_id
288
291
  Thread.current[PASSENGER_UNION_STATION_KEY] = union_station_key
289
292
  if OBJECT_SPACE_SUPPORTS_LIVE_OBJECTS
290
- log.message("Initial objects on heap: #{ObjectSpace.live_objects}")
293
+ transaction.message("Initial objects on heap: #{ObjectSpace.live_objects}")
291
294
  end
292
295
  if OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS
293
- log.message("Initial objects allocated so far: #{ObjectSpace.allocated_objects}")
296
+ transaction.message("Initial objects allocated so far: #{ObjectSpace.allocated_objects}")
294
297
  elsif OBJECT_SPACE_SUPPORTS_COUNT_OBJECTS
295
298
  count = ObjectSpace.count_objects
296
- log.message("Initial objects allocated so far: #{count[:TOTAL] - count[:FREE]}")
299
+ transaction.message("Initial objects allocated so far: #{count[:TOTAL] - count[:FREE]}")
297
300
  end
298
301
  if GC_SUPPORTS_TIME
299
- log.message("Initial GC time: #{GC.time}")
302
+ transaction.message("Initial GC time: #{GC.time}")
300
303
  end
301
- log.begin_measure("app request handler processing")
304
+ transaction.begin_measure("app request handler processing")
302
305
  end
303
306
 
304
307
  #################
305
308
  end
306
309
 
307
310
  def finalize_request(connection, headers, has_error)
308
- log = headers[PASSENGER_ANALYTICS_WEB_LOG]
309
- if log && !log.closed?
311
+ transaction = headers[UNION_STATION_REQUEST_TRANSACTION]
312
+ Thread.current[UNION_STATION_CORE] = nil
313
+ Thread.current[UNION_STATION_REQUEST_TRANSACTION] = nil
314
+
315
+ if transaction && !transaction.closed?
310
316
  exception_occurred = false
311
317
  begin
312
- log.end_measure("app request handler processing", has_error)
318
+ transaction.end_measure("app request handler processing", has_error)
313
319
  if OBJECT_SPACE_SUPPORTS_LIVE_OBJECTS
314
- log.message("Final objects on heap: #{ObjectSpace.live_objects}")
320
+ transaction.message("Final objects on heap: #{ObjectSpace.live_objects}")
315
321
  end
316
322
  if OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS
317
- log.message("Final objects allocated so far: #{ObjectSpace.allocated_objects}")
323
+ transaction.message("Final objects allocated so far: #{ObjectSpace.allocated_objects}")
318
324
  elsif OBJECT_SPACE_SUPPORTS_COUNT_OBJECTS
319
325
  count = ObjectSpace.count_objects
320
- log.message("Final objects allocated so far: #{count[:TOTAL] - count[:FREE]}")
326
+ transaction.message("Final objects allocated so far: #{count[:TOTAL] - count[:FREE]}")
321
327
  end
322
328
  if GC_SUPPORTS_TIME
323
- log.message("Final GC time: #{GC.time}")
329
+ transaction.message("Final GC time: #{GC.time}")
324
330
  end
325
331
  if GC_SUPPORTS_CLEAR_STATS
326
332
  # Clear statistics to void integer wraps.
327
333
  GC.clear_stats
328
334
  end
329
- Thread.current[PASSENGER_ANALYTICS_WEB_LOG] = nil
330
335
  rescue Exception
331
336
  # Maybe this exception was raised while communicating
332
337
  # with the logging agent. If that is the case then
333
- # log.close may also raise an exception, but we're only
338
+ # transaction.close may also raise an exception, but we're only
334
339
  # interested in the original exception. So if this
335
340
  # situation occurs we must ignore any exceptions raised
336
- # by log.close.
341
+ # by transaction.close.
337
342
  exception_occurred = true
338
343
  raise
339
344
  ensure
@@ -343,7 +348,7 @@ private
343
348
  # the helper agent may close the transaction before this
344
349
  # process's openTransaction command is processed.
345
350
  begin
346
- log.close
351
+ transaction.close
347
352
  rescue
348
353
  raise if !exception_occurred
349
354
  end
@@ -352,29 +357,6 @@ private
352
357
 
353
358
  #################
354
359
  end
355
-
356
- def log_analytics_exception(env, exception)
357
- log = @analytics_logger.new_transaction(
358
- @app_group_name,
359
- :exceptions,
360
- env[PASSENGER_UNION_STATION_KEY])
361
- begin
362
- request_txn_id = env[PASSENGER_TXN_ID]
363
- message = exception.message
364
- message = exception.to_s if message.empty?
365
- message = [message].pack('m')
366
- message.gsub!("\n", "")
367
- backtrace_string = [exception.backtrace.join("\n")].pack('m')
368
- backtrace_string.gsub!("\n", "")
369
-
370
- log.message("Request transaction ID: #{request_txn_id}")
371
- log.message("Message: #{message}")
372
- log.message("Class: #{exception.class.name}")
373
- log.message("Backtrace: #{backtrace_string}")
374
- ensure
375
- log.close
376
- end
377
- end
378
360
 
379
361
  def should_reraise_error?(e)
380
362
  # Stubable by unit tests.
@@ -22,6 +22,7 @@
22
22
  # THE SOFTWARE.
23
23
  require 'optparse'
24
24
  PhusionPassenger.require_passenger_lib 'constants'
25
+ PhusionPassenger.require_passenger_lib 'utils'
25
26
  PhusionPassenger.require_passenger_lib 'standalone/utils'
26
27
 
27
28
  module PhusionPassenger
@@ -91,6 +92,10 @@ private
91
92
  def require_erb
92
93
  require 'erb' unless defined?(ERB)
93
94
  end
95
+
96
+ def require_etc
97
+ require 'etc' unless defined?(Etc)
98
+ end
94
99
 
95
100
  def require_optparse
96
101
  require 'optparse' unless defined?(OptionParser)
@@ -105,9 +110,11 @@ private
105
110
  end
106
111
 
107
112
  def parse_options!(command_name, description = nil)
113
+ require_etc
108
114
  help = false
109
115
 
110
- global_config_file = File.join(ENV['HOME'], USER_NAMESPACE_DIRNAME, "standalone", "config")
116
+ home_dir = PhusionPassenger::Utils.home_dir
117
+ global_config_file = File.join(home_dir, USER_NAMESPACE_DIRNAME, "standalone", "config")
111
118
  if File.exist?(global_config_file)
112
119
  PhusionPassenger.require_passenger_lib 'standalone/config_file' unless defined?(ConfigFile)
113
120
  global_options = ConfigFile.new(:global_config, global_config_file).options
@@ -30,7 +30,6 @@ class Main
30
30
  ['start', 'StartCommand'],
31
31
  ['stop', 'StopCommand'],
32
32
  ['status', 'StatusCommand'],
33
- ['package-runtime', 'PackageRuntimeCommand'],
34
33
  ['version', 'VersionCommand'],
35
34
  ['help', 'HelpCommand']
36
35
  ]
@@ -217,6 +217,10 @@ private
217
217
  wrap_desc("Turn off friendly error pages")) do
218
218
  @options[:friendly_error_pages] = false
219
219
  end
220
+ opts.on("--load-shell-envvars",
221
+ wrap_desc("Load shell startup files before loading application")) do
222
+ @options[:load_shell_envvars] = true
223
+ end
220
224
 
221
225
  opts.separator ""
222
226
  opts.separator "Process management options:"
@@ -0,0 +1,67 @@
1
+ # Phusion Passenger - https://www.phusionpassenger.com/
2
+ # Copyright (c) 2010-2014 Phusion
3
+ #
4
+ # "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
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
+ PhusionPassenger.require_passenger_lib 'message_channel'
25
+
26
+ module PhusionPassenger
27
+ module UnionStation
28
+
29
+ class Connection
30
+ attr_reader :mutex
31
+ attr_accessor :channel
32
+
33
+ def initialize(io)
34
+ @mutex = Mutex.new
35
+ @refcount = 1
36
+ @channel = MessageChannel.new(io) if io
37
+ end
38
+
39
+ def connected?
40
+ return !!@channel
41
+ end
42
+
43
+ def disconnect
44
+ @channel.close if @channel
45
+ @channel = nil
46
+ end
47
+
48
+ def ref
49
+ @refcount += 1
50
+ end
51
+
52
+ def unref
53
+ @refcount -= 1
54
+ if @refcount == 0
55
+ disconnect
56
+ end
57
+ end
58
+
59
+ def synchronize
60
+ @mutex.synchronize do
61
+ yield
62
+ end
63
+ end
64
+ end
65
+
66
+ end # module UnionStation
67
+ end # module PhusionPassenger
@@ -22,154 +22,23 @@
22
22
  # THE SOFTWARE.
23
23
 
24
24
  require 'thread'
25
+ PhusionPassenger.require_passenger_lib 'union_station/connection'
26
+ PhusionPassenger.require_passenger_lib 'union_station/transaction'
25
27
  PhusionPassenger.require_passenger_lib 'utils'
26
- PhusionPassenger.require_passenger_lib 'utils/native_support_utils'
28
+ PhusionPassenger.require_passenger_lib 'utils/lock'
27
29
  PhusionPassenger.require_passenger_lib 'debug_logging'
28
- PhusionPassenger.require_passenger_lib 'message_channel'
29
30
 
30
31
  module PhusionPassenger
32
+ module UnionStation
31
33
 
32
- class AnalyticsLogger
34
+ class Core
33
35
  RETRY_SLEEP = 0.2
34
36
  NETWORK_ERRORS = [Errno::EPIPE, Errno::ECONNREFUSED, Errno::ECONNRESET,
35
37
  Errno::EHOSTUNREACH, Errno::ENETDOWN, Errno::ENETUNREACH, Errno::ETIMEDOUT]
36
-
38
+
37
39
  include Utils
38
-
39
- class Log
40
- attr_reader :txn_id
41
-
42
- def initialize(connection = nil, txn_id = nil)
43
- if connection
44
- @connection = connection
45
- @txn_id = txn_id
46
- connection.ref
47
- end
48
- end
49
-
50
- def null?
51
- return !@connection || !@connection.connected?
52
- end
53
-
54
- def message(text)
55
- if !@connection
56
- timestamp_string = AnalyticsLogger.timestamp_string
57
- DebugLogging.trace(3, "[Union Station log to null] #{@txn_id} #{timestamp_string} #{text}")
58
- return
59
- end
60
- @connection.synchronize do
61
- return if !@connection.connected?
62
- begin
63
- timestamp_string = AnalyticsLogger.timestamp_string
64
- DebugLogging.trace(3, "[Union Station log] #{@txn_id} #{timestamp_string} #{text}")
65
- @connection.channel.write("log", @txn_id, timestamp_string)
66
- @connection.channel.write_scalar(text)
67
- rescue SystemCallError, IOError => e
68
- @connection.disconnect
69
- DebugLogging.warn("Error communicating with the logging agent: #{e.message}")
70
- rescue Exception => e
71
- @connection.disconnect
72
- raise e
73
- end
74
- end
75
- end
76
-
77
- def begin_measure(name, extra_info = nil)
78
- if extra_info
79
- extra_info_base64 = [extra_info].pack("m")
80
- extra_info_base64.gsub!("\n", "")
81
- extra_info_base64.strip!
82
- else
83
- extra_info_base64 = nil
84
- end
85
- times = Utils::NativeSupportUtils.process_times
86
- message "BEGIN: #{name} (#{current_timestamp.to_s(36)},#{times.utime.to_s(36)},#{times.stime.to_s(36)}) #{extra_info_base64}"
87
- end
88
-
89
- def end_measure(name, error_encountered = false)
90
- times = Utils::NativeSupportUtils.process_times
91
- if error_encountered
92
- message "FAIL: #{name} (#{current_timestamp.to_s(36)},#{times.utime.to_s(36)},#{times.stime.to_s(36)})"
93
- else
94
- message "END: #{name} (#{current_timestamp.to_s(36)},#{times.utime.to_s(36)},#{times.stime.to_s(36)})"
95
- end
96
- end
97
-
98
- def measure(name, extra_info = nil)
99
- begin_measure(name, extra_info)
100
- begin
101
- yield
102
- rescue Exception
103
- error = true
104
- is_closed = closed?
105
- raise
106
- ensure
107
- end_measure(name, error) if !is_closed
108
- end
109
- end
110
-
111
- def measured_time_points(name, begin_time, end_time, extra_info = nil)
112
- if extra_info
113
- extra_info_base64 = [extra_info].pack("m")
114
- extra_info_base64.gsub!("\n", "")
115
- extra_info_base64.strip!
116
- else
117
- extra_info_base64 = nil
118
- end
119
- begin_timestamp = begin_time.to_i * 1_000_000 + begin_time.usec
120
- end_timestamp = end_time.to_i * 1_000_000 + end_time.usec
121
- message "BEGIN: #{name} (#{begin_timestamp.to_s(36)}) #{extra_info_base64}"
122
- message "END: #{name} (#{end_timestamp.to_s(36)})"
123
- end
124
-
125
- def close(flush_to_disk = false)
126
- @connection.synchronize do
127
- return if !@connection.connected?
128
- begin
129
- # We need an ACK here. See thread_handler.rb finalize_request.
130
- @connection.channel.write("closeTransaction", @txn_id,
131
- AnalyticsLogger.timestamp_string, true)
132
- result = @connection.channel.read
133
- if result != ["ok"]
134
- raise "Expected logging agent to respond with 'ok', but got #{result.inspect} instead"
135
- end
136
- if flush_to_disk
137
- @connection.channel.write("flush")
138
- result = @connection.channel.read
139
- if result != ["ok"]
140
- raise "Invalid logging agent response #{result.inspect} to the 'flush' command"
141
- end
142
- end
143
- rescue SystemCallError, IOError => e
144
- @connection.disconnect
145
- DebugLogging.warn("Error communicating with the logging agent: #{e.message}")
146
- rescue Exception => e
147
- @connection.disconnect
148
- raise e
149
- ensure
150
- @connection.unref
151
- @connection = nil
152
- end
153
- end if @connection
154
- end
155
-
156
- def closed?
157
- if @connection
158
- @connection.synchronize do
159
- return !@connection.connected?
160
- end
161
- else
162
- return nil
163
- end
164
- end
165
-
166
- private
167
- def current_timestamp
168
- time = AnalyticsLogger.current_time
169
- return time.to_i * 1_000_000 + time.usec
170
- end
171
- end
172
-
40
+ include DebugLogging
41
+
173
42
  def self.new_from_options(options)
174
43
  if options["analytics"] && options["logging_agent_address"]
175
44
  return new(options["logging_agent_address"],
@@ -180,10 +49,10 @@ class AnalyticsLogger
180
49
  return nil
181
50
  end
182
51
  end
183
-
52
+
184
53
  attr_accessor :max_connect_tries
185
54
  attr_accessor :reconnect_timeout
186
-
55
+
187
56
  def initialize(logging_agent_address, username, password, node_name)
188
57
  @server_address = logging_agent_address
189
58
  @username = username
@@ -194,11 +63,11 @@ class AnalyticsLogger
194
63
  @node_name = `hostname`.strip
195
64
  end
196
65
  @random_dev = File.open("/dev/urandom")
197
-
66
+
198
67
  # This mutex protects the following instance variables, but
199
68
  # not the contents of @connection.
200
69
  @mutex = Mutex.new
201
-
70
+
202
71
  @connection = Connection.new(nil)
203
72
  if @server_address && local_socket_address?(@server_address)
204
73
  @max_connect_tries = 10
@@ -208,7 +77,7 @@ class AnalyticsLogger
208
77
  @reconnect_timeout = 1
209
78
  @next_reconnect_time = Time.utc(1980, 1, 1)
210
79
  end
211
-
80
+
212
81
  def clear_connection
213
82
  @mutex.synchronize do
214
83
  @connection.synchronize do
@@ -218,7 +87,7 @@ class AnalyticsLogger
218
87
  end
219
88
  end
220
89
  end
221
-
90
+
222
91
  def close
223
92
  @mutex.synchronize do
224
93
  @connection.synchronize do
@@ -228,43 +97,43 @@ class AnalyticsLogger
228
97
  end
229
98
  end
230
99
  end
231
-
100
+
232
101
  def new_transaction(group_name, category = :requests, union_station_key = "-")
233
102
  if !@server_address
234
- return Log.new
103
+ return Transaction.new
235
104
  elsif !group_name || group_name.empty?
236
105
  raise ArgumentError, "Group name may not be empty"
237
106
  end
238
-
239
- txn_id = (AnalyticsLogger.current_time.to_i / 60).to_s(36)
107
+
108
+ txn_id = (Core.current_time.to_i / 60).to_s(36)
240
109
  txn_id << "-#{random_token(11)}"
241
-
242
- Lock.new(@mutex).synchronize do |lock|
110
+
111
+ Utils::Lock.new(@mutex).synchronize do |lock|
243
112
  if current_time < @next_reconnect_time
244
- return Log.new
113
+ return Transaction.new
245
114
  end
246
-
247
- Lock.new(@connection.mutex).synchronize do |connection_lock|
115
+
116
+ Utils::Lock.new(@connection.mutex).synchronize do |connection_lock|
248
117
  if !@connection.connected?
249
118
  begin
250
119
  connect
251
120
  connection_lock.reset(@connection.mutex)
252
121
  rescue SystemCallError, IOError
253
122
  @connection.disconnect
254
- DebugLogging.warn("Cannot connect to the logging agent at #{@server_address}; " +
123
+ warn("Cannot connect to the logging agent at #{@server_address}; " +
255
124
  "retrying in #{@reconnect_timeout} second(s).")
256
125
  @next_reconnect_time = current_time + @reconnect_timeout
257
- return Log.new
126
+ return Transaction.new
258
127
  rescue Exception => e
259
128
  @connection.disconnect
260
129
  raise e
261
130
  end
262
131
  end
263
-
132
+
264
133
  begin
265
134
  @connection.channel.write("openTransaction",
266
135
  txn_id, group_name, "", category,
267
- AnalyticsLogger.timestamp_string,
136
+ Core.timestamp_string,
268
137
  union_station_key,
269
138
  true,
270
139
  true)
@@ -272,14 +141,14 @@ class AnalyticsLogger
272
141
  if result != ["ok"]
273
142
  raise "Expected logging server to respond with 'ok', but got #{result.inspect} instead"
274
143
  end
275
- return Log.new(@connection, txn_id)
144
+ return Transaction.new(@connection, txn_id)
276
145
  rescue SystemCallError, IOError
277
146
  @connection.disconnect
278
- DebugLogging.warn("The logging agent at #{@server_address}" <<
147
+ warn("The logging agent at #{@server_address}" <<
279
148
  " closed the connection; will reconnect in " <<
280
149
  "#{@reconnect_timeout} second(s).")
281
150
  @next_reconnect_time = current_time + @reconnect_timeout
282
- return Log.new
151
+ return Transaction.new
283
152
  rescue Exception => e
284
153
  @connection.disconnect
285
154
  raise e
@@ -287,50 +156,50 @@ class AnalyticsLogger
287
156
  end
288
157
  end
289
158
  end
290
-
159
+
291
160
  def continue_transaction(txn_id, group_name, category = :requests, union_station_key = "-")
292
161
  if !@server_address
293
- return Log.new
162
+ return Transaction.new
294
163
  elsif !txn_id || txn_id.empty?
295
164
  raise ArgumentError, "Transaction ID may not be empty"
296
165
  end
297
-
298
- Lock.new(@mutex).synchronize do |lock|
166
+
167
+ Utils::Lock.new(@mutex).synchronize do |lock|
299
168
  if current_time < @next_reconnect_time
300
- return Log.new
169
+ return Transaction.new
301
170
  end
302
-
303
- Lock.new(@connection.mutex).synchronize do |connection_lock|
171
+
172
+ Utils::Lock.new(@connection.mutex).synchronize do |connection_lock|
304
173
  if !@connection.connected?
305
174
  begin
306
175
  connect
307
176
  connection_lock.reset(@connection.mutex)
308
177
  rescue SystemCallError, IOError
309
178
  @connection.disconnect
310
- DebugLogging.warn("Cannot connect to the logging agent at #{@server_address}; " +
179
+ warn("Cannot connect to the logging agent at #{@server_address}; " +
311
180
  "retrying in #{@reconnect_timeout} second(s).")
312
181
  @next_reconnect_time = current_time + @reconnect_timeout
313
- return Log.new
182
+ return Transaction.new
314
183
  rescue Exception => e
315
184
  @connection.disconnect
316
185
  raise e
317
186
  end
318
187
  end
319
-
188
+
320
189
  begin
321
190
  @connection.channel.write("openTransaction",
322
191
  txn_id, group_name, "", category,
323
- AnalyticsLogger.timestamp_string,
192
+ Core.timestamp_string,
324
193
  union_station_key,
325
194
  true)
326
- return Log.new(@connection, txn_id)
195
+ return Transaction.new(@connection, txn_id)
327
196
  rescue SystemCallError, IOError
328
197
  @connection.disconnect
329
- DebugLogging.warn("The logging agent at #{@server_address}" <<
198
+ warn("The logging agent at #{@server_address}" <<
330
199
  " closed the connection; will reconnect in " <<
331
200
  "#{@reconnect_timeout} second(s).")
332
201
  @next_reconnect_time = current_time + @reconnect_timeout
333
- return Log.new
202
+ return Transaction.new
334
203
  rescue Exception => e
335
204
  @connection.disconnect
336
205
  raise e
@@ -345,82 +214,11 @@ private
345
214
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
346
215
  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
347
216
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
348
-
349
- class Lock
350
- def initialize(mutex)
351
- @mutex = mutex
352
- @locked = false
353
- end
354
-
355
- def reset(mutex, lock_now = true)
356
- unlock if @locked
357
- @mutex = mutex
358
- lock if lock_now
359
- end
360
-
361
- def synchronize
362
- lock if !@locked
363
- begin
364
- yield(self)
365
- ensure
366
- unlock if @locked
367
- end
368
- end
369
-
370
- def lock
371
- raise if @locked
372
- @mutex.lock
373
- @locked = true
374
- end
375
-
376
- def unlock
377
- raise if !@locked
378
- @mutex.unlock
379
- @locked = false
380
- end
381
- end
382
-
383
- class Connection
384
- attr_reader :mutex
385
- attr_accessor :channel
386
-
387
- def initialize(io)
388
- @mutex = Mutex.new
389
- @refcount = 1
390
- @channel = MessageChannel.new(io) if io
391
- end
392
-
393
- def connected?
394
- return !!@channel
395
- end
396
-
397
- def disconnect
398
- @channel.close if @channel
399
- @channel = nil
400
- end
401
-
402
- def ref
403
- @refcount += 1
404
- end
405
-
406
- def unref
407
- @refcount -= 1
408
- if @refcount == 0
409
- disconnect
410
- end
411
- end
412
-
413
- def synchronize
414
- @mutex.synchronize do
415
- yield
416
- end
417
- end
418
- end
419
-
217
+
420
218
  def connect
421
219
  socket = connect_to_server(@server_address)
422
220
  channel = MessageChannel.new(socket)
423
-
221
+
424
222
  result = channel.read
425
223
  if result.nil?
426
224
  raise EOFError
@@ -429,17 +227,17 @@ private
429
227
  elsif result[1] != "1"
430
228
  raise IOError, "Unsupported logging agent protocol version #{result[1]}"
431
229
  end
432
-
230
+
433
231
  channel.write_scalar(@username)
434
232
  channel.write_scalar(@password)
435
-
233
+
436
234
  result = channel.read
437
235
  if result.nil?
438
236
  raise EOFError
439
237
  elsif result[0] != "ok"
440
238
  raise SecurityError, result[0]
441
239
  end
442
-
240
+
443
241
  channel.write("init", @node_name)
444
242
  args = channel.read
445
243
  if !args
@@ -451,14 +249,14 @@ private
451
249
  elsif args[0] != "ok"
452
250
  raise IOError, "Logging agent returned an invalid reply for the 'init' command"
453
251
  end
454
-
252
+
455
253
  @connection.unref
456
254
  @connection = Connection.new(socket)
457
255
  rescue Exception => e
458
256
  socket.close if socket && !socket.closed?
459
257
  raise e
460
258
  end
461
-
259
+
462
260
  def random_token(length)
463
261
  token = ""
464
262
  @random_dev.read(length).each_byte do |c|
@@ -466,19 +264,20 @@ private
466
264
  end
467
265
  return token
468
266
  end
469
-
267
+
470
268
  def current_time
471
269
  return self.class.current_time
472
270
  end
473
-
271
+
474
272
  def self.current_time
475
273
  return Time.now
476
274
  end
477
-
275
+
478
276
  def self.timestamp_string(time = current_time)
479
277
  timestamp = time.to_i * 1_000_000 + time.usec
480
278
  return timestamp.to_s(36)
481
279
  end
482
280
  end
483
281
 
282
+ end # module UnionStation
484
283
  end # module PhusionPassenger