ddtrace 0.15.0.beta1 → 0.15.0.internaltracinfeature1

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.
@@ -16,15 +16,11 @@ module Datadog
16
16
  end
17
17
 
18
18
  def trace_id
19
- value = header(HTTP_HEADER_TRACE_ID).to_i
20
- return if value <= 0 || value >= Span::MAX_ID
21
- value
19
+ id HTTP_HEADER_TRACE_ID
22
20
  end
23
21
 
24
22
  def parent_id
25
- value = header(HTTP_HEADER_PARENT_ID).to_i
26
- return if value <= 0 || value >= Span::MAX_ID
27
- value
23
+ id HTTP_HEADER_PARENT_ID
28
24
  end
29
25
 
30
26
  def sampling_priority
@@ -44,5 +40,11 @@ module Datadog
44
40
 
45
41
  @env[rack_header]
46
42
  end
43
+
44
+ def id(header)
45
+ value = header(header).to_i
46
+ return if value.zero? || value >= Span::MAX_ID
47
+ value < 0 ? value + 0x1_0000_0000_0000_0000 : value
48
+ end
47
49
  end
48
50
  end
@@ -25,8 +25,10 @@ module Datadog
25
25
  end
26
26
 
27
27
  def [](name)
28
- entry = @data[name]
29
- entry.klass if entry
28
+ @mutex.synchronize do
29
+ entry = @data[name]
30
+ entry.klass if entry
31
+ end
30
32
  end
31
33
 
32
34
  def to_h
@@ -98,14 +98,32 @@ module Datadog
98
98
  end
99
99
 
100
100
  def sample(span)
101
- span.context.sampling_priority = Datadog::Ext::Priority::AUTO_REJECT if span.context
102
- return unless @base_sampler.sample(span)
103
- return unless @post_sampler.sample(span)
104
- span.context.sampling_priority = Datadog::Ext::Priority::AUTO_KEEP if span.context
105
-
106
- true
101
+ return perform_sampling(span) unless span.context
102
+ return sampled_by_upstream(span) if span.context.sampling_priority
103
+
104
+ perform_sampling(span).tap do |sampled|
105
+ span.context.sampling_priority = if sampled
106
+ Datadog::Ext::Priority::AUTO_KEEP
107
+ else
108
+ Datadog::Ext::Priority::AUTO_REJECT
109
+ end
110
+ end
107
111
  end
108
112
 
109
113
  def_delegators :@post_sampler, :update
114
+
115
+ private
116
+
117
+ def sampled_by_upstream(span)
118
+ span.sampled = priority_keep?(span.context.sampling_priority)
119
+ end
120
+
121
+ def priority_keep?(sampling_priority)
122
+ sampling_priority == Datadog::Ext::Priority::USER_KEEP || sampling_priority == Datadog::Ext::Priority::AUTO_KEEP
123
+ end
124
+
125
+ def perform_sampling(span)
126
+ @base_sampler.sample(span) && @post_sampler.sample(span)
127
+ end
110
128
  end
111
129
  end
@@ -20,7 +20,7 @@ module Datadog
20
20
  # rubocop:disable Metrics/ClassLength
21
21
  class Tracer
22
22
  attr_reader :sampler, :services, :tags, :provider
23
- attr_accessor :enabled, :writer
23
+ attr_accessor :enabled, :writer, :internal_traces
24
24
  attr_writer :default_service
25
25
 
26
26
  ALLOWED_SPAN_OPTIONS = [:service, :resource, :span_type].freeze
@@ -102,7 +102,9 @@ module Datadog
102
102
  @provider ||= Datadog::DefaultContextProvider.new # @provider should never be nil
103
103
 
104
104
  @context_flush = options[:partial_flush] ? Datadog::ContextFlush.new(options) : nil
105
+ @internal_traces = options[:internal_traces] || false
105
106
 
107
+ @mutex = Mutex.new
106
108
  @services = {}
107
109
  @tags = {}
108
110
  end
@@ -122,6 +124,7 @@ module Datadog
122
124
  enabled = options.fetch(:enabled, nil)
123
125
  hostname = options.fetch(:hostname, nil)
124
126
  port = options.fetch(:port, nil)
127
+ @internal_traces = options.fetch(:internal_traces, false)
125
128
 
126
129
  # Those are rare "power-user" options.
127
130
  sampler = options.fetch(:sampler, nil)
@@ -3,6 +3,7 @@ require 'net/http'
3
3
 
4
4
  require 'ddtrace/encoding'
5
5
  require 'ddtrace/version'
