aws-xray-sdk 0.10.2 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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