aws-xray-sdk 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/lib/aws-xray-sdk/configuration.rb +10 -7
  3. data/lib/aws-xray-sdk/daemon_config.rb +59 -0
  4. data/lib/aws-xray-sdk/emitter/default_emitter.rb +10 -21
  5. data/lib/aws-xray-sdk/emitter/emitter.rb +1 -3
  6. data/lib/aws-xray-sdk/facets/aws_sdk.rb +14 -6
  7. data/lib/aws-xray-sdk/facets/helper.rb +4 -11
  8. data/lib/aws-xray-sdk/facets/net_http.rb +4 -0
  9. data/lib/aws-xray-sdk/facets/rack.rb +8 -6
  10. data/lib/aws-xray-sdk/facets/rails/railtie.rb +1 -1
  11. data/lib/aws-xray-sdk/model/cause.rb +2 -2
  12. data/lib/aws-xray-sdk/model/dummy_entities.rb +4 -0
  13. data/lib/aws-xray-sdk/model/entity.rb +2 -2
  14. data/lib/aws-xray-sdk/model/metadata.rb +2 -2
  15. data/lib/aws-xray-sdk/model/segment.rb +8 -1
  16. data/lib/aws-xray-sdk/plugins/elastic_beanstalk.rb +2 -2
  17. data/lib/aws-xray-sdk/recorder.rb +7 -4
  18. data/lib/aws-xray-sdk/sampling/connector.rb +72 -0
  19. data/lib/aws-xray-sdk/sampling/default_sampler.rb +72 -78
  20. data/lib/aws-xray-sdk/sampling/lead_poller.rb +72 -0
  21. data/lib/aws-xray-sdk/sampling/local/reservoir.rb +35 -0
  22. data/lib/aws-xray-sdk/sampling/local/sampler.rb +110 -0
  23. data/lib/aws-xray-sdk/sampling/local/sampling_rule.rb +63 -0
  24. data/lib/aws-xray-sdk/sampling/reservoir.rb +68 -24
  25. data/lib/aws-xray-sdk/sampling/rule_cache.rb +86 -0
  26. data/lib/aws-xray-sdk/sampling/rule_poller.rb +39 -0
  27. data/lib/aws-xray-sdk/sampling/sampler.rb +3 -3
  28. data/lib/aws-xray-sdk/sampling/sampling_decision.rb +8 -0
  29. data/lib/aws-xray-sdk/sampling/sampling_rule.rb +102 -35
  30. data/lib/aws-xray-sdk/version.rb +1 -1
  31. metadata +34 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f2d9c2d4965cb1d4635d4e6a735f514c67adb2af4dbf26bb8188c162f6c1c0e
4
- data.tar.gz: b8dc27fa2569bab52976d702e3d5b1112f29b614038b469defb8959fa6feab69
3
+ metadata.gz: 91c311c0f94185bc3f4a17afbfde8f7a4f881503feeb7eb116a43631c6649efc
4
+ data.tar.gz: 4a2791948dc071dfa2e57f410eb60311ff812aa276f0968dfc85db28632a2565
5
5
  SHA512:
6
- metadata.gz: f63ce08cbdf5d6d913966c3485c4e75b444c8a1866ede982a8e2950b251a05c83b35351c46ff64749ccc4d74f2a6dc9950f10bd23f3a5e6bcf8cefa764ac87ff
7
- data.tar.gz: 3a6a2183e526b5dbabe8d36e540cfefe20bc0f93d2ab8da8a77e2c6060bd91b8deda2bf0c93f13f4f5ab286230b509774a08f50059001fa328ab8b095d18e009
6
+ metadata.gz: 7c9caa633a61621f6c17a281c416855aef63033f8c8119589c0c1bff70218c80f7b84234251f97be5a7cf5b3d39a1d0dc8c54fb8c619ec46c9ab37aabb323bf8
7
+ data.tar.gz: 42cce4783e4dfa257317a1c3b6bca7fd52e2b4b1521b313048b1c8e1037a9bb85596e2b70a15fca54b15777fc941b380114bf5deec452062a1ea5bc73ac4b4ef
@@ -2,6 +2,7 @@ require 'aws-xray-sdk/exceptions'
2
2
  require 'aws-xray-sdk/patcher'
