ddtrace 0.47.0 → 0.48.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 (100) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +4 -2
  3. data/.circleci/images/primary/Dockerfile-2.0.0 +11 -1
  4. data/.circleci/images/primary/Dockerfile-2.1.10 +11 -1
  5. data/.circleci/images/primary/Dockerfile-2.2.10 +11 -1
  6. data/.circleci/images/primary/Dockerfile-2.3.8 +10 -0
  7. data/.circleci/images/primary/Dockerfile-2.4.6 +10 -0
  8. data/.circleci/images/primary/Dockerfile-2.5.6 +10 -0
  9. data/.circleci/images/primary/Dockerfile-2.6.4 +10 -0
  10. data/.circleci/images/primary/Dockerfile-2.7.0 +10 -0
  11. data/.circleci/images/primary/Dockerfile-jruby-9.2-latest +10 -0
  12. data/.gitlab-ci.yml +18 -18
  13. data/.rubocop.yml +19 -0
  14. data/.rubocop_todo.yml +44 -3
  15. data/Appraisals +55 -1
  16. data/CHANGELOG.md +47 -1
  17. data/Gemfile +10 -0
  18. data/Rakefile +9 -0
  19. data/bin/ddtracerb +15 -0
  20. data/ddtrace.gemspec +4 -2
  21. data/docs/GettingStarted.md +36 -53
  22. data/docs/ProfilingDevelopment.md +88 -0
  23. data/integration/README.md +1 -2
  24. data/integration/apps/rack/Dockerfile +3 -0
  25. data/integration/apps/rack/script/build-images +1 -1
  26. data/integration/apps/rack/script/ci +1 -1
  27. data/integration/apps/rails-five/script/build-images +1 -1
  28. data/integration/apps/rails-five/script/ci +1 -1
  29. data/integration/apps/ruby/script/build-images +1 -1
  30. data/integration/apps/ruby/script/ci +1 -1
  31. data/integration/images/include/http-health-check +1 -1
  32. data/integration/images/wrk/scripts/entrypoint.sh +1 -1
  33. data/integration/script/build-images +1 -1
  34. data/lib/ddtrace.rb +1 -0
  35. data/lib/ddtrace/configuration.rb +39 -13
  36. data/lib/ddtrace/configuration/components.rb +85 -3
  37. data/lib/ddtrace/configuration/settings.rb +31 -0
  38. data/lib/ddtrace/contrib/active_record/configuration/makara_resolver.rb +30 -0
  39. data/lib/ddtrace/contrib/active_record/configuration/resolver.rb +9 -3
  40. data/lib/ddtrace/contrib/resque/configuration/settings.rb +17 -1
  41. data/lib/ddtrace/contrib/resque/patcher.rb +4 -4
  42. data/lib/ddtrace/contrib/resque/resque_job.rb +22 -1
  43. data/lib/ddtrace/contrib/shoryuken/configuration/settings.rb +1 -0
  44. data/lib/ddtrace/contrib/shoryuken/tracer.rb +7 -3
  45. data/lib/ddtrace/diagnostics/environment_logger.rb +1 -1
  46. data/lib/ddtrace/error.rb +2 -0
  47. data/lib/ddtrace/ext/profiling.rb +52 -0
  48. data/lib/ddtrace/ext/transport.rb +1 -0
  49. data/lib/ddtrace/metrics.rb +4 -0
  50. data/lib/ddtrace/profiling.rb +54 -0
  51. data/lib/ddtrace/profiling/backtrace_location.rb +32 -0
  52. data/lib/ddtrace/profiling/buffer.rb +41 -0
  53. data/lib/ddtrace/profiling/collectors/stack.rb +253 -0
  54. data/lib/ddtrace/profiling/encoding/profile.rb +31 -0
  55. data/lib/ddtrace/profiling/event.rb +13 -0
  56. data/lib/ddtrace/profiling/events/stack.rb +102 -0
  57. data/lib/ddtrace/profiling/exporter.rb +23 -0
  58. data/lib/ddtrace/profiling/ext/cpu.rb +54 -0
  59. data/lib/ddtrace/profiling/ext/cthread.rb +134 -0
  60. data/lib/ddtrace/profiling/ext/forking.rb +97 -0
  61. data/lib/ddtrace/profiling/flush.rb +41 -0
  62. data/lib/ddtrace/profiling/pprof/builder.rb +121 -0
  63. data/lib/ddtrace/profiling/pprof/converter.rb +85 -0
  64. data/lib/ddtrace/profiling/pprof/message_set.rb +12 -0
  65. data/lib/ddtrace/profiling/pprof/payload.rb +18 -0
  66. data/lib/ddtrace/profiling/pprof/pprof.proto +212 -0
  67. data/lib/ddtrace/profiling/pprof/pprof_pb.rb +81 -0
  68. data/lib/ddtrace/profiling/pprof/stack_sample.rb +90 -0
  69. data/lib/ddtrace/profiling/pprof/string_table.rb +10 -0
  70. data/lib/ddtrace/profiling/pprof/template.rb +114 -0
  71. data/lib/ddtrace/profiling/preload.rb +3 -0
  72. data/lib/ddtrace/profiling/profiler.rb +28 -0
  73. data/lib/ddtrace/profiling/recorder.rb +87 -0
  74. data/lib/ddtrace/profiling/scheduler.rb +84 -0
  75. data/lib/ddtrace/profiling/tasks/setup.rb +77 -0
  76. data/lib/ddtrace/profiling/transport/client.rb +12 -0
  77. data/lib/ddtrace/profiling/transport/http.rb +122 -0
  78. data/lib/ddtrace/profiling/transport/http/api.rb +43 -0
  79. data/lib/ddtrace/profiling/transport/http/api/endpoint.rb +90 -0
  80. data/lib/ddtrace/profiling/transport/http/api/instance.rb +36 -0
  81. data/lib/ddtrace/profiling/transport/http/api/spec.rb +40 -0
  82. data/lib/ddtrace/profiling/transport/http/builder.rb +28 -0
  83. data/lib/ddtrace/profiling/transport/http/client.rb +33 -0
  84. data/lib/ddtrace/profiling/transport/http/response.rb +21 -0
  85. data/lib/ddtrace/profiling/transport/io.rb +30 -0
  86. data/lib/ddtrace/profiling/transport/io/client.rb +27 -0
  87. data/lib/ddtrace/profiling/transport/io/response.rb +16 -0
  88. data/lib/ddtrace/profiling/transport/parcel.rb +17 -0
  89. data/lib/ddtrace/profiling/transport/request.rb +15 -0
  90. data/lib/ddtrace/profiling/transport/response.rb +8 -0
  91. data/lib/ddtrace/runtime/container.rb +11 -3
  92. data/lib/ddtrace/sampling/rule_sampler.rb +3 -9
  93. data/lib/ddtrace/tasks/exec.rb +48 -0
  94. data/lib/ddtrace/tasks/help.rb +14 -0
  95. data/lib/ddtrace/tracer.rb +21 -0
  96. data/lib/ddtrace/transport/io/client.rb +15 -8
  97. data/lib/ddtrace/transport/parcel.rb +4 -0
  98. data/lib/ddtrace/version.rb +3 -1
  99. data/lib/ddtrace/workers/runtime_metrics.rb +14 -1
  100. metadata +70 -9
