Skip to content

Commit

Permalink
Merge pull request #12 from haotianw465/master
Browse files Browse the repository at this point in the history
Service sampling rules support
  • Loading branch information
haotianw465 authored Sep 25, 2018
2 parents f6f4718 + 3e6910b commit 1869ca5
Show file tree
Hide file tree
Showing 37 changed files with 1,143 additions and 226 deletions.
1 change: 1 addition & 0 deletions .yardopts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
--title 'AWS X-Ray SDK for Ruby'
--markup markdown
--template-path doc-src/templates
--no-progress
- LICENSE
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
0.11.0 (2018-09-25)
-------------------
* **Breaking**: The default sampler now launches background tasks to poll sampling rules from X-Ray service. See more details on how to create sampling rules: https://docs.aws.amazon.com/xray/latest/devguide/xray-console-sampling.html.
* **Breaking**: The sampling modules related to local sampling rules have been renamed and moved to `sampling/local` namespace.
* **Breaking**: The default json serializer is switched to `multi_json` for experimental JRuby support. Now you need to specify `oj` or `jrjackson` in Gemfile. [PR#5](https://github.com/aws/aws-xray-sdk-ruby/pull/5)
* **Breaking**: The SDK now requires `aws-sdk-xray` >= `1.4.0`.
* Feature: Environment variable `AWS_XRAY_DAEMON_ADDRESS` now takes an additional notation in `tcp:127.0.0.1:2000 udp:127.0.0.2:2001` to set TCP and UDP destination separately. By default it assumes a X-Ray daemon listening to both UDP and TCP traffic on `127.0.0.1:2000`.
* Bugfix - Call only once if `current_entity` is nil. [PR#9](https://github.com/aws/aws-xray-sdk-ruby/pull/9)

0.10.2 (2018-03-30)
-------------------
* Feature - Added SDK and Ruby runtime information to sampled segments.
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,15 @@ The [API Reference](http://docs.aws.amazon.com/xray-sdk-for-ruby/latest/referenc
```ruby
require 'aws-xray-sdk'

# configure path based sampling rules in case of web app
# For configuring sampling rules through X-Ray service
# please see https://docs.aws.amazon.com/xray/latest/devguide/xray-console-sampling.html.
# The doc below defines local fallback sampling rules which has lower priority.
my_sampling_rules = {
version: 1,
version: 2,
rules: [
{
description: 'healthcheck',
service_name: '*',
host: '*',
http_method: 'GET',
url_path: '/ping',
fixed_target: 0,
Expand Down
1 change: 1 addition & 0 deletions aws-xray-sdk.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']

spec.add_dependency 'aws-sdk-xray', '~> 1.4.0'
spec.add_dependency 'multi_json', '~> 1'

spec.add_development_dependency 'aws-sdk-dynamodb', '~> 1'
Expand Down
6 changes: 6 additions & 0 deletions doc-src/templates/default/layout/html/footer.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div id="footer">
Generated on <%= Time.now.strftime("%c") %> by
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
<%= YARD::VERSION %> (ruby-<%= RUBY_VERSION %>).
<script src="/SdkStatic/sdk-priv.js" async="true"></script>
</div>
3 changes: 3 additions & 0 deletions doc-src/templates/default/setup.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def javascripts
(super + %w(js/tabs.js)).uniq
end
17 changes: 10 additions & 7 deletions lib/aws-xray-sdk/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require 'aws-xray-sdk/patcher'
require 'aws-xray-sdk/emitter/default_emitter'
require 'aws-xray-sdk/context/default_context'
require 'aws-xray-sdk/daemon_config'
require 'aws-xray-sdk/sampling/default_sampler'
require 'aws-xray-sdk/streaming/default_streamer'
require 'aws-xray-sdk/segment_naming/dynamic_naming'
Expand All @@ -17,9 +18,9 @@ class Configuration
include Patcher

SEGMENT_NAME_KEY = 'AWS_XRAY_TRACING_NAME'.freeze
CONFIG_KEY = %I[logger name sampling plugins daemon_address segment_naming
naming_pattern emitter streamer context context_missing
sampling_rules stream_threshold patch].freeze
CONFIG_KEY = %I[logger name sampling plugins daemon_address
segment_naming naming_pattern emitter streamer context
context_missing sampling_rules stream_threshold patch].freeze

def initialize
@name = ENV[SEGMENT_NAME_KEY]
Expand All @@ -38,9 +39,12 @@ def name=(v)
@name = ENV[SEGMENT_NAME_KEY] || v
end

# proxy method to the emitter's daemon_address config.
# setting daemon address for components communicate with X-Ray daemon.
def daemon_address=(v)
emitter.daemon_address = v
v = ENV[DaemonConfig::DAEMON_ADDRESS_KEY] || v
config = DaemonConfig.new(v)
emitter.daemon_config = config
sampler.daemon_config = config if sampler.respond_to?(:daemon_config=)
end

# proxy method to the context's context_missing config.
Expand All @@ -63,8 +67,7 @@ def naming_pattern=(v)
segment_naming.pattern = v
end

# makes a sampling decision based on internal configure, e.g.
# if sampling enabled and the default sampling rule.
# makes a sampling decision without incoming filters.
def sample?
return true unless sampling
sampler.sample?
Expand Down
59 changes: 59 additions & 0 deletions lib/aws-xray-sdk/daemon_config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
require 'aws-xray-sdk/exceptions'

module XRay
# The class that stores X-Ray daemon configuration about
# the ip address and port for UDP and TCP port. It gets the address
# string from `AWS_XRAY_DAEMON_ADDRESS` and then from recorder's
# configuration for `daemon_address`.
# A notation of `127.0.0.1:2000` or `tcp:127.0.0.1:2000 udp:127.0.0.2:2001`
# are both acceptable. The former one means UDP and TCP are running at
# the same address. By default it assumes a X-Ray daemon
# running at `127.0.0.1:2000` listening to both UDP and TCP traffic.
class DaemonConfig
DAEMON_ADDRESS_KEY = 'AWS_XRAY_DAEMON_ADDRESS'.freeze
attr_reader :tcp_ip, :tcp_port, :udp_ip, :udp_port
@@dafault_addr = '127.0.0.1:2000'

def initialize(addr: @@dafault_addr)
update_address(addr)
end

def update_address(v)
v = ENV[DAEMON_ADDRESS_KEY] || v
update_addr(v)
rescue StandardError
raise InvalidDaemonAddressError, %(Invalid X-Ray daemon address specified: #{v}.)
end

private

def update_addr(v)
parts = v.split(' ')
if parts.length == 1 # format of '127.0.0.1:2000'
addr = parts[0].split(':')
raise InvalidDaemonAddressError unless addr.length == 2
@tcp_ip = addr[0]
@tcp_port = addr[1].to_i
@udp_ip = addr[0]
@udp_port = addr[1].to_i
else
set_tcp_udp(parts) # format of 'tcp:127.0.0.1:2000 udp:127.0.0.2:2001'
end
end

def set_tcp_udp(parts)
part1 = parts[0]
part2 = parts[1]
key1 = part1.split(':')[0]
key2 = part2.split(':')[0]
addr_h = {}
addr_h[key1] = part1.split(':')
addr_h[key2] = part2.split(':')

@tcp_ip = addr_h['tcp'][1]
@tcp_port = addr_h['tcp'][2].to_i
@udp_ip = addr_h['udp'][1]
@udp_port = addr_h['udp'][2].to_i
end
end
end
31 changes: 10 additions & 21 deletions lib/aws-xray-sdk/emitter/default_emitter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require 'aws-xray-sdk/logger'
require 'aws-xray-sdk/emitter/emitter'
require 'aws-xray-sdk/exceptions'
require 'aws-xray-sdk/daemon_config'

module XRay
# The default emitter the X-Ray recorder uses to send segments/subsegments
Expand All @@ -10,12 +11,11 @@ class DefaultEmitter
include Emitter
include Logging

attr_reader :address
attr_reader :daemon_config

def initialize
def initialize(daemon_config: DaemonConfig.new)
@socket = UDPSocket.new
@address = ENV[DAEMON_ADDRESS_KEY] || '127.0.0.1:2000'
configure_socket(@address)
self.daemon_config = daemon_config
end

# Serializes a segment/subsegment and sends it to the X-Ray daemon
Expand All @@ -25,29 +25,18 @@ def send_entity(entity:)
return nil unless entity.sampled
begin
payload = %(#{@@protocol_header}#{@@protocol_delimiter}#{entity.to_json})
logger.debug %(sending payload #{payload} to daemon at #{address}.)
logger.debug %(sending payload #{payload} to daemon at #{@address}.)
@socket.send payload, 0
rescue StandardError => e
logger.warn %(failed to send payload due to #{e.message})
end
end

def daemon_address=(v)
v = ENV[DAEMON_ADDRESS_KEY] || v
@address = v
configure_socket(v)
end

private

def configure_socket(v)
begin
addr = v.split(':')
host, ip = addr[0], addr[1].to_i
@socket.connect(host, ip)
rescue StandardError
raise InvalidDaemonAddressError, %(Invalid X-Ray daemon address specified: #{v}.)
end
def daemon_config=(v)
@address = %(#{v.udp_ip}:#{v.udp_port})
@socket.connect(v.udp_ip, v.udp_port)
rescue StandardError
raise InvalidDaemonAddressError, %(Invalid X-Ray daemon address specified: #{v}.)
end
end
end
4 changes: 1 addition & 3 deletions lib/aws-xray-sdk/emitter/emitter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ module XRay
# The emitter interface the X-Ray recorder uses to send segments/subsegments
# to the X-Ray daemon over UDP.
module Emitter
DAEMON_ADDRESS_KEY = 'AWS_XRAY_DAEMON_ADDRESS'.freeze

@@protocol_header = {
format: 'json',
version: 1
Expand All @@ -17,7 +15,7 @@ def send_entity(entity:)
raise 'Not implemented'
end

def daemon_address=(v)
def daemon_config=(v)
raise 'Not implemented'
end
end
Expand Down
16 changes: 12 additions & 4 deletions lib/aws-xray-sdk/facets/aws_sdk.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@ class Handler < Seahorse::Client::Handler
include XRay::Facets::Helper

def call(context)
operation = context.operation_name
service_name = context.client.class.api.metadata['serviceAbbreviation'] ||
context.client.class.to_s.split('::')[1]
if skip?(service_name, operation)
return super
end

recorder = Aws.config[:xray_recorder]
if recorder.current_entity.nil?
if recorder.nil? || recorder.current_entity.nil?
return super
end

operation = context.operation_name
service_name = context.client.class.api.metadata['serviceAbbreviation'] ||
context.client.class.to_s.split('::')[1]
recorder.capture(service_name, namespace: 'aws') do |subsegment|
# inject header string before calling downstream AWS services
context.http_request.headers[TRACE_HEADER] = prep_header_str entity: subsegment
Expand Down Expand Up @@ -109,6 +113,10 @@ def process_descriptor(target:, descriptor:, meta:)
v = target.keys if descriptor[:get_keys]
meta[descriptor[:rename_to]] = v
end

def skip?(service, op)
return service == 'XRay' && (op == :get_sampling_rules || op == :get_sampling_targets)
end
end
end

Expand Down
15 changes: 4 additions & 11 deletions lib/aws-xray-sdk/facets/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,14 @@ def construct_header(headers:)
# the highest precedence. If the `trace_header` doesn't contain
# sampling decision then it checks if sampling is enabled or not
# in the recorder. If not enbaled it returns 'true'. Otherwise it uses
# sampling rule to decide.
def should_sample?(header_obj:, recorder:,
host: nil, method: nil, path: nil,
**args)
# sampling rules to decide.
def should_sample?(header_obj:, recorder:, sampling_req:, **args)
# check outside decision
if i = header_obj.sampled
if i.zero?
false
else
true
end
!i.zero?
# check sampling rules
elsif recorder.sampling_enabled?
recorder.sampler.sample_request?(service_name: host, url_path: path,
http_method: method)
recorder.sampler.sample_request?(sampling_req)
# sample if no reason not to
else
true
Expand Down
4 changes: 4 additions & 0 deletions lib/aws-xray-sdk/facets/net_http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ def initialize(*options)
end

def request(req, body = nil, &block)
if req.path && (req.path == ('/GetSamplingRules') || req.path == ('/SamplingTargets'))
return super
end

entity = XRay.recorder.current_entity
capture = !(entity && entity.namespace && entity.namespace == 'aws'.freeze)
if started? && capture && entity
Expand Down
9 changes: 4 additions & 5 deletions lib/aws-xray-sdk/facets/rack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,15 @@ def call(env)
host = req.host
url_path = req.path
method = req.request_method
# get segment name from host header if applicable
seg_name = @recorder.segment_naming.provide_name(host: req.host)

# get sampling decision
sampled = should_sample?(
header_obj: header, recorder: @recorder,
host: host, method: method, path: url_path
header_obj: header, recorder: @recorder, sampling_req:
{ host: host, http_method: method, url_path: url_path, service: seg_name }
)

# get segment name from host header if applicable
seg_name = @recorder.segment_naming.provide_name(host: req.host)

# begin the segment
segment = @recorder.begin_segment seg_name, trace_id: header.root, parent_id: header.parent_id,
sampled: sampled
Expand Down
4 changes: 4 additions & 0 deletions lib/aws-xray-sdk/model/dummy_entities.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ def aws=(v)
# no-op
end

def sampling_rule_name=(v)
# no-op
end

def to_h
# no-op
end
Expand Down
9 changes: 8 additions & 1 deletion lib/aws-xray-sdk/model/segment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ module XRay
# details about the request, and details about the work done.
class Segment
include Entity
attr_accessor :ref_counter, :subsegment_size, :origin, :user, :service
attr_accessor :ref_counter, :subsegment_size, :origin,
:user, :service

# @param [String] trace_id Manually crafted trace id.
# @param [String] name Must be specified either on object creation or
Expand Down Expand Up @@ -39,6 +40,12 @@ def remove_subsegment(subsegment:)
@subsegment_size = subsegment_size - subsegment.all_children_count - 1
end

def sampling_rule_name=(v)
@aws ||= {}
@aws[:xray] ||= {}
@aws[:xray][:sampling_rule_name] = v
end

def decrement_ref_counter
@ref_counter -= 1
end
Expand Down
Loading

0 comments on commit 1869ca5

Please sign in to comment.