Skip to content

Commit

Permalink
Trace sampler interface is now just a Proc
Browse files Browse the repository at this point in the history
  • Loading branch information
dazuma authored and blowmage committed Dec 21, 2016
1 parent b1c31bc commit b83c2a6
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 110 deletions.
8 changes: 7 additions & 1 deletion google-cloud-trace/lib/google/cloud/trace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
require "google/cloud/trace/notifications"
require "google/cloud/trace/project"
require "google/cloud/trace/result_set"
require "google/cloud/trace/sampling"
require "google/cloud/trace/service"
require "google/cloud/trace/span"
require "google/cloud/trace/span_kind"
require "google/cloud/trace/time_sampler"
require "google/cloud/trace/trace_record"
require "google/cloud/trace/utils"

Expand Down Expand Up @@ -69,6 +69,12 @@ module Cloud
# Flexible and Google Container Engine to provide additional information
# for applications hosted in those environments.
#
# Note that not all requests will have traces. By default, the library will
# sample about one trace every ten seconds per Ruby process, to prevent
# heavily used applications from reporting too much data. It will also
# omit certain requests used by Google App Engine for health checking. See
# {Google::Cloud::Trace::TimeSampler} for more details.
#
# ### Using instrumentation with Ruby on Rails
#
# To install application instrumentation in your Ruby on Rails app, add
Expand Down
25 changes: 5 additions & 20 deletions google-cloud-trace/lib/google/cloud/trace/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,6 @@ module Trace
# ```
#
class Middleware
##
# Default list of paths for which to disable traces. Currently includes
# App Engine Flex health checks.
DEFAULT_PATH_BLACKLIST = ["/_ah/health"].freeze

##
# The name of this trace agent as reported to the Stackdriver backend.
AGENT_NAME = "ruby #{Google::Cloud::Trace::VERSION}".freeze
Expand All @@ -102,22 +97,18 @@ class Middleware
# @param [Rack Application] app Rack application
# @param [Google::Cloud::Trace::Service] service The service object.
# Optional if running on GCE.
# @param [Array{String,Regex}] path_blacklist An array of paths or
# path patterns indicating URIs that should never be traced.
# Default is DEFAULT_PATH_BLACKLIST.
# @param [Boolean] capture_stack Whether to capture stack traces for
# each span. Default is false.
# @param [#check] sampler A sampler to use, or nil to use the default.
# See {Google::Cloud::Trace.sampler=}
# @param [Proc] sampler A sampler to use, or `nil` to use the default.
# See {Google::Cloud::Trace::TimeSampler}. Note that the sampler
# may be any Proc that implements the sampling contract.
#
def initialize app,
service: nil,
path_blacklist: nil,
capture_stack: false,
sampler: nil,
span_id_generator: nil
@app = app
@path_blacklist = path_blacklist || DEFAULT_PATH_BLACKLIST
@capture_stack = capture_stack
@sampler = sampler
@span_id_generator = span_id_generator
Expand Down Expand Up @@ -164,14 +155,8 @@ def call env
def get_trace_context env
Stackdriver::Core::TraceContext.parse_rack_env(env) do |tc|
if tc.sampled?.nil?
path = get_path env
if @path_blacklist.find { |p| p === path }
sampled = false
elsif @sampler
sampled = @sampler.check {}
else
sampled = Google::Cloud::Trace.check_sampler
end
sampler = @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,
Expand Down
79 changes: 0 additions & 79 deletions google-cloud-trace/lib/google/cloud/trace/sampling.rb

This file was deleted.

99 changes: 99 additions & 0 deletions google-cloud-trace/lib/google/cloud/trace/time_sampler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