@@ -0,0 +1,33 @@
1
+ require 'ddtrace/transport/http/client'
2
+ require 'ddtrace/profiling/transport/client'
3
+
4
+ module Datadog
5
+ module Profiling
6
+ module Transport
7
+ module HTTP
8
+ # Routes, encodes, and sends tracer data to the trace agent via HTTP.
9
+ class Client < Datadog::Transport::HTTP::Client
10
+ include Transport::Client
11
+
12
+ def send_profiling_flush(flush)
13
+ # Build a request
14
+ request = Profiling::Transport::Request.new(flush)
15
+ send_payload(request).tap do |response|
16
+ if response.ok?
17
+ Datadog.logger.debug('Successfully reported profiling data')
18
+ else
19
+ Datadog.logger.debug { "Failed to report profiling data -- #{response.inspect}" }
20
+ end
21
+ end
22
+ end
23
+
24
+ def send_payload(request)
25
+ send_request(request) do |api, env|
26
+ api.send_profiling_flush(env)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ require 'ddtrace/transport/http/response'
2
+ require 'ddtrace/profiling/transport/response'
3
+
4
+ module Datadog
5
+ module Profiling
6
+ module Transport
7
+ # HTTP transport behavior for profiling
8
+ module HTTP
9
+ # Response from HTTP transport for profiling
10
+ class Response
11
+ include Datadog::Transport::HTTP::Response
12
+ include Profiling::Transport::Response
13
+
14
+ def initialize(http_response, options = {})
15
+ super(http_response)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,30 @@
1
+ require 'ddtrace/profiling/transport/io/client'
2
+ require 'ddtrace/profiling/encoding/profile'
3
+
4
+ module Datadog
5
+ module Profiling
6
+ module Transport
7
+ # Namespace for profiling IO transport components
8
+ module IO
9
+ module_function
10
+
11
+ # Builds a new Profiling::Transport::IO::Client
12
+ def new(out, encoder, options = {})
13
+ Client.new(out, encoder, options)
14
+ end
15
+
16
+ # Builds a new Profiling::Transport::IO::Client with default settings
17
+ # Pass options to override any settings.
18
+ def default(options = {})
19
+ options = options.dup
20
+
21
+ new(
22
+ options.delete(:out) || $stdout,
23
+ options.delete(:encoder) || Profiling::Encoding::Profile::Protobuf,
24
+ options
25
+ )
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ require 'ddtrace/transport/io/client'
2
+ require 'ddtrace/profiling/transport/client'
3
+ require 'ddtrace/profiling/transport/request'
4
+ require 'ddtrace/profiling/transport/io/response'
5
+
6
+ module Datadog
7
+ module Profiling
8
+ module Transport
9
+ module IO
10
+ # IO transport for profiling
11
+ class Client < Datadog::Transport::IO::Client
12
+ include Transport::Client
13
+
14
+ def send_profiling_flush(flush)
15
+ # Build a request
16
+ request = Profiling::Transport::Request.new(flush)
17
+ send_request(request)
18
+ end
19
+
20
+ def build_response(_request, _data, result)
21
+ Profiling::Transport::IO::Response.new(result)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,16 @@
1
+ require 'ddtrace/transport/io/response'
2
+ require 'ddtrace/profiling/transport/response'
3
+
4
+ module Datadog
5
+ module Profiling
6
+ module Transport
7
+ # IO transport behavior for profiling
8
+ module IO
9
+ # Response from IO transport for profiling
10
+ class Response < Datadog::Transport::IO::Response
11
+ include Profiling::Transport::Response
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require 'ddtrace/transport/parcel'
2
+
3
+ module Datadog
4
+ module Profiling
5
+ module Transport
6
+ # Data transfer object for profiling data
7
+ class Parcel
8
+ include Datadog::Transport::Parcel
9
+
10
+ def encode_with(encoder)
11
+ # TODO: Determine encoding behavior
12
+ encoder.encode(data)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ require 'ddtrace/transport/request'
2
+ require 'ddtrace/profiling/transport/parcel'
3
+
4
+ module Datadog
5
+ module Profiling
6
+ module Transport
7
+ # Profiling request
8
+ class Request < Datadog::Transport::Request
9
+ def initialize(flush)
10
+ super(Parcel.new(flush))
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ module Datadog
2
+ module Profiling
3
+ module Transport
4
+ # Profiling response
5
+ module Response; end
6
+ end
7
+ end
8
+ end
@@ -10,6 +10,7 @@ module Datadog
10
10
 
