ddtrace 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -1
  3. data/CONTRIBUTING.md +1 -1
  4. data/README.md +7 -2
  5. data/ddtrace.gemspec +5 -2
  6. data/docs/GettingStarted.md +27 -3
  7. data/docs/ProfilingDevelopment.md +27 -28
  8. data/docs/UpgradeGuide.md +1 -1
  9. data/ext/ddtrace_profiling_loader/ddtrace_profiling_loader.c +1 -1
  10. data/ext/ddtrace_profiling_loader/extconf.rb +1 -0
  11. data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +6 -5
  12. data/ext/ddtrace_profiling_native_extension/clock_id.h +1 -1
  13. data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +1 -1
  14. data/ext/ddtrace_profiling_native_extension/clock_id_noop.c +1 -1
  15. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +269 -0
  16. data/ext/ddtrace_profiling_native_extension/collectors_stack.c +12 -12
  17. data/ext/ddtrace_profiling_native_extension/collectors_stack.h +9 -0
  18. data/ext/ddtrace_profiling_native_extension/extconf.rb +44 -3
  19. data/ext/ddtrace_profiling_native_extension/http_transport.c +341 -0
  20. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +92 -4
  21. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +76 -1
  22. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +3 -0
  23. data/ext/ddtrace_profiling_native_extension/profiling.c +4 -0
  24. data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +33 -0
  25. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +18 -10
  26. data/ext/ddtrace_profiling_native_extension/stack_recorder.h +10 -1
  27. data/lib/datadog/core/configuration/components.rb +39 -24
  28. data/lib/datadog/core/configuration/settings.rb +8 -1
  29. data/lib/datadog/core/environment/platform.rb +40 -0
  30. data/lib/datadog/core/utils.rb +1 -1
  31. data/lib/datadog/opentracer/thread_local_scope_manager.rb +26 -3
  32. data/lib/datadog/profiling/collectors/code_provenance.rb +1 -0
  33. data/lib/datadog/profiling/collectors/cpu_and_wall_time.rb +42 -0
  34. data/lib/datadog/profiling/collectors/stack.rb +2 -0
  35. data/lib/datadog/profiling/encoding/profile.rb +7 -11
  36. data/lib/datadog/profiling/exporter.rb +58 -9
  37. data/lib/datadog/profiling/ext/forking.rb +8 -8
  38. data/lib/datadog/profiling/ext.rb +2 -15
  39. data/lib/datadog/profiling/flush.rb +25 -53
  40. data/lib/datadog/profiling/http_transport.rb +131 -0
  41. data/lib/datadog/profiling/old_ext.rb +42 -0
  42. data/lib/datadog/profiling/{recorder.rb → old_recorder.rb} +20 -31
  43. data/lib/datadog/profiling/scheduler.rb +24 -43
  44. data/lib/datadog/profiling/transport/http/api/endpoint.rb +9 -31
  45. data/lib/datadog/profiling/transport/http/client.rb +5 -3
  46. data/lib/datadog/profiling/transport/http/response.rb +0 -2
  47. data/lib/datadog/profiling/transport/http.rb +1 -1
  48. data/lib/datadog/profiling.rb +3 -3
  49. data/lib/datadog/tracing/context_provider.rb +17 -1
  50. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -0
  51. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +1 -0
  52. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +1 -1
  53. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor.rb +4 -0
  54. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +35 -0
  55. data/lib/datadog/tracing/contrib/pg/ext.rb +31 -0
  56. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +129 -0
  57. data/lib/datadog/tracing/contrib/pg/integration.rb +43 -0
  58. data/lib/datadog/tracing/contrib/pg/patcher.rb +31 -0
  59. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +3 -0
  60. data/lib/datadog/tracing/contrib/rails/framework.rb +2 -1
  61. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +1 -0
  62. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +1 -1
  63. data/lib/datadog/tracing/contrib.rb +1 -0
  64. data/lib/datadog/tracing/distributed/headers/b3.rb +1 -1
  65. data/lib/datadog/tracing/distributed/headers/b3_single.rb +4 -4
  66. data/lib/datadog/tracing/distributed/headers/datadog.rb +1 -1
  67. data/lib/datadog/tracing/distributed/headers/parser.rb +37 -0
  68. data/lib/datadog/tracing/distributed/helpers.rb +34 -0
  69. data/lib/datadog/tracing/distributed/metadata/b3.rb +55 -0
  70. data/lib/datadog/tracing/distributed/metadata/b3_single.rb +66 -0
  71. data/lib/datadog/tracing/distributed/metadata/datadog.rb +73 -0
  72. data/lib/datadog/tracing/distributed/metadata/parser.rb +34 -0
  73. data/lib/datadog/tracing/metadata/ext.rb +25 -0
  74. data/lib/datadog/tracing/metadata/tagging.rb +6 -0
  75. data/lib/datadog/tracing/propagation/grpc.rb +65 -55
  76. data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -2
  77. data/lib/datadog/tracing/sampling/span/matcher.rb +80 -0
  78. data/lib/datadog/tracing/span.rb +21 -1
  79. data/lib/datadog/tracing/span_operation.rb +2 -1
  80. data/lib/ddtrace/version.rb +1 -1
  81. metadata +24 -13
  82. data/lib/datadog/profiling/transport/client.rb +0 -16
  83. data/lib/datadog/profiling/transport/io/client.rb +0 -29
  84. data/lib/datadog/profiling/transport/io/response.rb +0 -18
  85. data/lib/datadog/profiling/transport/io.rb +0 -32
  86. data/lib/datadog/profiling/transport/parcel.rb +0 -19
  87. data/lib/datadog/profiling/transport/request.rb +0 -17
  88. data/lib/datadog/profiling/transport/response.rb +0 -10
  89. data/lib/datadog/tracing/distributed/parser.rb +0 -70