3
3
  require 'aws-xray-sdk/emitter/default_emitter'
4
4
  require 'aws-xray-sdk/context/default_context'
5
+ require 'aws-xray-sdk/daemon_config'
5
6
  require 'aws-xray-sdk/sampling/default_sampler'
6
7
  require 'aws-xray-sdk/streaming/default_streamer'
7
8
  require 'aws-xray-sdk/segment_naming/dynamic_naming'
@@ -17,9 +18,9 @@ module XRay
17
18
  include Patcher
18
19
 
19
20
  SEGMENT_NAME_KEY = 'AWS_XRAY_TRACING_NAME'.freeze
20
- CONFIG_KEY = %I[logger name sampling plugins daemon_address segment_naming
21
- naming_pattern emitter streamer context context_missing
22
- sampling_rules stream_threshold patch].freeze
21
+ CONFIG_KEY = %I[logger name sampling plugins daemon_address
22
+ segment_naming naming_pattern emitter streamer context
23
+ context_missing sampling_rules stream_threshold patch].freeze
23
24
 
24
25
  def initialize
25
26
  @name = ENV[SEGMENT_NAME_KEY]
@@ -38,9 +39,12 @@ module XRay
38
39
  @name = ENV[SEGMENT_NAME_KEY] || v
39
40
  end
40
41
 
41
- # proxy method to the emitter's daemon_address config.
42
+ # setting daemon address for components communicate with X-Ray daemon.
42
43
  def daemon_address=(v)
43
- emitter.daemon_address = v
44
+ v = ENV[DaemonConfig::DAEMON_ADDRESS_KEY] || v
45
+ config = DaemonConfig.new(v)
46
+ emitter.daemon_config = config
47
+ sampler.daemon_config = config if sampler.respond_to?(:daemon_config=)
44
48
  end
45
49
 
46
50
  # proxy method to the context's context_missing config.
@@ -63,8 +67,7 @@ module XRay
63
67
  segment_naming.pattern = v
64
68
  end
65
69
 
66
- # makes a sampling decision based on internal configure, e.g.
67
- # if sampling enabled and the default sampling rule.
70
+ # makes a sampling decision without incoming filters.
68
71
  def sample?
69
72
  return true unless sampling
70
73
  sampler.sample?
