class Google::Cloud::Trace::Middleware
A Rack middleware that manages trace context and captures a trace of the request. Specifically, it:
-
Reads the trace context from the request headers, if present. Otherwise, generates a new trace context.
-
Makes a sampling decision if one is not already specified.
-
Records a span measuring the entire handling of the request, annotated with a set of standard request data.
-
Makes the trace context available so downstream middlewares and the app can add further spans to the trace.
-
Sends the completed trace to the Stackdriver service.
## Installing
To use this middleware, simply install it in your middleware stack. Here is an example Sinatra application that includes the Trace
middleware:
“`ruby # Simple sinatra application
require “sinatra” require “google/cloud/trace”
use Google::Cloud::Trace::Middleware
get “/” do
"Hello World!"
end “`
Here is an example `config.ru` file for a web application that uses the standard Rack configuration mechanism.
“`ruby # config.ru for simple Rack application
require “google/cloud/trace” use Google::Cloud::Trace::Middleware
run MyApp “`
If your application uses Ruby On Rails, you may also use the provided {Google::Cloud::Trace::Railtie} for close integration with Rails and ActiveRecord.
## Custom measurements
By default, this middleware creates traces that measure just the http request handling as a whole. If you want to provide more detailed measurements of smaller processes, use the classes provided in this library. Below is a Sinatra example to get you started.
“`ruby # Simple sinatra application
require “sinatra” require “google/cloud/trace”
use Google::Cloud::Trace::Middleware
get “/” do
Google::Cloud::Trace.in_span "Sleeping on the job!" do sleep rand end "Hello World!"
end “`
## Error handling
An error encountered during the reporting of traces by the middleware can be handled using a Proc set in the `on_error` configuration. (See {Google::Cloud::Trace.configure}.) The Proc must take the error object as the single argument.
“`ruby # Configure error handling
require “sinatra” require “google/cloud/trace” require “google/cloud/error_reporting”
Google::Cloud::Trace.configure
do |config|
config.on_error = lambda do |error| Google::Cloud::ErrorReporting.report error end
end
use Google::Cloud::Trace::Middleware
get “/” do
Google::Cloud::Trace.in_span "Sleeping on the job!" do sleep rand end "Hello World!"
end “`
## Sampling and blacklisting
A sampler makes the decision whether to record a trace for each request (if the decision was not made by the context, e.g. by providing a request header). By default, this sampler is the default {Google::Cloud::Trace::TimeSampler}, which enforces a maximum QPS per process, and blacklists a small number of request paths such as health checks sent by Google
App Engine. You may adjust this behavior by providing an alternate sampler. See {Google::Cloud::Trace::TimeSampler}.
Constants
- AGENT_NAME
The name of this trace agent as reported to the Stackdriver backend.
Public Class Methods
Create a new Middleware
for traces
@param [Rack Application] app Rack application @param [Google::Cloud::Trace::Service, AsyncReporter] service
The service object to update traces. Optional if running on GCE.
@param [Hash] kwargs Hash of configuration settings. Used for backward
API compatibility. See the {file:INSTRUMENTATION.md Instrumentation Guide} and [Configuration Guide](https://googleapis.dev/ruby/stackdriver/latest/file.INSTRUMENTATION_CONFIGURATION.html) for the prefered way to set configuration parameters.
# File lib/google/cloud/trace/middleware.rb, line 154 def initialize app, service: nil, **kwargs @app = app load_config(**kwargs) if service @service = service else project_id = configuration.project_id if project_id credentials = configuration.credentials tracer = Google::Cloud::Trace.new project_id: project_id, credentials: credentials @service = Google::Cloud::Trace::AsyncReporter.new tracer.service end end end
Public Instance Methods
Implementation of the trace middleware. Creates a trace for this request, populates it with a root span for the entire request, and ensures it is reported back to Stackdriver.
@param [Hash] env Rack environment hash @return [Rack::Response] The response from downstream Rack app
# File lib/google/cloud/trace/middleware.rb, line 181 def call env trace = create_trace env begin Google::Cloud::Trace.set trace Google::Cloud::Trace.in_span "rack-request" do |span| configure_span span, env result = @app.call env configure_result span, result result end ensure Google::Cloud::Trace.set nil send_trace trace, env end end
Performs post-request tasks, including adding result-dependent labels to the root span, and adding trace context headers to the HTTP response.
@private @param [Google::Cloud::Trace::TraceSpan] span The root span to
configure.
@param [Array] result The Rack response.
# File lib/google/cloud/trace/middleware.rb, line 373 def configure_result span, result if result.is_a?(::Array) && result.size == 3 span.labels[Google::Cloud::Trace::LabelKey::HTTP_STATUS_CODE] = result[0].to_s result[1]["X-Cloud-Trace-Context"] = span.trace.trace_context.to_string end result end
Configures the root span for this request. This may be called before the request is actually handled because it doesn't depend on the result.
@private @param [Google::Cloud::Trace::TraceSpan] span The root span to
configure.
@param [Hash] env Rack environment hash
# File lib/google/cloud/trace/middleware.rb, line 302 def configure_span span, env span.name = get_path env set_basic_labels span.labels, env set_extended_labels span.labels, span.trace.trace_context.capture_stack? span end
Create a new trace for this request.
@private @param [Hash] env The Rack environment.
# File lib/google/cloud/trace/middleware.rb, line 227 def create_trace env trace_context = get_trace_context env Google::Cloud::Trace::TraceRecord.new \ @service.project, trace_context, span_id_generator: configuration.span_id_generator end
Gets the URI hostname from the given Rack environment.
@private @param [Hash] env Rack environment hash @return [String] The hostname.
# File lib/google/cloud/trace/middleware.rb, line 271 def get_host env env["HTTP_HOST"] || env["SERVER_NAME"] end
Gets the URI path from the given Rack environment.
@private @param [Hash] env Rack environment hash @return [String] The URI path.
# File lib/google/cloud/trace/middleware.rb, line 258 def get_path env path = "#{env['SCRIPT_NAME']}#{env['PATH_INFO']}" path = "/#{path}" unless path.start_with? "/" path end
Gets the current trace context from the given Rack environment. Makes a sampling decision if one has not been made already.
@private @param [Hash] env Rack environment hash @return [Stackdriver::Core::TraceContext] The trace context.
# File lib/google/cloud/trace/middleware.rb, line 205 def get_trace_context env Stackdriver::Core::TraceContext.parse_rack_env env do |tc| if tc.sampled?.nil? sampler = configuration.sampler || Google::Cloud::Trace::TimeSampler.default sampled = sampler.call env tc = Stackdriver::Core::TraceContext.new \ trace_id: tc.trace_id, span_id: tc.span_id, sampled: sampled, capture_stack: sampled && configuration.capture_stack end tc end end
Gets the full URL from the given Rack environment.
@private @param [Hash] env Rack environment hash @return [String] The URL.
# File lib/google/cloud/trace/middleware.rb, line 282 def get_url env path = get_path env host = get_host env scheme = env["rack.url_scheme"] query_string = env["QUERY_STRING"].to_s url = "#{scheme}://#{host}#{path}" url = "#{url}?#{query_string}" unless query_string.empty? url end
Send the given trace to the trace service, if requested.
@private @param [Google::Cloud::Trace::TraceRecord] trace The trace to send. @param [Hash] env The Rack environment.
# File lib/google/cloud/trace/middleware.rb, line 242 def send_trace trace, env return unless @service && trace.trace_context.sampled? begin @service.patch_traces trace rescue StandardError => e handle_error e, logger: env["rack.logger"] end end
Configures standard labels. @private
# File lib/google/cloud/trace/middleware.rb, line 314 def set_basic_labels labels, env set_label labels, Google::Cloud::Trace::LabelKey::AGENT, AGENT_NAME set_label labels, Google::Cloud::Trace::LabelKey::HTTP_HOST, get_host(env) set_label labels, Google::Cloud::Trace::LabelKey::HTTP_METHOD, env["REQUEST_METHOD"] set_label labels, Google::Cloud::Trace::LabelKey::HTTP_CLIENT_PROTOCOL, env["SERVER_PROTOCOL"] set_label labels, Google::Cloud::Trace::LabelKey::HTTP_USER_AGENT, env["HTTP_USER_AGENT"] set_label labels, Google::Cloud::Trace::LabelKey::HTTP_URL, get_url(env) set_label labels, Google::Cloud::Trace::LabelKey::PID, ::Process.pid.to_s set_label labels, Google::Cloud::Trace::LabelKey::TID, ::Thread.current.object_id.to_s end
Configures stack and gae labels. @private
# File lib/google/cloud/trace/middleware.rb, line 337 def set_extended_labels labels, capture_stack if capture_stack Google::Cloud::Trace::LabelKey.set_stack_trace labels, skip_frames: 3 end if Google::Cloud.env.app_engine? set_label labels, Google::Cloud::Trace::LabelKey::GAE_APP_MODULE, Google::Cloud.env.app_engine_service_id set_label labels, Google::Cloud::Trace::LabelKey::GAE_APP_MODULE_VERSION, Google::Cloud.env.app_engine_service_version end end
Sets the given label if the given value is a proper string.
@private @param [Hash] labels The labels hash. @param [String] key The key of the label to set. @param [Object] value The value to set.
# File lib/google/cloud/trace/middleware.rb, line 359 def set_label labels, key, value labels[key] = value if value.is_a? ::String end
Private Instance Methods
@private Get Google::Cloud::Trace.configure
# File lib/google/cloud/trace/middleware.rb, line 413 def configuration Google::Cloud::Trace.configure end
@private Get the error callback from the configuration. This value is memoized to reduce calls to the configuration.
# File lib/google/cloud/trace/middleware.rb, line 420 def error_callback if @error_callback.nil? @error_callback = :unset configuration_callback = configuration.on_error configuration_callback ||= Cloud.configure.on_error @error_callback = configuration_callback if configuration_callback end return nil if @error_callback == :unset @error_callback end
@private Handle errors raised when making patch_traces API calls.
# File lib/google/cloud/trace/middleware.rb, line 434 def handle_error error, logger: nil # Use on_error from configuration if error_callback error_callback.call error else # log error msg = "Transmit to Stackdriver Trace failed: #{error.inspect}" if logger logger.error msg else warn msg end end end
Fallback to default configuration values if not defined already
# File lib/google/cloud/trace/middleware.rb, line 405 def init_default_config configuration.project_id ||= Trace.default_project_id configuration.credentials ||= Cloud.configure.credentials configuration.capture_stack ||= false end
Consolidate configurations from various sources. Also set instrumentation config parameters to default values if not set already.
# File lib/google/cloud/trace/middleware.rb, line 390 def load_config **kwargs capture_stack = kwargs[:capture_stack] configuration.capture_stack = capture_stack unless capture_stack.nil? sampler = kwargs[:sampler] configuration.sampler = sampler unless sampler.nil? generator = kwargs[:span_id_generator] configuration.span_id_generator = generator unless generator.nil? init_default_config end