@@ -5,65 +5,37 @@ require 'datadog/core/environment/socket'
5
5
 
6
6
  module Datadog
7
7
  module Profiling
8
+ # Represents a collection of events of a specific type being flushed.
9
+ EventGroup = Struct.new(:event_class, :events)
10
+
8
11
  # Entity class used to represent metadata for a given profile
9
- OldFlush = Struct.new(
10
- :start,
11
- :finish,
12
- :event_groups,
13
- :event_count,
14
- :code_provenance,
15
- :runtime_id,
16
- :service,
17
- :env,
18
- :version,
19
- :host,
20
- :language,
21
- :runtime_engine,
22
- :runtime_platform,
23
- :runtime_version,
24
- :profiler_version,
25
- :tags
26
- ) do
12
+ class Flush
13
+ attr_reader \
14
+ :start,
15
+ :finish,
16
+ :pprof_file_name,
17
+ :pprof_data, # gzipped pprof bytes
18
+ :code_provenance_file_name,
19
+ :code_provenance_data, # gzipped json bytes
20
+ :tags_as_array
21
+
27
22
  def initialize(
28
23
  start:,
29
24
  finish:,
30
- event_groups:,
31
- event_count:,
32
- code_provenance:,
33
- runtime_id: Core::Environment::Identity.id,
34
- service: Datadog.configuration.service,
35
- env: Datadog.configuration.env,
36
- version: Datadog.configuration.version,
37
- host: Core::Environment::Socket.hostname,
38
- language: Core::Environment::Identity.lang,
39
- runtime_engine: Core::Environment::Identity.lang_engine,
40
- runtime_platform: Core::Environment::Identity.lang_platform,
41
- runtime_version: Core::Environment::Identity.lang_version,
42
- profiler_version: Core::Environment::Identity.tracer_version,
43
- tags: Datadog.configuration.tags
25
+ pprof_file_name:,
26
+ pprof_data:,
27
+ code_provenance_file_name:,
28
+ code_provenance_data:,
29
+ tags_as_array:
44
30
  )
45
- super(
46
- start,
47
- finish,
48
- event_groups,
49
- event_count,
50
- code_provenance,
51
- runtime_id,
52
- service,
53
- env,
54
- version,
55
- host,
56
- language,
57
- runtime_engine,
58
- runtime_platform,
59
- runtime_version,
60
- profiler_version,
61
- tags,
62
- )
31
+ @start = start
32
+ @finish = finish
33
+ @pprof_file_name = pprof_file_name
34
+ @pprof_data = pprof_data
35
+ @code_provenance_file_name = code_provenance_file_name
36
+ @code_provenance_data = code_provenance_data
37
+ @tags_as_array = tags_as_array
63
38
  end