module Google
module Cloud
module Trace
##
# A sampler determines whether a given request's latency trace should
# actually be reported. It is usually not necessary to trace every
# request, especially for an application serving heavy traffic. You may
# use a sampler to decide, for a given request, whether to report its
# trace. A sampler is simply a Proc that takes the Rack environment as an
# argument and returns a boolean indicating whether or not to sample the
# current request. Alternately, it could be an object that duck-types the
# Proc interface by implementing the `call` method.
#
# TimeSampler is the default sampler. It enforces a certain QPS by
# delaying a minimum time between each sample. It also supports
# blacklisting certain URI paths that should never be traced.
#
class TimeSampler
##
# Default list of paths for which to disable traces. Currently includes
# App Engine Flex health checks.
DEFAULT_PATH_BLACKLIST = ["/_ah/health"].freeze

##
# Create a TimeSampler for the given QPS.
#
# @param [Number] qps Samples per second. Default is 0.1.
# @param [Array{String,Regex}] path_blacklist An array of paths or
# path patterns indicating URIs that should never be traced.
# Default is DEFAULT_PATH_BLACKLIST.
#
def initialize qps: 0.1, path_blacklist: DEFAULT_PATH_BLACKLIST
@delay_secs = 1.0 / qps
@last_time = ::Time.now.to_f - @delay_secs
@path_blacklist = path_blacklist
end

@default = new

##
# Get the default global TimeSampler.
#
# @return [TimeSampler]
#
def self.default
@default
end

##
# Implements the sampler contract. Checks to see whether a sample
# should be taken at this time.
#
# @param [Hash] env Rack environment.
# @return [Boolean] Whether to sample at this time.
#
def call env
return false if path_blacklisted? env
time = ::Time.now.to_f
delays = (time - @last_time) / @delay_secs
if delays >= 2.0
@last_time = time - @delay_secs
true
elsif delays >= 1.0
@last_time += @delay_secs
true
else
false
end
end

##
# Determines if the URI path in the given Rack environment is
# blacklisted in this sampler.
#
# @private
#
def path_blacklisted? env
path = "#{env['SCRIPT_NAME']}#{env['PATH_INFO']}"
path = "/#{path}" unless path.start_with? "/"
@path_blacklist.find { |p| p === path }
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -16,56 +16,63 @@

describe Google::Cloud::Trace::TimeSampler do
let(:start_time) { ::Time.at(12345678) }
let(:env) { {} }

def sampler
::Time.stub :now, start_time do
Google::Cloud::Trace::TimeSampler.new
end
end

describe ".check" do
describe ".call" do
it "samples the first time called" do
sam = Google::Cloud::Trace::TimeSampler.new
sam.check.must_equal true
sam.call(env).must_equal true
end

it "omits the default blacklisted path" do
sam = Google::Cloud::Trace::TimeSampler.new
blacklisted_env = { "PATH_INFO" => "/_ah/health" }
sam.call(blacklisted_env).must_equal false
end

it "doesn't sample when called too soon" do
sam = sampler
::Time.stub :now, start_time - 1 do
sam.check.must_equal false
sam.call(env).must_equal false
end
end

it "samples when called after a suitable delay" do
sam = sampler
::Time.stub :now, start_time + 1 do
sam.check.must_equal true
sam.call(env).must_equal true
end
end

it "advances last sampling time" do
sam = sampler
::Time.stub :now, start_time + 3 do
sam.check.must_equal true
sam.call(env).must_equal true
end
::Time.stub :now, start_time + 9 do
sam.check.must_equal false
sam.call(env).must_equal false
end
::Time.stub :now, start_time + 11 do
sam.check.must_equal true
sam.call(env).must_equal true
end
end

it "advances last sampling time after a large gap" do
sam = sampler
::Time.stub :now, start_time + 30 do
sam.check.must_equal true
sam.call(env).must_equal true
end
::Time.stub :now, start_time + 31 do
sam.check.must_equal true
sam.call(env).must_equal true
end
::Time.stub :now, start_time + 32 do
sam.check.must_equal false
sam.call(env).must_equal false
end
end
end
Expand Down

0 comments on commit b83c2a6

Please sign in to comment.