ddtrace 0.37.0 → 0.38.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/Appraisals +15 -0
  4. data/CHANGELOG.md +33 -1
  5. data/Rakefile +11 -10
  6. data/docker-compose.yml +2 -2
  7. data/docs/GettingStarted.md +55 -0
  8. data/lib/ddtrace.rb +2 -0
  9. data/lib/ddtrace/configuration/settings.rb +18 -0
  10. data/lib/ddtrace/contrib/active_support/notifications/subscription.rb +1 -1
  11. data/lib/ddtrace/contrib/extensions.rb +10 -0
  12. data/lib/ddtrace/contrib/faraday/middleware.rb +5 -3
  13. data/lib/ddtrace/contrib/faraday/patcher.rb +3 -0
  14. data/lib/ddtrace/contrib/grpc/datadog_interceptor/client.rb +1 -3
  15. data/lib/ddtrace/contrib/httprb/configuration/settings.rb +27 -0
  16. data/lib/ddtrace/contrib/httprb/ext.rb +14 -0
  17. data/lib/ddtrace/contrib/httprb/instrumentation.rb +163 -0
  18. data/lib/ddtrace/contrib/httprb/integration.rb +43 -0
  19. data/lib/ddtrace/contrib/httprb/patcher.rb +35 -0
  20. data/lib/ddtrace/contrib/kafka/configuration/settings.rb +25 -0
  21. data/lib/ddtrace/contrib/kafka/consumer_event.rb +14 -0
  22. data/lib/ddtrace/contrib/kafka/consumer_group_event.rb +14 -0
  23. data/lib/ddtrace/contrib/kafka/event.rb +51 -0
  24. data/lib/ddtrace/contrib/kafka/events.rb +44 -0
  25. data/lib/ddtrace/contrib/kafka/events/connection/request.rb +34 -0
  26. data/lib/ddtrace/contrib/kafka/events/consumer/process_batch.rb +41 -0
  27. data/lib/ddtrace/contrib/kafka/events/consumer/process_message.rb +39 -0
  28. data/lib/ddtrace/contrib/kafka/events/consumer_group/heartbeat.rb +39 -0
  29. data/lib/ddtrace/contrib/kafka/events/consumer_group/join_group.rb +29 -0
  30. data/lib/ddtrace/contrib/kafka/events/consumer_group/leave_group.rb +29 -0
  31. data/lib/ddtrace/contrib/kafka/events/consumer_group/sync_group.rb +29 -0
  32. data/lib/ddtrace/contrib/kafka/events/produce_operation/send_messages.rb +32 -0
  33. data/lib/ddtrace/contrib/kafka/events/producer/deliver_messages.rb +35 -0
  34. data/lib/ddtrace/contrib/kafka/ext.rb +38 -0
  35. data/lib/ddtrace/contrib/kafka/integration.rb +39 -0
  36. data/lib/ddtrace/contrib/kafka/patcher.rb +26 -0
  37. data/lib/ddtrace/contrib/rack/middlewares.rb +15 -12
  38. data/lib/ddtrace/contrib/rest_client/request_patch.rb +2 -2
  39. data/lib/ddtrace/contrib/sidekiq/ext.rb +1 -0
  40. data/lib/ddtrace/contrib/sidekiq/patcher.rb +8 -1
  41. data/lib/ddtrace/contrib/sidekiq/server_tracer.rb +1 -0
  42. data/lib/ddtrace/diagnostics/environment_logger.rb +278 -0
  43. data/lib/ddtrace/environment.rb +5 -1
  44. data/lib/ddtrace/ext/diagnostics.rb +2 -0
  45. data/lib/ddtrace/ext/environment.rb +2 -0
  46. data/lib/ddtrace/pipeline/span_filter.rb +15 -15
  47. data/lib/ddtrace/sampler.rb +2 -0
  48. data/lib/ddtrace/span.rb +10 -0
  49. data/lib/ddtrace/tracer.rb +13 -6
  50. data/lib/ddtrace/transport/http/adapters/net.rb +8 -0
  51. data/lib/ddtrace/transport/http/adapters/test.rb +4 -0
  52. data/lib/ddtrace/transport/http/adapters/unix_socket.rb +4 -0
  53. data/lib/ddtrace/transport/response.rb +11 -0
  54. data/lib/ddtrace/version.rb +1 -1
  55. data/lib/ddtrace/workers/trace_writer.rb +3 -0
  56. data/lib/ddtrace/writer.rb +33 -12
  57. metadata +27 -3