11
11
  POD_REGEX = /(pod)?(#{UUID_PATTERN})(?:.slice)?$/.freeze
12
12
  CONTAINER_REGEX = /(#{UUID_PATTERN}|#{CONTAINER_PATTERN})(?:.scope)?$/.freeze
13
+ FARGATE_14_CONTAINER_REGEX = /[0-9a-f]{32}-[0-9]{10}/.freeze
13
14
 
14
15
  Descriptor = Struct.new(
15
16
  :platform,
@@ -42,12 +43,19 @@ module Datadog
42
43
  # Split path into parts
43
44
  parts = path.split('/')
44
45
  parts.shift # Remove leading empty part
45
- next if parts.length < 2
46
46
 
47
47
  # Read info from path
48
48
  platform = parts[0]
49
- container_id = parts[-1][CONTAINER_REGEX]
50
- task_uid = parts[-2][POD_REGEX]
49
+ container_id, task_uid = nil
50
+
51
+ case parts.length
52
+ when 2
53
+ container_id = parts[-1][CONTAINER_REGEX] || parts[-1][FARGATE_14_CONTAINER_REGEX]
54
+ else
55
+ platform = parts[0]
56
+ container_id = parts[-1][CONTAINER_REGEX]
57
+ task_uid = parts[-2][POD_REGEX]
58
+ end
51
59
 
52
60
  # If container ID wasn't found, ignore.
53
61
  # Path might describe a non-container environment.
@@ -5,6 +5,7 @@ require 'ddtrace/ext/priority'
5
5
  require 'ddtrace/ext/sampling'
6
6
  require 'ddtrace/sampler'
7
7
  require 'ddtrace/sampling/rate_limiter'
8
+ require 'ddtrace/sampling/rule'
8
9
 
9
10
  module Datadog
10
11
  module Sampling
@@ -45,15 +46,8 @@ module Datadog
45
46
  @default_sampler = if default_sampler
46
47
  default_sampler
47
48
  elsif default_sample_rate
48
- # We want to allow 0.0 to drop all traces, but \RateSampler
49
- # considers 0.0 an invalid rate and falls back to 100% sampling.
50
- #
51
- # We address that here by not setting the rate in the constructor,
52
- # but using the setter method.
53
- #
54
- # We don't want to make this change directly to \RateSampler
55
- # because it breaks its current contract to existing users.
56
- Datadog::RateSampler.new.tap { |s| s.sample_rate = default_sample_rate }
49
+ # Add to the end of the rule list a rule always matches any span
50
+ @rules << SimpleRule.new(sample_rate: default_sample_rate)
57
51
  else
58
52
  RateByServiceSampler.new(1.0, env: -> { Datadog.tracer.tags[:env] })
59
53
  end
@@ -0,0 +1,48 @@
1
+ module Datadog
2
+ module Tasks
3
+ # Wraps command with Datadog tracing
4
+ class Exec
5
+ attr_reader :args
6
+
7
+ def initialize(args)
8
+ @args = args
9
+ end
10
+
11
+ def run
12
+ set_rubyopt!
13
+ exec_with_error_handling(args)
14
+ end
15
+
16
+ def rubyopts
17
+ [
18
+ '-rddtrace/profiling/preload'
19
+ ]
20
+ end
21
+
22
+ private
23
+
24
+ def set_rubyopt!
25
+ if ENV.key?('RUBYOPT')
26
+ ENV['RUBYOPT'] += " #{rubyopts.join(' ')}"
27
+ else
28
+ ENV['RUBYOPT'] = rubyopts.join(' ')
29
+ end
30
+ end
31
+
32
+ # If there's an error here, rather than throwing a cryptic stack trace, let's instead have clearer messages, and
33
+ # follow the same status codes as the shell uses
34
+ # See also:
35
+ # * https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
36
+ # * https://github.com/rubygems/rubygems/blob/dd93966cac224532035deda533cba2685dfa30cc/bundler/lib/bundler/cli/exec.rb#L45
37
+ def exec_with_error_handling(args)
38
+ Kernel.exec(*args)
39
+ rescue Errno::ENOENT => e
40
+ Kernel.warn "ddtracerb exec failed: #{e.message} (command was '#{args.join(' ')}')"
41
+ Kernel.exit 127
42
+ rescue Errno::EACCES, Errno::ENOEXEC => e
43
+ Kernel.warn "ddtracerb exec failed: #{e.message} (command was '#{args.join(' ')}')"
44
+ Kernel.exit 126
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,14 @@
1
+ module Datadog
2
+ module Tasks
3
+ # Prints help message for usage of `ddtrace`
4
+ class Help
5
+ def run
6
+ puts %(
7
+ Usage: ddtrace [command] [arguments]
8
+ exec [command]: Executes command with tracing & profiling preloaded.
9
+ help: Prints this help message.
10
+ )
11
+ end
12
+ end
13
+ end
14
+ end
@@ -10,6 +10,7 @@ require 'ddtrace/runtime/identity'
10
10
  require 'ddtrace/sampler'
11
11
  require 'ddtrace/sampling'
12
12
  require 'ddtrace/correlation'
13
+ require 'ddtrace/event'
13
14
  require 'ddtrace/utils/only_once'
14
15
 
15
16
  # \Datadog global namespace that includes all tracing functionality for Tracer and Span classes.
@@ -315,6 +316,10 @@ module Datadog
315
316
  end
316
317
  end
317
318
 
319
+ def trace_completed
320
+ @trace_completed ||= TraceCompleted.new
321
+ end
322
+
318
323
  # Record the given +context+. For compatibility with previous versions,
319
324
  # +context+ can also be a span. It is similar to the +child_of+ argument,
320
325
  # method will figure out what to do, submitting a +span+ for recording
@@ -366,6 +371,22 @@ module Datadog
366
371
  end
367
372
 
368
373
  @writer.write(trace)
374
+ trace_completed.publish(trace)
375
+ end
376
+
377
+ # Triggered whenever a trace is completed
378
+ class TraceCompleted < Datadog::Event
379
+ def initialize
380
+ super(:trace_completed)
381
+ end
382
+
383
+ # NOTE: Ignore Rubocop rule. This definition allows for
384
+ # description of and constraints on arguments.
385
+ # rubocop:disable Lint/UselessMethodDefinition
386
+ def publish(trace)
387
+ super(trace)
388
+ end
389
+ # rubocop:enable Lint/UselessMethodDefinition
369
390
  end
370
391
 
371
392
  # TODO: Move this kind of configuration building out of the tracer.
@@ -12,19 +12,24 @@ module Datadog
12
12
  :encoder,
13
13
  :out
14
14
 
15
- def initialize(out, encoder)
15
+ def initialize(out, encoder, options = {})
16
16
  @out = out
17
17
  @encoder = encoder
18
+
19
+ @request_block = options.fetch(:request, method(:send_default_request))
20
+ @encode_block = options.fetch(:encode, method(:encode_data))
21
+ @write_block = options.fetch(:write, method(:write_data))
22
+ @response_block = options.fetch(:response, method(:build_response))
18
23
  end
19
24
 
20
25
  def send_request(request)
21
26
  # Write data to IO
22
27
  # If block is given, allow it to handle writing
23
- # Otherwise use default encoding.
28
+ # Otherwise do a standard encode/write/response.
24
29
  response = if block_given?
25
30
  yield(out, request)
26
31
  else
27
- send_default_request(out, request)
32
+ @request_block.call(out, request)
28
33
  end
29
34
 
30
35
  # Update statistics
@@ -48,8 +53,6 @@ module Datadog
48
53
  InternalErrorResponse.new(e)
49
54
  end
50
55
 
51
- protected
52
-
53
56
  def encode_data(encoder, request)
54
57
  request.parcel.encode_with(encoder)
55
58
  end
@@ -58,17 +61,21 @@ module Datadog
58
61
  out.puts(data)
59
62
  end
60
63
 
64
+ def build_response(_request, _data, result)
65
+ IO::Response.new(result)
66
+ end
67
+
61
68
  private
62
69
 
63
70
  def send_default_request(out, request)
64
71
  # Encode data
65
- data = encode_data(encoder, request)
72
+ data = @encode_block.call(encoder, request)
66
73
 
67
74
  # Write to IO
68
- result = write_data(out, data)
75
+ result = @write_block.call(out, data)
69
76
 
70
77
  # Generate a response
71
- IO::Response.new(result)
78
+ @response_block.call(request, data, result)
72
79
  end
73
80
  end
74
81
  end
@@ -8,6 +8,10 @@ module Datadog
8
8
  def initialize(data)
9
9
  @data = data
10
10
  end
11
+
12
+ def encode_with(encoder)
13
+ raise NotImplementedError
14
+ end
11
15
  end
12
16
  end
13
17
  end
@@ -1,12 +1,14 @@
1
1
  module Datadog
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 47
4
+ MINOR = 48
5
5
  PATCH = 0
6
6
  PRE = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.')
9
9
 
10
+ # Support for Ruby < 2.1 is currently deprecated in the tracer.
11
+ # Support will be dropped in the near future.
10
12
  MINIMUM_RUBY_VERSION = '2.0.0'.freeze
11
13
  end
12
14
  end
@@ -20,7 +20,7 @@ module Datadog
20
20
  :metrics
21
21
 
22
22
  def initialize(options = {})
23
- @metrics = options.fetch(:metrics, Runtime::Metrics.new)
23
+ @metrics = options.fetch(:metrics) { Runtime::Metrics.new }
24
24
 
25
25
  # Workers::Async::Thread settings
26
26
  self.fork_policy = options.fetch(:fork_policy, Workers::Async::Thread::FORK_POLICY_STOP)
@@ -43,6 +43,19 @@ module Datadog
43
43
  metrics.associate_with_span(*args).tap { perform }
44
44
  end
45
45
 
46
+ # TODO: `close_metrics` is only needed because
47
+ # Datadog::Components directly manipulates the lifecycle of
48
+ # Runtime::Metrics.statsd instances.
49
+ # This should be avoided, as it prevents this class from
50
+ # ensuring correct resource decommission of its internal
51
+ # dependencies.
52
+ def stop(*args, close_metrics: true)
53
+ self.enabled = false
54
+ result = super(*args)
55
+ @metrics.close if close_metrics
56
+ result
57
+ end
58
+
46
59
  def_delegators \
47
60
  :metrics,
48
61
  :register_service