union_station_hooks_core 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/LICENSE.md +19 -0
- data/README.md +117 -0
- data/lib/union_station_hooks_core.rb +360 -0
- data/lib/union_station_hooks_core/api.rb +182 -0
- data/lib/union_station_hooks_core/connection.rb +67 -0
- data/lib/union_station_hooks_core/context.rb +297 -0
- data/lib/union_station_hooks_core/lock.rb +62 -0
- data/lib/union_station_hooks_core/log.rb +66 -0
- data/lib/union_station_hooks_core/message_channel.rb +157 -0
- data/lib/union_station_hooks_core/request_reporter.rb +141 -0
- data/lib/union_station_hooks_core/request_reporter/basics.rb +132 -0
- data/lib/union_station_hooks_core/request_reporter/controllers.rb +187 -0
- data/lib/union_station_hooks_core/request_reporter/misc.rb +218 -0
- data/lib/union_station_hooks_core/request_reporter/view_rendering.rb +76 -0
- data/lib/union_station_hooks_core/spec_helper.rb +241 -0
- data/lib/union_station_hooks_core/time_point.rb +53 -0
- data/lib/union_station_hooks_core/transaction.rb +182 -0
- data/lib/union_station_hooks_core/utils.rb +161 -0
- data/lib/union_station_hooks_core/version.rb +32 -0
- data/lib/union_station_hooks_core/version_data.rb +44 -0
- metadata +64 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
# Union Station - https://www.unionstationapp.com/
|
2
|
+
# Copyright (c) 2010-2015 Phusion Holding B.V.
|
3
|
+
#
|
4
|
+
# "Union Station" and "Passenger" are trademarks of Phusion Holding B.V.
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
11
|
+
# furnished to do so, subject to the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be included in
|
14
|
+
# all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
# THE SOFTWARE.
|
23
|
+
|
24
|
+
|
25
|
+
module UnionStationHooks
|
26
|
+
# A wrapper around a mutex, for use within Connection.
|
27
|
+
#
|
28
|
+
# @private
|
29
|
+
class Lock
|
30
|
+
def initialize(mutex)
|
31
|
+
@mutex = mutex
|
32
|
+
@locked = false
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset(mutex, lock_now = true)
|
36
|
+
unlock if @locked
|
37
|
+
@mutex = mutex
|
38
|
+
lock if lock_now
|
39
|
+
end
|
40
|
+
|
41
|
+
def synchronize
|
42
|
+
lock if !@locked
|
43
|
+
begin
|
44
|
+
yield(self)
|
45
|
+
ensure
|
46
|
+
unlock if @locked
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def lock
|
51
|
+
raise if @locked
|
52
|
+
@mutex.lock
|
53
|
+
@locked = true
|
54
|
+
end
|
55
|
+
|
56
|
+
def unlock
|
57
|
+
raise if !@locked
|
58
|
+
@mutex.unlock
|
59
|
+
@locked = false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Union Station - https://www.unionstationapp.com/
|
2
|
+
# Copyright (c) 2010-2015 Phusion Holding B.V.
|
3
|
+
#
|
4
|
+
# "Union Station" and "Passenger" are trademarks of Phusion Holding B.V.
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
11
|
+
# furnished to do so, subject to the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be included in
|
14
|
+
# all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
# THE SOFTWARE.
|
23
|
+
|
24
|
+
|
25
|
+
module UnionStationHooks
|
26
|
+
# Provides methods for `union_station_*` gems to log internal warnings and
|
27
|
+
# debugging messages. This module is *not* to be used by application
|
28
|
+
# developers for the purpose of logging information to Union Station.
|
29
|
+
#
|
30
|
+
# @private
|
31
|
+
module Log
|
32
|
+
@@debugging = false
|
33
|
+
@@warn_callback = nil
|
34
|
+
@@debug_callback = nil
|
35
|
+
|
36
|
+
def self.debugging=(value)
|
37
|
+
@@debugging = value
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.warn(message)
|
41
|
+
if @@warn_callback
|
42
|
+
@@warn_callback.call(message)
|
43
|
+
else
|
44
|
+
STDERR.puts("[UnionStationHooks] #{message}")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.debug(message)
|
49
|
+
if @@debugging
|
50
|
+
if @@debug_callback
|
51
|
+
@@debug_callback.call(message)
|
52
|
+
else
|
53
|
+
STDERR.puts("[UnionStationHooks] #{message}")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.warn_callback=(cb)
|
59
|
+
@@warn_callback = cb
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.debug_callback=(cb)
|
63
|
+
@@debug_callback = cb
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# encoding: binary
|
2
|
+
# Union Station - https://www.unionstationapp.com/
|
3
|
+
# Copyright (c) 2010-2015 Phusion Holding B.V.
|
4
|
+
#
|
5
|
+
# "Union Station" and "Passenger" are trademarks of Phusion Holding B.V.
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
# THE SOFTWARE.
|
24
|
+
|
25
|
+
|
26
|
+
module UnionStationHooks
|
27
|
+
# This class allows reading and writing structured messages over
|
28
|
+
# I/O channels. This is the Ruby implementation of Passenger's
|
29
|
+
# src/cxx_supportlib/Utils/MessageIO.h; see that file for more information.
|
30
|
+
#
|
31
|
+
# @private
|
32
|
+
class MessageChannel
|
33
|
+
HEADER_SIZE = 2
|
34
|
+
DELIMITER = "\0"
|
35
|
+
DELIMITER_NAME = 'null byte'
|
36
|
+
UINT16_PACK_FORMAT = 'n'
|
37
|
+
UINT32_PACK_FORMAT = 'N'
|
38
|
+
|
39
|
+
class InvalidHashError < StandardError
|
40
|
+
end
|
41
|
+
|
42
|
+
# The wrapped IO object.
|
43
|
+
attr_accessor :io
|
44
|
+
|
45
|
+
# Create a new MessageChannel by wrapping the given IO object.
|
46
|
+
def initialize(io = nil)
|
47
|
+
@io = io
|
48
|
+
# Make it binary just in case.
|
49
|
+
@io.binmode if @io
|
50
|
+
end
|
51
|
+
|
52
|
+
# rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
53
|
+
# rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize
|
54
|
+
|
55
|
+
# Read an array message from the underlying file descriptor.
|
56
|
+
# Returns the array message as an array, or nil when end-of-stream has
|
57
|
+
# been reached.
|
58
|
+
#
|
59
|
+
# Might raise SystemCallError, IOError or SocketError when something
|
60
|
+
# goes wrong.
|
61
|
+
def read
|
62
|
+
buffer = new_buffer
|
63
|
+
if !@io.read(HEADER_SIZE, buffer)
|
64
|
+
return nil
|
65
|
+
end
|
66
|
+
while buffer.size < HEADER_SIZE
|
67
|
+
tmp = @io.read(HEADER_SIZE - buffer.size)
|
68
|
+
if tmp.empty?
|
69
|
+
return nil
|
70
|
+
else
|
71
|
+
buffer << tmp
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
chunk_size = buffer.unpack(UINT16_PACK_FORMAT)[0]
|
76
|
+
if !@io.read(chunk_size, buffer)
|
77
|
+
return nil
|
78
|
+
end
|
79
|
+
while buffer.size < chunk_size
|
80
|
+
tmp = @io.read(chunk_size - buffer.size)
|
81
|
+
if tmp.empty?
|
82
|
+
return nil
|
83
|
+
else
|
84
|
+
buffer << tmp
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
message = []
|
89
|
+
offset = 0
|
90
|
+
delimiter_pos = buffer.index(DELIMITER, offset)
|
91
|
+
while !delimiter_pos.nil?
|
92
|
+
if delimiter_pos == 0
|
93
|
+
message << ''
|
94
|
+
else
|
95
|
+
message << buffer[offset..delimiter_pos - 1]
|
96
|
+
end
|
97
|
+
offset = delimiter_pos + 1
|
98
|
+
delimiter_pos = buffer.index(DELIMITER, offset)
|
99
|
+
end
|
100
|
+
message
|
101
|
+
rescue Errno::ECONNRESET
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
106
|
+
# rubocop:enable Metrics/PerceivedComplexity, Metrics/AbcSize
|
107
|
+
|
108
|
+
# Send an array message, which consists of the given elements, over the
|
109
|
+
# underlying file descriptor. _name_ is the first element in the message,
|
110
|
+
# and _args_ are the other elements. These arguments will internally be
|
111
|
+
# converted to strings by calling to_s().
|
112
|
+
#
|
113
|
+
# Might raise SystemCallError, IOError or SocketError when something
|
114
|
+
# goes wrong.
|
115
|
+
def write(name, *args)
|
116
|
+
check_argument(name)
|
117
|
+
args.each do |arg|
|
118
|
+
check_argument(arg)
|
119
|
+
end
|
120
|
+
|
121
|
+
message = "#{name}#{DELIMITER}"
|
122
|
+
args.each do |arg|
|
123
|
+
message << arg.to_s << DELIMITER
|
124
|
+
end
|
125
|
+
@io.write([message.size].pack('n') << message)
|
126
|
+
@io.flush
|
127
|
+
end
|
128
|
+
|
129
|
+
# Send a scalar message over the underlying IO object.
|
130
|
+
#
|
131
|
+
# Might raise SystemCallError, IOError or SocketError when something
|
132
|
+
# goes wrong.
|
133
|
+
def write_scalar(data)
|
134
|
+
@io.write([data.size].pack('N') << data)
|
135
|
+
@io.flush
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def check_argument(arg)
|
141
|
+
if arg.to_s.index(DELIMITER)
|
142
|
+
raise ArgumentError,
|
143
|
+
"Message name and arguments may not contain #{DELIMITER_NAME}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
if defined?(ByteString)
|
148
|
+
def new_buffer
|
149
|
+
ByteString.new
|
150
|
+
end
|
151
|
+
else
|
152
|
+
def new_buffer
|
153
|
+
''
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end # module UnionStationHooks
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# Union Station - https://www.unionstationapp.com/
|
2
|
+
# Copyright (c) 2010-2015 Phusion Holding B.V.
|
3
|
+
#
|
4
|
+
# "Union Station" and "Passenger" are trademarks of Phusion Holding B.V.
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
11
|
+
# furnished to do so, subject to the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be included in
|
14
|
+
# all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
# THE SOFTWARE.
|
23
|
+
|
24
|
+
UnionStationHooks.require_lib 'request_reporter/basics'
|
25
|
+
UnionStationHooks.require_lib 'request_reporter/controllers'
|
26
|
+
UnionStationHooks.require_lib 'request_reporter/view_rendering'
|
27
|
+
UnionStationHooks.require_lib 'request_reporter/misc'
|
28
|
+
|
29
|
+
module UnionStationHooks
|
30
|
+
# A RequestReporter object is used for logging request-specific information
|
31
|
+
# to Union Station. "Information" may include (and are not limited to):
|
32
|
+
#
|
33
|
+
# * Web framework controller and action name.
|
34
|
+
# * Exceptions raised during the request.
|
35
|
+
# * Cache hits and misses.
|
36
|
+
# * Database actions.
|
37
|
+
#
|
38
|
+
# A unique RequestReporter is created by Passenger at the beginning of every
|
39
|
+
# request (by calling {UnionStationHooks.begin_rack_request}). This object is
|
40
|
+
# closed at the end of the same request (after the Rack body object is
|
41
|
+
# closed).
|
42
|
+
#
|
43
|
+
# As an application developer, the RequestReporter is the main class
|
44
|
+
# that you will be interfacing with. See the {UnionStationHooks} module
|
45
|
+
# description for an example of how you can use RequestReporter.
|
46
|
+
#
|
47
|
+
# ## Obtaining a RequestReporter
|
48
|
+
#
|
49
|
+
# You are not supposed to create a RequestReporter object directly.
|
50
|
+
# You are supposed to obtain the RequestReporter object that Passenger creates
|
51
|
+
# for you. This is done through the `union_station_hooks` key in the Rack
|
52
|
+
# environment hash, as well as through the `:union_station_hooks` key in
|
53
|
+
# the current thread's object:
|
54
|
+
#
|
55
|
+
# env['union_station_hooks']
|
56
|
+
# # => RequestReporter object or nil
|
57
|
+
#
|
58
|
+
# Thread.current[:union_station_hooks]
|
59
|
+
# # => RequestReporter object or nil
|
60
|
+
#
|
61
|
+
# Note that Passenger may not have created such an object because of an
|
62
|
+
# error. At present, there are two error conditions that would cause a
|
63
|
+
# RequestReporter object not to be created. However, your code should take
|
64
|
+
# into account that in the future more error conditions may trigger this.
|
65
|
+
#
|
66
|
+
# 1. There is no transaction ID associated with the current request.
|
67
|
+
# When Union Station support is enabled in Passenger, Passenger always
|
68
|
+
# assigns a transaction ID. However, administrators can also
|
69
|
+
# {https://www.phusionpassenger.com/library/admin/nginx/request_individual_processes.html
|
70
|
+
# access Ruby processes directly} through process-private HTTP sockets,
|
71
|
+
# bypassing Passenger's load balancing mechanism. In that case, no
|
72
|
+
# transaction ID will be assigned.
|
73
|
+
# 2. An error occurred recently while sending data to the UstRouter, either
|
74
|
+
# because the UstRouter crashed or because of some other kind of
|
75
|
+
# communication error occurred. This error condition isn't cleared until
|
76
|
+
# certain a timeout has passed.
|
77
|
+
#
|
78
|
+
# The UstRouter is a Passenger process which runs locally and is
|
79
|
+
# responsible for aggregating Union Station log data from multiple
|
80
|
+
# processes, with the goal of sending the aggregate data over the network
|
81
|
+
# to the Union Station service.
|
82
|
+
#
|
83
|
+
# This kind of error is automatically recovered from after a certain
|
84
|
+
# period of time.
|
85
|
+
#
|
86
|
+
# ## Null mode
|
87
|
+
#
|
88
|
+
# The error condition 2 described above may also cause an existing
|
89
|
+
# RequestReporter object to enter the "null mode". When this mode is entered,
|
90
|
+
# any further actions on the RequestReporter object will become no-ops.
|
91
|
+
# You can check whether the null mode is active by calling {#null?}.
|
92
|
+
#
|
93
|
+
# Closing a RequestReporter also causes it to enter the null mode.
|
94
|
+
class RequestReporter
|
95
|
+
# Returns a new RequestReporter object. You should not call
|
96
|
+
# `RequestReporter.new` directly. See "Obtaining a RequestReporter"
|
97
|
+
# in the {RequestReporter class description}.
|
98
|
+
#
|
99
|
+
# @api private
|
100
|
+
def initialize(context, txn_id, app_group_name, key)
|
101
|
+
raise ArgumentError, 'Transaction ID must be given' if txn_id.nil?
|
102
|
+
raise ArgumentError, 'App group name must be given' if app_group_name.nil?
|
103
|
+
raise ArgumentError, 'Union Station key must be given' if key.nil?
|
104
|
+
@context = context
|
105
|
+
@txn_id = txn_id
|
106
|
+
@app_group_name = app_group_name
|
107
|
+
@key = key
|
108
|
+
@transaction = continue_transaction
|
109
|
+
end
|
110
|
+
|
111
|
+
# Indicates that no further information will be logged for this
|
112
|
+
# request.
|
113
|
+
#
|
114
|
+
# @api private
|
115
|
+
def close
|
116
|
+
@transaction.close
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns whether is this RequestReporter object is in null mode.
|
120
|
+
# See the {RequestReporter class description} for more information.
|
121
|
+
def null?
|
122
|
+
@transaction.null?
|
123
|
+
end
|
124
|
+
|
125
|
+
# Other methods are implemented in the files in the
|
126
|
+
# 'request_reporter/' subdirectory.
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def continue_transaction
|
131
|
+
@context.continue_transaction(@txn_id, @app_group_name,
|
132
|
+
:requests, @key)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Called when one of the methods return early upon detecting null
|
136
|
+
# mode. Used by tests to verify that methods return early.
|
137
|
+
def do_nothing_on_null(_source)
|
138
|
+
# Do nothing by default. Tests will stub this.
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# Union Station - https://www.unionstationapp.com/
|
2
|
+
# Copyright (c) 2010-2015 Phusion Holding B.V.
|
3
|
+
#
|
4
|
+
# "Union Station" and "Passenger" are trademarks of Phusion Holding B.V.
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
11
|
+
# furnished to do so, subject to the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be included in
|
14
|
+
# all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
# THE SOFTWARE.
|
23
|
+
|
24
|
+
require 'thread'
|
25
|
+
|
26
|
+
module UnionStationHooks
|
27
|
+
class RequestReporter
|
28
|
+
###### Logging basic request information ######
|
29
|
+
|
30
|
+
# A mutex for synchronizing GC stats reporting. We do this because in
|
31
|
+
# multithreaded situations we don't want to interleave GC stats access with
|
32
|
+
# calls to `GC.clear_stats`. Not that GC stats are very helpful in
|
33
|
+
# multithreaded situations, but this is better than nothing.
|
34
|
+
#
|
35
|
+
# @private
|
36
|
+
GC_MUTEX = Mutex.new
|
37
|
+
|
38
|
+
# @private
|
39
|
+
OBJECT_SPACE_SUPPORTS_LIVE_OBJECTS = ObjectSpace.respond_to?(:live_objects)
|
40
|
+
|
41
|
+
# @private
|
42
|
+
OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS =
|
43
|
+
ObjectSpace.respond_to?(:allocated_objects)
|
44
|
+
|
45
|
+
# @private
|
46
|
+
OBJECT_SPACE_SUPPORTS_COUNT_OBJECTS =
|
47
|
+
ObjectSpace.respond_to?(:count_objects)
|
48
|
+
|
49
|
+
# @private
|
50
|
+
GC_SUPPORTS_TIME = GC.respond_to?(:time)
|
51
|
+
|
52
|
+
# @private
|
53
|
+
GC_SUPPORTS_CLEAR_STATS = GC.respond_to?(:clear_stats)
|
54
|
+
|
55
|
+
# Log the beginning of a Rack request. This is automatically called
|
56
|
+
# from {UnionStationHooks.begin_rack_request} (and thus automatically
|
57
|
+
# from Passenger).
|
58
|
+
#
|
59
|
+
# @private
|
60
|
+
def log_request_begin
|
61
|
+
return do_nothing_on_null(:log_request_begin) if null?
|
62
|
+
@transaction.log_activity_begin('app request handler processing')
|
63
|
+
end
|
64
|
+
|
65
|
+
# Log the end of a Rack request. This is automatically called
|
66
|
+
# from {UnionStationHooks.begin_rack_request} (and thus automatically
|
67
|
+
# from Passenger).
|
68
|
+
#
|
69
|
+
# @private
|
70
|
+
def log_request_end(uncaught_exception_raised_during_request = false)
|
71
|
+
return do_nothing_on_null(:log_request_end) if null?
|
72
|
+
@transaction.log_activity_end('app request handler processing',
|
73
|
+
UnionStationHooks.now, uncaught_exception_raised_during_request)
|
74
|
+
end
|
75
|
+
|
76
|
+
# rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
77
|
+
|
78
|
+
# @private
|
79
|
+
def log_gc_stats_on_request_begin
|
80
|
+
return do_nothing_on_null(:log_gc_stats_on_request_begin) if null?
|
81
|
+
|
82
|
+
# See the docs for MUTEX on why we synchronize this.
|
83
|
+
GC_MUTEX.synchronize do
|
84
|
+
if OBJECT_SPACE_SUPPORTS_LIVE_OBJECTS
|
85
|
+
@transaction.message('Initial objects on heap: ' \
|
86
|
+
"#{ObjectSpace.live_objects}")
|
87
|
+
end
|
88
|
+
if OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS
|
89
|
+
@transaction.message('Initial objects allocated so far: ' \
|
90
|
+
"#{ObjectSpace.allocated_objects}")
|
91
|
+
elsif OBJECT_SPACE_SUPPORTS_COUNT_OBJECTS
|
92
|
+
count = ObjectSpace.count_objects
|
93
|
+
@transaction.message('Initial objects allocated so far: ' \
|
94
|
+
"#{count[:TOTAL] - count[:FREE]}")
|
95
|
+
end
|
96
|
+
if GC_SUPPORTS_TIME
|
97
|
+
@transaction.message("Initial GC time: #{GC.time}")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# @private
|
103
|
+
def log_gc_stats_on_request_end
|
104
|
+
return do_nothing_on_null(:log_gc_stats_on_request_end) if null?
|
105
|
+
|
106
|
+
# See the docs for MUTEX on why we synchronize this.
|
107
|
+
GC_MUTEX.synchronize do
|
108
|
+
if OBJECT_SPACE_SUPPORTS_LIVE_OBJECTS
|
109
|
+
@transaction.message('Final objects on heap: ' \
|
110
|
+
"#{ObjectSpace.live_objects}")
|
111
|
+
end
|
112
|
+
if OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS
|
113
|
+
@transaction.message('Final objects allocated so far: ' \
|
114
|
+
"#{ObjectSpace.allocated_objects}")
|
115
|
+
elsif OBJECT_SPACE_SUPPORTS_COUNT_OBJECTS
|
116
|
+
count = ObjectSpace.count_objects
|
117
|
+
@transaction.message('Final objects allocated so far: ' \
|
118
|
+
"#{count[:TOTAL] - count[:FREE]}")
|
119
|
+
end
|
120
|
+
if GC_SUPPORTS_TIME
|
121
|
+
@transaction.message("Final GC time: #{GC.time}")
|
122
|
+
end
|
123
|
+
if GC_SUPPORTS_CLEAR_STATS
|
124
|
+
# Clear statistics to void integer wraps.
|
125
|
+
GC.clear_stats
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
131
|
+
end
|
132
|
+
end
|