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.
- checksums.yaml +8 -8
- checksums.yaml.gz.asc +7 -7
- data.tar.gz.asc +7 -7
- data/.editorconfig +19 -0
- data/CHANGELOG +47 -0
- data/CONTRIBUTING.md +9 -1
- data/CONTRIBUTORS +4 -0
- data/Vagrantfile +7 -3
- data/build/agents.rb +1 -0
- data/build/misc.rb +6 -4
- data/dev/vagrant/bashrc +2 -0
- data/doc/Design and Architecture.txt +9 -7
- data/doc/Users guide Apache.idmap.txt +2 -0
- data/doc/Users guide Apache.txt +24 -4
- data/doc/Users guide Nginx.idmap.txt +4 -0
- data/doc/Users guide Nginx.txt +23 -4
- data/doc/images/code_walkthrough.jpg +0 -0
- data/doc/users_guide_snippets/installation.txt +38 -0
- data/ext/common/AgentsStarter.h +6 -1
- data/ext/common/ApplicationPool2/Common.h +17 -2
- data/ext/common/ApplicationPool2/DirectSpawner.h +5 -11
- data/ext/common/ApplicationPool2/DummySpawner.h +2 -4
- data/ext/common/ApplicationPool2/ErrorRenderer.h +119 -0
- data/ext/common/ApplicationPool2/Implementation.cpp +159 -11
- data/ext/common/ApplicationPool2/Options.h +16 -7
- data/ext/common/ApplicationPool2/Pool.h +28 -24
- data/ext/common/ApplicationPool2/Process.h +1 -9
- data/ext/common/ApplicationPool2/SmartSpawner.h +15 -18
- data/ext/common/ApplicationPool2/Spawner.h +18 -14
- data/ext/common/ApplicationPool2/SpawnerFactory.h +12 -30
- data/ext/common/Constants.h +1 -1
- data/ext/common/Exceptions.h +15 -2
- data/ext/common/UnionStation/Core.h +9 -0
- data/ext/common/Utils/JsonUtils.h +53 -0
- data/ext/common/Utils/ProcessMetricsCollector.h +1 -1
- data/ext/common/Utils/SpeedMeter.h +7 -3
- data/ext/common/Utils/SystemMetricsCollector.h +8 -6
- data/ext/common/agents/HelperAgent/Main.cpp +4 -4
- data/ext/common/agents/HelperAgent/RequestHandler.h +115 -56
- data/ext/nginx/ConfigurationCommands.c +1 -1
- data/ext/nginx/ConfigurationCommands.c.erb +6 -1
- data/ext/nginx/ContentHandler.c +2 -1
- data/ext/nginx/config +1 -1
- data/helper-scripts/node-loader.js +23 -0
- data/helper-scripts/wsgi-loader.py +12 -4
- data/lib/phusion_passenger.rb +1 -1
- data/lib/phusion_passenger/active_support3_extensions/init.rb +39 -78
- data/lib/phusion_passenger/constants.rb +3 -1
- data/lib/phusion_passenger/loader_shared_helpers.rb +10 -5
- data/lib/phusion_passenger/nginx/config_options.rb +3 -1
- data/lib/phusion_passenger/packaging.rb +1 -0
- data/lib/phusion_passenger/public_api.rb +108 -16
- data/lib/phusion_passenger/rack/thread_handler_extension.rb +1 -0
- data/lib/phusion_passenger/request_handler.rb +2 -2
- data/lib/phusion_passenger/request_handler/thread_handler.rb +28 -46
- data/lib/phusion_passenger/standalone/command.rb +8 -1
- data/lib/phusion_passenger/standalone/main.rb +0 -1
- data/lib/phusion_passenger/standalone/start_command.rb +4 -0
- data/lib/phusion_passenger/union_station/connection.rb +67 -0
- data/lib/phusion_passenger/{analytics_logger.rb → union_station/core.rb} +55 -256
- data/lib/phusion_passenger/union_station/transaction.rb +168 -0
- data/lib/phusion_passenger/utils.rb +4 -0
- data/lib/phusion_passenger/utils/lock.rb +62 -0
- data/resources/mime.types +1 -0
- data/resources/templates/error_layout.html.template +2 -0
- data/resources/templates/standalone/config.erb +1 -0
- data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +5 -3
- data/test/cxx/ApplicationPool2/PoolTest.cpp +13 -3
- data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +16 -13
- data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +6 -0
- data/test/cxx/FileBackedPipeTest.cpp +1 -1
- data/test/cxx/RequestHandlerTest.cpp +158 -2
- data/test/cxx/ServerInstanceDirTest.cpp +2 -0
- data/test/cxx/TestSupport.h +21 -2
- data/test/cxx/UtilsTest.cpp +1 -0
- data/test/ruby/classic_rails/loader_spec.rb +0 -1
- data/test/ruby/classic_rails/preloader_spec.rb +0 -1
- data/test/ruby/rails3.0/loader_spec.rb +2 -2
- data/test/ruby/rails3.0/preloader_spec.rb +2 -2
- data/test/ruby/rails3.1/loader_spec.rb +2 -2
- data/test/ruby/rails3.1/preloader_spec.rb +2 -2
- data/test/ruby/rails3.2/loader_spec.rb +2 -2
- data/test/ruby/rails3.2/preloader_spec.rb +2 -2
- data/test/ruby/rails4.0/loader_spec.rb +2 -2
- data/test/ruby/rails4.0/preloader_spec.rb +2 -2
- data/test/ruby/request_handler_spec.rb +8 -8
- data/test/ruby/shared/rails/{analytics_logging_extensions_sharedspec.rb → union_station_extensions_sharedspec.rb} +5 -4
- data/test/ruby/union_station_spec.rb +283 -0
- data/test/stub/wsgi/passenger_wsgi.py +41 -5
- metadata +12 -7
- metadata.gz.asc +7 -7
- data/helper-scripts/wsgi-preloader.py +0 -1
- data/lib/phusion_passenger/standalone/package_runtime_command.rb +0 -105
- 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
|
-
"
|
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
|
-
:
|
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
|
-
:
|
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
|
170
|
-
|
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 @
|
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
|
-
|
282
|
+
transaction = @union_station_core.continue_transaction(txn_id,
|
283
283
|
@app_group_name,
|
284
284
|
:requests, union_station_key)
|
285
|
-
headers[
|
286
|
-
|
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
|
-
|
293
|
+
transaction.message("Initial objects on heap: #{ObjectSpace.live_objects}")
|
291
294
|
end
|
292
295
|
if OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS
|
293
|
-
|
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
|
-
|
299
|
+
transaction.message("Initial objects allocated so far: #{count[:TOTAL] - count[:FREE]}")
|
297
300
|
end
|
298
301
|
if GC_SUPPORTS_TIME
|
299
|
-
|
302
|
+
transaction.message("Initial GC time: #{GC.time}")
|
300
303
|
end
|
301
|
-
|
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
|
-
|
309
|
-
|
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
|
-
|
318
|
+
transaction.end_measure("app request handler processing", has_error)
|
313
319
|
if OBJECT_SPACE_SUPPORTS_LIVE_OBJECTS
|
314
|
-
|
320
|
+
transaction.message("Final objects on heap: #{ObjectSpace.live_objects}")
|
315
321
|
end
|
316
322
|
if OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS
|
317
|
-
|
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
|
-
|
326
|
+
transaction.message("Final objects allocated so far: #{count[:TOTAL] - count[:FREE]}")
|
321
327
|
end
|
322
328
|
if GC_SUPPORTS_TIME
|
323
|
-
|
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
|
-
#
|
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
|
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
|
-
|
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
|
-
|
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
|
@@ -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/
|
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
|
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
|
-
|
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
|
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 = (
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
144
|
+
return Transaction.new(@connection, txn_id)
|
276
145
|
rescue SystemCallError, IOError
|
277
146
|
@connection.disconnect
|
278
|
-
|
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
|
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
|
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
|
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
|
-
|
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
|
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
|
-
|
192
|
+
Core.timestamp_string,
|
324
193
|
union_station_key,
|
325
194
|
true)
|
326
|
-
return
|
195
|
+
return Transaction.new(@connection, txn_id)
|
327
196
|
rescue SystemCallError, IOError
|
328
197
|
@connection.disconnect
|
329
|
-
|
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
|
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
|