6
+ require 'ddtrace/utils'
6
7
 
7
8
  module Datadog
8
9
  # Transport class that handles the spans delivery to the
@@ -10,6 +11,9 @@ module Datadog
10
11
  # so that the Transport is thread-safe.
11
12
  # rubocop:disable Metrics/ClassLength
12
13
  class HTTPTransport
14
+ include Datadog::Utils::InternalTraces
15
+ self.internal_trace_service = 'datadog.transport'.freeze
16
+
13
17
  attr_accessor :hostname, :port
14
18
  attr_reader :traces_endpoint, :services_endpoint
15
19
 
@@ -45,6 +49,12 @@ module Datadog
45
49
 
46
50
  private_constant :API
47
51
 
52
+ CONTENT_TYPE = 'Content-Type'.freeze
53
+ DATADOG_META_LANG = 'Datadog-Meta-Lang'.freeze
54
+ DATADOG_META_LANG_VERSION = 'Datadog-Meta-Lang-Version'.freeze
55
+ DATADOG_META_LANG_INTERPRETER = 'Datadog-Meta-Lang-Interpreter'.freeze
56
+ DATADOG_META_TRACER_VERSION = 'Datadog-Meta-Tracer-Version'.freeze
57
+
48
58
  def initialize(hostname, port, options = {})
49
59
  api_version = options.fetch(:api_version, V3)
50
60
 
@@ -56,11 +66,11 @@ module Datadog
56
66
 
57
67
  # overwrite the Content-type with the one chosen in the Encoder
58
68
  @headers = options.fetch(:headers, {})
59
- @headers['Content-Type'] = @encoder.content_type
60
- @headers['Datadog-Meta-Lang'] = 'ruby'
61
- @headers['Datadog-Meta-Lang-Version'] = RUBY_VERSION
62
- @headers['Datadog-Meta-Lang-Interpreter'] = RUBY_INTERPRETER
63
- @headers['Datadog-Meta-Tracer-Version'] = Datadog::VERSION::STRING
69
+ @headers[CONTENT_TYPE] = @encoder.content_type
70
+ @headers[DATADOG_META_LANG] = 'ruby'.freeze
71
+ @headers[DATADOG_META_LANG_VERSION] = RUBY_VERSION
72
+ @headers[DATADOG_META_LANG_INTERPRETER] = RUBY_INTERPRETER
73
+ @headers[DATADOG_META_TRACER_VERSION] = Datadog::VERSION::STRING
64
74
 
65
75
  # stats
66
76
  @mutex = Mutex.new
@@ -68,32 +78,32 @@ module Datadog
68
78
  @count_client_error = 0
69
79
  @count_server_error = 0
70
80
  @count_internal_error = 0
81
+ @count_consecutive_errors = 0
71
82
  end
72
83
 
73
84
  # route the send to the right endpoint
74
85
  def send(endpoint, data)
75
- case endpoint
76
- when :services
77
- payload = @encoder.encode_services(data)
78
- status_code = post(@api[:services_endpoint], payload) do |response|
79
- process_callback(:services, response)
80
- end
81
- when :traces
82
- count = data.length
83
- payload = @encoder.encode_traces(data)
84
- status_code = post(@api[:traces_endpoint], payload, count) do |response|
85
- process_callback(:traces, response)
86
+ internal_span_when(-> { do_trace?(data) }, 'datadog.send'.freeze) do
87
+ case endpoint
88
+ when :services
89
+ status_code = send_services(data)
90
+ when :traces
91
+ status_code = send_traces(data)
92
+ else
93
+ with_active_internal_span { |s| s.set_error(RuntimeError.new("Unsupported endpoint: #{endpoint}")) }
94
+
95
+ Datadog::Tracer.log.error("Unsupported endpoint: #{endpoint}")
96
+ return nil
86
97
  end
87
- else
88
- Datadog::Tracer.log.error("Unsupported endpoint: #{endpoint}")
89
- return nil
90
- end
91
98
 
92
- if downgrade?(status_code)
93
- downgrade!
94
- send(endpoint, data)
95
- else
96
- status_code
99
+ if downgrade?(status_code)
100
+ internal_child_span('datadog.send.downgrade'.freeze) do
101
+ downgrade!
102
+ send(endpoint, data)
103
+ end
104
+ else
105
+ status_code
106
+ end
97
107
  end
98
108
  end
99
109
 
@@ -105,11 +115,12 @@ module Datadog
105
115
  headers = headers.merge(@headers)
106
116
  request = Net::HTTP::Post.new(url, headers)