64
39
  end
65
-
66
- # Represents a collection of events of a specific type being flushed.
67
- EventGroup = Struct.new(:event_class, :events).freeze
68
40
  end
69
41
  end
@@ -0,0 +1,131 @@
1
+ # typed: false
2
+
3
+ module Datadog
4
+ module Profiling
5
+ # Used to report profiling data to Datadog.
6
+ # Methods prefixed with _native_ are implemented in `http_transport.c`
7
+ class HttpTransport
8
+ def initialize(agent_settings:, site:, api_key:, upload_timeout_seconds:)
9
+ @upload_timeout_milliseconds = (upload_timeout_seconds * 1_000).to_i
10
+
11
+ validate_agent_settings(agent_settings)
12
+
13
+ @exporter_configuration =
14
+ if agentless?(site, api_key)
15
+ [:agentless, site, api_key]
16
+ else
17
+ [:agent, base_url_from(agent_settings)]
18
+ end
19
+
20
+ status, result = validate_exporter(@exporter_configuration)
21
+
22
+ raise(ArgumentError, "Failed to initialize transport: #{result}") if status == :error
23
+ end
24
+
25
+ def export(flush)
26
+ status, result = do_export(
27
+ exporter_configuration: @exporter_configuration,
28
+ upload_timeout_milliseconds: @upload_timeout_milliseconds,
29
+
30
+ # why "timespec"?
31
+ # libddprof represents time using POSIX's struct timespec, see
32
+ # https://www.gnu.org/software/libc/manual/html_node/Time-Types.html
33
+ # aka it represents the seconds part separate from the nanoseconds part
34
+ start_timespec_seconds: flush.start.tv_sec,
35
+ start_timespec_nanoseconds: flush.start.tv_nsec,
36
+ finish_timespec_seconds: flush.finish.tv_sec,
37
+ finish_timespec_nanoseconds: flush.finish.tv_nsec,
38
+
39
+ pprof_file_name: flush.pprof_file_name,
40
+ pprof_data: flush.pprof_data,
41
+ code_provenance_file_name: flush.code_provenance_file_name,
42
+ code_provenance_data: flush.code_provenance_data,
43
+
44
+ tags_as_array: flush.tags_as_array,
45
+ )
46
+
47
+ if status == :ok
48
+ if (200..299).cover?(result)
49
+ Datadog.logger.debug('Successfully reported profiling data')
50
+ true
51
+ else
52
+ Datadog.logger.error("Failed to report profiling data: server returned unexpected HTTP #{result} status code")
53
+ false
54
+ end
55
+ else
56
+ Datadog.logger.error("Failed to report profiling data: #{result}")
57
+ false
58
+ end
59
+ end
60
+
61
+ # Used to log soft failures in `ddprof_ffi_Vec_tag_push` (e.g. we still report the profile in these cases)
62
+ # Called from native code
63
+ def self.log_failure_to_process_tag(failure_details)
64
+ Datadog.logger.warn("Failed to add tag to profiling request: #{failure_details}")
65
+ end
66
+
67
+ private
68
+
69
+ def base_url_from(agent_settings)
70
+ case agent_settings.adapter
71
+ when Datadog::Transport::Ext::HTTP::ADAPTER
72
+ "#{agent_settings.ssl ? 'https' : 'http'}://#{agent_settings.hostname}:#{agent_settings.port}/"
73
+ when Datadog::Transport::Ext::UnixSocket::ADAPTER
74
+ "unix://#{agent_settings.uds_path}"
75
+ else
76
+ raise ArgumentError, "Unexpected adapter: #{agent_settings.adapter}"
77
+ end
78
+ end
79
+
80
+ def validate_agent_settings(agent_settings)
81
+ supported_adapters = [Datadog::Transport::Ext::HTTP::ADAPTER, Datadog::Transport::Ext::UnixSocket::ADAPTER]
82
+ unless supported_adapters.include?(agent_settings.adapter)
83
+ raise ArgumentError, "Unsupported transport configuration for profiling: Adapter #{agent_settings.adapter} " \
84
+ ' is not supported'
85
+ end
86
+
87
+ if agent_settings.deprecated_for_removal_transport_configuration_proc
88
+ Datadog.logger.warn(
89
+ 'Ignoring custom c.tracing.transport_options setting as the profiler does not support it.'
90
+ )
91
+ end
92
+ end
93
+
94
+ def agentless?(site, api_key)
95
+ site && api_key && Core::Environment::VariableHelpers.env_to_bool(Profiling::Ext::ENV_AGENTLESS, false)
96
+ end
97
+
98
+ def validate_exporter(exporter_configuration)
99
+ self.class._native_validate_exporter(exporter_configuration)
100
+ end
101
+
102
+ def do_export(
103
+ exporter_configuration:,
104
+ upload_timeout_milliseconds:,
105
+ start_timespec_seconds:,
106
+ start_timespec_nanoseconds:,
107
+ finish_timespec_seconds:,
108
+ finish_timespec_nanoseconds:,
109
+ pprof_file_name:,
110
+ pprof_data:,
111
+ code_provenance_file_name:,
112
+ code_provenance_data:,
113
+ tags_as_array:
114
+ )
115
+ self.class._native_do_export(
116
+ exporter_configuration,
117
+ upload_timeout_milliseconds,
118
+ start_timespec_seconds,
119
+ start_timespec_nanoseconds,
120
+ finish_timespec_seconds,
121
+ finish_timespec_nanoseconds,
122
+ pprof_file_name,
123
+ pprof_data,
124
+ code_provenance_file_name,
125
+ code_provenance_data,
126
+ tags_as_array,
127
+ )
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,42 @@
1
+ # typed: true
2
+
3
+ module Datadog
4
+ module Profiling
5
+ # NOTE: This OldExt file is temporary and expected to be removed once the migration to the new `HttpTransport` class
6
+ # is complete
7
+ module OldExt
8
+ module Transport
9
+ module HTTP
10
+ URI_TEMPLATE_DD_API = 'https://intake.profile.%s/'.freeze
11
+
12
+ FORM_FIELD_RECORDING_START = 'start'.freeze
13
+ FORM_FIELD_RECORDING_END = 'end'.freeze
14
+ FORM_FIELD_FAMILY = 'family'.freeze
15
+ FORM_FIELD_TAG_ENV = 'env'.freeze
16
+ FORM_FIELD_TAG_HOST = 'host'.freeze
17
+ FORM_FIELD_TAG_LANGUAGE = 'language'.freeze
18
+ FORM_FIELD_TAG_PID = 'process_id'.freeze
19
+ FORM_FIELD_TAG_PROFILER_VERSION = 'profiler_version'.freeze
20
+ FORM_FIELD_TAG_RUNTIME = 'runtime'.freeze
21
+ FORM_FIELD_TAG_RUNTIME_ENGINE = 'runtime_engine'.freeze
22
+ FORM_FIELD_TAG_RUNTIME_ID = 'runtime-id'.freeze
23
+ FORM_FIELD_TAG_RUNTIME_PLATFORM = 'runtime_platform'.freeze
24
+ FORM_FIELD_TAG_RUNTIME_VERSION = 'runtime_version'.freeze
25
+ FORM_FIELD_TAG_SERVICE = 'service'.freeze
26
+ FORM_FIELD_TAG_VERSION = 'version'.freeze
27
+ FORM_FIELD_TAGS = 'tags'.freeze
28
+ FORM_FIELD_INTAKE_VERSION = 'version'.freeze
29
+
30
+ HEADER_CONTENT_TYPE = 'Content-Type'.freeze
31
+ HEADER_CONTENT_TYPE_OCTET_STREAM = 'application/octet-stream'.freeze
32
+
33
+ FORM_FIELD_PPROF_DATA = 'data[rubyprofile.pprof]'.freeze
34
+ PPROF_DEFAULT_FILENAME = 'rubyprofile.pprof.gz'.freeze
35
+
36
+ FORM_FIELD_CODE_PROVENANCE_DATA = 'data[code-provenance.json]'.freeze
37
+ CODE_PROVENANCE_FILENAME = 'code-provenance.json.gz'.freeze
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,35 +1,22 @@
1
1
  # typed: true
