ddtrace 0.15.0.beta1 → 0.15.0.internaltracinfeature1

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