Class: Datadog::Profiling::Exporter

Inherits:
Object
  • Object
show all
Defined in:
lib/datadog/profiling/exporter.rb

Overview

Exports profiling data gathered by the multiple recorders in a Flush.

@ivoanjo: Note that the recorder that gathers pprof data is special, since we use its start/finish/empty? to decide if there's data to flush, as well as the timestamp for that data. I could've made the whole design more generic, but I'm unsure if we'll ever have more than a handful of recorders, so I've decided to make it specific until we actually need to support more recorders.

Constant Summary collapse

PROFILE_DURATION_THRESHOLD_SECONDS =

Profiles with duration less than this will not be reported

1

Instance Method Summary collapse

Constructor Details

#initialize(pprof_recorder:, worker:, info_collector:, code_provenance_collector:, internal_metadata:, minimum_duration_seconds: PROFILE_DURATION_THRESHOLD_SECONDS, time_provider: Time, sequence_tracker: Datadog::Profiling::SequenceTracker) ⇒ Exporter

Returns a new instance of Exporter.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/datadog/profiling/exporter.rb', line 45

def initialize(
  pprof_recorder:,
  worker:,
  info_collector:,
  code_provenance_collector:,
  internal_metadata:,
  minimum_duration_seconds: PROFILE_DURATION_THRESHOLD_SECONDS,
  time_provider: Time,
  sequence_tracker: Datadog::Profiling::SequenceTracker
)
  @pprof_recorder = pprof_recorder
  @worker = worker
  @code_provenance_collector = code_provenance_collector
  @minimum_duration_seconds = minimum_duration_seconds
  @time_provider = time_provider
  @last_flush_finish_at = nil
  @created_at = time_provider.now.utc
  @internal_metadata = 
  # NOTE: At the time of this comment collected info does not change over time so we'll hardcode
  #       it on startup to prevent serializing the same info on every flush.
  @info_json = JSON.generate(info_collector.info).freeze
  @sequence_tracker = sequence_tracker
end

Instance Method Details

#can_flush? ⇒ Boolean

: () -> bool

Returns:

  • (Boolean)


122
123
124
# File 'lib/datadog/profiling/exporter.rb', line 122

def can_flush?
  !duration_below_threshold?(last_flush_finish_at || created_at, time_provider.now.utc)
end

#flush ⇒ Object

: () -> Datadog::Profiling::Flush?



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/datadog/profiling/exporter.rb', line 70

def flush
  worker_stats = @worker.stats_and_reset_not_thread_safe
  serialization_result = pprof_recorder.serialize
  return if serialization_result.nil?

  start, finish, encoded_profile, profile_stats = serialization_result
  @last_flush_finish_at = finish

  if duration_below_threshold?(start, finish)
    Datadog.logger.debug("Skipped exporting profiling events as profile duration is below minimum")
    return
  end

  uncompressed_code_provenance =
    if (collector = code_provenance_collector)
      collector.refresh.generate_json
    end

  metrics = [] #: Array[[::String, ::Numeric]]

  # The key is always there, but the value might be nil if GVL profiling is disabled.
  # We delete it to avoid reporting the same data point twice.
  gvl_waiting_time_ns_total = worker_stats.delete(:gvl_waiting_time_ns_total)
  metrics << ["ruby_global_lock_wait_time_total", gvl_waiting_time_ns_total] if gvl_waiting_time_ns_total

  process_tags = Datadog.configuration.experimental_propagate_process_tags_enabled ?
    Core::Environment::Process.serialized : ""

  Flush.new(
    start: start,
    finish: finish,
    encoded_profile: encoded_profile,
    code_provenance_data: uncompressed_code_provenance,
    metrics: metrics,
    tags_as_array: Datadog::Profiling::TagBuilder.call(
      settings: Datadog.configuration,
      profile_seq: sequence_tracker.get_next,
    ).to_a,
    process_tags: process_tags,
    internal_metadata: .merge(
      {
        worker_stats: worker_stats,
        profile_stats: profile_stats,
        recorder_stats: pprof_recorder.stats,
        gc: GC.stat,
      }
    ),
    info_json: info_json,
  )
end

#reset_after_fork ⇒ Object

: () -> void



127
128
129
130
# File 'lib/datadog/profiling/exporter.rb', line 127

def reset_after_fork
  @last_flush_finish_at = time_provider.now.utc
  nil
end