107
117
  request.body = data
108
-
109
118
  response = Net::HTTP.start(@hostname, @port, read_timeout: TIMEOUT) { |http| http.request(request) }
110
119
  handle_response(response)
111
120
  rescue StandardError => e
112
- Datadog::Tracer.log.error(e.message)
121
+ with_active_internal_span { |s| s.set_error(e) }
122
+
123
+ log_error_once(e.message)
113
124
  500
114
125
  end.tap do
115
126
  yield(response) if block_given?
@@ -125,7 +136,7 @@ module Datadog
125
136
 
126
137
  @api = API.fetch(fallback_version)
127
138
  @encoder = @api[:encoder].new
128
- @headers['Content-Type'] = @encoder.content_type
139
+ @headers[CONTENT_TYPE] = @encoder.content_type
129
140
  end
130
141
  end
131
142
 
@@ -167,24 +178,27 @@ module Datadog
167
178
  # function is handled within the HTTP mutex.synchronize so it's thread-safe.
168
179
  def handle_response(response)
169
180
  status_code = response.code.to_i
181
+ with_active_internal_span { |s| s.set_tag('response.code', status_code) }
170
182
 
171
183
  if success?(status_code)
172
- Datadog::Tracer.log.debug('Payload correctly sent to the trace agent.')
184
+ Datadog::Tracer.log.debug('Payload correctly sent to the trace agent.'.freeze)
185
+ @mutex.synchronize { @count_consecutive_errors = 0 }
173
186
  @mutex.synchronize { @count_success += 1 }
174
187
  elsif downgrade?(status_code)
175
188
  Datadog::Tracer.log.debug("calling the endpoint but received #{status_code}; downgrading the API")
176
189
  elsif client_error?(status_code)
177
- Datadog::Tracer.log.error("Client error: #{response.message}")
190
+ log_error_once("Client error: #{response.message}")
178
191
  @mutex.synchronize { @count_client_error += 1 }
179
192
  elsif server_error?(status_code)
180
- Datadog::Tracer.log.error("Server error: #{response.message}")
181
- @mutex.synchronize { @count_server_error += 1 }
193
+ log_error_once("Server error: #{response.message}")
182
194
  end
183
195
 
184
196
  status_code
185
197
  rescue StandardError => e
186
- Datadog::Tracer.log.error(e.message)
198
+ log_error_once(e.message)
187
199
  @mutex.synchronize { @count_internal_error += 1 }
200
+ with_active_internal_span { |s| s.set_error(e) }
201
+
188
202
  500
189
203
  end
190
204
 
@@ -201,6 +215,64 @@ module Datadog
201
215
 
202
216
  private
203
217
 
218
+ def do_trace?(data)
219
+ # Create the span if we already are traced
220
+ return true if Datadog.tracer.active_span
221
+ # Don't trace empty data
222
+ return false unless data
223
+
224
+ # over 3 traces means that we most certainly send more than only internal traces
225
+ return true if data.length > 3
226
+
227
+ # all spans in all traces shouldn't be only 'datadog.internal' spans
228
+ !data.respond_to?(:all?) || data.all? do |trace|
229
+ !trace.respond_to?(:none?) || trace.none? do |span|
230
+ span.respond_to?(:get_tag) && span.get_tag(Utils::InternalTraces::INTERNAL_TAG)
231
+ end
232
+ end
233
+ end
234
+
235
+ def send_traces(data)
236
+ count = data.length
237
+
238
+ payload = internal_child_span('datadog.encode.traces'.freeze) do
239
+ with_active_internal_span { |s| s.set_tag('traces.count'.freeze, count) }
240
+ @encoder.encode_traces(data)
241
+ end
242
+
243
+ internal_child_span('datadog.traces.post'.freeze) do
244
+ post(@api[:traces_endpoint], payload, count) do |response|
245
+ internal_child_span('datadog.traces.response_callback'.freeze) do
246
+ process_callback(:traces, response)
247
+ end
248
+ end
249
+ end
250
+ end
251
+
252
+ def send_services(data)
253
+ payload = internal_child_span('datadog.services.encode'.freeze) do
254
+ @encoder.encode_services(data)
255
+ end
256
+
257
+ internal_child_span('datadog.services.post'.freeze) do
258
+ post(@api[:services_endpoint], payload) do |response|
259
+ internal_child_span('datadog.services.response_callback'.freeze) do
260
+ process_callback(:services, response)
261
+ end
262
+ end
263
+ end
264
+ end
265
+
266
+ def log_error_once(*args)
267
+ if @count_consecutive_errors > 0
268
+ Datadog::Tracer.log.debug(*args)
269
+ else
270
+ Datadog::Tracer.log.error(*args)
271
+ end
272
+
273
+ @mutex.synchronize { @count_consecutive_errors += 1 }
274
+ end
275
+
204
276
  def process_callback(action, response)