2
2
 
3
3
  require 'datadog/profiling/buffer'
4
- require 'datadog/profiling/flush'
4
+ require 'datadog/profiling/encoding/profile'
5
5
 
6
6
  module Datadog
7
7
  module Profiling
8
- # Stores profiling events gathered by `Collector`s
9
- class Recorder
8
+ # Stores profiling events gathered by the `Stack` collector
9
+ class OldRecorder
10
10
  attr_reader :max_size
11
11
 
12
- # TODO: Why does the Recorder directly reference the `code_provenance_collector`?
13
- #
14
- # For starters, this is weird/a problem because the relationship is supposed to go in the other direction:
15
- # collectors are supposed to record their results in the `Recorder`, rather than the `Recorder` having to know
16
- # about individual collectors.
17
- #
18
- # But the `code_provenance_collector` is different from other existing and potential collectors because it is not
19
- # asynchronous. It does not gather data over time and record it as it goes. Instead, you call it once per profile,
20
- # synchronously, and just use what it spits out.
21
- #
22
- # The current design of the `Recorder` is quite tied to the asynchronous model. Modifying our current design
23
- # to support synchronous collectors is non-trivial, and I decided not to go through with it because we're
24
- # soon going to replace the `Recorder` and many other existing classes with a
25
- # [libddprof](https://github.com/datadog/libddprof)-based implementation, and thus I don't think massive refactors
26
- # are worth it before moving to libddprof.
27
-
28
- def initialize(event_classes, max_size, code_provenance_collector:, last_flush_time: Time.now.utc)
12
+ def initialize(
13
+ event_classes,
14
+ max_size,
15
+ last_flush_time: Time.now.utc
16
+ )
29
17
  @buffers = {}