@@ -98,23 +98,26 @@ module Datadog
98
98
  request_span.set_error(e) unless request_span.nil?
99
99
  raise e
100
100
  ensure
101
- # Rack is a really low level interface and it doesn't provide any
102
- # advanced functionality like routers. Because of that, we assume that
103
- # the underlying framework or application has more knowledge about
104
- # the result for this request; `resource` and `tags` are expected to
105
- # be set in another level but if they're missing, reasonable defaults
106
- # are used.
107
- set_request_tags!(request_span, env, status, headers, response, original_env)
108
-
109
- # ensure the request_span is finished and the context reset;
110
- # this assumes that the Rack middleware creates a root span
111
- request_span.finish
101
+ if request_span
102
+ # Rack is a really low level interface and it doesn't provide any
103
+ # advanced functionality like routers. Because of that, we assume that
104
+ # the underlying framework or application has more knowledge about
105
+ # the result for this request; `resource` and `tags` are expected to
106
+ # be set in another level but if they're missing, reasonable defaults
107
+ # are used.
108
+ set_request_tags!(request_span, env, status, headers, response, original_env || env)
109
+
110
+ # ensure the request_span is finished and the context reset;
111
+ # this assumes that the Rack middleware creates a root span
112
+ request_span.finish
113
+ end
114
+
112
115
  frontend_span.finish unless frontend_span.nil?
113
116
 
114
117
  # TODO: Remove this once we change how context propagation works. This
115
118
  # ensures we clean thread-local variables on each HTTP request avoiding
116
119
  # memory leaks.
117
- tracer.provider.context = Datadog::Context.new
120
+ tracer.provider.context = Datadog::Context.new if tracer
118
121
  end
119
122
 
120
123
  def resource_name_for(env, status)
@@ -62,11 +62,11 @@ module Datadog
62
62
  # rubocop:disable Lint/RescueException
63
63
  rescue Exception => e
64
64
  # rubocop:enable Lint/RescueException
65
- span.set_error(e)
65
+ span.set_error(e) if span
66
66
 
67
67
  raise e
68
68
  ensure
69
- span.finish
69
+ span.finish if span
70
70
  end
71
71
 
72
72
  private
@@ -15,6 +15,7 @@ module Datadog
15
15
  TAG_JOB_ID = 'sidekiq.job.id'.freeze
16
16
  TAG_JOB_QUEUE = 'sidekiq.job.queue'.freeze
17
17
  TAG_JOB_RETRY = 'sidekiq.job.retry'.freeze
18
+ TAG_JOB_RETRY_COUNT = 'sidekiq.job.retry_count'.freeze
18
19
  TAG_JOB_WRAPPER = 'sidekiq.job.wrapper'.freeze
19
20
  TAG_JOB_ARGS = 'sidekiq.job.args'.freeze
20
21
  end
@@ -15,14 +15,21 @@ module Datadog
15
15
 
16
16
  def patch
17
17
  require 'ddtrace/contrib/sidekiq/client_tracer'
18
+ require 'ddtrace/contrib/sidekiq/server_tracer'
19
+
18
20
  ::Sidekiq.configure_client do |config|
19
21
  config.client_middleware do |chain|
20
22
  chain.add(Sidekiq::ClientTracer)
21
23
  end
22
24
  end
23
25
 
24
- require 'ddtrace/contrib/sidekiq/server_tracer'
25
26
  ::Sidekiq.configure_server do |config|
27
+ # If a job enqueues another job, make sure it has the same client
28
+ # middleware.
29
+ config.client_middleware do |chain|
30
+ chain.add(Sidekiq::ClientTracer)
31
+ end
32
+
26
33
  config.server_middleware do |chain|
27
34
  chain.add(Sidekiq::ServerTracer)
28
35
  end
@@ -31,6 +31,7 @@ module Datadog
31
31
 
32
32
  span.set_tag(Ext::TAG_JOB_ID, job['jid'])
33
33
  span.set_tag(Ext::TAG_JOB_RETRY, job['retry'])
34
+ span.set_tag(Ext::TAG_JOB_RETRY_COUNT, job['retry_count'])
34
35
  span.set_tag(Ext::TAG_JOB_QUEUE, job['queue'])
35
36
  span.set_tag(Ext::TAG_JOB_WRAPPER, job['class']) if job['wrapped']