205
277
  return unless @response_callback && @response_callback.respond_to?(:call)
206
278
 
data/lib/ddtrace/utils.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'ddtrace/utils/database'
2
+ require 'ddtrace/utils/internal_traces'
2
3
 
3
4
  module Datadog
4
5
  # Utils contains low-level utilities, typically to provide pseudo-random trace IDs.
@@ -0,0 +1,53 @@
1
+ module Datadog
2
+ module Utils
3
+ # Traces generated by the client internals
4
+ module InternalTraces
5
+ def self.included(klass)
6
+ klass.extend(ClassMethods)
7
+ end
8
+
9
+ # ClassMethods
10
+ module ClassMethods
11
+ attr_writer :internal_trace_service
12
+
13
+ def internal_trace_service
14
+ @internal_trace_service ||= 'datadog.unknown'.freeze
15
+ end
16
+ end
17
+
18
+ INTERNAL_TAG = 'datadog.internal'.freeze
19
+
20
+ def internal_span(name, *args)
21
+ return yield unless Datadog.tracer.internal_traces
22
+
23
+ Datadog.tracer.trace(name, *args) do |span|
24
+ span.set_tag(INTERNAL_TAG, true)
25
+ span.service = self.class.internal_trace_service
26
+ yield
27
+ end
28
+ end
29
+
30
+ def internal_span_when(condition, name, *args, &block)
31
+ return yield unless Datadog.tracer.internal_traces
32
+
33
+ condition = condition.call if condition.respond_to?(:send)
34
+ return yield unless condition
35
+
36
+ internal_span(name, *args, &block)
37
+ end
38
+
39
+ def internal_child_span(name, *args, &block)
40
+ return yield unless Datadog.tracer.internal_traces
41
+
42
+ internal_span_when(-> { Datadog.tracer.active_span }, name, *args, &block)
43
+ end
44
+
45
+ def with_active_internal_span
46
+ return unless Datadog.tracer.internal_traces
47
+
48
+ span = Datadog.tracer.active_span
49
+ yield(span) if span && span.get_tag(INTERNAL_TAG)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -3,7 +3,7 @@ module Datadog
3
3
  MAJOR = 0
4
4
  MINOR = 15
5
5
  PATCH = 0
6
- PRE = 'beta1'.freeze
6
+ PRE = 'internaltracinfeature1'
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.')
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddtrace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0.beta1
4
+ version: 0.15.0.internaltracinfeature1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-08-17 00:00:00.000000000 Z
11
+ date: 2018-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -299,6 +299,11 @@ files:
299
299
  - lib/ddtrace/contrib/aws/patcher.rb
300
300
  - lib/ddtrace/contrib/aws/services.rb
301
301
  - lib/ddtrace/contrib/base.rb
302
+ - lib/ddtrace/contrib/concurrent_ruby/configuration/settings.rb
303
+ - lib/ddtrace/contrib/concurrent_ruby/context_composite_executor_service.rb
304
+ - lib/ddtrace/contrib/concurrent_ruby/future_patch.rb
305
+ - lib/ddtrace/contrib/concurrent_ruby/integration.rb
306
+ - lib/ddtrace/contrib/concurrent_ruby/patcher.rb
302
307
  - lib/ddtrace/contrib/configurable.rb
303
308
  - lib/ddtrace/contrib/configuration/option.rb
304
309
  - lib/ddtrace/contrib/configuration/option_definition.rb
@@ -415,6 +420,7 @@ files:
415
420
  - lib/ddtrace/transport.rb
416
421
  - lib/ddtrace/utils.rb
417
422
  - lib/ddtrace/utils/database.rb
423
+ - lib/ddtrace/utils/internal_traces.rb
418
424
  - lib/ddtrace/vendor/active_record/connection_specification.rb
419
425
  - lib/ddtrace/version.rb
420
426
  - lib/ddtrace/workers.rb
@@ -440,7 +446,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
440
446
  version: 1.3.1
441
447
  requirements: []
442
448
  rubyforge_project:
443
- rubygems_version: 2.6.14
449
+ rubygems_version: 2.7.7
444
450
  signing_key:
445
451
  specification_version: 4
446
452
  summary: Datadog tracing code for your Ruby applications