@@ -0,0 +1,59 @@
1
+ require 'aws-xray-sdk/exceptions'
2
+
3
+ module XRay
4
+ # The class that stores X-Ray daemon configuration about
5
+ # the ip address and port for UDP and TCP port. It gets the address
6
+ # string from `AWS_XRAY_DAEMON_ADDRESS` and then from recorder's
7
+ # configuration for `daemon_address`.
8
+ # A notation of `127.0.0.1:2000` or `tcp:127.0.0.1:2000 udp:127.0.0.2:2001`
9
+ # are both acceptable. The former one means UDP and TCP are running at
10
+ # the same address. By default it assumes a X-Ray daemon
11
+ # running at `127.0.0.1:2000` listening to both UDP and TCP traffic.
12
+ class DaemonConfig
13
+ DAEMON_ADDRESS_KEY = 'AWS_XRAY_DAEMON_ADDRESS'.freeze
14
+ attr_reader :tcp_ip, :tcp_port, :udp_ip, :udp_port
15
+ @@dafault_addr = '127.0.0.1:2000'
16
+
17
+ def initialize(addr: @@dafault_addr)
18
+ update_address(addr)
19
+ end
20
+
21
+ def update_address(v)
22
+ v = ENV[DAEMON_ADDRESS_KEY] || v
23
+ update_addr(v)
24
+ rescue StandardError
25
+ raise InvalidDaemonAddressError, %(Invalid X-Ray daemon address specified: #{v}.)
26
+ end
27
+
28
+ private
29
+
30
+ def update_addr(v)
31
+ parts = v.split(' ')
32
+ if parts.length == 1 # format of '127.0.0.1:2000'
33
+ addr = parts[0].split(':')
34
+ raise InvalidDaemonAddressError unless addr.length == 2
35
+ @tcp_ip = addr[0]
36
+ @tcp_port = addr[1].to_i
37
+ @udp_ip = addr[0]
38
+ @udp_port = addr[1].to_i
39
+ else
40
+ set_tcp_udp(parts) # format of 'tcp:127.0.0.1:2000 udp:127.0.0.2:2001'
41
+ end
42
+ end
43
+
44
+ def set_tcp_udp(parts)
45
+ part1 = parts[0]
46
+ part2 = parts[1]
47
+ key1 = part1.split(':')[0]
48
+ key2 = part2.split(':')[0]
49
+ addr_h = {}
50
+ addr_h[key1] = part1.split(':')
51
+ addr_h[key2] = part2.split(':')
52
+
53
+ @tcp_ip = addr_h['tcp'][1]
54
+ @tcp_port = addr_h['tcp'][2].to_i
55
+ @udp_ip = addr_h['udp'][1]
56
+ @udp_port = addr_h['udp'][2].to_i
57
+ end
58
+ end
59
+ end
@@ -2,6 +2,7 @@ require 'socket'
2
2
  require 'aws-xray-sdk/logger'
3
3
  require 'aws-xray-sdk/emitter/emitter'
4
4
  require 'aws-xray-sdk/exceptions'
5
+ require 'aws-xray-sdk/daemon_config'
5
6
 
6
7
  module XRay
7
8
  # The default emitter the X-Ray recorder uses to send segments/subsegments
@@ -10,12 +11,11 @@ module XRay
10
11
  include Emitter
11
12
  include Logging
12
13
 
13
- attr_reader :address
14
+ attr_reader :daemon_config
14
15
 
15
- def initialize
16
+ def initialize(daemon_config: DaemonConfig.new)
16
17
  @socket = UDPSocket.new
17
- @address = ENV[DAEMON_ADDRESS_KEY] || '127.0.0.1:2000'
18
- configure_socket(@address)
18
+ self.daemon_config = daemon_config
19
19
  end
20
20
 
21
21
  # Serializes a segment/subsegment and sends it to the X-Ray daemon
@@ -25,29 +25,18 @@ module XRay
25
25
  return nil unless entity.sampled
26
26
  begin
27
27
  payload = %(#{@@protocol_header}#{@@protocol_delimiter}#{entity.to_json})
28
- logger.debug %(sending payload #{payload} to daemon at #{address}.)
28
+ logger.debug %(sending payload #{payload} to daemon at #{@address}.)
29
29
  @socket.send payload, 0
30
30
  rescue StandardError => e
31
31
  logger.warn %(failed to send payload due to #{e.message})
32
32
  end
33
33
  end
34
34
 
35
- def daemon_address=(v)
36
- v = ENV[DAEMON_ADDRESS_KEY] || v
37
- @address = v
38
- configure_socket(v)
39
- end
40
-
41
- private
42
-
43
- def configure_socket(v)
44
- begin
45
- addr = v.split(':')
46
- host, ip = addr[0], addr[1].to_i
47
- @socket.connect(host, ip)
48
- rescue StandardError
49
- raise InvalidDaemonAddressError, %(Invalid X-Ray daemon address specified: #{v}.)
50
- end
35
+ def daemon_config=(v)
36
+ @address = %(#{v.udp_ip}:#{v.udp_port})
37
+ @socket.connect(v.udp_ip, v.udp_port)
38
+ rescue StandardError
39
+ raise InvalidDaemonAddressError, %(Invalid X-Ray daemon address specified: #{v}.)
51
40
  end
52
41
  end
53
42
  end
@@ -4,8 +4,6 @@ module XRay
4
4
  # The emitter interface the X-Ray recorder uses to send segments/subsegments
5
5
  # to the X-Ray daemon over UDP.
6
6
  module Emitter
7
- DAEMON_ADDRESS_KEY = 'AWS_XRAY_DAEMON_ADDRESS'.freeze
8
-
9
7
  @@protocol_header = {
10
8
  format: 'json',
11
9
  version: 1
@@ -17,7 +15,7 @@ module XRay
17
15
  raise 'Not implemented'
18
16
  end
19
17
 
20
- def daemon_address=(v)
18
+ def daemon_config=(v)
21
19
  raise 'Not implemented'
22
20
  end
23
21
  end
@@ -17,15 +17,19 @@ module XRay
17
17
  include XRay::Facets::Helper
18
18
 
19
19
  def call(context)
20
- recorder = Aws.config[:xray_recorder]
21
- if recorder.current_entity.nil?
22
- super
23
- end
24
-
25
20
  operation = context.operation_name
26
21
  service_name = context.client.class.api.metadata['serviceAbbreviation'] ||
27
22
  context.client.class.to_s.split('::')[1]
28
- recorder.capture service_name, namespace: 'aws' do |subsegment|
23
+ if skip?(service_name, operation)
24
+ return super
25
+ end
26
+
27
+ recorder = Aws.config[:xray_recorder]
28
+ if recorder.nil? || recorder.current_entity.nil?
29
+ return super
30
+ end
31
+
32
+ recorder.capture(service_name, namespace: 'aws') do |subsegment|
29
33
  # inject header string before calling downstream AWS services
30
34
  context.http_request.headers[TRACE_HEADER] = prep_header_str entity: subsegment
31
35
  response = @handler.call(context)
@@ -109,6 +113,10 @@ module XRay
109
113
  v = target.keys if descriptor[:get_keys]
110
114
  meta[descriptor[:rename_to]] = v
111
115
  end
116
+
117
+ def skip?(service, op)
118
+ return service == 'XRay' && (op == :get_sampling_rules || op == :get_sampling_targets)
119
+ end
112
120
  end
113
121
  end
114
122
 
@@ -26,21 +26,14 @@ module XRay
26
26
  # the highest precedence. If the `trace_header` doesn't contain
27
27
  # sampling decision then it checks if sampling is enabled or not
28
28
  # in the recorder. If not enbaled it returns 'true'. Otherwise it uses
29
- # sampling rule to decide.
30
- def should_sample?(header_obj:, recorder:,
31
- host: nil, method: nil, path: nil,
32
- **args)
29
+ # sampling rules to decide.
30
+ def should_sample?(header_obj:, recorder:, sampling_req:, **args)
33
31
  # check outside decision
34
32
  if i = header_obj.sampled
35
- if i.zero?
36
- false
37
- else
38
- true
39
- end
33
+ !i.zero?
40
34
  # check sampling rules
41
35
  elsif recorder.sampling_enabled?
42
- recorder.sampler.sample_request?(service_name: host, url_path: path,
43
- http_method: method)
36
+ recorder.sampler.sample_request?(sampling_req)
44
37
  # sample if no reason not to
45
38
  else
46
39
  true
@@ -21,6 +21,10 @@ module XRay
21
21
  end
22
22
 
23
23
  def request(req, body = nil, &block)
24
+ if req.path && (req.path == ('/GetSamplingRules') || req.path == ('/SamplingTargets'))
25
+ return super
26
+ end
27
+
24
28
  entity = XRay.recorder.current_entity
25
29
  capture = !(entity && entity.namespace && entity.namespace == 'aws'.freeze)
26
30
  if started? && capture && entity
@@ -8,6 +8,7 @@ module XRay
8
8
  class Middleware
9
9
  include XRay::Facets::Helper
10
10
  X_FORWARD = 'HTTP_X_FORWARDED_FOR'.freeze
11
+ SCHEME_SEPARATOR = "://".freeze
11
12
 
12
13
  def initialize(app, recorder: nil)
13
14
  @app = app
@@ -22,16 +23,15 @@ module XRay
22
23
  host = req.host
23
24
  url_path = req.path
24
25
  method = req.request_method
26
+ # get segment name from host header if applicable
27
+ seg_name = @recorder.segment_naming.provide_name(host: req.host)
25
28
 
26
29
  # get sampling decision
27
30
  sampled = should_sample?(
28
- header_obj: header, recorder: @recorder,
29
- host: host, method: method, path: url_path
31
+ header_obj: header, recorder: @recorder, sampling_req:
32
+ { host: host, http_method: method, url_path: url_path, service: seg_name }
30
33
  )
31
34
 
32
- # get segment name from host header if applicable
33
- seg_name = @recorder.segment_naming.provide_name(host: req.host)
34
-
35
35
  # begin the segment
36
36
  segment = @recorder.begin_segment seg_name, trace_id: header.root, parent_id: header.parent_id,
37
37
  sampled: sampled
@@ -63,7 +63,9 @@ module XRay
63
63
 
64
64
  def extract_request_meta(req)
65
65
  req_meta = {}
66
- req_meta[:url] = req.url if req.url
66
+ req_meta[:url] = req.scheme + SCHEME_SEPARATOR if req.scheme
67
+ req_meta[:url] += req.host_with_port if req.host_with_port
68
+ req_meta[:url] += req.path if req.path
67
69
  req_meta[:user_agent] = req.user_agent if req.user_agent
68
70
  req_meta[:method] = req.request_method if req.request_method
69
71
  if req.has_header?(X_FORWARD)
@@ -6,7 +6,7 @@ module XRay
6
6
  class Railtie < ::Rails::Railtie
7
7
  RAILS_OPTIONS = %I[active_record].freeze
8
8
 
9
- initializer "aws-xray-sdk.rack_middleware" do |app|
9
+ initializer("aws-xray-sdk.rack_middleware") do |app|
10
10
  app.middleware.insert 0, Rack::Middleware
11
11
  app.middleware.use XRay::Rails::ExceptionMiddleware
12
12
  end
@@ -1,4 +1,4 @@
1
- require 'oj'
1
+ require 'multi_json'
2
2
 
3
3
  module XRay
4
4
  # Represents cause section in segment and subsegment document.
@@ -26,7 +26,7 @@ module XRay
26
26
 
27
27
  def to_json
28
28
  @to_json ||= begin
29
- Oj.dump to_h, mode: :compat, use_as_json: true
29
+ MultiJson.dump to_h
30
30
  end
31
31
  end
32
32
 
@@ -38,6 +38,10 @@ module XRay
38
38
  # no-op
39
39
  end
40
40
 
41
+ def sampling_rule_name=(v)
42
+ # no-op
43
+ end
44
+
41
45
  def to_h
42
46
  # no-op
43
47
  end
@@ -1,6 +1,6 @@
1
1
  require 'securerandom'
2
2
  require 'bigdecimal'
3
- require 'oj'
3
+ require 'multi_json'
4
4
  require 'aws-xray-sdk/exceptions'
5
5
  require 'aws-xray-sdk/model/cause'
6
6
  require 'aws-xray-sdk/model/annotations'
@@ -168,7 +168,7 @@ module XRay
168
168
 
169
169
  def to_json
170
170
  @to_json ||= begin
171
- Oj.dump to_h, mode: :compat, use_as_json: true
171
+ MultiJson.dump(to_h)
172
172
  end
173
173
  end
174
174
 
@@ -1,4 +1,4 @@
1
- require 'oj'
1
+ require 'multi_json'
2
2
  require 'aws-xray-sdk/exceptions'
3
3
 
4
4
  module XRay
@@ -48,7 +48,7 @@ module XRay
48
48
 
49
49
  def to_json
50
50
  @to_json ||= begin
51
- Oj.dump to_h, mode: :compat, use_as_json: true
51
+ MultiJson.dump to_h
52
52
  end
53
53
  end
54
54
  end
@@ -6,7 +6,8 @@ module XRay
6
6
  # details about the request, and details about the work done.
7
7
  class Segment
8
8
  include Entity
9
- attr_accessor :ref_counter, :subsegment_size, :origin, :user, :service
9
+ attr_accessor :ref_counter, :subsegment_size, :origin,
10
+ :user, :service
10
11
 
11
12
  # @param [String] trace_id Manually crafted trace id.
12
13
  # @param [String] name Must be specified either on object creation or
@@ -39,6 +40,12 @@ module XRay
39
40
  @subsegment_size = subsegment_size - subsegment.all_children_count - 1
40
41
  end
41
42
 
43
+ def sampling_rule_name=(v)
44
+ @aws ||= {}
45
+ @aws[:xray] ||= {}
46
+ @aws[:xray][:sampling_rule_name] = v
47
+ end
48
+
42
49
  def decrement_ref_counter
43
50
  @ref_counter -= 1
44
51
  end
@@ -1,4 +1,4 @@
1
- require 'oj'
1
+ require 'multi_json'
2
2
  require 'aws-xray-sdk/logger'
3
3
 
4
4
  module XRay
@@ -14,7 +14,7 @@ module XRay
14
14
  def self.aws
15
15
  @@aws ||= begin
16
16
  file = File.open(CONF_PATH)
17
- { elastic_beanstalk: Oj.load(file) }
17
+ { elastic_beanstalk: MultiJson.load(file) }
18
18
  rescue StandardError => e
19
19
  @@aws = {}
20
20
  Logging.logger.warn %(can not get the environment config due to: #{e.message}.)
@@ -12,11 +12,12 @@ module XRay
12
12
  # and send them to the X-Ray daemon. It is also responsible for managing
13
13
  # context.
14
14
  class Recorder
15
- attr_reader :config
15
+ attr_reader :config, :origin
16
16
 
17
17
  def initialize(user_config: nil)
18
18
  @config = Configuration.new
19
19
  @config.configure(user_config) unless user_config.nil?
20
+ @origin = nil
20
21
  end
21
22
 
22
23
  # Begin a segment for the current context. The recorder
@@ -31,7 +32,7 @@ module XRay
31
32
  sample = sampled.nil? ? config.sample? : sampled
32
33
  if sample
33
34
  segment = Segment.new name: seg_name, trace_id: trace_id, parent_id: parent_id
34
- populate_runtime_context(segment)
35
+ populate_runtime_context(segment, sample)
35
36
  else
36
37
  segment = DummySegment.new name: seg_name, trace_id: trace_id, parent_id: parent_id
37
38
  end
@@ -204,14 +205,14 @@ module XRay
204
205
 
205
206
  private_class_method
206
207
 
207
- def populate_runtime_context(segment)
208
+ def populate_runtime_context(segment, sample)
208
209
  @aws ||= begin
209
210
  aws = {}
210
211
  config.plugins.each do |p|
211
212
  meta = p.aws
212
213
  if meta.is_a?(Hash) && !meta.empty?
213
214
  aws.merge! meta
214
- segment.origin = p::ORIGIN
215
+ @origin = p::ORIGIN
215
216
  end
216
217
  end
217
218
  xray_meta = { xray:
@@ -230,6 +231,8 @@ module XRay
230
231
 
231
232
  segment.aws = @aws
232
233
  segment.service = @service
234
+ segment.origin = @origin
235
+ segment.sampling_rule_name = sample if sample.is_a?(String)
233
236
  end
234
237
  end
235
238
  end