google-cloud-debugger 0.40.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.yardopts +18 -0
- data/AUTHENTICATION.md +178 -0
- data/CHANGELOG.md +233 -0
- data/CODE_OF_CONDUCT.md +40 -0
- data/CONTRIBUTING.md +188 -0
- data/INSTRUMENTATION.md +115 -0
- data/LICENSE +201 -0
- data/LOGGING.md +32 -0
- data/OVERVIEW.md +266 -0
- data/TROUBLESHOOTING.md +31 -0
- data/ext/google/cloud/debugger/debugger_c/debugger.c +31 -0
- data/ext/google/cloud/debugger/debugger_c/debugger.h +26 -0
- data/ext/google/cloud/debugger/debugger_c/evaluator.c +115 -0
- data/ext/google/cloud/debugger/debugger_c/evaluator.h +25 -0
- data/ext/google/cloud/debugger/debugger_c/extconf.rb +22 -0
- data/ext/google/cloud/debugger/debugger_c/tracer.c +542 -0
- data/ext/google/cloud/debugger/debugger_c/tracer.h +25 -0
- data/lib/google-cloud-debugger.rb +181 -0
- data/lib/google/cloud/debugger.rb +259 -0
- data/lib/google/cloud/debugger/agent.rb +255 -0
- data/lib/google/cloud/debugger/backoff.rb +70 -0
- data/lib/google/cloud/debugger/breakpoint.rb +443 -0
- data/lib/google/cloud/debugger/breakpoint/evaluator.rb +1099 -0
- data/lib/google/cloud/debugger/breakpoint/source_location.rb +74 -0
- data/lib/google/cloud/debugger/breakpoint/stack_frame.rb +109 -0
- data/lib/google/cloud/debugger/breakpoint/status_message.rb +93 -0
- data/lib/google/cloud/debugger/breakpoint/validator.rb +92 -0
- data/lib/google/cloud/debugger/breakpoint/variable.rb +595 -0
- data/lib/google/cloud/debugger/breakpoint/variable_table.rb +96 -0
- data/lib/google/cloud/debugger/breakpoint_manager.rb +311 -0
- data/lib/google/cloud/debugger/credentials.rb +50 -0
- data/lib/google/cloud/debugger/debuggee.rb +222 -0
- data/lib/google/cloud/debugger/debuggee/app_uniquifier_generator.rb +76 -0
- data/lib/google/cloud/debugger/logpoint.rb +98 -0
- data/lib/google/cloud/debugger/middleware.rb +200 -0
- data/lib/google/cloud/debugger/project.rb +110 -0
- data/lib/google/cloud/debugger/rails.rb +174 -0
- data/lib/google/cloud/debugger/request_quota_manager.rb +95 -0
- data/lib/google/cloud/debugger/service.rb +88 -0
- data/lib/google/cloud/debugger/snappoint.rb +208 -0
- data/lib/google/cloud/debugger/tracer.rb +137 -0
- data/lib/google/cloud/debugger/transmitter.rb +199 -0
- data/lib/google/cloud/debugger/version.rb +22 -0
- metadata +353 -0
@@ -0,0 +1,222 @@
|
|
1
|
+
# Copyright 2017 Google LLC
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
require "digest/sha1"
|
17
|
+
require "google/cloud/debugger/backoff"
|
18
|
+
require "google/cloud/debugger/debuggee/app_uniquifier_generator"
|
19
|
+
require "google/cloud/debugger/version"
|
20
|
+
require "google/cloud/env"
|
21
|
+
require "json"
|
22
|
+
|
23
|
+
module Google
|
24
|
+
module Cloud
|
25
|
+
module Debugger
|
26
|
+
##
|
27
|
+
# # Debuggee
|
28
|
+
#
|
29
|
+
# Represent a debuggee application. Contains information that identifies
|
30
|
+
# debuggee applications from each other. Maps to gRPC struct
|
31
|
+
# Google::Cloud::Debugger::V2::Debuggee.
|
32
|
+
#
|
33
|
+
# It also automatically loads source context information generated from
|
34
|
+
# Cloud SDK. See [Stackdriver Debugger
|
35
|
+
# doc](https://cloud.google.com/debugger/docs/source-context#app_engine_standard_python)
|
36
|
+
# for more information on how to generate this source context information
|
37
|
+
# when used on Google Container Engine and Google Compute engine. This
|
38
|
+
# step is taken care of if debuggee application is hosted on Google App
|
39
|
+
# Engine Flexibile.
|
40
|
+
#
|
41
|
+
# To ensure the multiple instances of the application are indeed the same
|
42
|
+
# application, the debuggee also compute a "uniquifier" generated from
|
43
|
+
# source context or application source code.
|
44
|
+
#
|
45
|
+
class Debuggee
|
46
|
+
##
|
47
|
+
# @private The gRPC Service object.
|
48
|
+
attr_reader :service
|
49
|
+
|
50
|
+
##
|
51
|
+
# Name for the debuggee application
|
52
|
+
# @return [String]
|
53
|
+
attr_reader :service_name
|
54
|
+
|
55
|
+
##
|
56
|
+
# Version identifier for the debuggee application
|
57
|
+
# @return [String]
|
58
|
+
attr_reader :service_version
|
59
|
+
|
60
|
+
##
|
61
|
+
# Registered Debuggee ID. Set by Stackdriver Debugger service when
|
62
|
+
# a debuggee application is sucessfully registered.
|
63
|
+
# @return [String]
|
64
|
+
attr_reader :id
|
65
|
+
|
66
|
+
##
|
67
|
+
# @private Construct a new instance of Debuggee
|
68
|
+
def initialize service, service_name:, service_version:
|
69
|
+
@service = service
|
70
|
+
@service_name = service_name
|
71
|
+
@service_version = service_version
|
72
|
+
@env = Google::Cloud.env
|
73
|
+
@computed_uniquifier = nil
|
74
|
+
@id = nil
|
75
|
+
@register_backoff = Google::Cloud::Debugger::Backoff.new
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# Register the current application as a debuggee with Stackdriver
|
80
|
+
# Debuggee service.
|
81
|
+
# @return [Boolean] True if registered sucessfully; otherwise false.
|
82
|
+
def register
|
83
|
+
# Wait if backoff applies
|
84
|
+
sleep @register_backoff.interval if @register_backoff.backing_off?
|
85
|
+
|
86
|
+
begin
|
87
|
+
response = service.register_debuggee to_grpc
|
88
|
+
@id = response.debuggee.id
|
89
|
+
rescue StandardError
|
90
|
+
revoke_registration
|
91
|
+
end
|
92
|
+
|
93
|
+
registered = registered?
|
94
|
+
registered ? @register_backoff.succeeded : @register_backoff.failed
|
95
|
+
|
96
|
+
registered
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Check whether this debuggee is currently registered or not
|
101
|
+
# @return [Boolean] True if debuggee is registered; otherwise false.
|
102
|
+
def registered?
|
103
|
+
!id.nil?
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Revoke the registration of this debuggee
|
108
|
+
def revoke_registration
|
109
|
+
@id = nil
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Convert this debuggee into a gRPC
|
114
|
+
# Google::Cloud::Debugger::V2::Debuggee struct.
|
115
|
+
def to_grpc
|
116
|
+
source_context = source_context_from_json_file "source-context.json"
|
117
|
+
Google::Cloud::Debugger::V2::Debuggee.new(
|
118
|
+
id: id.to_s,
|
119
|
+
project: project_id_for_request_arg.to_s,
|
120
|
+
description: description.to_s,
|
121
|
+
labels: labels,
|
122
|
+
agent_version: agent_version.to_s
|
123
|
+
).tap do |d|
|
124
|
+
if source_context
|
125
|
+
d.source_contexts << source_context
|
126
|
+
d.ext_source_contexts << ext_source_context_grpc(source_context)
|
127
|
+
end
|
128
|
+
d.uniquifier = compute_uniquifier d
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def source_context_from_json_file file_path
|
135
|
+
json = File.read file_path
|
136
|
+
Devtools::Source::V1::SourceContext.decode_json json
|
137
|
+
rescue StandardError
|
138
|
+
nil
|
139
|
+
end
|
140
|
+
|
141
|
+
def ext_source_context_grpc source_context
|
142
|
+
Devtools::Source::V1::SourceContext.new context: source_context
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# @private Build labels hash for this debuggee
|
147
|
+
def labels
|
148
|
+
{
|
149
|
+
"projectid" => String(project_id),
|
150
|
+
"module" => String(service_name),
|
151
|
+
"version" => String(service_version)
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# @private Build description string for this debuggee. In
|
157
|
+
# "<module name> - <module version>" format. Or just the module
|
158
|
+
# version if module name is missing.
|
159
|
+
#
|
160
|
+
# @return [String] A compact debuggee description.
|
161
|
+
#
|
162
|
+
def description
|
163
|
+
if service_version.nil? || service_version.empty?
|
164
|
+
service_name
|
165
|
+
else
|
166
|
+
"#{service_name} - #{service_version}"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# @private Get debuggee project id
|
172
|
+
def project_id
|
173
|
+
service.project
|
174
|
+
end
|
175
|
+
|
176
|
+
##
|
177
|
+
# @private
|
178
|
+
# Get project to send as a debuggee argument. This is the numeric
|
179
|
+
# project ID if available (and if it matches the project set in the
|
180
|
+
# configuration). Otherwise, use the configured project.
|
181
|
+
def project_id_for_request_arg
|
182
|
+
if project_id == @env.lookup_metadata("project", "project-id")
|
183
|
+
numeric_id = @env.numeric_project_id
|
184
|
+
return numeric_id.to_s if numeric_id
|
185
|
+
end
|
186
|
+
project_id
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# @private Build debuggee agent version identifier
|
191
|
+
def agent_version
|
192
|
+
"google.com/ruby#{RUBY_VERSION}-#{Google::Cloud::Debugger::VERSION}"
|
193
|
+
end
|
194
|
+
|
195
|
+
##
|
196
|
+
# @private Generate a debuggee uniquifier from source context
|
197
|
+
# information or application source code
|
198
|
+
def compute_uniquifier partial_debuggee
|
199
|
+
return @computed_uniquifier if @computed_uniquifier
|
200
|
+
|
201
|
+
sha = Digest::SHA1.new
|
202
|
+
sha << partial_debuggee.to_s
|
203
|
+
|
204
|
+
if partial_debuggee.source_contexts.empty? &&
|
205
|
+
partial_debuggee.ext_source_contexts.empty?
|
206
|
+
AppUniquifierGenerator.generate_app_uniquifier sha
|
207
|
+
end
|
208
|
+
|
209
|
+
@computed_uniquifier = sha.hexdigest
|
210
|
+
end
|
211
|
+
|
212
|
+
##
|
213
|
+
# @private Helper method to parse json file
|
214
|
+
def read_app_json_file file_path
|
215
|
+
JSON.parse File.read(file_path), symbolize_names: true
|
216
|
+
rescue StandardError
|
217
|
+
nil
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Copyright 2017 Google LLC
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
require "pathname"
|
17
|
+
require "set"
|
18
|
+
|
19
|
+
module Google
|
20
|
+
module Cloud
|
21
|
+
module Debugger
|
22
|
+
class Debuggee
|
23
|
+
##
|
24
|
+
# Generates an application debuggee uniquifier by hashing the
|
25
|
+
# application file stats.
|
26
|
+
#
|
27
|
+
module AppUniquifierGenerator
|
28
|
+
##
|
29
|
+
# Max number of directory levels the generator looks into.
|
30
|
+
MAX_DEPTH = 10
|
31
|
+
|
32
|
+
##
|
33
|
+
# Computes the application uniquifier by examine the file stats of
|
34
|
+
# the files in the given application root directory. It only looks at
|
35
|
+
# .rb files and Gemfile.lock
|
36
|
+
#
|
37
|
+
# @param [Digest::SHA] sha A digest SHA object used to add the hashing
|
38
|
+
# values
|
39
|
+
# @param [String] app_path Application root directory where the Ruby
|
40
|
+
# application is located.
|
41
|
+
#
|
42
|
+
# @return [NilClass]
|
43
|
+
#
|
44
|
+
def self.generate_app_uniquifier sha, app_path = nil
|
45
|
+
app_path ||= if defined?(::Rack::Directory)
|
46
|
+
Rack::Directory.new("").root
|
47
|
+
else
|
48
|
+
Dir.getwd
|
49
|
+
end
|
50
|
+
|
51
|
+
process_directory sha, app_path
|
52
|
+
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# @private Recursively parse files and directories.
|
58
|
+
def self.process_directory sha, path, depth = 1
|
59
|
+
return if depth > MAX_DEPTH
|
60
|
+
|
61
|
+
pathname = Pathname.new path
|
62
|
+
pathname.each_child do |entry|
|
63
|
+
if entry.directory?
|
64
|
+
process_directory sha, entry, depth + 1
|
65
|
+
else
|
66
|
+
next unless entry.extname == ".rb" ||
|
67
|
+
entry.basename.to_s == "Gemfile.lock"
|
68
|
+
sha << "#{entry.expand_path}:#{entry.stat.size}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# Copyright 2017 Google LLC
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
require "google/cloud/debugger/breakpoint"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module Debugger
|
21
|
+
##
|
22
|
+
# # Logpoint
|
23
|
+
#
|
24
|
+
# A kind of {Google::Cloud::Debugger::Breakpoint} that can be evaluated
|
25
|
+
# to generate a formatted log string, which later can be submitted to
|
26
|
+
# Stackdriver Logging service
|
27
|
+
#
|
28
|
+
class Logpoint < Breakpoint
|
29
|
+
##
|
30
|
+
# Evaluate the breakpoint unless it's already marked as completed.
|
31
|
+
# Store evaluted expressions and stack frame variables in
|
32
|
+
# `@evaluated_expressions` and `@evaluated_log_message`.
|
33
|
+
#
|
34
|
+
# @param [Array<Binding>] call_stack_bindings An array of Ruby Binding
|
35
|
+
# objects, from the call stack that leads to the triggering of the
|
36
|
+
# breakpoints.
|
37
|
+
#
|
38
|
+
# @return [Boolean] True if evaluated successfully; false otherwise.
|
39
|
+
#
|
40
|
+
def evaluate call_stack_bindings
|
41
|
+
synchronize do
|
42
|
+
binding = call_stack_bindings[0]
|
43
|
+
|
44
|
+
return false if complete? || !check_condition(binding)
|
45
|
+
|
46
|
+
begin
|
47
|
+
evaluate_log_message binding
|
48
|
+
rescue StandardError
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# @private Evaluate the expressions and log message. Store the result
|
58
|
+
# in @evaluated_log_message
|
59
|
+
def evaluate_log_message binding
|
60
|
+
evaluated_expressions = expressions.map do |expression|
|
61
|
+
Evaluator.readonly_eval_expression binding, expression
|
62
|
+
end
|
63
|
+
|
64
|
+
@evaluated_log_message =
|
65
|
+
format_message log_message_format, evaluated_expressions
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# @private Format log message by interpolate expressions.
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
# log_point = Google::Cloud::Debugger::Logpoint.new
|
73
|
+
# log_point.format_message(
|
74
|
+
# "Hello $0", ["World"]) #=> "Hello \"World\""
|
75
|
+
#
|
76
|
+
# @param [String] message_format The message with with
|
77
|
+
# expression placeholders such as `$0`, `$1`, etc.
|
78
|
+
# @param [Array<Google::Cloud::Debugger::Breakpoint::Variable>]
|
79
|
+
# expressions An array of evaluated expression variables to be
|
80
|
+
# placed into message_format's placeholders. The variables need
|
81
|
+
# to have type equal String.
|
82
|
+
#
|
83
|
+
# @return [String] The formatted message string
|
84
|
+
#
|
85
|
+
def format_message message_format, expressions
|
86
|
+
# Substitute placeholders with expressions
|
87
|
+
message = message_format.gsub(/(?<!\$)\$\d+/) do |placeholder|
|
88
|
+
index = placeholder.match(/\$(\d+)/)[1].to_i
|
89
|
+
index < expressions.size ? expressions[index].inspect : ""
|
90
|
+
end
|
91
|
+
|
92
|
+
# Unescape "$" characters
|
93
|
+
message.gsub(/\$\$/, "$")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
# Copyright 2017 Google LLC
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
require "set"
|
17
|
+
|
18
|
+
require "google/cloud/logging/logger"
|
19
|
+
require "google/cloud/debugger/request_quota_manager"
|
20
|
+
|
21
|
+
module Google
|
22
|
+
module Cloud
|
23
|
+
module Debugger
|
24
|
+
##
|
25
|
+
# Rack Middleware implementation that supports Stackdriver Debugger Agent
|
26
|
+
# in Rack-based Ruby frameworks. It instantiates a new debugger agent if
|
27
|
+
# one isn't given already. It helps optimize Debugger Agent Tracer
|
28
|
+
# performance by suspending and resuming the tracer between each request.
|
29
|
+
#
|
30
|
+
# To use this middleware, simply install it in your Rack configuration.
|
31
|
+
# The middleware will take care of registering itself with the
|
32
|
+
# Stackdriver Debugger and activating the debugger agent. The location
|
33
|
+
# of the middleware in the middleware stack matters: breakpoints will be
|
34
|
+
# detected in middleware appearing after but not before this middleware.
|
35
|
+
#
|
36
|
+
# For best results, you should also call {Middleware.start_agents}
|
37
|
+
# during application initialization. See its documentation for details.
|
38
|
+
#
|
39
|
+
class Middleware
|
40
|
+
##
|
41
|
+
# Reset deferred start mechanism.
|
42
|
+
# @private
|
43
|
+
#
|
44
|
+
def self.reset_deferred_start
|
45
|
+
@debuggers_to_start = Set.new
|
46
|
+
end
|
47
|
+
|
48
|
+
reset_deferred_start
|
49
|
+
|
50
|
+
##
|
51
|
+
# Start the debugger. Either adds it to the deferred list, or, if the
|
52
|
+
# deferred list has already been started, starts immediately.
|
53
|
+
#
|
54
|
+
# @param [Google::Cloud::Debugger::Project] debugger
|
55
|
+
#
|
56
|
+
# @private
|
57
|
+
#
|
58
|
+
def self.deferred_start debugger
|
59
|
+
if @debuggers_to_start
|
60
|
+
@debuggers_to_start << debugger
|
61
|
+
else
|
62
|
+
debugger.start
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# This should be called once the application determines that it is safe
|
68
|
+
# to start background threads and open gRPC connections. It informs
|
69
|
+
# the middleware system that it can start debugger agents.
|
70
|
+
#
|
71
|
+
# Generally, this matters if the application forks worker processes;
|
72
|
+
# this method should be called only after workers are forked, since
|
73
|
+
# threads and network connections interact badly with fork. For
|
74
|
+
# example, when running Puma in
|
75
|
+
# [clustered mode](https://github.com/puma/puma#clustered-mode), this
|
76
|
+
# method should be called in an `on_worker_boot` block.
|
77
|
+
#
|
78
|
+
# If the application does no forking, this method can be called any
|
79
|
+
# time early in the application initialization process.
|
80
|
+
#
|
81
|
+
# If {Middleware.start_agents} is never called, the debugger agent will
|
82
|
+
# be started when the first request is received. This should be safe,
|
83
|
+
# but it will probably mean breakpoints will not be recognized during
|
84
|
+
# that first request. For best results, an application should call this
|
85
|
+
# method at the appropriate time.
|
86
|
+
#
|
87
|
+
def self.start_agents
|
88
|
+
return unless @debuggers_to_start
|
89
|
+
@debuggers_to_start.each(&:start)
|
90
|
+
@debuggers_to_start = nil
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Create a new Debugger Middleware.
|
95
|
+
#
|
96
|
+
# @param [Rack Application] app Rack application
|
97
|
+
# @param [Google::Cloud::Debugger::Project] debugger A debugger to be
|
98
|
+
# used by this middleware. If not given, will construct a new one
|
99
|
+
# using the other parameters.
|
100
|
+
# @param [Hash] kwargs Hash of configuration settings. Used for backward
|
101
|
+
# API compatibility. See the [Configuration
|
102
|
+
# Guide](https://googleapis.dev/ruby/stackdriver/latest/file.INSTRUMENTATION_CONFIGURATION.html)
|
103
|
+
# for the prefered way to set configuration parameters.
|
104
|
+
#
|
105
|
+
# @return [Google::Cloud::Debugger::Middleware] A new
|
106
|
+
# Google::Cloud::Debugger::Middleware instance
|
107
|
+
#
|
108
|
+
def initialize app, debugger: nil, **kwargs
|
109
|
+
@app = app
|
110
|
+
|
111
|
+
load_config kwargs
|
112
|
+
|
113
|
+
if debugger
|
114
|
+
@debugger = debugger
|
115
|
+
else
|
116
|
+
@debugger =
|
117
|
+
Debugger.new(project_id: configuration.project_id,
|
118
|
+
credentials: configuration.credentials,
|
119
|
+
service_name: configuration.service_name,
|
120
|
+
service_version: configuration.service_version)
|
121
|
+
|
122
|
+
@debugger.agent.quota_manager =
|
123
|
+
Google::Cloud::Debugger::RequestQuotaManager.new
|
124
|
+
end
|
125
|
+
|
126
|
+
Middleware.deferred_start @debugger
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Rack middleware entry point. In most Rack based frameworks, a request
|
131
|
+
# is served by one thread. It enables/resume the debugger breakpoints
|
132
|
+
# tracing and stops/pauses the tracing afterwards to help improve
|
133
|
+
# debugger performance.
|
134
|
+
#
|
135
|
+
# @param [Hash] env Rack environment hash
|
136
|
+
#
|
137
|
+
# @return [Rack::Response] The response from downstream Rack app
|
138
|
+
#
|
139
|
+
def call env
|
140
|
+
# Ensure the agent is running. (Note the start method is idempotent.)
|
141
|
+
@debugger.start
|
142
|
+
|
143
|
+
# Enable/resume breakpoints tracing
|
144
|
+
@debugger.agent.tracer.start
|
145
|
+
|
146
|
+
# Use Stackdriver Logger for debugger if available
|
147
|
+
if env["rack.logger"].is_a? Google::Cloud::Logging::Logger
|
148
|
+
@debugger.agent.logger = env["rack.logger"]
|
149
|
+
end
|
150
|
+
|
151
|
+
@app.call env
|
152
|
+
ensure
|
153
|
+
# Stop breakpoints tracing beyond this point
|
154
|
+
@debugger.agent.tracer.disable_traces_for_thread
|
155
|
+
|
156
|
+
# Reset quotas after each request finishes.
|
157
|
+
@debugger.agent.quota_manager.reset if @debugger.agent.quota_manager
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
##
|
163
|
+
# Consolidate configurations from various sources. Also set
|
164
|
+
# instrumentation config parameters to default values if not set
|
165
|
+
# already.
|
166
|
+
#
|
167
|
+
def load_config **kwargs
|
168
|
+
project_id = kwargs[:project] || kwargs[:project_id]
|
169
|
+
configuration.project_id = project_id unless project_id.nil?
|
170
|
+
|
171
|
+
creds = kwargs[:credentials] || kwargs[:keyfile]
|
172
|
+
configuration.credentials = creds unless creds.nil?
|
173
|
+
|
174
|
+
service_name = kwargs[:service_name]
|
175
|
+
configuration.service_name = service_name unless service_name.nil?
|
176
|
+
|
177
|
+
service_vers = kwargs[:service_version]
|
178
|
+
configuration.service_version = service_vers unless service_vers.nil?
|
179
|
+
|
180
|
+
init_default_config
|
181
|
+
end
|
182
|
+
|
183
|
+
##
|
184
|
+
# Fallback to default configuration values if not defined already
|
185
|
+
def init_default_config
|
186
|
+
configuration.project_id ||= Debugger.default_project_id
|
187
|
+
configuration.credentials ||= Debugger.default_credentials
|
188
|
+
configuration.service_name ||= Debugger.default_service_name
|
189
|
+
configuration.service_version ||= Debugger.default_service_version
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# @private Get Google::Cloud::Debugger.configure
|
194
|
+
def configuration
|
195
|
+
Google::Cloud::Debugger.configure
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|