36
37
  span.set_tag(Ext::TAG_JOB_DELAY, 1000.0 * (Time.now.utc.to_f - job['enqueued_at'].to_f))
@@ -0,0 +1,278 @@
1
+ require 'date'
2
+ require 'json'
3
+ require 'rbconfig'
4
+
5
+ module Datadog
6
+ module Diagnostics
7
+ # A holistic collection of the environment in which ddtrace is running.
8
+ # This logger should allow for easy reporting by users to Datadog support.
9
+ #
10
+ # rubocop:disable Style/DoubleNegation
11
+ module EnvironmentLogger
12
+ class << self
13
+ # Outputs environment information to {Datadog.logger}.
14
+ # Executes only for the lifetime of the program.
15
+ def log!(transport_responses)
16
+ return if @executed || !log?
17
+ @executed = true
18
+
19
+ data = EnvironmentCollector.new.collect!(transport_responses)
20
+ data.reject! { |_, v| v.nil? } # Remove empty values from hash output
21
+
22
+ log_environment!(data.to_json)
23
+ log_error!('Agent Error'.freeze, data[:agent_error]) if data[:agent_error]
24
+ rescue => e
25
+ Datadog.logger.warn("Failed to collect environment information: #{e} location: #{e.backtrace.first}")
26
+ end
27
+
28
+ private
29
+
30
+ def log_environment!(line)
31
+ Datadog.logger.warn("DATADOG TRACER CONFIGURATION - #{line}")
32
+ end
33
+
34
+ def log_error!(type, error)
35
+ Datadog.logger.warn("DATADOG TRACER DIAGNOSTIC - #{type}: #{error}")
36
+ end
37
+
38
+ # Are we logging the environment data?
39
+ def log?
40
+ startup_logs_enabled = Datadog.configuration.diagnostics.startup_logs.enabled
41
+ if startup_logs_enabled.nil?
42
+ !repl? # Suppress logs if we running in a REPL
43
+ else
44
+ startup_logs_enabled
45
+ end
46
+ end
47
+
48
+ REPL_PROGRAM_NAMES = %w[irb pry].freeze
49
+
50
+ def repl?
51
+ REPL_PROGRAM_NAMES.include?($PROGRAM_NAME)
52
+ end
53
+ end
54
+ end
55
+
56
+ # Collects environment information for diagnostic logging
57
+ class EnvironmentCollector
58
+ # @return [String] current time in ISO8601 format
59
+ def date
60
+ DateTime.now.iso8601
61
+ end
62
+
63
+ # Best portable guess of OS information.
64
+ # @return [String] platform string
65
+ def os_name
66
+ RbConfig::CONFIG['host'.freeze]
67
+ end
68
+
69
+ # @return [String] ddtrace version
70
+ def version
71
+ VERSION::STRING
72
+ end
73
+
74
+ # @return [String] "ruby"
75
+ def lang
76
+ Ext::Runtime::LANG
77
+ end
78
+
79
+ # Supported Ruby language version.
80
+ # Will be distinct from VM version for non-MRI environments.
81
+ # @return [String]
82
+ def lang_version
83
+ Ext::Runtime::LANG_VERSION
84
+ end
85
+
86
+ # @return [String] configured application environment
87
+ def env
88
+ Datadog.configuration.env
89
+ end
90
+
91
+ # @return [Boolean, nil]
92
+ def enabled
93
+ Datadog.configuration.tracer.enabled
94
+ end
95
+
96
+ # @return [String] configured application service name
97
+ def service
98
+ Datadog.configuration.service
99
+ end
100
+
101
+ # @return [String] configured application version
102
+ def dd_version
103
+ Datadog.configuration.version
104
+ end
105
+
106
+ # @return [String] target agent URL for trace flushing
107
+ def agent_url
108
+ # Retrieve the effect agent URL, regardless of how it was configured
109
+ transport = Datadog.tracer.writer.transport
110
+ adapter = transport.client.api.adapter
111
+ adapter.url
112
+ end
113
+
114
+ # Error returned by Datadog agent during a tracer flush attempt
115
+ # @return [String] concatenated list of transport errors
116
+ def agent_error(transport_responses)
117
+ error_responses = transport_responses.reject(&:ok?)
118
+
119
+ return nil if error_responses.empty?
120
+
121
+ error_responses.map(&:inspect).join(','.freeze)
122
+ end
123
+
124
+ # @return [Boolean, nil] debug mode enabled in configuration
125
+ def debug
126
+ !!Datadog.configuration.diagnostics.debug
127
+ end
128
+
129
+ # @return [Boolean, nil] analytics enabled in configuration
130
+ def analytics_enabled
131
+ !!Datadog.configuration.analytics.enabled
132
+ end
133
+
134
+ # @return [Numeric, nil] tracer sample rate configured
135
+ def sample_rate
136
+ sampler = Datadog.configuration.tracer.sampler
137
+ return nil unless sampler
138
+
139
+ sampler.sample_rate(nil) rescue nil
140
+ end
141
+
142
+ # DEV: We currently only support SimpleRule instances.
143
+ # DEV: These are the most commonly used rules.
144
+ # DEV: We should expand support for other rules in the future,
145
+ # DEV: although it is tricky to serialize arbitrary rules.
146
+ #
147
+ # @return [Hash, nil] sample rules configured
148
+ def sampling_rules
149
+ sampler = Datadog.configuration.tracer.sampler
150
+ return nil unless sampler.is_a?(Datadog::PrioritySampler) &&
151
+ sampler.priority_sampler.is_a?(Datadog::Sampling::RuleSampler)
152
+
153
+ sampler.priority_sampler.rules.map do |rule|
154
+ next unless rule.is_a?(Datadog::Sampling::SimpleRule)
155
+
156
+ {
157
+ name: rule.matcher.name,
158
+ service: rule.matcher.service,
159
+ sample_rate: rule.sampler.sample_rate(nil)
160
+ }
161
+ end.compact
162
+ end
163
+
164
+ # @return [Hash, nil] concatenated list of global tracer tags configured
165
+ def tags
166
+ tags = Datadog.configuration.tags
167
+ return nil if tags.empty?
168
+ hash_serializer(tags)
169
+ end
170
+
171
+ # @return [Boolean, nil] runtime metrics enabled in configuration
172
+ def runtime_metrics_enabled
173
+ Datadog.configuration.runtime_metrics.enabled
174
+ end
175
+
176
+ # Concatenated list of integrations activated, with their gem version.
177
+ # Example: "rails@6.0.3,rack@2.2.3"
178
+ #
179
+ # @return [String, nil]
180
+ def integrations_loaded
181
+ integrations = instrumented_integrations
182
+ return if integrations.empty?
183
+
184
+ integrations.map { |name, integration| "#{name}@#{integration.class.version}" }.join(','.freeze)
185
+ end
186
+
187
+ # Ruby VM name and version.
188
+ # Examples: "ruby-2.7.1", "jruby-9.2.11.1", "truffleruby-20.1.0"
189
+ # @return [String, nil]
190
+ def vm
191
+ # RUBY_ENGINE_VERSION returns the VM version, which
192
+ # will differ from RUBY_VERSION for non-mri VMs.
193
+ if defined?(RUBY_ENGINE_VERSION)
194
+ "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}"
195
+ else
196
+ # Ruby < 2.3 doesn't support RUBY_ENGINE_VERSION
197
+ "#{RUBY_ENGINE}-#{RUBY_VERSION}"
198
+ end
199
+ end
200
+
201
+ # @return [Boolean, nil] partial flushing enabled in configuration
202
+ def partial_flushing_enabled
203
+ !!Datadog.configuration.tracer.partial_flush.enabled
204
+ end
205
+
206
+ # @return [Boolean, nil] priority sampling enabled in configuration
207
+ def priority_sampling_enabled
208
+ !!Datadog.configuration.tracer.priority_sampling
209
+ end
210
+
211
+ # @return [Boolean, nil] health metrics enabled in configuration
212
+ def health_metrics_enabled
213
+ !!Datadog.configuration.diagnostics.health_metrics.enabled
214
+ end
215
+
216
+ # TODO: Populate when profiling is implemented
217
+ # def profiling_enabled
218
+ # end
219
+
220
+ # TODO: Populate when automatic log correlation is implemented
221
+ # def logs_correlation_enabled
222
+ # end
223
+
224
+ # @return [Hash] environment information available at call time
225
+ def collect!(transport_responses)
226
+ {
227
+ date: date,
228
+ os_name: os_name,
229
+ version: version,
230
+ lang: lang,
231
+ lang_version: lang_version,
232
+ env: env,
233
+ enabled: enabled,
234
+ service: service,
235
+ dd_version: dd_version,
236
+ agent_url: agent_url,
237
+ agent_error: agent_error(transport_responses),
238
+ debug: debug,
239
+ analytics_enabled: analytics_enabled,
240
+ sample_rate: sample_rate,
241
+ sampling_rules: sampling_rules,
242
+ tags: tags,
243
+ runtime_metrics_enabled: runtime_metrics_enabled,
244
+ integrations_loaded: integrations_loaded,
245
+ vm: vm,
246
+ partial_flushing_enabled: partial_flushing_enabled,
247
+ priority_sampling_enabled: priority_sampling_enabled,
248
+ health_metrics_enabled: health_metrics_enabled,
249
+ **instrumented_integrations_settings
250
+ }
251
+ end
252
+
253
+ private
254
+
255
+ def instrumented_integrations
256
+ Datadog.configuration.instrumented_integrations
257
+ end
258
+
259
+ # Capture all active integration settings into "integrationName_settingName: value" entries.
260
+ def instrumented_integrations_settings
261
+ Hash[instrumented_integrations.flat_map do |name, integration|
262
+ integration.configuration.to_h.flat_map do |setting, value|
263
+ next [] if setting == :tracer # Skip internal Ruby objects
264
+
265
+ # Convert value to a string to avoid custom #to_json
266
+ # handlers possibly causing errors.
267
+ [[:"integration_#{name}_#{setting}", value.to_s]]
268
+ end
269
+ end]
270
+ end
271
+
272
+ # Outputs "k1:v1,k2:v2,..."
273
+ def hash_serializer(h)
274
+ h.map { |k, v| "#{k}:#{v}" }.join(','.freeze)
275
+ end
276
+ end
277
+ end
278
+ end
@@ -6,7 +6,11 @@ module Datadog
6
6
  # Defines helper methods for environment