30
18
  @last_flush_time = last_flush_time
31
19
  @max_size = max_size
32
- @code_provenance_collector = code_provenance_collector
33
20
 
34
21
  # Add a buffer for each class
35
22
  event_classes.each do |event_class|
@@ -60,7 +47,7 @@ module Datadog
60
47
  end
61
48
  end
62
49
 
63
- def flush
50
+ def serialize
64
51
  event_count = 0
65
52
 
66
53
  event_groups, start, finish = update_time do
@@ -73,15 +60,17 @@ module Datadog
73
60
  end.compact
74
61
  end
75
62
 
76
- code_provenance = @code_provenance_collector.refresh.generate_json if @code_provenance_collector
63
+ return if event_count.zero? # We don't want to report empty profiles
77
64
 
78
- OldFlush.new(
79
- start: start,
80
- finish: finish,
81
- event_groups: event_groups,
82
- event_count: event_count,
83
- code_provenance: code_provenance,
84
- )
65
+ encoded_pprof =
66
+ Datadog::Profiling::Encoding::Profile::Protobuf.encode(
67
+ event_count: event_count,
68
+ event_groups: event_groups,
69
+ start: start,
70
+ finish: finish,
71
+ )
72
+
73
+ [start, finish, encoded_pprof]
85
74
  end
86
75
 
87
76
  # NOTE: Remember that if the recorder is being accessed by multiple threads, this is an inherently racy operation.
@@ -89,7 +78,7 @@ module Datadog
89
78
  @buffers.values.all?(&:empty?)
90
79
  end
91
80
 
92
- # Error when event of an unknown type is used with the Recorder
81
+ # Error when event of an unknown type is used with the OldRecorder
93
82
  class UnknownEventError < StandardError
94
83
  attr_reader :event_class
95
84
 
@@ -7,36 +7,35 @@ require 'datadog/core/workers/polling'
7
7
 
8
8
  module Datadog
9
9
  module Profiling
10
- # Periodically (every DEFAULT_INTERVAL_SECONDS) takes data from the `Recorder` and pushes them to all configured
11
- # `Exporter`s. Runs on its own background thread.
10
+ # Periodically (every DEFAULT_INTERVAL_SECONDS) takes a profile from the `Exporter` and reports it using the
11
+ # configured transport. Runs on its own background thread.
12
12
  class Scheduler < Core::Worker
13
13
  include Core::Workers::Polling
14
14
 
15
15
  DEFAULT_INTERVAL_SECONDS = 60
16
16
  MINIMUM_INTERVAL_SECONDS = 0
17
17
 
