From cd831327bc4185b22d21593e04d487d6c5e6079b Mon Sep 17 00:00:00 2001 From: Marco Costa Date: Tue, 14 Jan 2025 13:16:05 -0800 Subject: [PATCH 1/6] Increase type checking coverage --- Steepfile | 31 ++------ lib/datadog/tracing/span.rb | 16 +++- lib/datadog/tracing/span_operation.rb | 4 + sig/datadog/core/configuration.rbs | 2 + sig/datadog/core/diagnostics/health.rbs | 2 +- sig/datadog/tracing.rbs | 2 + .../tracing/contrib/resque/resque_job.rbs | 2 +- sig/datadog/tracing/diagnostics/health.rbs | 20 ++++- sig/datadog/tracing/metadata.rbs | 4 +- sig/datadog/tracing/span.rbs | 55 ++++++++++++- sig/datadog/tracing/span_operation.rbs | 28 +++++-- sig/datadog/tracing/trace_operation.rbs | 4 +- sig/datadog/tracing/transport/http/api.rbs | 14 ++-- .../tracing/transport/http/builder.rbs | 76 +++++++++--------- sig/datadog/tracing/transport/http/client.rbs | 18 +++-- .../tracing/transport/http/statistics.rbs | 20 ++--- sig/datadog/tracing/transport/http/traces.rbs | 76 +++++++++--------- sig/datadog/tracing/transport/io/response.rbs | 16 ++-- vendor/rbs/minitest/0/minitest.rbs | 57 ++++++++++++++ vendor/rbs/minitest/0/minitest/unit.rbs | 4 + vendor/rbs/msgpack/0/message_pack.rbs | 23 ++++++ vendor/rbs/msgpack/0/message_pack/packer.rbs | 77 +++++++++++++++++++ 22 files changed, 400 insertions(+), 151 deletions(-) create mode 100644 vendor/rbs/minitest/0/minitest.rbs create mode 100644 vendor/rbs/minitest/0/minitest/unit.rbs create mode 100644 vendor/rbs/msgpack/0/message_pack.rbs create mode 100644 vendor/rbs/msgpack/0/message_pack/packer.rbs diff --git a/Steepfile b/Steepfile index b0151312b7d..89fb7d4a8b7 100644 --- a/Steepfile +++ b/Steepfile @@ -127,16 +127,12 @@ target :datadog do ignore 'lib/datadog/tracing/sampling/rule.rb' ignore 'lib/datadog/tracing/sampling/rule_sampler.rb' ignore 'lib/datadog/tracing/sampling/span/rule.rb' - ignore 'lib/datadog/tracing/span.rb' - ignore 'lib/datadog/tracing/span_operation.rb' ignore 'lib/datadog/tracing/sync_writer.rb' ignore 'lib/datadog/tracing/trace_operation.rb' ignore 'lib/datadog/tracing/tracer.rb' ignore 'lib/datadog/tracing/transport/http.rb' ignore 'lib/datadog/tracing/transport/http/api.rb' - ignore 'lib/datadog/tracing/transport/http/builder.rb' ignore 'lib/datadog/tracing/transport/http/client.rb' - ignore 'lib/datadog/tracing/transport/http/statistics.rb' ignore 'lib/datadog/tracing/transport/http/traces.rb' ignore 'lib/datadog/tracing/transport/io/client.rb' ignore 'lib/datadog/tracing/transport/io/traces.rb' @@ -161,30 +157,13 @@ target :datadog do library 'digest' library 'zlib' library 'time' + library 'pp' + # Load all dependency signatures from the `vendor/rbs` directory repo_path 'vendor/rbs' - library 'cucumber' - library 'jruby' - library 'gem' - library 'rails' - library 'spring' - library 'sinatra' - library 'google-protobuf' - library 'protobuf-cucumber' - library 'minitest' - library 'mysql2' - library 'mysql2-aurora' - library 'concurrent-ruby' - library 'faraday' - library 'seahorse' - library 'excon' - library 'grpc' - library 'delayed_job' - library 'opentelemetry-api' - library 'passenger' - library 'webmock' - library 'graphql' - library 'datadog-ci' + Dir.children('vendor/rbs').each do |vendor_gem| + library vendor_gem + end # ffi version 1.17 was shipped with invalid rbs types: # https://github.com/ffi/ffi/issues/1107 diff --git a/lib/datadog/tracing/span.rb b/lib/datadog/tracing/span.rb index be827c78c65..5eaa73d7387 100644 --- a/lib/datadog/tracing/span.rb +++ b/lib/datadog/tracing/span.rb @@ -112,7 +112,10 @@ def stopped? def duration return @duration if @duration - return @end_time - @start_time if @start_time && @end_time + + start_time = @start_time + end_time = @end_time + end_time - start_time if start_time && end_time end def set_error(e) @@ -135,6 +138,8 @@ def to_s # TODO: Change this to reflect attributes when serialization # isn't handled by this method. def to_hash + @meta['events'] = @events.map(&:to_hash).to_json unless @events.empty? + h = { error: @status, meta: @meta, @@ -154,8 +159,6 @@ def to_hash h[:duration] = duration_nano end - h[:meta]['events'] = @events.map(&:to_hash).to_json unless @events.empty? - h end @@ -196,12 +199,17 @@ def pretty_print(q) # Used for serialization # @return [Integer] in nanoseconds since Epoch def start_time_nano - @start_time.to_i * 1000000000 + @start_time.nsec + return unless (start_time = @start_time) + + start_time.to_i * 1000000000 + start_time.nsec end # Used for serialization # @return [Integer] in nanoseconds since Epoch def duration_nano + duration = self.duration + return unless duration + (duration * 1e9).to_i end diff --git a/lib/datadog/tracing/span_operation.rb b/lib/datadog/tracing/span_operation.rb index 52a8fc469af..483da6ae1b1 100644 --- a/lib/datadog/tracing/span_operation.rb +++ b/lib/datadog/tracing/span_operation.rb @@ -199,6 +199,9 @@ def start(start_time = nil) end # Mark the span stopped at the current time + # + # steep:ignore:start + # Steep issue fixed in https://github.com/soutaro/steep/pull/1467 def stop(stop_time = nil) # A span should not be stopped twice. Note that this is not thread-safe, # stop is called from multiple threads, a given span might be stopped @@ -221,6 +224,7 @@ def stop(stop_time = nil) self end + # steep:ignore:end # Return whether the duration is started or not def started? diff --git a/sig/datadog/core/configuration.rbs b/sig/datadog/core/configuration.rbs index e241fb56a65..ae76973d007 100644 --- a/sig/datadog/core/configuration.rbs +++ b/sig/datadog/core/configuration.rbs @@ -1,6 +1,8 @@ module Datadog module Core module Configuration + def health_metrics: -> Diagnostics::Health::Metrics + def tracer: () -> Datadog::Tracing::Tracer def logger: () -> Datadog::Core::Logger diff --git a/sig/datadog/core/diagnostics/health.rbs b/sig/datadog/core/diagnostics/health.rbs index d31c41f9cde..ec54e0b87b0 100644 --- a/sig/datadog/core/diagnostics/health.rbs +++ b/sig/datadog/core/diagnostics/health.rbs @@ -3,7 +3,7 @@ module Datadog module Diagnostics module Health class Metrics < Core::Metrics::Client - extend Tracing::Diagnostics::Health::Metrics + include Tracing::Diagnostics::Health::Metrics end end end diff --git a/sig/datadog/tracing.rbs b/sig/datadog/tracing.rbs index 9135960953e..87a06f1c9f7 100644 --- a/sig/datadog/tracing.rbs +++ b/sig/datadog/tracing.rbs @@ -4,5 +4,7 @@ module Datadog def self.active_trace: -> TraceSegment? def self.active_span: -> SpanOperation? + + type on_error = ^(SpanOperation span_op, Exception error) -> void end end diff --git a/sig/datadog/tracing/contrib/resque/resque_job.rbs b/sig/datadog/tracing/contrib/resque/resque_job.rbs index 59eea098b27..4c1b1cde88c 100644 --- a/sig/datadog/tracing/contrib/resque/resque_job.rbs +++ b/sig/datadog/tracing/contrib/resque/resque_job.rbs @@ -18,7 +18,7 @@ module Datadog def forked?: () -> untyped - def span_options: () -> { service: untyped, on_error: untyped } + def span_options: () -> { service: untyped, on_error: on_error } def datadog_configuration: () -> untyped end diff --git a/sig/datadog/tracing/diagnostics/health.rbs b/sig/datadog/tracing/diagnostics/health.rbs index dd3fd3fc616..deeb13ff221 100644 --- a/sig/datadog/tracing/diagnostics/health.rbs +++ b/sig/datadog/tracing/diagnostics/health.rbs @@ -3,7 +3,25 @@ module Datadog module Diagnostics module Health module Metrics - def self.extended: (untyped base) -> untyped + def api_errors: (untyped ?value) ?{ (untyped) -> untyped } -> void + def api_requests: (untyped ?value) ?{ (untyped) -> untyped } -> void + def api_responses: (untyped ?value) ?{ (untyped) -> untyped } -> void + def error_context_overflow: (untyped ?value) ?{ (untyped) -> untyped } -> void + def error_instrumentation_patch: (untyped ?value) ?{ (untyped) -> untyped } -> void + def error_span_finish: (untyped ?value) ?{ (untyped) -> untyped } -> void + def error_unfinished_spans: (untyped ?value) ?{ (untyped) -> untyped } -> void + def instrumentation_patched: (untyped ?value) ?{ (untyped) -> untyped } -> void + def queue_accepted: (untyped ?value) ?{ (untyped) -> untyped } -> void + def queue_accepted_lengths: (untyped ?value) ?{ (untyped) -> untyped } -> void + def queue_dropped: (untyped ?value) ?{ (untyped) -> untyped } -> void + def traces_filtered: (untyped ?value) ?{ (untyped) -> untyped } -> void + def transport_trace_too_large: (untyped ?value) ?{ (untyped) -> untyped } -> void + def transport_chunked: (untyped ?value) ?{ (untyped) -> untyped } -> void + def writer_cpu_time: (untyped ?value) ?{ (untyped) -> untyped } -> void + def queue_length: (untyped ?value) ?{ (untyped) -> untyped } -> void + def queue_max_length: (untyped ?value) ?{ (untyped) -> untyped } -> void + def queue_spans: (untyped ?value) ?{ (untyped) -> untyped } -> void + def sampling_service_cache_length: (untyped ?value) ?{ (untyped) -> untyped } -> void end end end diff --git a/sig/datadog/tracing/metadata.rbs b/sig/datadog/tracing/metadata.rbs index c5c79f73ed0..34efb3f3071 100644 --- a/sig/datadog/tracing/metadata.rbs +++ b/sig/datadog/tracing/metadata.rbs @@ -1,7 +1,9 @@ module Datadog module Tracing module Metadata - def self.included: (untyped base) -> untyped + include Metadata::Tagging + include Metadata::Errors + prepend Metadata::Analytics end end end diff --git a/sig/datadog/tracing/span.rbs b/sig/datadog/tracing/span.rbs index 9adb31f126a..b610dcaa7eb 100644 --- a/sig/datadog/tracing/span.rbs +++ b/sig/datadog/tracing/span.rbs @@ -1,10 +1,59 @@ module Datadog module Tracing class Span - attr_accessor span_id: Integer + include Metadata - def set_tag: (String key, ?untyped? value) -> void + attr_accessor end_time: (Time | nil) + attr_accessor id: Integer + attr_accessor meta: Hash[String, String] + attr_accessor metrics: Hash[String, Float] + attr_accessor name: String + attr_accessor parent_id: Integer + attr_accessor resource: String + attr_accessor service: (String | nil) + attr_accessor links: Array[untyped] + attr_accessor events: Array[untyped] + attr_accessor type: (String | nil) + attr_accessor start_time: (Time | nil) + attr_accessor status: Integer + attr_accessor trace_id: Integer + attr_writer duration: (Float | nil) + + def initialize: ( + String name, + ?duration: (Float | nil), + ?end_time: (Time | nil), + ?id: (Integer | nil), + ?meta: (Hash[String, String] | nil), + ?metrics: (Hash[String, Float] | nil), + ?parent_id: Integer, + ?resource: String, + ?service: (String | nil), + ?start_time: (Time | nil), + ?status: Integer, + ?type: (String | nil), + ?trace_id: (Integer | nil), + ?service_entry: (bool | nil), + ?links: (Array[untyped] | nil), + ?events: (Array[untyped] | nil) + ) -> void + + def started?: -> bool + def stopped?: -> bool + def duration: -> (Float | nil) + def set_error: (Exception e) -> void + def ==: (Span other) -> bool + def to_s: -> String + def to_hash: -> Hash[Symbol, untyped] + def pretty_print: (PP q) -> void + + private + + def duration_nano: -> Integer? + + def service_entry?: -> bool + + def start_time_nano: -> Integer? end end end - diff --git a/sig/datadog/tracing/span_operation.rbs b/sig/datadog/tracing/span_operation.rbs index 8002a33ae2a..a50641c0aa8 100644 --- a/sig/datadog/tracing/span_operation.rbs +++ b/sig/datadog/tracing/span_operation.rbs @@ -10,7 +10,7 @@ module Datadog attr_reader span_events: untyped - attr_reader end_time: untyped + attr_reader end_time: ::Time attr_reader id: untyped @@ -22,7 +22,7 @@ module Datadog attr_reader service: untyped - attr_reader start_time: untyped + attr_reader start_time: ::Time attr_reader trace_id: untyped @@ -30,7 +30,21 @@ module Datadog attr_accessor status: untyped - def initialize: (untyped name, ?child_of: untyped?, ?events: untyped?, ?on_error: untyped?, ?parent_id: ::Integer, ?resource: untyped, ?service: untyped?, ?start_time: untyped?, ?tags: untyped?, ?trace_id: untyped?, ?type: untyped?, ?links: untyped?, ?span_events: untyped?) -> void + def initialize: ( + String name, + ?events: Events, + ?on_error: on_error, + ?parent_id: Integer, + ?resource: String, + ?service: (String | nil), + ?start_time: (Time | nil), + ?tags: (Hash[String, (String|Numeric)] | nil), + ?trace_id: (Integer | nil), + ?type: (String | nil), + ?links: (Array[SpanLink] | nil), + ?span_events: (Array[SpanEvent] | nil), + ?id: (Integer | nil) + ) -> void def name=: (untyped name) -> untyped @@ -44,7 +58,7 @@ module Datadog def start: (?untyped? start_time) -> self - def stop: (?untyped? stop_time) -> (nil | self) + def stop: (?untyped? stop_time) -> self? def started?: () -> untyped @@ -71,7 +85,7 @@ module Datadog class Events include Tracing::Events - DEFAULT_ON_ERROR: untyped + DEFAULT_ON_ERROR: on_error attr_reader after_finish: untyped @@ -79,9 +93,9 @@ module Datadog attr_reader before_start: untyped - def initialize: (?on_error: untyped?) -> void + def initialize: (?on_error: on_error) -> void - def on_error: () -> untyped + def on_error: () -> OnError class AfterFinish < Tracing::Event def initialize: () -> void diff --git a/sig/datadog/tracing/trace_operation.rbs b/sig/datadog/tracing/trace_operation.rbs index e434c9872d6..551ce4ccf48 100644 --- a/sig/datadog/tracing/trace_operation.rbs +++ b/sig/datadog/tracing/trace_operation.rbs @@ -35,8 +35,8 @@ module Datadog def resource: () -> untyped def resource_override?: () -> bool def service: () -> untyped - def measure: (untyped op_name, ?events: untyped?, ?on_error: untyped?, ?resource: untyped?, ?service: untyped?, ?start_time: untyped?, ?tags: untyped?, ?type: untyped?) { (untyped, untyped) -> untyped } -> untyped - def build_span: (untyped op_name, ?events: untyped?, ?on_error: untyped?, ?resource: untyped?, ?service: untyped?, ?start_time: untyped?, ?tags: untyped?, ?type: untyped?) -> untyped + def measure: (untyped op_name, ?events: untyped?, ?on_error: on_error, ?resource: untyped?, ?service: untyped?, ?start_time: untyped?, ?tags: untyped?, ?type: untyped?) { (untyped, untyped) -> untyped } -> untyped + def build_span: (untyped op_name, ?events: untyped?, ?on_error: on_error, ?resource: untyped?, ?service: untyped?, ?start_time: untyped?, ?tags: untyped?, ?type: untyped?) -> untyped def flush!: () { (untyped) -> untyped } -> untyped def to_digest: () -> untyped def fork_clone: () -> untyped diff --git a/sig/datadog/tracing/transport/http/api.rbs b/sig/datadog/tracing/transport/http/api.rbs index ac130133591..f3a063f2d9c 100644 --- a/sig/datadog/tracing/transport/http/api.rbs +++ b/sig/datadog/tracing/transport/http/api.rbs @@ -1,12 +1,14 @@ module Datadog - module Transport - module HTTP - module API - V4: "v0.4" + module Tracing + module Transport + module HTTP + module API + V4: "v0.4" - V3: "v0.3" + V3: "v0.3" - def self?.defaults: () -> untyped + def self?.defaults: () -> untyped + end end end end diff --git a/sig/datadog/tracing/transport/http/builder.rbs b/sig/datadog/tracing/transport/http/builder.rbs index dff2d9ef5bc..95a11954c11 100644 --- a/sig/datadog/tracing/transport/http/builder.rbs +++ b/sig/datadog/tracing/transport/http/builder.rbs @@ -1,65 +1,67 @@ module Datadog - module Transport - module HTTP - class Builder - REGISTRY: untyped + module Tracing + module Transport + module HTTP + class Builder + REGISTRY: untyped - attr_reader apis: untyped + attr_reader apis: untyped - attr_reader api_options: untyped + attr_reader api_options: untyped - attr_reader default_adapter: untyped + attr_reader default_adapter: untyped - attr_reader default_api: untyped + attr_reader default_api: untyped - attr_reader default_headers: untyped + attr_reader default_headers: untyped - def initialize: () { (untyped) -> untyped } -> void + def initialize: () { (untyped) -> untyped } -> void - def adapter: (untyped config, *untyped args, **untyped kwargs) -> untyped + def adapter: (untyped config, *untyped args, **untyped kwargs) -> untyped - def headers: (?::Hash[untyped, untyped] values) -> untyped + def headers: (?::Hash[untyped, untyped] values) -> untyped - def api: (untyped key, untyped spec, ?::Hash[untyped, untyped] options) -> untyped + def api: (untyped key, untyped spec, ?::Hash[untyped, untyped] options) -> untyped - def default_api=: (untyped key) -> untyped + def default_api=: (untyped key) -> untyped - def to_transport: () -> untyped + def to_transport: () -> untyped - def to_api_instances: () -> untyped + def to_api_instances: () -> untyped - def api_instance_class: () -> untyped + def api_instance_class: () -> untyped - class UnknownApiError < StandardError - attr_reader key: untyped + class UnknownApiError < StandardError + attr_reader key: untyped - def initialize: (untyped key) -> void + def initialize: (untyped key) -> void - def message: () -> ::String - end + def message: () -> ::String + end - class UnknownAdapterError < StandardError - attr_reader type: untyped + class UnknownAdapterError < StandardError + attr_reader type: untyped - def initialize: (untyped `type`) -> void + def initialize: (untyped `type`) -> void - def message: () -> ::String - end + def message: () -> ::String + end - class NoAdapterForApiError < StandardError - attr_reader key: untyped + class NoAdapterForApiError < StandardError + attr_reader key: untyped - def initialize: (untyped key) -> void + def initialize: (untyped key) -> void - def message: () -> ::String - end + def message: () -> ::String + end - class NoApisError < StandardError - def message: () -> "No APIs configured for transport!" - end + class NoApisError < StandardError + def message: () -> "No APIs configured for transport!" + end - class NoDefaultApiError < StandardError - def message: () -> "No default API configured for transport!" + class NoDefaultApiError < StandardError + def message: () -> "No default API configured for transport!" + end end end end diff --git a/sig/datadog/tracing/transport/http/client.rbs b/sig/datadog/tracing/transport/http/client.rbs index 5ddbe16ae17..b723af2bd54 100644 --- a/sig/datadog/tracing/transport/http/client.rbs +++ b/sig/datadog/tracing/transport/http/client.rbs @@ -1,16 +1,18 @@ module Datadog - module Transport - module HTTP - class Client - include Transport::HTTP::Statistics + module Tracing + module Transport + module HTTP + class Client + include Transport::HTTP::Statistics - attr_reader api: untyped + attr_reader api: untyped - def initialize: (untyped api) -> void + def initialize: (untyped api) -> void - def send_request: (untyped request) { (untyped, untyped) -> untyped } -> untyped + def send_request: (untyped request) { (untyped, untyped) -> untyped } -> untyped - def build_env: (untyped request) -> untyped + def build_env: (untyped request) -> untyped + end end end end diff --git a/sig/datadog/tracing/transport/http/statistics.rbs b/sig/datadog/tracing/transport/http/statistics.rbs index 0ee2cc8bc95..95ef7ac5661 100644 --- a/sig/datadog/tracing/transport/http/statistics.rbs +++ b/sig/datadog/tracing/transport/http/statistics.rbs @@ -1,17 +1,19 @@ module Datadog - module Transport - module HTTP - module Statistics - def self.included: (untyped base) -> untyped + module Tracing + module Transport + module HTTP + module Statistics + def self.included: (untyped base) -> untyped - module InstanceMethods - def metrics_for_response: (untyped response) -> untyped + module InstanceMethods + def metrics_for_response: (untyped response) -> untyped - private + private - STATUS_CODE_200: "status_code:200" + STATUS_CODE_200: "status_code:200" - def metrics_tag_value: (untyped status_code) -> (untyped | ::String) + def metrics_tag_value: (untyped status_code) -> (untyped | ::String) + end end end end diff --git a/sig/datadog/tracing/transport/http/traces.rbs b/sig/datadog/tracing/transport/http/traces.rbs index 0f1d12916b5..5c4b88b0bb1 100644 --- a/sig/datadog/tracing/transport/http/traces.rbs +++ b/sig/datadog/tracing/transport/http/traces.rbs @@ -1,64 +1,66 @@ module Datadog - module Transport - module HTTP - module Traces - class Response - include Core::Transport::HTTP::Response + module Tracing + module Transport + module HTTP + module Traces + class Response + include Core::Transport::HTTP::Response - include Tracing::Transport::Traces::Response + include Tracing::Transport::Traces::Response - def initialize: (untyped http_response, ?::Hash[untyped, untyped] options) -> void - end + def initialize: (untyped http_response, ?::Hash[untyped, untyped] options) -> void + end - module Client - def send_traces_payload: (untyped request) -> untyped - end + module Client + def send_traces_payload: (untyped request) -> untyped + end - module API - module Spec - attr_reader traces: untyped + module API + module Spec + attr_reader traces: untyped - def traces=: (untyped endpoint) -> untyped + def traces=: (untyped endpoint) -> untyped - def send_traces: (untyped env) ?{ () -> untyped } -> untyped + def send_traces: (untyped env) ?{ () -> untyped } -> untyped - def encoder: () -> untyped + def encoder: () -> untyped - class NoTraceEndpointDefinedError < StandardError - attr_reader spec: untyped + class NoTraceEndpointDefinedError < StandardError + attr_reader spec: untyped - def initialize: (untyped spec) -> void + def initialize: (untyped spec) -> void - def message: () -> "No trace endpoint is defined for API specification!" + def message: () -> "No trace endpoint is defined for API specification!" + end end - end - module Instance - def send_traces: (untyped env) -> untyped + module Instance + def send_traces: (untyped env) -> untyped - class TracesNotSupportedError < StandardError - attr_reader spec: untyped + class TracesNotSupportedError < StandardError + attr_reader spec: untyped - def initialize: (untyped spec) -> void + def initialize: (untyped spec) -> void - def message: () -> "Traces not supported for this API!" + def message: () -> "Traces not supported for this API!" + end end - end - class Endpoint < Core::Transport::HTTP::API::Endpoint - HEADER_CONTENT_TYPE: "Content-Type" + class Endpoint < Core::Transport::HTTP::API::Endpoint + HEADER_CONTENT_TYPE: "Content-Type" - HEADER_TRACE_COUNT: "X-Datadog-Trace-Count" + HEADER_TRACE_COUNT: "X-Datadog-Trace-Count" - SERVICE_RATE_KEY: "rate_by_service" + SERVICE_RATE_KEY: "rate_by_service" - attr_reader encoder: untyped + attr_reader encoder: untyped - def initialize: (untyped path, untyped encoder, ?::Hash[untyped, untyped] options) -> void + def initialize: (untyped path, untyped encoder, ?::Hash[untyped, untyped] options) -> void - def service_rates?: () -> untyped + def service_rates?: () -> untyped - def call: (untyped env) ?{ () -> untyped } -> untyped + def call: (untyped env) ?{ () -> untyped } -> untyped + end end end end diff --git a/sig/datadog/tracing/transport/io/response.rbs b/sig/datadog/tracing/transport/io/response.rbs index 591910610d7..05e5b7c8bdd 100644 --- a/sig/datadog/tracing/transport/io/response.rbs +++ b/sig/datadog/tracing/transport/io/response.rbs @@ -1,17 +1,17 @@ module Datadog module Tracing - module Transport - module IO - class Response - include Datadog::Core::Transport::Response + module Transport + module IO + class Response + include Datadog::Core::Transport::Response - attr_reader result: untyped + attr_reader result: untyped - def initialize: (untyped result) -> void + def initialize: (untyped result) -> void - def ok?: () -> true + def ok?: () -> true + end end end end end -end diff --git a/vendor/rbs/minitest/0/minitest.rbs b/vendor/rbs/minitest/0/minitest.rbs new file mode 100644 index 00000000000..b49f5a99fae --- /dev/null +++ b/vendor/rbs/minitest/0/minitest.rbs @@ -0,0 +1,57 @@ +module Minitest + def self.__run: (untyped reporter, untyped options) -> untyped + + def self.after_run: () { (*untyped) -> untyped } -> untyped + + def self.allow_fork: () -> untyped + + def self.allow_fork=: (untyped) -> untyped + + def self.autorun: () -> untyped + + def self.backtrace_filter: () -> untyped + + def self.backtrace_filter=: (untyped) -> untyped + + def self.cattr_accessor: (untyped name) -> untyped + + def self.clock_time: () -> untyped + + def self.empty_run!: (untyped options) -> untyped + + def self.extensions: () -> untyped + + def self.extensions=: (untyped) -> untyped + + def self.filter_backtrace: (untyped bt) -> untyped + + def self.info_signal: () -> untyped + + def self.info_signal=: (untyped) -> untyped + + def self.init_plugins: (untyped options) -> untyped + + def self.load_plugins: () -> untyped + + def self.parallel_executor: () -> untyped + + def self.parallel_executor=: (untyped) -> untyped + + def self.process_args: (?untyped args) -> untyped + + def self.register_plugin: (untyped name_or_mod) -> untyped + + def self.reporter: () -> untyped + + def self.reporter=: (untyped) -> untyped + + def self.run: (?untyped args) -> untyped + + def self.run_one_method: (untyped klass, untyped method_name) -> untyped + + def self.seed: () -> untyped + + def self.seed=: (untyped) -> untyped + + VERSION: ::String +end diff --git a/vendor/rbs/minitest/0/minitest/unit.rbs b/vendor/rbs/minitest/0/minitest/unit.rbs new file mode 100644 index 00000000000..614e35e3014 --- /dev/null +++ b/vendor/rbs/minitest/0/minitest/unit.rbs @@ -0,0 +1,4 @@ +module Minitest + class Unit + end +end \ No newline at end of file diff --git a/vendor/rbs/msgpack/0/message_pack.rbs b/vendor/rbs/msgpack/0/message_pack.rbs new file mode 100644 index 00000000000..87e4c64fce8 --- /dev/null +++ b/vendor/rbs/msgpack/0/message_pack.rbs @@ -0,0 +1,23 @@ +module MessagePack + alias self.dump self.pack + + def self.load: (untyped src, ?untyped param) -> untyped + + def self.pack: (untyped v, ?untyped io, ?untyped options) -> untyped + + alias self.unpack self.load + + private + + alias dump pack + + def load: (untyped src, ?untyped param) -> untyped + + def pack: (untyped v, ?untyped io, ?untyped options) -> untyped + + alias unpack load + + DefaultFactory: ::MessagePack::Factory + + VERSION: ::String +end diff --git a/vendor/rbs/msgpack/0/message_pack/packer.rbs b/vendor/rbs/msgpack/0/message_pack/packer.rbs new file mode 100644 index 00000000000..064ff4bb3ba --- /dev/null +++ b/vendor/rbs/msgpack/0/message_pack/packer.rbs @@ -0,0 +1,77 @@ +module MessagePack + class Packer + def buffer: () -> untyped + + alias clear reset + + def compatibility_mode?: () -> untyped + + def empty?: () -> untyped + + def flush: () -> untyped + + def full_pack: () -> untyped + + alias pack write + + def register_type: (untyped type, untyped klass, ?untyped method_name) { (*untyped) -> untyped } -> untyped + + def register_type_internal: (untyped, untyped, untyped) -> untyped + + def registered_types: () -> untyped + + def reset: () -> untyped + + def size: () -> untyped + + def to_a: () -> untyped + + alias to_s to_str + + def to_str: () -> untyped + + def type_registered?: (untyped klass_or_type) -> untyped + + def write: (untyped) -> untyped + + def write_array: (untyped) -> untyped + + def write_array_header: (untyped) -> untyped + + def write_bin: (untyped) -> untyped + + def write_bin_header: (untyped) -> untyped + + def write_ext: (untyped, untyped) -> untyped + + def write_extension: (untyped) -> untyped + + def write_false: () -> untyped + + def write_float: (untyped) -> untyped + + def write_float32: (untyped) -> untyped + + def write_hash: (untyped) -> untyped + + def write_int: (untyped) -> untyped + + def write_map_header: (untyped) -> untyped + + def write_nil: () -> untyped + + def write_string: (untyped) -> untyped + + def write_symbol: (untyped) -> untyped + + def write_to: (untyped) -> untyped + + def write_true: () -> untyped + + private + + def initialize: (*untyped) -> void + + def registered_types_internal: () -> untyped + end +end From 148f1b090581e0b6badbc61d6bb399f90a549bb1 Mon Sep 17 00:00:00 2001 From: Marco Costa Date: Tue, 14 Jan 2025 15:43:38 -0800 Subject: [PATCH 2/6] Serialize span events via a dedicated field --- .github/forced-tests-list.json | 13 ++- Steepfile | 2 - lib/datadog/core/configuration/components.rb | 8 +- lib/datadog/core/encoding.rb | 16 ++++ lib/datadog/core/environment/agent_info.rb | 77 +++++++++++++++++ .../core/remote/transport/http/negotiation.rb | 1 + .../core/remote/transport/negotiation.rb | 14 ++- .../tracing/transport/serializable_trace.rb | 2 +- lib/datadog/tracing/transport/traces.rb | 30 +++++-- sig/datadog/core/configuration/components.rbs | 2 + sig/datadog/core/encoding.rbs | 30 ++++--- sig/datadog/core/environment/agent_info.rbs | 13 +++ .../core/remote/transport/negotiation.rbs | 12 ++- sig/datadog/core/transport/response.rbs | 2 +- .../tracing/transport/serializable_trace.rbs | 2 +- sig/datadog/tracing/transport/traces.rbs | 10 ++- .../core/configuration/components_spec.rb | 2 + spec/datadog/tracing/integration_spec.rb | 85 +++++++++++++++++++ spec/datadog/tracing/span_spec.rb | 5 +- spec/datadog/tracing/transport/traces_spec.rb | 80 +++++++++++++++-- spec/support/spy_transport.rb | 2 +- 21 files changed, 361 insertions(+), 47 deletions(-) create mode 100644 lib/datadog/core/environment/agent_info.rb create mode 100644 sig/datadog/core/environment/agent_info.rbs diff --git a/.github/forced-tests-list.json b/.github/forced-tests-list.json index 077404aaa41..539f04821a9 100644 --- a/.github/forced-tests-list.json +++ b/.github/forced-tests-list.json @@ -1,3 +1,14 @@ { - + "DEFAULT": + [ + "tests/test_span_events.py::Test_SpanEvents_WithAgentSupport::test_v04_v07_default_format" + ], + "AGENT_NOT_SUPPORTING_SPAN_EVENTS": + [ + "tests/test_span_events.py" + ], + "PARAMETRIC": + [ + "tests/parametric/test_span_events.py" + ] } \ No newline at end of file diff --git a/Steepfile b/Steepfile index 7acf3d2057b..26bf7f7fc24 100644 --- a/Steepfile +++ b/Steepfile @@ -137,10 +137,8 @@ target :datadog do ignore 'lib/datadog/tracing/transport/http/traces.rb' ignore 'lib/datadog/tracing/transport/io/client.rb' ignore 'lib/datadog/tracing/transport/io/traces.rb' - ignore 'lib/datadog/tracing/transport/serializable_trace.rb' ignore 'lib/datadog/tracing/transport/statistics.rb' ignore 'lib/datadog/tracing/transport/trace_formatter.rb' - ignore 'lib/datadog/tracing/transport/traces.rb' ignore 'lib/datadog/tracing/workers.rb' ignore 'lib/datadog/tracing/workers/trace_writer.rb' ignore 'lib/datadog/tracing/writer.rb' diff --git a/lib/datadog/core/configuration/components.rb b/lib/datadog/core/configuration/components.rb index 25afac8bff2..1bf96a53f71 100644 --- a/lib/datadog/core/configuration/components.rb +++ b/lib/datadog/core/configuration/components.rb @@ -16,6 +16,8 @@ require_relative '../../di/component' require_relative '../crashtracking/component' +require_relative '../environment/agent_info' + module Datadog module Core module Configuration @@ -85,7 +87,8 @@ def build_crashtracker(settings, agent_settings, logger:) :tracer, :crashtracker, :dynamic_instrumentation, - :appsec + :appsec, + :agent_info def initialize(settings) @logger = self.class.build_logger(settings) @@ -96,6 +99,9 @@ def initialize(settings) # the Core resolver from within your product/component's namespace. agent_settings = AgentSettingsResolver.call(settings, logger: @logger) + # Exposes agent capability information for detection by any components + @agent_info = Core::Environment::AgentInfo.new(agent_settings) + @telemetry = self.class.build_telemetry(settings, agent_settings, @logger) @remote = Remote::Component.build(settings, agent_settings, telemetry: telemetry) diff --git a/lib/datadog/core/encoding.rb b/lib/datadog/core/encoding.rb index 951d221d4e2..8061d5a898b 100644 --- a/lib/datadog/core/encoding.rb +++ b/lib/datadog/core/encoding.rb @@ -10,6 +10,7 @@ module Encoding # Encoder interface that provides the logic to encode traces and service # @abstract module Encoder + # :nocov: def content_type raise NotImplementedError end @@ -23,6 +24,13 @@ def join(encoded_elements) def encode(_) raise NotImplementedError end + + # Deserializes a value serialized with {#encode}. + # This method is used for debugging purposes. + def decode(_) + raise NotImplementedError + end + # :nocov: end # Encoder for the JSON format @@ -41,6 +49,10 @@ def encode(obj) JSON.dump(obj) end + def decode(obj) + JSON.parse(obj) + end + def join(encoded_data) "[#{encoded_data.join(',')}]" end @@ -62,6 +74,10 @@ def encode(obj) MessagePack.pack(obj) end + def decode(obj) + MessagePack.unpack(obj) + end + def join(encoded_data) packer = MessagePack::Packer.new packer.write_array_header(encoded_data.size) diff --git a/lib/datadog/core/environment/agent_info.rb b/lib/datadog/core/environment/agent_info.rb new file mode 100644 index 00000000000..249b6af9b74 --- /dev/null +++ b/lib/datadog/core/environment/agent_info.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module Datadog + module Core + module Environment + # Retrieves the agent's `/info` endpoint data. + # This data can be used to determine the capabilities of the local Datadog agent. + # + # @example Example response payload + # { + # "version" : "7.57.2", + # "git_commit" : "38ba0c7", + # "endpoints" : [ "/v0.4/traces", "/v0.4/services", "/v0.7/traces", "/v0.7/config" ], + # "client_drop_p0s" : true, + # "span_meta_structs" : true, + # "long_running_spans" : true, + # "evp_proxy_allowed_headers" : [ "Content-Type", "Accept-Encoding", "Content-Encoding", "User-Agent" ], + # "config" : { + # "default_env" : "none", + # "target_tps" : 10, + # "max_eps" : 200, + # "receiver_port" : 8126, + # "receiver_socket" : "/var/run/datadog/apm.socket", + # "connection_limit" : 0, + # "receiver_timeout" : 0, + # "max_request_bytes" : 26214400, + # "statsd_port" : 8125, + # "analyzed_spans_by_service" : { }, + # "obfuscation" : { + # "elastic_search" : true, + # "mongo" : true, + # "sql_exec_plan" : false, + # "sql_exec_plan_normalize" : false, + # "http" : { + # "remove_query_string" : false, + # "remove_path_digits" : false + # }, + # "remove_stack_traces" : false, + # "redis" : { + # "Enabled" : true, + # "RemoveAllArgs" : false + # }, + # "memcached" : { + # "Enabled" : true, + # "KeepCommand" : false + # } + # } + # }, + # "peer_tags" : null + # } + # + # @see https://github.com/DataDog/datadog-agent/blob/f07df0a3c1fca0c83b5a15f553bd994091b0c8ac/pkg/trace/api/info.go#L20 + class AgentInfo + attr_reader :agent_settings + + def initialize(agent_settings) + @agent_settings = agent_settings + @client = Remote::Transport::HTTP.root(agent_settings: agent_settings) + end + + # Fetches the information from the agent. + # @return [Datadog::Core::Remote::Transport::HTTP::Negotiation::Response] the response from the agent + # @return [nil] if an error occurred while fetching the information + def fetch + res = @client.send_info + return unless res.ok? + + res + end + + def ==(other) + other.is_a?(self.class) && other.agent_settings == agent_settings + end + end + end + end +end diff --git a/lib/datadog/core/remote/transport/http/negotiation.rb b/lib/datadog/core/remote/transport/http/negotiation.rb index 5cc00c8a8f0..4a939222ef6 100644 --- a/lib/datadog/core/remote/transport/http/negotiation.rb +++ b/lib/datadog/core/remote/transport/http/negotiation.rb @@ -43,6 +43,7 @@ def initialize(http_response, options = {}) @version = options[:version] @endpoints = options[:endpoints] @config = options[:config] + @span_events = options[:span_events] end end diff --git a/lib/datadog/core/remote/transport/negotiation.rb b/lib/datadog/core/remote/transport/negotiation.rb index e9209087092..b6e7f6c8833 100644 --- a/lib/datadog/core/remote/transport/negotiation.rb +++ b/lib/datadog/core/remote/transport/negotiation.rb @@ -32,7 +32,19 @@ class Request < Datadog::Core::Transport::Request # Negotiation response module Response - attr_reader :version, :endpoints, :config + # @!attribute [r] version + # The version of the agent. + # @return [String] + # @!attribute [r] endpoints + # The HTTP endpoints the agent supports. + # @return [Array] + # @!attribute [r] config + # The agent configuration. These are configured by the user when starting the agent, as well as any defaults. + # @return [Hash] + # @!attribute [r] span_events + # Whether the agent supports the top-level span events field in flushed spans. + # @return [Boolean,nil] + attr_reader :version, :endpoints, :config, :span_events end # Negotiation transport diff --git a/lib/datadog/tracing/transport/serializable_trace.rb b/lib/datadog/tracing/transport/serializable_trace.rb index 380f8623954..b05c324e164 100644 --- a/lib/datadog/tracing/transport/serializable_trace.rb +++ b/lib/datadog/tracing/transport/serializable_trace.rb @@ -14,7 +14,7 @@ class SerializableTrace # @param trace [Datadog::Trace] the trace to serialize # @param native_events_supported [Boolean] whether the agent supports span events as a top-level field - def initialize(trace, native_events_supported = false) + def initialize(trace, native_events_supported) @trace = trace @native_events_supported = native_events_supported end diff --git a/lib/datadog/tracing/transport/traces.rb b/lib/datadog/tracing/transport/traces.rb index 644f80fe94c..58389b578c9 100644 --- a/lib/datadog/tracing/transport/traces.rb +++ b/lib/datadog/tracing/transport/traces.rb @@ -50,8 +50,9 @@ class Chunker # # @param encoder [Datadog::Core::Encoding::Encoder] # @param max_size [String] maximum acceptable payload size - def initialize(encoder, max_size: DEFAULT_MAX_PAYLOAD_SIZE) + def initialize(encoder, native_events_supported, max_size: DEFAULT_MAX_PAYLOAD_SIZE) @encoder = encoder + @native_events_supported = native_events_supported @max_size = max_size end @@ -77,7 +78,7 @@ def encode_in_chunks(traces) private def encode_one(trace) - encoded = Encoder.encode_trace(encoder, trace) + encoded = Encoder.encode_trace(encoder, trace, @native_events_supported) if encoded.size > max_size # This single trace is too large, we can't flush it @@ -95,17 +96,18 @@ def encode_one(trace) module Encoder module_function - def encode_trace(encoder, trace) + def encode_trace(encoder, trace, native_events_supported) # Format the trace for transport TraceFormatter.format!(trace) # Make the trace serializable - serializable_trace = SerializableTrace.new(trace) - - Datadog.logger.debug { "Flushing trace: #{JSON.dump(serializable_trace)}" } + serializable_trace = SerializableTrace.new(trace, native_events_supported) # Encode the trace - encoder.encode(serializable_trace) + encoder.encode(serializable_trace).tap do |encoded| + # Print the actual serialized trace, since the encoder can change make non-trivial changes + Datadog.logger.debug { "Flushing trace: #{encoder.decode(encoded)}" } + end end end @@ -126,7 +128,7 @@ def initialize(apis, default_api) def send_traces(traces) encoder = current_api.encoder - chunker = Datadog::Tracing::Transport::Traces::Chunker.new(encoder) + chunker = Datadog::Tracing::Transport::Traces::Chunker.new(encoder, native_events_supported?) responses = chunker.encode_in_chunks(traces.lazy).map do |encoded_traces, trace_count| request = Request.new(EncodedParcel.new(encoded_traces, trace_count)) @@ -188,6 +190,18 @@ def change_api!(api_id) @client = HTTP::Client.new(current_api) end + # Queries the agent for native span events serialization support. + # This changes how the serialization of span events performed. + def native_events_supported? + return @native_events_supported if defined?(@native_events_supported) + + if (res = Datadog.send(:components).agent_info.fetch) + @native_events_supported = res.span_events == true + else + false + end + end + # Raised when configured with an unknown API version class UnknownApiVersionError < StandardError attr_reader :version diff --git a/sig/datadog/core/configuration/components.rbs b/sig/datadog/core/configuration/components.rbs index b4e2ad4aa92..b7d96f7b728 100644 --- a/sig/datadog/core/configuration/components.rbs +++ b/sig/datadog/core/configuration/components.rbs @@ -36,6 +36,8 @@ module Datadog attr_reader remote: Datadog::Core::Remote::Component + attr_reader agent_info: Datadog::Core::Environment::AgentInfo + def initialize: (untyped settings) -> untyped def startup!: (untyped settings) -> untyped diff --git a/sig/datadog/core/encoding.rbs b/sig/datadog/core/encoding.rbs index f3fee9b1aed..9320f0fb615 100644 --- a/sig/datadog/core/encoding.rbs +++ b/sig/datadog/core/encoding.rbs @@ -5,39 +5,43 @@ module Datadog # Encoder interface that provides the logic to encode traces and service # @abstract module Encoder - def content_type: () -> untyped + def content_type: () -> String - # Concatenates a list of elements previously encoded by +#encode+. - def join: (untyped encoded_elements) -> untyped + def encode: (untyped obj) -> String - # Serializes a single trace into a String suitable for network transmission. - def encode: (untyped _) -> untyped + def join: (Array[untyped] encoded_data) -> String + + def decode: (String obj)-> untyped end # Encoder for the JSON format module JSONEncoder extend Encoder - CONTENT_TYPE: "application/json" + CONTENT_TYPE: String + + def self?.content_type: () -> String - def self?.content_type: () -> untyped + def self?.encode: (untyped obj) -> String - def self?.encode: (untyped obj) -> untyped + def self?.join: (Array[untyped] encoded_data) -> String - def self?.join: (untyped encoded_data) -> ::String + def self?.decode: (String obj)-> untyped end # Encoder for the Msgpack format module MsgpackEncoder extend Encoder - CONTENT_TYPE: "application/msgpack" + CONTENT_TYPE: String + + def self?.content_type: () -> String - def self?.content_type: () -> untyped + def self?.encode: (untyped obj) -> String - def self?.encode: (untyped obj) -> untyped + def self?.join: (Array[untyped] encoded_data) -> String - def self?.join: (untyped encoded_data) -> untyped + def self?.decode: (String obj)-> untyped end end end diff --git a/sig/datadog/core/environment/agent_info.rbs b/sig/datadog/core/environment/agent_info.rbs new file mode 100644 index 00000000000..ddf013809b9 --- /dev/null +++ b/sig/datadog/core/environment/agent_info.rbs @@ -0,0 +1,13 @@ +module Datadog + module Core + module Environment + class AgentInfo + attr_reader agent_settings: Configuration::AgentSettingsResolver::AgentSettings + + def initialize: (Configuration::AgentSettingsResolver::AgentSettings agent_settings) -> void + + def fetch: -> Remote::Transport::HTTP::Negotiation::Response? + end + end + end +end diff --git a/sig/datadog/core/remote/transport/negotiation.rbs b/sig/datadog/core/remote/transport/negotiation.rbs index 0f90aa77b57..82d21ebe05e 100644 --- a/sig/datadog/core/remote/transport/negotiation.rbs +++ b/sig/datadog/core/remote/transport/negotiation.rbs @@ -7,11 +7,13 @@ module Datadog end module Response - attr_reader version: untyped + attr_reader version: String - attr_reader endpoints: untyped + attr_reader endpoints: Array[String] - attr_reader config: untyped + attr_reader config: Hash[String,untyped] + + attr_reader span_events: bool end class Transport @@ -25,7 +27,9 @@ module Datadog def initialize: (untyped apis, untyped default_api) -> void - def send_info: () -> untyped + type send_info_return = HTTP::Negotiation::Response & Core::Transport::InternalErrorResponse + + def send_info: () -> send_info_return def current_api: () -> untyped end diff --git a/sig/datadog/core/transport/response.rbs b/sig/datadog/core/transport/response.rbs index 888e67568e1..11558cfe48b 100644 --- a/sig/datadog/core/transport/response.rbs +++ b/sig/datadog/core/transport/response.rbs @@ -22,7 +22,7 @@ module Datadog class InternalErrorResponse include Response - attr_reader error: untyped + attr_reader error: Exception def initialize: (untyped error) -> void diff --git a/sig/datadog/tracing/transport/serializable_trace.rbs b/sig/datadog/tracing/transport/serializable_trace.rbs index ab84fcfc23d..f8cd87e7bff 100644 --- a/sig/datadog/tracing/transport/serializable_trace.rbs +++ b/sig/datadog/tracing/transport/serializable_trace.rbs @@ -4,7 +4,7 @@ module Datadog class SerializableTrace @native_events_supported: bool - attr_reader trace: Span + attr_reader trace: TraceSegment def initialize: (untyped trace, bool native_events_supported) -> void diff --git a/sig/datadog/tracing/transport/traces.rbs b/sig/datadog/tracing/transport/traces.rbs index 00e3eabc06d..5ecffec6cd3 100644 --- a/sig/datadog/tracing/transport/traces.rbs +++ b/sig/datadog/tracing/transport/traces.rbs @@ -28,7 +28,7 @@ module Datadog attr_reader max_size: untyped - def initialize: (untyped encoder, ?max_size: untyped) -> void + def initialize: (untyped encoder, bool native_events_supported, ?max_size: untyped) -> void def encode_in_chunks: (untyped traces) -> untyped @@ -38,10 +38,12 @@ module Datadog end module Encoder - def self?.encode_trace: (untyped encoder, untyped trace) -> untyped + def self?.encode_trace: (untyped encoder, untyped trace, bool native_events_supported) -> untyped end class Transport + @native_events_supported: bool + attr_reader client: untyped attr_reader apis: untyped @@ -52,7 +54,7 @@ module Datadog def initialize: (untyped apis, untyped default_api) -> void - def send_traces: (untyped traces) -> untyped + def send_traces: (Array[Tracing::TraceOperation] traces) -> untyped def stats: () -> untyped @@ -81,6 +83,8 @@ module Datadog def message: () -> ::String end + + def native_events_supported?: -> bool end end end diff --git a/spec/datadog/core/configuration/components_spec.rb b/spec/datadog/core/configuration/components_spec.rb index 6957185b1c2..f53b5b9d9bc 100644 --- a/spec/datadog/core/configuration/components_spec.rb +++ b/spec/datadog/core/configuration/components_spec.rb @@ -31,6 +31,7 @@ let(:logger) { instance_double(Datadog::Core::Logger) } let(:settings) { Datadog::Core::Configuration::Settings.new } let(:agent_settings) { Datadog::Core::Configuration::AgentSettingsResolver.call(settings, logger: nil) } + let(:agent_info) { Datadog::Core::Environment::AgentInfo.new(agent_settings) } let(:profiler_setup_task) { Datadog::Profiling.supported? ? instance_double(Datadog::Profiling::Tasks::Setup) : nil } let(:remote) { instance_double(Datadog::Core::Remote::Component, start: nil, shutdown!: nil) } @@ -95,6 +96,7 @@ expect(components.profiler).to be profiler expect(components.runtime_metrics).to be runtime_metrics expect(components.health_metrics).to be health_metrics + expect(components.agent_info).to eq agent_info end describe '@environment_logger_extra' do diff --git a/spec/datadog/tracing/integration_spec.rb b/spec/datadog/tracing/integration_spec.rb index 6824a123612..649a2f17505 100644 --- a/spec/datadog/tracing/integration_spec.rb +++ b/spec/datadog/tracing/integration_spec.rb @@ -867,6 +867,83 @@ def agent_receives_span_step3(previous_success) end end + shared_examples 'flushes traces with span events' do |native_span_events_support: true| + context 'a trace with span events' do + subject(:trace_with_event) do + tracer.trace('parent_span') do |span| + span.span_events << Datadog::Tracing::SpanEvent.new( + 'event_name', + time_unix_nano: 123, + attributes: { 'key' => 'value' } + ) + end + + try_wait_until(seconds: 2) { tracer.writer.stats[:traces_flushed] >= 1 } + end + + before do + allow_any_instance_of(Datadog::Core::Remote::Transport::HTTP::Negotiation::Response) + .to receive(:span_events).and_return(span_events_support) + + allow(encoder).to receive(:encode).and_wrap_original do |m, *args| + encoded = m.call(*args) + traces = encoder.decode(encoded) + + traces = traces['traces'].first if traces.is_a?(Hash) # For Transport::IO + + expect(traces).to have(1).item + @flushed_trace = traces.first + + encoded + end + end + + let(:flushed_trace) { @flushed_trace } + + context 'with agent supporting native span events' do + before do + skip 'Environment does not support native span events' unless native_span_events_support + end + + let(:span_events_support) { true } + + it 'flushes events using the span_events field' do + trace_with_event + + expect(flushed_trace['meta']).to_not have_key('events') + expect(flushed_trace['span_events']).to eq( + [ + { 'name' => 'event_name', + 'time_unix_nano' => 123, + 'attributes' => { 'key' => { + 'string_value' => 'value', 'type' => 0 + } }, } + ] + ) + end + end + + context 'with agent not supporting native span events' do + let(:span_events_support) { false } + + it 'flushes events using the span_events field' do + trace_with_event + + expect(flushed_trace['meta']['events']).to eq( + JSON.dump( + [ + { 'name' => 'event_name', + 'time_unix_nano' => 123, + 'attributes' => { 'key' => 'value' } } + ] + ) + ) + expect(flushed_trace).to_not have_key('span_events') + end + end + end + end + describe 'Transport::IO' do include_context 'agent-based test' @@ -921,6 +998,10 @@ def agent_receives_span_step3(previous_success) expect(out).to have_received(:puts) end + + it_behaves_like 'flushes traces with span events', native_span_events_support: false do + let(:encoder) { Datadog.send(:components).tracer.writer.transport.encoder } + end end describe 'Transport::HTTP' do @@ -965,6 +1046,10 @@ def agent_receives_span_step3(previous_success) expect(stats[:transport].internal_error).to eq(0) end end + + it_behaves_like 'flushes traces with span events' do + let(:encoder) { Datadog.send(:components).tracer.writer.transport.current_api.encoder } + end end describe 'fiber-local context' do diff --git a/spec/datadog/tracing/span_spec.rb b/spec/datadog/tracing/span_spec.rb index 0c6bca7f3f0..6a061114627 100644 --- a/spec/datadog/tracing/span_spec.rb +++ b/spec/datadog/tracing/span_spec.rb @@ -2,7 +2,6 @@ require 'json' require 'msgpack' -require 'pp' require 'time' require 'datadog/core' @@ -268,7 +267,9 @@ subject(:pretty_print) { PP.pp(span) } it 'output without errors' do - expect { pretty_print }.to output.to_stdout + Datadog::Tracing.trace('x') { |s| pp s } + # pretty_print + # expect { pretty_print }.to output.to_stdout end end end diff --git a/spec/datadog/tracing/transport/traces_spec.rb b/spec/datadog/tracing/transport/traces_spec.rb index ab78c7ac8b0..2371874aedd 100644 --- a/spec/datadog/tracing/transport/traces_spec.rb +++ b/spec/datadog/tracing/transport/traces_spec.rb @@ -60,8 +60,9 @@ end RSpec.describe Datadog::Tracing::Transport::Traces::Chunker do - let(:chunker) { described_class.new(encoder, max_size: max_size) } + let(:chunker) { described_class.new(encoder, native_events_supported, max_size: max_size) } let(:encoder) { instance_double(Datadog::Core::Encoding::Encoder) } + let(:native_events_supported) { double } let(:trace_encoder) { Datadog::Tracing::Transport::Traces::Encoder } let(:max_size) { 10 } @@ -72,9 +73,9 @@ let(:traces) { get_test_traces(3) } before do - allow(trace_encoder).to receive(:encode_trace).with(encoder, traces[0]).and_return('1') - allow(trace_encoder).to receive(:encode_trace).with(encoder, traces[1]).and_return('22') - allow(trace_encoder).to receive(:encode_trace).with(encoder, traces[2]).and_return('333') + allow(trace_encoder).to receive(:encode_trace).with(encoder, traces[0], native_events_supported).and_return('1') + allow(trace_encoder).to receive(:encode_trace).with(encoder, traces[1], native_events_supported).and_return('22') + allow(trace_encoder).to receive(:encode_trace).with(encoder, traces[2], native_events_supported).and_return('333') allow(encoder).to receive(:join) { |arr| arr.join(',') } end @@ -138,8 +139,8 @@ let(:api_v1) { instance_double(Datadog::Tracing::Transport::HTTP::API::Instance, 'v1', encoder: encoder_v1) } let(:api_v2) { instance_double(Datadog::Tracing::Transport::HTTP::API::Instance, 'v2', encoder: encoder_v2) } - let(:encoder_v1) { instance_double(Datadog::Core::Encoding::Encoder, content_type: 'text/plain') } - let(:encoder_v2) { instance_double(Datadog::Core::Encoding::Encoder, content_type: 'text/csv') } + let(:encoder_v1) { instance_double(Datadog::Core::Encoding::Encoder, 'v1', content_type: 'text/plain') } + let(:encoder_v2) { instance_double(Datadog::Core::Encoding::Encoder, 'v2', content_type: 'text/csv') } end describe '#initialize' do @@ -157,6 +158,7 @@ subject(:send_traces) { transport.send_traces(traces) } let(:traces) { [] } + let(:response) { Class.new { include Datadog::Core::Transport::Response }.new } let(:responses) { [response] } @@ -170,10 +172,14 @@ let(:client_v1) { instance_double(Datadog::Tracing::Transport::HTTP::Client) } let(:chunker) { instance_double(Datadog::Tracing::Transport::Traces::Chunker, max_size: 1) } + let(:native_events_supported) { nil } + let(:agent_info_response) do + instance_double(Datadog::Core::Remote::Transport::HTTP::Negotiation::Response, span_events: native_events_supported) + end before do - allow(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with(encoder_v1).and_return(chunker) - allow(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with(encoder_v2).and_return(chunker) + allow(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with(encoder_v1, false).and_return(chunker) + allow(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with(encoder_v2, false).and_return(chunker) allow(chunker).to receive(:encode_in_chunks).and_return(lazy_chunks) @@ -183,6 +189,9 @@ allow(client_v2).to receive(:send_traces_payload).with(request).and_return(response) allow(Datadog::Tracing::Transport::Traces::Request).to receive(:new).and_return(request) + + allow_any_instance_of(Datadog::Core::Environment::AgentInfo).to receive(:fetch) + .and_return(agent_info_response) end context 'which returns an OK response' do @@ -254,6 +263,61 @@ expect(health_metrics).to have_received(:transport_chunked).with(1) end end + + context 'for native span event support by the agent' do + context 'on a successful agent info call' do + context 'with support not advertised' do + let(:native_events_supported) { nil } + + it 'does not encode native span events' do + expect(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with( + encoder_v2, + false + ).and_return(chunker) + send_traces + end + end + + context 'with support advertised as supported' do + let(:native_events_supported) { true } + + it 'encodes native span events' do + expect(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with(encoder_v2, true).and_return(chunker) + send_traces + end + end + + context 'with support advertised as unsupported' do + let(:native_events_supported) { false } + + it 'encodes native span events' do + expect(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with( + encoder_v2, + false + ).and_return(chunker) + send_traces + end + end + + it 'caches the agent result' do + transport.send_traces(traces) + transport.send_traces(traces) + + expect(Datadog.send(:components).agent_info).to have_received(:fetch).once + end + end + + context 'on an unsuccessful agent info call' do + let(:agent_info_response) { nil } + + it 'does not cache the agent result' do + transport.send_traces(traces) + transport.send_traces(traces) + + expect(Datadog.send(:components).agent_info).to have_received(:fetch).twice + end + end + end end describe '#downgrade?' do diff --git a/spec/support/spy_transport.rb b/spec/support/spy_transport.rb index f36439e408f..a7317ab3ac6 100644 --- a/spec/support/spy_transport.rb +++ b/spec/support/spy_transport.rb @@ -43,7 +43,7 @@ def initialize(*) def send_traces(data) encoded_data = data.map do |trace| - @helper_encoder.join([Datadog::Tracing::Transport::Traces::Encoder.encode_trace(@helper_encoder, trace)]) + @helper_encoder.join([Datadog::Tracing::Transport::Traces::Encoder.encode_trace(@helper_encoder, trace, true)]) end @helper_mutex.synchronize do From ad0c8f17103a9463fb227750d87d9d027512fb0e Mon Sep 17 00:00:00 2001 From: Marco Costa Date: Tue, 28 Jan 2025 14:28:52 -0800 Subject: [PATCH 3/6] Revert unrelated test changes --- spec/datadog/tracing/span_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/datadog/tracing/span_spec.rb b/spec/datadog/tracing/span_spec.rb index 6a061114627..0c6bca7f3f0 100644 --- a/spec/datadog/tracing/span_spec.rb +++ b/spec/datadog/tracing/span_spec.rb @@ -2,6 +2,7 @@ require 'json' require 'msgpack' +require 'pp' require 'time' require 'datadog/core' @@ -267,9 +268,7 @@ subject(:pretty_print) { PP.pp(span) } it 'output without errors' do - Datadog::Tracing.trace('x') { |s| pp s } - # pretty_print - # expect { pretty_print }.to output.to_stdout + expect { pretty_print }.to output.to_stdout end end end From 042b27c14c376022640d94739cccddb51df30fa9 Mon Sep 17 00:00:00 2001 From: Marco Costa Date: Mon, 3 Feb 2025 16:01:50 -0800 Subject: [PATCH 4/6] Skip test running against incorrect combinations --- .github/forced-tests-list.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/forced-tests-list.json b/.github/forced-tests-list.json index 539f04821a9..fa3cc348b11 100644 --- a/.github/forced-tests-list.json +++ b/.github/forced-tests-list.json @@ -1,8 +1,4 @@ { - "DEFAULT": - [ - "tests/test_span_events.py::Test_SpanEvents_WithAgentSupport::test_v04_v07_default_format" - ], "AGENT_NOT_SUPPORTING_SPAN_EVENTS": [ "tests/test_span_events.py" From b2786f76ea345a163ac34a5160ffa4125bbcb7e0 Mon Sep 17 00:00:00 2001 From: Marco Costa Date: Wed, 5 Feb 2025 16:08:13 -0800 Subject: [PATCH 5/6] Use kwarg --- .../tracing/transport/serializable_trace.rb | 12 ++++-- lib/datadog/tracing/transport/traces.rb | 13 ++++--- .../tracing/transport/serializable_trace.rbs | 4 +- sig/datadog/tracing/transport/traces.rbs | 4 +- .../transport/serializable_trace_spec.rb | 2 +- spec/datadog/tracing/transport/traces_spec.rb | 39 ++++++++++++++----- 6 files changed, 51 insertions(+), 23 deletions(-) diff --git a/lib/datadog/tracing/transport/serializable_trace.rb b/lib/datadog/tracing/transport/serializable_trace.rb index b05c324e164..b3786903312 100644 --- a/lib/datadog/tracing/transport/serializable_trace.rb +++ b/lib/datadog/tracing/transport/serializable_trace.rb @@ -14,7 +14,7 @@ class SerializableTrace # @param trace [Datadog::Trace] the trace to serialize # @param native_events_supported [Boolean] whether the agent supports span events as a top-level field - def initialize(trace, native_events_supported) + def initialize(trace, native_events_supported:) @trace = trace @native_events_supported = native_events_supported end @@ -29,13 +29,17 @@ def initialize(trace, native_events_supported) # @param packer [MessagePack::Packer] serialization buffer, can be +nil+ with JRuby def to_msgpack(packer = nil) # As of 1.3.3, JRuby implementation doesn't pass an existing packer - trace.spans.map { |s| SerializableSpan.new(s, @native_events_supported) }.to_msgpack(packer) + trace.spans.map do |s| + SerializableSpan.new(s, native_events_supported: @native_events_supported) + end.to_msgpack(packer) end # JSON serializer interface. # Used by older version of the transport. def to_json(*args) - trace.spans.map { |s| SerializableSpan.new(s, @native_events_supported).to_hash }.to_json(*args) + trace.spans.map do |s| + SerializableSpan.new(s, native_events_supported: @native_events_supported).to_hash + end.to_json(*args) end end @@ -46,7 +50,7 @@ class SerializableSpan # @param span [Datadog::Span] the span to serialize # @param native_events_supported [Boolean] whether the agent supports span events as a top-level field - def initialize(span, native_events_supported) + def initialize(span, native_events_supported:) @span = span @trace_id = Tracing::Utils::TraceId.to_low_order(span.trace_id) @native_events_supported = native_events_supported diff --git a/lib/datadog/tracing/transport/traces.rb b/lib/datadog/tracing/transport/traces.rb index 58389b578c9..441477ac7ca 100644 --- a/lib/datadog/tracing/transport/traces.rb +++ b/lib/datadog/tracing/transport/traces.rb @@ -50,7 +50,7 @@ class Chunker # # @param encoder [Datadog::Core::Encoding::Encoder] # @param max_size [String] maximum acceptable payload size - def initialize(encoder, native_events_supported, max_size: DEFAULT_MAX_PAYLOAD_SIZE) + def initialize(encoder, native_events_supported:, max_size: DEFAULT_MAX_PAYLOAD_SIZE) @encoder = encoder @native_events_supported = native_events_supported @max_size = max_size @@ -78,7 +78,7 @@ def encode_in_chunks(traces) private def encode_one(trace) - encoded = Encoder.encode_trace(encoder, trace, @native_events_supported) + encoded = Encoder.encode_trace(encoder, trace, native_events_supported: @native_events_supported) if encoded.size > max_size # This single trace is too large, we can't flush it @@ -96,12 +96,12 @@ def encode_one(trace) module Encoder module_function - def encode_trace(encoder, trace, native_events_supported) + def encode_trace(encoder, trace, native_events_supported:) # Format the trace for transport TraceFormatter.format!(trace) # Make the trace serializable - serializable_trace = SerializableTrace.new(trace, native_events_supported) + serializable_trace = SerializableTrace.new(trace, native_events_supported: native_events_supported) # Encode the trace encoder.encode(serializable_trace).tap do |encoded| @@ -128,7 +128,10 @@ def initialize(apis, default_api) def send_traces(traces) encoder = current_api.encoder - chunker = Datadog::Tracing::Transport::Traces::Chunker.new(encoder, native_events_supported?) + chunker = Datadog::Tracing::Transport::Traces::Chunker.new( + encoder, + native_events_supported: native_events_supported? + ) responses = chunker.encode_in_chunks(traces.lazy).map do |encoded_traces, trace_count| request = Request.new(EncodedParcel.new(encoded_traces, trace_count)) diff --git a/sig/datadog/tracing/transport/serializable_trace.rbs b/sig/datadog/tracing/transport/serializable_trace.rbs index f8cd87e7bff..de27a49c2cd 100644 --- a/sig/datadog/tracing/transport/serializable_trace.rbs +++ b/sig/datadog/tracing/transport/serializable_trace.rbs @@ -6,7 +6,7 @@ module Datadog attr_reader trace: TraceSegment - def initialize: (untyped trace, bool native_events_supported) -> void + def initialize: (untyped trace, native_events_supported: bool) -> void def to_msgpack: (?untyped? packer) -> untyped @@ -19,7 +19,7 @@ module Datadog attr_reader span: Span - def initialize: (untyped span, bool native_events_supported) -> void + def initialize: (untyped span, native_events_supported: bool) -> void def to_msgpack: (?untyped? packer) -> untyped diff --git a/sig/datadog/tracing/transport/traces.rbs b/sig/datadog/tracing/transport/traces.rbs index 5ecffec6cd3..25f22332c17 100644 --- a/sig/datadog/tracing/transport/traces.rbs +++ b/sig/datadog/tracing/transport/traces.rbs @@ -28,7 +28,7 @@ module Datadog attr_reader max_size: untyped - def initialize: (untyped encoder, bool native_events_supported, ?max_size: untyped) -> void + def initialize: (untyped encoder, native_events_supported: bool, ?max_size: untyped) -> void def encode_in_chunks: (untyped traces) -> untyped @@ -38,7 +38,7 @@ module Datadog end module Encoder - def self?.encode_trace: (untyped encoder, untyped trace, bool native_events_supported) -> untyped + def self?.encode_trace: (untyped encoder, untyped trace, native_events_supported: bool) -> untyped end class Transport diff --git a/spec/datadog/tracing/transport/serializable_trace_spec.rb b/spec/datadog/tracing/transport/serializable_trace_spec.rb index de88a519225..afef82a68ef 100644 --- a/spec/datadog/tracing/transport/serializable_trace_spec.rb +++ b/spec/datadog/tracing/transport/serializable_trace_spec.rb @@ -8,7 +8,7 @@ require 'datadog/tracing/transport/serializable_trace' RSpec.describe Datadog::Tracing::Transport::SerializableTrace do - subject(:serializable_trace) { described_class.new(trace, native_events_supported) } + subject(:serializable_trace) { described_class.new(trace, native_events_supported: native_events_supported) } let(:trace) { Datadog::Tracing::TraceSegment.new(spans) } let(:native_events_supported) { false } diff --git a/spec/datadog/tracing/transport/traces_spec.rb b/spec/datadog/tracing/transport/traces_spec.rb index 2371874aedd..5931bc17326 100644 --- a/spec/datadog/tracing/transport/traces_spec.rb +++ b/spec/datadog/tracing/transport/traces_spec.rb @@ -60,7 +60,7 @@ end RSpec.describe Datadog::Tracing::Transport::Traces::Chunker do - let(:chunker) { described_class.new(encoder, native_events_supported, max_size: max_size) } + let(:chunker) { described_class.new(encoder, native_events_supported: native_events_supported, max_size: max_size) } let(:encoder) { instance_double(Datadog::Core::Encoding::Encoder) } let(:native_events_supported) { double } let(:trace_encoder) { Datadog::Tracing::Transport::Traces::Encoder } @@ -73,9 +73,21 @@ let(:traces) { get_test_traces(3) } before do - allow(trace_encoder).to receive(:encode_trace).with(encoder, traces[0], native_events_supported).and_return('1') - allow(trace_encoder).to receive(:encode_trace).with(encoder, traces[1], native_events_supported).and_return('22') - allow(trace_encoder).to receive(:encode_trace).with(encoder, traces[2], native_events_supported).and_return('333') + allow(trace_encoder).to receive(:encode_trace).with( + encoder, + traces[0], + native_events_supported: native_events_supported + ).and_return('1') + allow(trace_encoder).to receive(:encode_trace).with( + encoder, + traces[1], + native_events_supported: native_events_supported + ).and_return('22') + allow(trace_encoder).to receive(:encode_trace).with( + encoder, + traces[2], + native_events_supported: native_events_supported + ).and_return('333') allow(encoder).to receive(:join) { |arr| arr.join(',') } end @@ -178,8 +190,14 @@ end before do - allow(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with(encoder_v1, false).and_return(chunker) - allow(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with(encoder_v2, false).and_return(chunker) + allow(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with( + encoder_v1, + native_events_supported: false + ).and_return(chunker) + allow(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with( + encoder_v2, + native_events_supported: false + ).and_return(chunker) allow(chunker).to receive(:encode_in_chunks).and_return(lazy_chunks) @@ -272,7 +290,7 @@ it 'does not encode native span events' do expect(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with( encoder_v2, - false + native_events_supported: false ).and_return(chunker) send_traces end @@ -282,7 +300,10 @@ let(:native_events_supported) { true } it 'encodes native span events' do - expect(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with(encoder_v2, true).and_return(chunker) + expect(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with( + encoder_v2, + native_events_supported: true + ).and_return(chunker) send_traces end end @@ -293,7 +314,7 @@ it 'encodes native span events' do expect(Datadog::Tracing::Transport::Traces::Chunker).to receive(:new).with( encoder_v2, - false + native_events_supported: false ).and_return(chunker) send_traces end From b6c34354cbf4cbf897d5d8f4c06d2115692efca5 Mon Sep 17 00:00:00 2001 From: Marco Costa Date: Wed, 5 Feb 2025 16:20:45 -0800 Subject: [PATCH 6/6] Fix spy test --- spec/support/spy_transport.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spec/support/spy_transport.rb b/spec/support/spy_transport.rb index a7317ab3ac6..45f28226a7a 100644 --- a/spec/support/spy_transport.rb +++ b/spec/support/spy_transport.rb @@ -43,7 +43,13 @@ def initialize(*) def send_traces(data) encoded_data = data.map do |trace| - @helper_encoder.join([Datadog::Tracing::Transport::Traces::Encoder.encode_trace(@helper_encoder, trace, true)]) + @helper_encoder.join( + [Datadog::Tracing::Transport::Traces::Encoder.encode_trace( + @helper_encoder, + trace, + native_events_supported: true + )] + ) end @helper_mutex.synchronize do