7
7
  module Helpers
8
8
  def env_to_bool(var, default = nil)
9
- ENV.key?(var) ? ENV[var].to_s.downcase == 'true' : default
9
+ ENV.key?(var) ? ENV[var].to_s.strip.downcase == 'true' : default
10
+ end
11
+
12
+ def env_to_int(var, default = nil)
13
+ ENV.key?(var) ? ENV[var].to_i : default
10
14
  end
11
15
 
12
16
  def env_to_float(var, default = nil)
@@ -1,6 +1,8 @@
1
1
  module Datadog
2
2
  module Ext
3
3
  module Diagnostics
4
+ DD_TRACE_STARTUP_LOGS = 'DD_TRACE_STARTUP_LOGS'.freeze
5
+
4
6
  # Health
5
7
  module Health
6
8
  # Metrics
@@ -1,8 +1,10 @@
1
1
  module Datadog
2
2
  module Ext
3
3
  module Environment
4
+ ENV_API_KEY = 'DD_API_KEY'.freeze
4
5
  ENV_ENVIRONMENT = 'DD_ENV'.freeze
5
6
  ENV_SERVICE = 'DD_SERVICE'.freeze
7
+ ENV_SITE = 'DD_SITE'.freeze
6
8
  ENV_TAGS = 'DD_TAGS'.freeze