18
- # Profiles with duration less than this will not be reported
19
- PROFILE_DURATION_THRESHOLD_SECONDS = 1
20
-
21
18
  # We sleep for at most this duration seconds before reporting data to avoid multi-process applications all
22
19
  # reporting profiles at the exact same time
23
20
  DEFAULT_FLUSH_JITTER_MAXIMUM_SECONDS = 3
24
21
 
25
- private_constant :DEFAULT_INTERVAL_SECONDS, :MINIMUM_INTERVAL_SECONDS, :PROFILE_DURATION_THRESHOLD_SECONDS
22
+ private
26
23
 
27
24
  attr_reader \
28
- :exporters,
29
- :recorder
25
+ :exporter,
26
+ :transport
27
+
28
+ public
30
29
 
31
30
  def initialize(
32
- recorder,
33
- exporters,
31
+ exporter:,
32
+ transport:,
34
33
  fork_policy: Core::Workers::Async::Thread::FORK_POLICY_RESTART, # Restart in forks by default
35
34
  interval: DEFAULT_INTERVAL_SECONDS,
36
35
  enabled: true
37
36
  )
38
- @recorder = recorder
39
- @exporters = [exporters].flatten
37
+ @exporter = exporter
38
+ @transport = transport
40
39
 
41
40
  # Workers::Async::Thread settings
42
41
  self.fork_policy = fork_policy
@@ -68,23 +67,22 @@ module Datadog
68
67
  end
69
68
 
70
69
  def after_fork
71
- # Clear recorder's buffers by flushing events.
72
- # Objects from parent process will copy-on-write,
73
- # and we don't want to send events for the wrong process.
74
- recorder.flush
70
+ # Clear any existing profiling state.
71
+ # We don't want the child process to report profiling data from its parent.
72
+ exporter.flush
75
73
  end
76
74
 
77
75
  # Configure Workers::IntervalLoop to not report immediately when scheduler starts
78
76
  #
79
77
  # When a scheduler gets created (or reset), we don't want it to immediately try to flush; we want it to wait for
80
78
  # the loop wait time first. This avoids an issue where the scheduler reported a mostly-empty profile if the
81
- # application just started but this thread took a bit longer so there's already samples in the recorder.
79
+ # application just started but this thread took a bit longer so there's already profiling data in the exporter.
82
80
  def loop_wait_before_first_iteration?
83
81
  true
84
82
  end
85
83
 
86
84
  def work_pending?
87
- !recorder.empty?
85
+ !exporter.empty?
88
86
  end
89
87
 
90
88
  private
@@ -100,16 +98,10 @@ module Datadog
100
98
  end
101
99
 
102
100
  def flush_events
103
- # Get events from recorder
104
- flush = recorder.flush
101
+ # Collect data to be exported
102
+ flush = exporter.flush
105
103
 
106
- if duration_below_threshold?(flush)
107
- Datadog.logger.debug do
108
- "Skipped exporting profiling events as profile duration is below minimum (#{flush.event_count} events skipped)"
109
- end
110
-
111
- return flush
112
- end
104
+ return false unless flush
113
105
 
114
106
  # Sleep for a bit to cause misalignment between profilers in multi-process applications
115
107
  #
@@ -127,24 +119,13 @@ module Datadog
127
119
  sleep(jitter_seconds)
128
120
  end
129
121
 
130
- # Send events to each exporter
131
- if flush.event_count > 0
132
- exporters.each do |exporter|
133
- begin
134
- exporter.export(flush)
135
- rescue StandardError => e
136
- Datadog.logger.error(
137
- "Unable to export #{flush.event_count} profiling events. Cause: #{e} Location: #{Array(e.backtrace).first}"
138
- )
139
- end
140
- end
122
+ begin
123
+ transport.export(flush)
124
+ rescue StandardError => e
125
+ Datadog.logger.error("Unable to report profile. Cause: #{e} Location: #{Array(e.backtrace).first}")
141
126
  end
142
127
 
143
- flush
144
- end
145
-
146
- def duration_below_threshold?(flush)
147
- (flush.finish - flush.start) < PROFILE_DURATION_THRESHOLD_SECONDS
128
+ true
148
129
  end
149
130
  end