7
9
  ENV_VERSION = 'DD_VERSION'.freeze
8
10
 
@@ -10,12 +10,22 @@ module Datadog
10
10
  @criteria = filter || block
11
11
  end
12
12
 
13
+ # Note: this SpanFilter implementation only handles traces in which child spans appear
14
+ # after parent spans in the trace array. If in the future child spans can be before
15
+ # parent spans, then the code below will need to be updated.
13
16
  def call(trace)
14
- black_list = trace.select(&method(:drop_it?))
15
-
16
- clean_trace(black_list, trace) while black_list.any?
17
-
18
- trace
17
+ deleted = Set.new
18
+
19
+ trace.delete_if do |span|
20
+ if deleted.include?(span.parent)
21
+ deleted << span
22
+ true
23
+ else
24
+ drop = drop_it?(span)
25
+ deleted << span if drop
26
+ drop
27
+ end
28
+ end
19
29
  end
20
30
 
21
31
  private
@@ -23,16 +33,6 @@ module Datadog
23
33
  def drop_it?(span)
24
34
  @criteria.call(span) rescue false
25
35
  end
26
-
27
- def clean_trace(black_list, trace)
28
- current = black_list.shift
29
-
30
- trace.delete(current)
31
-
32
- trace.each do |span|
33
- black_list << span if span.parent == current
34
- end
35
- end
36
36
  end
37
37
  end
38
38
  end