150
131
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'datadog/core/utils/compression'
4
4
  require 'datadog/core/vendor/multipart-post/multipart/post/composite_read_io'
5
- require 'datadog/profiling/ext'
5
+ require 'datadog/profiling/old_ext'
6
6
  require 'datadog/profiling/transport/http/response'
7
7
  require 'ddtrace/transport/http/api/endpoint'
8
8
 
@@ -13,7 +13,7 @@ module Datadog
13
13
  module API
14
14
  # Datadog API endpoint for profiling
15
15
  class Endpoint < Datadog::Transport::HTTP::API::Endpoint
16
- include Profiling::Ext::Transport::HTTP
16
+ include Profiling::OldExt::Transport::HTTP
17
17
 
18
18
  # These tags are read from the flush object (see below) directly and so we ignore any extra copies that
19
19
  # may come in the tags hash to avoid duplicates.
@@ -23,7 +23,7 @@ module Datadog
23
23
  attr_reader \
24
24
  :encoder
25
25
 
26
- def initialize(path, encoder)
26
+ def initialize(path, encoder = nil)
27
27
  super(:post, path)
28
28
  @encoder = encoder
29
29
  end
@@ -40,48 +40,26 @@ module Datadog
40
40
  end
41
41
 
42
42
  def build_form(env)
43
- flush = env.request.parcel.data
43
+ flush = env.request
44
44
  pprof_file = build_pprof(flush)
45
45
 
46
46
  form = {
47
47
  FORM_FIELD_INTAKE_VERSION => '3', # Aka 1.3 intake format
48
48
  FORM_FIELD_RECORDING_START => flush.start.utc.iso8601,
49
49
  FORM_FIELD_RECORDING_END => flush.finish.utc.iso8601,
50
- FORM_FIELD_TAGS => [
51
- "#{FORM_FIELD_TAG_RUNTIME}:#{flush.language}",
52
- "#{FORM_FIELD_TAG_RUNTIME_ID}:#{flush.runtime_id}",
53
- "#{FORM_FIELD_TAG_RUNTIME_ENGINE}:#{flush.runtime_engine}",
54
- "#{FORM_FIELD_TAG_RUNTIME_PLATFORM}:#{flush.runtime_platform}",
55
- "#{FORM_FIELD_TAG_RUNTIME_VERSION}:#{flush.runtime_version}",
56
- "#{FORM_FIELD_TAG_PID}:#{Process.pid}",
57
- "#{FORM_FIELD_TAG_PROFILER_VERSION}:#{flush.profiler_version}",
58
- # NOTE: Redundant w/ 'runtime'; may want to remove this later.
59
- "#{FORM_FIELD_TAG_LANGUAGE}:#{flush.language}",
60
- "#{FORM_FIELD_TAG_HOST}:#{flush.host}",
61
- *flush
62
- .tags
63
- .reject { |tag_key| TAGS_TO_IGNORE_IN_TAGS_HASH.include?(tag_key) }
64
- .map { |tag_key, tag_value| "#{tag_key}:#{tag_value}" }
65
- ],
50
+ FORM_FIELD_TAGS => flush.tags_as_array.map { |key, value| "#{key}:#{value}" },
66
51
  FORM_FIELD_PPROF_DATA => pprof_file,
67
- FORM_FIELD_FAMILY => flush.language,
52
+ FORM_FIELD_FAMILY => 'ruby',
68
53
  }
69
54
 
70
- # Optional fields
71
- form[FORM_FIELD_TAGS] << "#{FORM_FIELD_TAG_SERVICE}:#{flush.service}" unless flush.service.nil?
72
- form[FORM_FIELD_TAGS] << "#{FORM_FIELD_TAG_ENV}:#{flush.env}" unless flush.env.nil?
73
- form[FORM_FIELD_TAGS] << "#{FORM_FIELD_TAG_VERSION}:#{flush.version}" unless flush.version.nil?
74
-
75
55
  # May not be available/enabled
76
- form[FORM_FIELD_CODE_PROVENANCE_DATA] = build_code_provenance(flush) if flush.code_provenance
56
+ form[FORM_FIELD_CODE_PROVENANCE_DATA] = build_code_provenance(flush) if flush.code_provenance_data
77
57
 
78
58
  form
79
59
  end
80
60
 
81
61
  def build_pprof(flush)
82
- pprof = encoder.encode(flush)
83
-
84
- gzipped_pprof_data = Core::Utils::Compression.gzip(pprof.data)
62
+ gzipped_pprof_data = flush.pprof_data
85
63
 
86
64
  Core::Vendor::Multipart::Post::UploadIO.new(
87
65
  StringIO.new(gzipped_pprof_data),
@@ -91,7 +69,7 @@ module Datadog
91
69
  end
92
70
 
93
71
  def build_code_provenance(flush)
94
- gzipped_code_provenance = Core::Utils::Compression.gzip(flush.code_provenance)
72
+ gzipped_code_provenance = flush.code_provenance_data
95
73
 
96
74
  Core::Vendor::Multipart::Post::UploadIO.new(
97
75
  StringIO.new(gzipped_code_provenance),
@@ -1,7 +1,7 @@
1
1
  # typed: true
2
2
 
3
3
  require 'ddtrace/transport/http/client'
4
- require 'datadog/profiling/transport/client'
4
+ require 'ddtrace/transport/request'
5
5
 
6
6
  module Datadog
7
7
  module Profiling
@@ -9,11 +9,13 @@ module Datadog
9
9
  module HTTP
10
10
  # Routes, encodes, and sends tracer data to the trace agent via HTTP.
11
11
  class Client < Datadog::Transport::HTTP::Client
12
- include Transport::Client
12
+ def export(flush)
13
+ send_profiling_flush(flush)
14
+ end
13
15
 
14
16
  def send_profiling_flush(flush)
15
17
  # Build a request
16
- request = Profiling::Transport::Request.new(flush)
18
+ request = flush
17
19
  send_payload(request).tap do |response|
18
20
  if response.ok?
19
21
  Datadog.logger.debug('Successfully reported profiling data')
@@ -1,7 +1,6 @@
1
1
  # typed: true
2
2
 
3
3
  require 'ddtrace/transport/http/response'
4
- require 'datadog/profiling/transport/response'
5
4
 
6
5
  module Datadog
7
6
  module Profiling
@@ -11,7 +10,6 @@ module Datadog
11
10
  # Response from HTTP transport for profiling
12
11
  class Response
13
12
  include Datadog::Transport::HTTP::Response
14
- include Profiling::Transport::Response
15
13
 
16
14
  def initialize(http_response, options = {})
17
15
  super(http_response)
@@ -80,7 +80,7 @@ module Datadog
80
80
  private_class_method def self.configure_for_agentless(transport, profiling_upload_timeout_seconds:, site:, api_key:)
81
81
  apis = API.api_defaults
82
82
 
83
- site_uri = URI(format(Profiling::Ext::Transport::HTTP::URI_TEMPLATE_DD_API, site))
83
+ site_uri = URI(format(Profiling::OldExt::Transport::HTTP::URI_TEMPLATE_DD_API, site))
84
84
  hostname = site_uri.host
85
85
  port = site_uri.port
86
86
 
@@ -147,20 +147,20 @@ module Datadog
147
147
 
148
148
  require 'datadog/profiling/ext/forking'
149
149
  require 'datadog/profiling/collectors/code_provenance'
150
+ require 'datadog/profiling/collectors/cpu_and_wall_time'
150
151
  require 'datadog/profiling/collectors/old_stack'
151
152
  require 'datadog/profiling/collectors/stack'
152
153
  require 'datadog/profiling/stack_recorder'
154
+ require 'datadog/profiling/old_recorder'
153
155
  require 'datadog/profiling/exporter'
154
- require 'datadog/profiling/recorder'
155
156
  require 'datadog/profiling/scheduler'
156
157
  require 'datadog/profiling/tasks/setup'
157
- require 'datadog/profiling/transport/io'
158
- require 'datadog/profiling/transport/http'
159
158
  require 'datadog/profiling/profiler'
160
159
  require 'datadog/profiling/native_extension'
161
160
  require 'datadog/profiling/trace_identifiers/helper'
162
161
  require 'datadog/profiling/pprof/pprof_pb'
163
162
  require 'datadog/profiling/tag_builder'
163
+ require 'datadog/profiling/http_transport'
164
164
 
165
165
  true
166
166
  end