From da55f142d2d70972fbf7906f804b01f76e7f71fa Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Thu, 8 Aug 2024 07:44:48 -0700 Subject: [PATCH 01/70] Add initial plugin file --- .../lib/aws-sdk-core/plugins/telemetry_plugin.rb | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry_plugin.rb diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry_plugin.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry_plugin.rb new file mode 100644 index 00000000000..0f438652ad6 --- /dev/null +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry_plugin.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Aws + module Plugin + class TelemetryPlugin < Seahorse::Client::Plugin + end + end +end From 4c68658dd9a253d0429f09bf87e7fb86cfc1534a Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Thu, 8 Aug 2024 08:00:59 -0700 Subject: [PATCH 02/70] Add inital files --- .../aws-sdk-core/plugins/telemetry_plugin.rb | 23 ++++++++++++++++ .../lib/aws-sdk-core/telemetry.rb | 26 +++++++++++++++++++ .../lib/aws-sdk-core/telemetry/base.rb | 0 .../lib/aws-sdk-core/telemetry/no_op.rb | 6 +++++ .../lib/aws-sdk-core/telemetry/otel.rb | 6 +++++ .../lib/aws-sdk-core/telemetry/span_kind.rb | 8 ++++++ .../lib/aws-sdk-core/telemetry/span_status.rb | 7 +++++ 7 files changed, 76 insertions(+) create mode 100644 gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb create mode 100644 gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb create mode 100644 gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb create mode 100644 gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb create mode 100644 gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_kind.rb create mode 100644 gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry_plugin.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry_plugin.rb index 0f438652ad6..9a028d62b50 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry_plugin.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry_plugin.rb @@ -3,6 +3,29 @@ module Aws module Plugin class TelemetryPlugin < Seahorse::Client::Plugin + # add config options + option( + :telemetry_provider, + default: 'TBD', + doc_type: 'TBD', + rbs_type: 'untyped', + docstring: <<~DOCS) do |cfg| + Allows you to provide a telemetry provider class. By default, + will use the NoOpTelemetryProvider. + DOCS + resolve_provider(cfg) + end + + # do we set default provider if none provided here + # do we need to validate if there is something + def self.resolve_provider(_cfg) + # resolve based on whether there's otel + # create an instance of NoOpTelemetryProvider + end + + # do we need handlers? + class Handler < Seahorse::Client::Handler + end end end end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb new file mode 100644 index 00000000000..6bd854c2567 --- /dev/null +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require_relative 'telemetry/base' +require_relative 'telemetry/no_op' +require_relative 'telemetry/otel' +require_relative 'telemetry/span_kind' +require_relative 'telemetry/span_status' + +module Aws + # TBD + module Telemetry + # @api private + def self.otel_loaded? + if @use_otel.nil? + @use_otel = + begin + require 'opentelemetry-sdk' + true + rescue LoadError, NameError + false + end + end + @use_otel + end + end +end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb new file mode 100644 index 00000000000..87d9e0618e1 --- /dev/null +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module Aws + module Telemetry + end +end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb new file mode 100644 index 00000000000..87d9e0618e1 --- /dev/null +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module Aws + module Telemetry + end +end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_kind.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_kind.rb new file mode 100644 index 00000000000..661fbe9c1e3 --- /dev/null +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_kind.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Aws + module Telemetry + module SpanKind + end + end +end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb new file mode 100644 index 00000000000..5c055715f5e --- /dev/null +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Aws + module Telemetry + class SpanStatus; end + end +end From 377883c8fd10e93bc68bb33cd5f5849b23fb3e87 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Thu, 8 Aug 2024 08:03:23 -0700 Subject: [PATCH 03/70] Minor update --- gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb index 6bd854c2567..68355f78a65 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb @@ -7,7 +7,7 @@ require_relative 'telemetry/span_status' module Aws - # TBD + # TODO module Telemetry # @api private def self.otel_loaded? From fcfebde9887317863e685f2319e2fe875941fb6b Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Thu, 8 Aug 2024 08:05:05 -0700 Subject: [PATCH 04/70] Port base and no-op from V4 --- .../lib/aws-sdk-core/telemetry/base.rb | 176 ++++++++++++++++++ .../lib/aws-sdk-core/telemetry/otel.rb | 62 ++++++ 2 files changed, 238 insertions(+) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb index e69de29bb2d..98a15cd9943 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb @@ -0,0 +1,176 @@ +# frozen_string_literal: true + +module Aws + module Telemetry + # Base for TelemetryProvider classes. + # They are used to emit telemetry data. It needs the + # following class instances to function: + # - +TracerProvider+ - A provider that returns a tracer + # instance. Then, a tracer will create spans and those + # spans will contain information in that given moment. + # - +ContextManager+ - Manages context and used to + # return the current context within a trace. + class TelemetryProviderBase + # @param [Class] tracer_provider A provider that returns + # a tracer instance. + # @param [Class] context_manager Manages context and + # used to return the current context. + def initialize(tracer_provider: nil, context_manager: nil) + @tracer_provider = tracer_provider + @context_manager = context_manager + end + # @return [TracerProvider] + attr_reader :tracer_provider + + # @return [ContextManager] + attr_reader :context_manager + end + + # Base for TracerProvider classes. + class TracerProviderBase + # Returns a Tracer instance. + # + # @param [String] name Tracer name + # @return [Tracer] + def tracer(name = nil) + raise NotImplementedError + end + end + + # Base for Tracer classes. + class TracerBase + # Used when a caller wants to manage the activation/deactivation and + # lifecycle of the Span and its parent manually. + # + # @param [String] name Span name + # @param [Object] with_parent Parent Context + # @param [Hash] attributes Attributes to attach to the span + # @param [Hearth::Telemetry::SpanKind] kind Type of Span + # @return [Span] + def start_span(name, with_parent: nil, attributes: nil, kind: nil) + raise NotImplementedError + end + + # A helper for the default use-case of extending the current trace + # with a span. + # On exit, the Span that was active before calling this method will + # be reactivated. If an exception occurs during the execution of the + # provided block, it will be recorded on the span and re-raised. + # + # @param [String] name Span name + # @param [Hash] attributes Attributes to attach to the span + # @param [Hearth::Telemetry::SpanKind] kind Type of Span + # @return [Span] + def in_span(name, attributes: nil, kind: nil) + raise NotImplementedError + end + end + + # Base for Span classes. + class SpanBase + # Set attribute. + # + # @param [String] key + # @param [String, Boolean, Numeric, Array] value + # Value must be non-nil and (array of) string, boolean or numeric type. + # Array values must not contain nil elements and all elements must be of + # the same basic type (string, numeric, boolean). + # @return [self] returns itself + def set_attribute(key, value) + raise NotImplementedError + end + alias []= set_attribute + + # Add attributes. + # + # @param [Hash{String => String, Numeric, Boolean, Array}] attributes Values must be non-nil and (array of) string, + # boolean or numeric type. Array values must not contain nil elements + # and all elements must be of the same basic type (string, numeric, + # boolean). + # @return [self] returns itself + def add_attributes(attributes) + raise NotImplementedError + end + + # Add event to a Span. + # + # @param [String] name Name of the event. + # @param [Hash{String => String, Numeric, Boolean, Array}] attributes Values must be non-nil and (array of) + # string, boolean or numeric type. Array values must not contain nil + # elements and all elements must be of the same basic type (string, + # numeric, boolean). + # @return [self] returns itself + def add_event(name, attributes: nil) + raise NotImplementedError + end + + # Sets the Span status. + # + # @param [Hearth::Telemetry::Status] status The new status, which + # overrides the default Span status, which is OK. + # @return [void] + def status=(status) + raise NotImplementedError + end + + # Finishes the Span. + # + # @param [Time] end_timestamp End timestamp for the span. + # @return [self] returns itself + def finish(end_timestamp: nil) + raise NotImplementedError + end + + # Record an exception during the execution of this span. Multiple + # exceptions can be recorded on a span. + # + # @param [Exception] exception The exception to be recorded + # @param [Hash{String => String, Numeric, Boolean, Array}] attributes One or more key:value pairs, where the + # keys must be strings and the values may be (array of) string, boolean + # or numeric type. + # @return [void] + def record_exception(exception, attributes: nil) + raise NotImplementedError + end + end + + # Base for all ContextManager classes. + class ContextManagerBase + # Returns current context. + # + # @return [Context] + def current + raise NotImplementedError + end + + # Returns the current span from current context. + # + # @return Span + def current_span + raise NotImplementedError + end + + # Associates a Context with the caller’s current execution unit. + # Returns a token to be used with the matching call to detach. + # + # @param [Object] context The new context + # @return [Object] token A token to be used when detaching + def attach(context) + raise NotImplementedError + end + + # Restore the previous Context associated with the current + # execution unit to the value it had before attaching a + # specified Context. + # + # @param [Object] token The token provided by matching the call to attach + # @return [Boolean] True if the calls matched, False otherwise + def detach(token) + raise NotImplementedError + end + end + end +end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb index 87d9e0618e1..443631f91e6 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb @@ -2,5 +2,67 @@ module Aws module Telemetry + # No-op implementation for TelemetryProvider. + class NoOpTelemetryProvider < TelemetryProviderBase + def initialize + super( + tracer_provider: NoOpTracerProvider.new, + context_manager: NoOpContextManager.new + ) + end + end + + # No-op implementation for TracerProvider. + class NoOpTracerProvider < TracerProviderBase + def tracer(name = nil) + @tracer ||= NoOpTracer.new + end + end + + # No-op implementation for Tracer. + class NoOpTracer < TracerBase + def start_span(name, with_parent: nil, attributes: nil, kind: nil) + NoOpSpan.new + end + + def in_span(name, attributes: nil, kind: nil) + yield NoOpSpan.new + end + end + + # No-op implementation for Span. + class NoOpSpan < SpanBase + def set_attribute(key, value) + self + end + alias []= set_attribute + + def add_attributes(attributes) + self + end + + def add_event(name, attributes: nil) + self + end + + def status=(status); end + + def finish(end_timestamp: nil) + self + end + + def record_exception(exception, attributes: nil); end + end + + # No-op implementation for ContextManager. + class NoOpContextManager < ContextManagerBase + def current; end + + def current_span; end + + def attach(context); end + + def detach(token); end + end end end From 5677d2f999bd637b3ea2c276fe00ebdaa0cc0a5d Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Thu, 8 Aug 2024 08:42:09 -0700 Subject: [PATCH 05/70] Add minor functionality to render plugin --- gems/aws-sdk-core/lib/aws-sdk-core.rb | 3 + .../{telemetry_plugin.rb => telemetry.rb} | 10 +-- .../lib/aws-sdk-core/telemetry/no_op.rb | 62 +++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) rename gems/aws-sdk-core/lib/aws-sdk-core/plugins/{telemetry_plugin.rb => telemetry.rb} (75%) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core.rb b/gems/aws-sdk-core/lib/aws-sdk-core.rb index f9685d2373b..2f865304dee 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core.rb @@ -54,6 +54,9 @@ require_relative 'aws-sdk-core/log/param_filter' require_relative 'aws-sdk-core/log/param_formatter' +# telemetry +require_relative 'aws-sdk-core/telemetry' + # stubbing require_relative 'aws-sdk-core/stubbing/empty_stub' diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry_plugin.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb similarity index 75% rename from gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry_plugin.rb rename to gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index 9a028d62b50..8fef7723592 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry_plugin.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -1,13 +1,14 @@ # frozen_string_literal: true module Aws - module Plugin - class TelemetryPlugin < Seahorse::Client::Plugin + module Plugins + # TODO + class Telemetry < Seahorse::Client::Plugin # add config options option( :telemetry_provider, - default: 'TBD', - doc_type: 'TBD', + default: Aws::Telemetry::NoOpTelemetryProvider, + doc_type: Aws::Telemetry::TelemetryProviderBase, rbs_type: 'untyped', docstring: <<~DOCS) do |cfg| Allows you to provide a telemetry provider class. By default, @@ -21,6 +22,7 @@ class TelemetryPlugin < Seahorse::Client::Plugin def self.resolve_provider(_cfg) # resolve based on whether there's otel # create an instance of NoOpTelemetryProvider + Telemetry::NoOpTelemetryProvider.new end # do we need handlers? diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb index 87d9e0618e1..443631f91e6 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb @@ -2,5 +2,67 @@ module Aws module Telemetry + # No-op implementation for TelemetryProvider. + class NoOpTelemetryProvider < TelemetryProviderBase + def initialize + super( + tracer_provider: NoOpTracerProvider.new, + context_manager: NoOpContextManager.new + ) + end + end + + # No-op implementation for TracerProvider. + class NoOpTracerProvider < TracerProviderBase + def tracer(name = nil) + @tracer ||= NoOpTracer.new + end + end + + # No-op implementation for Tracer. + class NoOpTracer < TracerBase + def start_span(name, with_parent: nil, attributes: nil, kind: nil) + NoOpSpan.new + end + + def in_span(name, attributes: nil, kind: nil) + yield NoOpSpan.new + end + end + + # No-op implementation for Span. + class NoOpSpan < SpanBase + def set_attribute(key, value) + self + end + alias []= set_attribute + + def add_attributes(attributes) + self + end + + def add_event(name, attributes: nil) + self + end + + def status=(status); end + + def finish(end_timestamp: nil) + self + end + + def record_exception(exception, attributes: nil); end + end + + # No-op implementation for ContextManager. + class NoOpContextManager < ContextManagerBase + def current; end + + def current_span; end + + def attach(context); end + + def detach(token); end + end end end From fdcb90ad085f29bc871befe955ba5119274a43c2 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Thu, 8 Aug 2024 08:44:31 -0700 Subject: [PATCH 06/70] Add plugin to default plugins --- .../lib/aws-sdk-code-generator/plugin_list.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/plugin_list.rb b/build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/plugin_list.rb index 6bf82f8220b..30ff8cba9ea 100644 --- a/build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/plugin_list.rb +++ b/build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/plugin_list.rb @@ -63,7 +63,8 @@ def default_plugins 'Aws::Plugins::ChecksumAlgorithm' => "#{core_plugins}/checksum_algorithm.rb", 'Aws::Plugins::RequestCompression' => "#{core_plugins}/request_compression.rb", 'Aws::Plugins::DefaultsMode' => "#{core_plugins}/defaults_mode.rb", - 'Aws::Plugins::RecursionDetection' => "#{core_plugins}/recursion_detection.rb" + 'Aws::Plugins::RecursionDetection' => "#{core_plugins}/recursion_detection.rb", + 'Aws::Plugins::Telemetry' => "#{core_plugins}/telemetry.rb" } end From 6336acb2e84a51b59b2ee24e5fd886518717f32e Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Thu, 8 Aug 2024 11:04:42 -0700 Subject: [PATCH 07/70] Add a way to generate tracer scope name --- .../templates/client_class.mustache | 3 ++ .../lib/aws-sdk-core/telemetry.rb | 28 +++++++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/build_tools/aws-sdk-code-generator/templates/client_class.mustache b/build_tools/aws-sdk-code-generator/templates/client_class.mustache index 4aea5de7e32..bb2c7ac36a3 100644 --- a/build_tools/aws-sdk-code-generator/templates/client_class.mustache +++ b/build_tools/aws-sdk-code-generator/templates/client_class.mustache @@ -108,6 +108,9 @@ module {{module_name}} config: config) context[:gem_name] = '{{gem_name}}' context[:gem_version] = '{{gem_version}}' + context[:tracer] = context.config.telemetry_provider.tracer_provider.tracer( + Aws::Telemetry.module_to_tracer_name('{{module_name}}') + ) Seahorse::Client::Request.new(handlers, context) end {{#waiters?}} diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb index 68355f78a65..2a4fb799660 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb @@ -9,18 +9,24 @@ module Aws # TODO module Telemetry - # @api private - def self.otel_loaded? - if @use_otel.nil? - @use_otel = - begin - require 'opentelemetry-sdk' - true - rescue LoadError, NameError - false - end + class << self + def module_to_tracer_name(module_name) + "#{module_name.gsub('::', '.')}.client" + end + + # @api private + def otel_loaded? + if @use_otel.nil? + @use_otel = + begin + require 'opentelemetry-sdk' + true + rescue LoadError, NameError + false + end + end + @use_otel end - @use_otel end end end From dca7152b1be464ae8b06e6a805895a8c6832444f Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 11:59:32 -0700 Subject: [PATCH 08/70] Correctly format module name --- gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb index 2a4fb799660..74c4011af4b 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb @@ -11,7 +11,7 @@ module Aws module Telemetry class << self def module_to_tracer_name(module_name) - "#{module_name.gsub('::', '.')}.client" + "#{module_name.gsub('::', '.')}.client".downcase end # @api private From d558c99c2c21618005a5ed0d8cdac2341e136c9a Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 12:18:13 -0700 Subject: [PATCH 09/70] Generate tracer on context --- .../templates/client_class.mustache | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build_tools/aws-sdk-code-generator/templates/client_class.mustache b/build_tools/aws-sdk-code-generator/templates/client_class.mustache index bb2c7ac36a3..b4d5dbbdec2 100644 --- a/build_tools/aws-sdk-code-generator/templates/client_class.mustache +++ b/build_tools/aws-sdk-code-generator/templates/client_class.mustache @@ -105,12 +105,13 @@ module {{module_name}} authorizer: authorizer,{{/authorizer?}} client: self, params: params, - config: config) + config: config, + tracer: config.telemetry_provider.tracer_provider.tracer( + Aws::Telemetry.module_to_tracer_name('{{module_name}}') + ) + ) context[:gem_name] = '{{gem_name}}' context[:gem_version] = '{{gem_version}}' - context[:tracer] = context.config.telemetry_provider.tracer_provider.tracer( - Aws::Telemetry.module_to_tracer_name('{{module_name}}') - ) Seahorse::Client::Request.new(handlers, context) end {{#waiters?}} From 94b862f66e03c0ac0966986d31bd5067dbc9c055 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 12:18:30 -0700 Subject: [PATCH 10/70] Add tracer to context --- gems/aws-sdk-core/lib/seahorse/client/request_context.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gems/aws-sdk-core/lib/seahorse/client/request_context.rb b/gems/aws-sdk-core/lib/seahorse/client/request_context.rb index 36f81a8ba2d..57a7f5e4c48 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/request_context.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/request_context.rb @@ -24,6 +24,7 @@ def initialize(options = {}) @http_request = options[:http_request] || Http::Request.new @http_response = options[:http_response] || Http::Response.new @retries = 0 + @tracer = options[:tracer] @metadata = {} end @@ -54,6 +55,9 @@ def initialize(options = {}) # @return [Integer] attr_accessor :retries + # @return [TracerProvider::Tracer] + attr_accessor :tracer + # @return [Hash] attr_reader :metadata From e90ec7dce1250bfb626f1b03c429a426c56720fd Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 12:23:04 -0700 Subject: [PATCH 11/70] Add initial parent span --- .../lib/aws-sdk-core/plugins/telemetry.rb | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index 8fef7723592..b2959397e4b 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -4,30 +4,52 @@ module Aws module Plugins # TODO class Telemetry < Seahorse::Client::Plugin - # add config options option( :telemetry_provider, default: Aws::Telemetry::NoOpTelemetryProvider, doc_type: Aws::Telemetry::TelemetryProviderBase, rbs_type: 'untyped', docstring: <<~DOCS) do |cfg| - Allows you to provide a telemetry provider class. By default, + Allows you to provide a telemetry provider. By default, will use the NoOpTelemetryProvider. DOCS resolve_provider(cfg) end - # do we set default provider if none provided here # do we need to validate if there is something def self.resolve_provider(_cfg) - # resolve based on whether there's otel - # create an instance of NoOpTelemetryProvider - Telemetry::NoOpTelemetryProvider.new + Aws::Telemetry::NoOpTelemetryProvider.new end - # do we need handlers? + # this is the root parent handler + # sets up initial attributes to emit class Handler < Seahorse::Client::Handler + def call(context) + # serviceId may not be present in older versions + # do I need to retrieve legacy serviceId? + attributes = { + 'rpc.service' => context.config.api.metadata['serviceId'], + 'rpc.method' => context.operation.name, + 'code.function' => context.operation_name.to_s, + 'code.namespace' => 'AWS::Plugins::Telemetry' + } + context.tracer.in_span( + parent_span_name(context), + attributes: attributes, + kind: Aws::Telemetry::SpanKind::CLIENT + ) do + @handler.call(context) + end + end + + private + + def parent_span_name(context) + "#{context.config.api.metadata['serviceId']}.#{context.operation.name}".strip + end end + + handler(Handler, step: :initialize, priority: 99) end end end From 26913f71e43b6434a52d6523c2413e4a2a551eb0 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 12:23:26 -0700 Subject: [PATCH 12/70] Update otel provider --- .../lib/aws-sdk-core/telemetry/otel.rb | 206 +++++++++++++++--- 1 file changed, 180 insertions(+), 26 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb index 443631f91e6..b351588f665 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb @@ -2,67 +2,221 @@ module Aws module Telemetry - # No-op implementation for TelemetryProvider. - class NoOpTelemetryProvider < TelemetryProviderBase + # OTelProvider allows to emit telemetry data based on OpenTelemetry. + # + # To use this provider, require the `opentelemetry-sdk` gem and then, + # pass in an instance of a `Aws::Telemetry::OTelProvider` as the + # telemetry provider in the client config. + # + # @example Configuration + # + # require 'opentelemetry-sdk' + # + # # sets up the OpenTelemetry SDK with their config defaults + # OpenTelemetry::SDK.configure + # + # otel_provider = Aws::Telemetry::OTelProvider.new + # client = Aws::S3::Client.new(telemetry_provider: otel_provider) + # + # OpenTelemetry supports many ways to export your telemetry data. + # See {https://opentelemetry.io/docs/languages/ruby/exporters here} for + # more information. + # + # @example Exporting via console + # + # require 'opentelemetry-sdk' + # + # ENV['OTEL_TRACES_EXPORTER'] ||= 'console' + # + # # configures the OpenTelemetry SDK with defaults + # OpenTelemetry::SDK.configure + # + # otel_provider = Aws::Telemetry::OTelProvider.new + # client = Aws::S3::Client.new(telemetry_provider: otel_provider) + class OTelProvider < TelemetryProviderBase def initialize + unless Aws::Telemetry.otel_loaded? + raise ArgumentError, + 'Requires the `opentelemetry-sdk` gem to use OTel Provider.' + end super( - tracer_provider: NoOpTracerProvider.new, - context_manager: NoOpContextManager.new + tracer_provider: OTelTracerProvider.new, + context_manager: OTelContextManager.new ) end end - # No-op implementation for TracerProvider. - class NoOpTracerProvider < TracerProviderBase + # OpenTelemetry-based TracerProvider, an entry point for + # creating Tracer instances. + class OTelTracerProvider < TracerProviderBase + def initialize + super + @tracer_provider = OpenTelemetry.tracer_provider + end + + # Returns a Tracer instance. + # + # @param [optional String] name Tracer name + # @return [Tracer] def tracer(name = nil) - @tracer ||= NoOpTracer.new + OTelTracer.new(@tracer_provider.tracer(name)) end end - # No-op implementation for Tracer. - class NoOpTracer < TracerBase + # OpenTelemetry-based Tracer, responsible for creating spans. + class OTelTracer < TracerBase + def initialize(tracer) + super() + @tracer = tracer + end + + # Used when a caller wants to manage the activation/deactivation and + # lifecycle of the Span and its parent manually. + # + # @param [String] name Span name + # @param [Object] with_parent Parent Context + # @param [Hash] attributes Attributes to attach to the span + # @param [Aws::Telemetry::SpanKind] kind Type of Span + # @return [Span] def start_span(name, with_parent: nil, attributes: nil, kind: nil) - NoOpSpan.new + span = @tracer.start_span( + name, + with_parent: with_parent, + attributes: attributes, + kind: kind + ) + OTelSpan.new(span) end - def in_span(name, attributes: nil, kind: nil) - yield NoOpSpan.new + # A helper for the default use-case of extending the current trace + # with a span. + # On exit, the Span that was active before calling this method will + # be reactivated. If an exception occurs during the execution of the + # provided block, it will be recorded on the span and re-raised. + # + # @param [String] name Span name + # @param [Hash] attributes Attributes to attach to the span + # @param [Aws::Telemetry::SpanKind] kind Type of Span + # @return [Span] + def in_span(name, attributes: nil, kind: nil, &block) + @tracer.in_span(name, attributes: attributes, kind: kind) do |span| + block.call(OTelSpan.new(span)) + end end end - # No-op implementation for Span. - class NoOpSpan < SpanBase + # OpenTelemetry-based Span, represents a single operation + # within a trace. + class OTelSpan < SpanBase + def initialize(span) + super() + @span = span + end + + # Set attribute. + # + # @param [String] key + # @param [String, Boolean, Numeric, Array] value + # Value must be non-nil and (array of) string, boolean or numeric type. + # Array values must not contain nil elements and all elements must be of + # the same basic type (string, numeric, boolean). + # @return [self] returns itself def set_attribute(key, value) - self + @span.set_attribute(key, value) end alias []= set_attribute + # Add attributes. + # + # @param [Hash{String => String, Numeric, Boolean, Array}] attributes Values must be non-nil and (array of) string, + # boolean or numeric type. Array values must not contain nil elements + # and all elements must be of the same basic type (string, numeric, + # boolean). + # @return [self] returns itself def add_attributes(attributes) - self + @span.add_attributes(attributes) end + # Add event to a Span. + # + # @param [String] name Name of the event. + # @param [Hash{String => String, Numeric, Boolean, Array}] attributes Values must be non-nil and (array of) + # string, boolean or numeric type. Array values must not contain nil + # elements and all elements must be of the same basic type (string, + # numeric, boolean). + # @return [self] returns itself def add_event(name, attributes: nil) - self + @span.add_event(name, attributes: attributes) end - def status=(status); end + # Sets the Span status. + # + # @param [Aws::Telemetry::Status] status The new status, which + # overrides the default Span status, which is OK. + # @return [void] + def status=(status) + @span.status = status + end + # Finishes the Span. + # + # @param [Time] end_timestamp End timestamp for the span. + # @return [self] returns itself def finish(end_timestamp: nil) - self + @span.finish(end_timestamp: end_timestamp) end - def record_exception(exception, attributes: nil); end + # Record an exception during the execution of this span. Multiple + # exceptions can be recorded on a span. + # + # @param [Exception] exception The exception to be recorded + # @param [Hash{String => String, Numeric, Boolean, Array}] attributes One or more key:value pairs, where the + # keys must be strings and the values may be (array of) string, boolean + # or numeric type. + # @return [void] + def record_exception(exception, attributes: nil) + @span.record_exception(exception, attributes: attributes) + end end - # No-op implementation for ContextManager. - class NoOpContextManager < ContextManagerBase - def current; end + # OpenTelemetry-based ContextManager, manages context and + # used to return the current context within a trace. + class OTelContextManager < ContextManagerBase + # Returns current context. + # + # @return [Context] + def current + OpenTelemetry::Context.current + end - def current_span; end + # Returns the current span from current context. + # + # @return Span + def current_span + OTelSpan.new(OpenTelemetry::Trace.current_span) + end - def attach(context); end + # Associates a Context with the caller’s current execution unit. + # Returns a token to be used with the matching call to detach. + # + # @param [Context] context The new context + # @return [Object] token A token to be used when detaching + def attach(context) + OpenTelemetry::Context.attach(context) + end - def detach(token); end + # Restore the previous Context associated with the current + # execution unit to the value it had before attaching a + # specified Context. + # + # @param [Object] token The token provided by matching the call to attach + # @return [Boolean] True if the calls matched, False otherwise + def detach(token) + OpenTelemetry::Context.detach(token) + end end end end From 2257d34bc0524b8630ddbe76af746536f7576552 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 12:23:47 -0700 Subject: [PATCH 13/70] Port span kind --- .../lib/aws-sdk-core/telemetry/span_kind.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_kind.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_kind.rb index 661fbe9c1e3..7bc43ada256 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_kind.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_kind.rb @@ -3,6 +3,20 @@ module Aws module Telemetry module SpanKind + # Default. Represents an internal operation within an application. + INTERNAL = :internal + + # Represents handling synchronous network requests. + SERVER = :server + + # Represents a request to some remote service. + CLIENT = :client + + # Represents a child of an asynchronous +PRODUCER+ request. + CONSUMER = :consumer + + # Represents an asynchronous request. + PRODUCER = :producer end end end From b2d8a4422b5358228e4ae88a3771689567d17aa5 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 12:24:34 -0700 Subject: [PATCH 14/70] Wrap NetHttp handler --- .../lib/seahorse/client/net_http/handler.rb | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb b/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb index c1fdc838c2c..8f960bcbd90 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb @@ -42,7 +42,13 @@ class InvalidHttpVerbError < StandardError; end # @param [RequestContext] context # @return [Response] def call(context) - transmit(context.config, context.http_request, context.http_response) + span_wrapper(context) do + transmit( + context.config, + context.http_request, + context.http_response + ) + end Response.new(context: context) end @@ -192,6 +198,47 @@ def extract_headers(response) end end + def span_wrapper(context, &block) + context.tracer.in_span( + 'Handler.NetHttp', + attributes: request_attrs(context) + ) do |span| + block.call + span.add_attributes(response_attrs(context)) + end + end + + def request_attrs(context) + { + 'http.method' => context.http_request.http_method, + 'net.protocol.name' => 'http', + 'net.protocol.version' => Net::HTTP::HTTPVersion, + 'net.peer.name' => context.http_request.endpoint.host, + 'net.peer.port' => context.http_request.endpoint.port.to_s + }.tap do |h| + if context.http_request.headers.key?('Content-Length') + h['http.request_context_length'] = + context.request.headers['Content-Length'] + end + end + end + + def response_attrs(context) + { + 'http.status_code' => context.http_response.status_code.to_s + }.tap do |h| + if context.http_response.headers.key?('Content-Length') + h['http.response.content_length'] = + context.http_response.headers['Content-Length'] + end + + if context.http_response.headers.key?('x-amz-request-id') + h['aws.request_id'] = + context.http_response.headers['x-amz-request-id'] + end + end + end + end end end From c0dd4d38e93f3cc792ebffd7ac3b1e0ed91c55d8 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 12:26:57 -0700 Subject: [PATCH 15/70] Port span status --- .../lib/aws-sdk-core/telemetry/span_status.rb | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb index 5c055715f5e..baec8bf254d 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb @@ -2,6 +2,58 @@ module Aws module Telemetry - class SpanStatus; end + # Represents the status of a finished span. + class SpanStatus + class << self + private :new + + # Returns a newly created {SpanStatus} with code == +UNSET+ + # and an optional description. + # + # @param [optional String] description + # @return [SpanStatus] + def unset(description = '') + new(UNSET, description: description) + end + + # Returns a newly created {SpanStatus} with code == +OK+ + # and an optional description. + # + # @param [optional String] description + # @return [SpanStatus] + def ok(description = '') + new(OK, description: description) + end + + # Returns a newly created {SpanStatus} with code == +ERROR+ + # and an optional description. + # + # @param [optional String] description + # @return [SpanStatus] + def error(description = '') + new(ERROR, description: description) + end + end + + def initialize(code, description: '') + @code = code + @description = description + end + + # @return [Integer] code + attr_reader :code + + # @return [String] description + attr_reader :description + + # The operation completed successfully. + OK = 0 + + # The default status. + UNSET = 1 + + # An error. + ERROR = 2 + end end end From 1490f6fc21333d8bca5109c24f6044f1277e87f4 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 12:28:43 -0700 Subject: [PATCH 16/70] Update documentation syntax --- gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb index 98a15cd9943..93377a5d64b 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb @@ -5,10 +5,10 @@ module Telemetry # Base for TelemetryProvider classes. # They are used to emit telemetry data. It needs the # following class instances to function: - # - +TracerProvider+ - A provider that returns a tracer + # - `TracerProvider` - A provider that returns a tracer # instance. Then, a tracer will create spans and those # spans will contain information in that given moment. - # - +ContextManager+ - Manages context and used to + # - `ContextManager` - Manages context and used to # return the current context within a trace. class TelemetryProviderBase # @param [Class] tracer_provider A provider that returns @@ -45,7 +45,7 @@ class TracerBase # @param [String] name Span name # @param [Object] with_parent Parent Context # @param [Hash] attributes Attributes to attach to the span - # @param [Hearth::Telemetry::SpanKind] kind Type of Span + # @param [Aws::Telemetry::SpanKind] kind Type of Span # @return [Span] def start_span(name, with_parent: nil, attributes: nil, kind: nil) raise NotImplementedError @@ -59,7 +59,7 @@ def start_span(name, with_parent: nil, attributes: nil, kind: nil) # # @param [String] name Span name # @param [Hash] attributes Attributes to attach to the span - # @param [Hearth::Telemetry::SpanKind] kind Type of Span + # @param [Aws::Telemetry::SpanKind] kind Type of Span # @return [Span] def in_span(name, attributes: nil, kind: nil) raise NotImplementedError @@ -108,7 +108,7 @@ def add_event(name, attributes: nil) # Sets the Span status. # - # @param [Hearth::Telemetry::Status] status The new status, which + # @param [Aws::Telemetry::SpanStatus] status The new status, which # overrides the default Span status, which is OK. # @return [void] def status=(status) From 32dd56b5622494b27de7fdccdb6decc0147536e4 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 12:55:44 -0700 Subject: [PATCH 17/70] Add config validation --- .../lib/aws-sdk-core/plugins/telemetry.rb | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index b2959397e4b..9774f2ecf7e 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -9,16 +9,31 @@ class Telemetry < Seahorse::Client::Plugin default: Aws::Telemetry::NoOpTelemetryProvider, doc_type: Aws::Telemetry::TelemetryProviderBase, rbs_type: 'untyped', - docstring: <<~DOCS) do |cfg| + docstring: <<-DOCS) do |cfg| Allows you to provide a telemetry provider. By default, will use the NoOpTelemetryProvider. DOCS resolve_provider(cfg) end - # do we need to validate if there is something - def self.resolve_provider(_cfg) - Aws::Telemetry::NoOpTelemetryProvider.new + def after_initialize(client) + validate_telemetry_provider(client.config) + end + + def validate_telemetry_provider(config) + unless config.telemetry_provider.is_a?(Aws::Telemetry::TelemetryProviderBase) + raise ArgumentError, + 'Must provide a telemetry provider for the '\ + '`telemetry_provider` configuration option.' + end + end + + class << self + private + + def resolve_provider(_cfg) + Aws::Telemetry::NoOpTelemetryProvider.new + end end # this is the root parent handler From 2f3557abbdb090d1722fc0c28e2b5051d484c70a Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 13:04:26 -0700 Subject: [PATCH 18/70] Handle service ids --- .../lib/aws-sdk-core/plugins/telemetry.rb | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index 9774f2ecf7e..5f6517c893a 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -42,14 +42,16 @@ class Handler < Seahorse::Client::Handler def call(context) # serviceId may not be present in older versions # do I need to retrieve legacy serviceId? + service_id = service_id(context) + attributes = { - 'rpc.service' => context.config.api.metadata['serviceId'], + 'rpc.service' => service_id, 'rpc.method' => context.operation.name, 'code.function' => context.operation_name.to_s, 'code.namespace' => 'AWS::Plugins::Telemetry' } context.tracer.in_span( - parent_span_name(context), + parent_span_name(context, service_id), attributes: attributes, kind: Aws::Telemetry::SpanKind::CLIENT ) do @@ -59,8 +61,13 @@ def call(context) private - def parent_span_name(context) - "#{context.config.api.metadata['serviceId']}.#{context.operation.name}".strip + def service_id(context) + context.config.api.metadata['serviceId'] || + context.config.api.metadata['serviceAbbreviation'] + end + + def parent_span_name(context, service_id) + "#{service_id}.#{context.operation.name}".strip end end From 2d23f535ba28a83f08195b12f5cf6f8c0eb3cd4e Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 13:06:52 -0700 Subject: [PATCH 19/70] Add documentation --- .../lib/aws-sdk-core/plugins/telemetry.rb | 2 +- .../lib/aws-sdk-core/telemetry.rb | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index 5f6517c893a..f80995b08da 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -2,7 +2,7 @@ module Aws module Plugins - # TODO + # @api private class Telemetry < Seahorse::Client::Plugin option( :telemetry_provider, diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb index 74c4011af4b..30e28f4ea43 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb @@ -7,9 +7,27 @@ require_relative 'telemetry/span_status' module Aws - # TODO + # Observability is the extent to which a system's current state can be + # inferred from the data it emits. The data emitted is commonly referred + # as Telemetry. The AWS SDK for Ruby currently supports traces as + # a telemetry signal. + # + # A telemetry provider is used to emit telemetry data. By default, the + # `NoOpTelemetryProvider` will not record or emit any telemetry data. + # The SDK currently supports OpenTelemetry (OTel) as a provider. See + # {OTelProvider} for more information. + # + # If a provider isn't supported, you can implement your own provider by + # inheriting the following base classes and implementing the interfaces + # defined: + # * {TelemetryProviderBase} + # * {ContextManagerBase} + # * {TracerProviderBase} + # * {TracerBase} + # * {SpanBase} module Telemetry class << self + # @api private def module_to_tracer_name(module_name) "#{module_name.gsub('::', '.')}.client".downcase end From d816851daa911302d257c69e79204a17fa48317d Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 13:13:24 -0700 Subject: [PATCH 20/70] Set up plugin specs --- gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb diff --git a/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb b/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb new file mode 100644 index 00000000000..00f0822b286 --- /dev/null +++ b/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require_relative '../../spec_helper' +module Aws + module Plugins + describe Telemetry do + + end + end +end From b6e7f4883d2609f90801c24ae91e37930c918340 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 15:26:17 -0700 Subject: [PATCH 21/70] Emit stub responses --- .../aws-sdk-core/plugins/stub_responses.rb | 63 ++++++++++++++++--- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb index 9ed88e8c5aa..832ec6db779 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb @@ -49,17 +49,21 @@ def after_initialize(client) class Handler < Seahorse::Client::Handler def call(context) - stub = context.client.next_stub(context) - resp = Seahorse::Client::Response.new(context: context) - async_mode = context.client.is_a? Seahorse::Client::AsyncBase - if Hash === stub && stub[:mutex] - stub[:mutex].synchronize { apply_stub(stub, resp, async_mode) } - else - apply_stub(stub, resp, async_mode) + span_wrapper(context) do + stub = context.client.next_stub(context) + resp = Seahorse::Client::Response.new(context: context) + async_mode = context.client.is_a? Seahorse::Client::AsyncBase + if Hash === stub && stub[:mutex] + stub[:mutex].synchronize { apply_stub(stub, resp, async_mode) } + else + apply_stub(stub, resp, async_mode) + end + async_mode ? Seahorse::Client::AsyncResponse.new( + context: context, + stream: context[:input_event_stream_handler].event_emitter.stream, + sync_queue: Queue.new + ) : resp end - - async_mode ? Seahorse::Client::AsyncResponse.new( - context: context, stream: context[:input_event_stream_handler].event_emitter.stream, sync_queue: Queue.new) : resp end def apply_stub(stub, response, async_mode = false) @@ -99,6 +103,45 @@ def signal_http(stub, http_resp, async_mode = false) http_resp.signal_done end + def span_wrapper(context, &block) + context.tracer.in_span( + 'Handler.StubResponses', + attributes: request_attrs(context) + ) do |span| + block.call + span.add_attributes(response_attrs(context)) + yield + end + end + + def request_attrs(context) + { + 'http.method' => context.http_request.http_method, + 'net.protocol.name' => 'http', + 'net.protocol.version' => Net::HTTP::HTTPVersion, + }.tap do |h| + if context.http_request.headers.key?('Content-Length') + h['http.request_context_length'] = + context.http_request.headers['Content-Length'] + end + end + end + + def response_attrs(context) + { + 'http.status_code' => context.http_response.status_code.to_s + }.tap do |h| + if context.http_response.headers.key?('Content-Length') + h['http.response.content_length'] = + context.http_response.headers['Content-Length'] + end + + if context.http_response.headers.key?('x-amz-request-id') + h['aws.request_id'] = + context.http_response.headers['x-amz-request-id'] + end + end + end end end end From 6e11a7941dfd285133a52691d9cf8f5078332f3e Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 12 Aug 2024 15:26:45 -0700 Subject: [PATCH 22/70] Fix span attrs --- gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb b/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb index 8f960bcbd90..6cc7de08a82 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb @@ -218,7 +218,7 @@ def request_attrs(context) }.tap do |h| if context.http_request.headers.key?('Content-Length') h['http.request_context_length'] = - context.request.headers['Content-Length'] + context.http_request.headers['Content-Length'] end end end From be7c01d747b03f0714cecc3e86ac6d4805c90883 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 13 Aug 2024 10:29:53 -0700 Subject: [PATCH 23/70] Add opentelemetry-sdk to testing gems --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index f2b27131c56..3edd16f06c7 100644 --- a/Gemfile +++ b/Gemfile @@ -68,4 +68,6 @@ group :test do gem 'multipart-post' gem 'rspec' + + gem 'opentelemetry-sdk' end From c909b47804d9fe781d7124ae0baf525e19cdfa61 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 13 Aug 2024 11:39:55 -0700 Subject: [PATCH 24/70] Update existing specs with tracer --- gems/aws-sdk-core/spec/aws/plugins/request_compression_spec.rb | 3 +-- .../aws-sdk-core/spec/seahorse/client/net_http/handler_spec.rb | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gems/aws-sdk-core/spec/aws/plugins/request_compression_spec.rb b/gems/aws-sdk-core/spec/aws/plugins/request_compression_spec.rb index 897b1a7ef8a..fb8434d959b 100644 --- a/gems/aws-sdk-core/spec/aws/plugins/request_compression_spec.rb +++ b/gems/aws-sdk-core/spec/aws/plugins/request_compression_spec.rb @@ -1,13 +1,12 @@ # frozen_string_literal: true require_relative '../../spec_helper' -require 'aws-sdk-core/plugins/request_compression' module Aws module Plugins describe RequestCompression do let(:creds) { Aws::Credentials.new('akid', 'secret') } - let(:client_opts) { { credentials: creds, stub_responses: true } } + let(:client_opts) { { credentials: creds, stub_responses: true } } RequestCompressionClient = ApiHelper.sample_service( metadata: { 'protocol' => 'rest-xml' }, diff --git a/gems/aws-sdk-core/spec/seahorse/client/net_http/handler_spec.rb b/gems/aws-sdk-core/spec/seahorse/client/net_http/handler_spec.rb index 2d71f139acc..7d1b715bd23 100644 --- a/gems/aws-sdk-core/spec/seahorse/client/net_http/handler_spec.rb +++ b/gems/aws-sdk-core/spec/seahorse/client/net_http/handler_spec.rb @@ -19,6 +19,7 @@ module NetHttp RequestContext.new.tap do |context| context.config = config context.http_request.endpoint = endpoint + context.tracer = Aws::Telemetry::NoOpTracer.new end end From 308f8f9b8aeaf48ac618cb139d112c74d51185ec Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 13 Aug 2024 11:42:20 -0700 Subject: [PATCH 25/70] Fix span wrapper on stub_responses --- .../lib/aws-sdk-core/plugins/stub_responses.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb index 832ec6db779..2676f34c76f 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb @@ -108,9 +108,9 @@ def span_wrapper(context, &block) 'Handler.StubResponses', attributes: request_attrs(context) ) do |span| - block.call - span.add_attributes(response_attrs(context)) - yield + block.call.tap do + span.add_attributes(response_attrs(context)) + end end end @@ -118,7 +118,7 @@ def request_attrs(context) { 'http.method' => context.http_request.http_method, 'net.protocol.name' => 'http', - 'net.protocol.version' => Net::HTTP::HTTPVersion, + 'net.protocol.version' => Net::HTTP::HTTPVersion }.tap do |h| if context.http_request.headers.key?('Content-Length') h['http.request_context_length'] = From dbbfe502dd74ffe674ded78be194fa9c3983a2a3 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 13 Aug 2024 11:43:02 -0700 Subject: [PATCH 26/70] Update sso and sts to include telemetry plugin --- gems/aws-sdk-core/lib/aws-sdk-sso/client.rb | 12 +++++++++++- gems/aws-sdk-core/lib/aws-sdk-sts/client.rb | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-sso/client.rb b/gems/aws-sdk-core/lib/aws-sdk-sso/client.rb index 27ab838f0e6..de5a4f2d26e 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-sso/client.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-sso/client.rb @@ -32,6 +32,7 @@ require 'aws-sdk-core/plugins/request_compression.rb' require 'aws-sdk-core/plugins/defaults_mode.rb' require 'aws-sdk-core/plugins/recursion_detection.rb' +require 'aws-sdk-core/plugins/telemetry.rb' require 'aws-sdk-core/plugins/sign.rb' require 'aws-sdk-core/plugins/protocols/rest_json.rb' @@ -83,6 +84,7 @@ class Client < Seahorse::Client::Base add_plugin(Aws::Plugins::RequestCompression) add_plugin(Aws::Plugins::DefaultsMode) add_plugin(Aws::Plugins::RecursionDetection) + add_plugin(Aws::Plugins::Telemetry) add_plugin(Aws::Plugins::Sign) add_plugin(Aws::Plugins::Protocols::RestJson) add_plugin(Aws::SSO::Plugins::Endpoints) @@ -330,6 +332,10 @@ class Client < Seahorse::Client::Base # ** Please note ** When response stubbing is enabled, no HTTP # requests are made, and retries are disabled. # + # @option options [Aws::Telemetry::TelemetryProviderBase] :telemetry_provider (Aws::Telemetry::NoOpTelemetryProvider) + # Allows you to provide a telemetry provider. By default, + # will use the NoOpTelemetryProvider. + # # @option options [Aws::TokenProvider] :token_provider # A Bearer Token Provider. This can be an instance of any one of the # following classes: @@ -640,7 +646,11 @@ def build_request(operation_name, params = {}) operation: config.api.operation(operation_name), client: self, params: params, - config: config) + config: config, + tracer: config.telemetry_provider.tracer_provider.tracer( + Aws::Telemetry.module_to_tracer_name('Aws::SSO') + ) + ) context[:gem_name] = 'aws-sdk-core' context[:gem_version] = '3.201.4' Seahorse::Client::Request.new(handlers, context) diff --git a/gems/aws-sdk-core/lib/aws-sdk-sts/client.rb b/gems/aws-sdk-core/lib/aws-sdk-sts/client.rb index 91ab61965e8..c9989aa1efd 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-sts/client.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-sts/client.rb @@ -32,6 +32,7 @@ require 'aws-sdk-core/plugins/request_compression.rb' require 'aws-sdk-core/plugins/defaults_mode.rb' require 'aws-sdk-core/plugins/recursion_detection.rb' +require 'aws-sdk-core/plugins/telemetry.rb' require 'aws-sdk-core/plugins/sign.rb' require 'aws-sdk-core/plugins/protocols/query.rb' require 'aws-sdk-sts/plugins/sts_regional_endpoints.rb' @@ -84,6 +85,7 @@ class Client < Seahorse::Client::Base add_plugin(Aws::Plugins::RequestCompression) add_plugin(Aws::Plugins::DefaultsMode) add_plugin(Aws::Plugins::RecursionDetection) + add_plugin(Aws::Plugins::Telemetry) add_plugin(Aws::Plugins::Sign) add_plugin(Aws::Plugins::Protocols::Query) add_plugin(Aws::STS::Plugins::STSRegionalEndpoints) @@ -337,6 +339,10 @@ class Client < Seahorse::Client::Base # ** Please note ** When response stubbing is enabled, no HTTP # requests are made, and retries are disabled. # + # @option options [Aws::Telemetry::TelemetryProviderBase] :telemetry_provider (Aws::Telemetry::NoOpTelemetryProvider) + # Allows you to provide a telemetry provider. By default, + # will use the NoOpTelemetryProvider. + # # @option options [Aws::TokenProvider] :token_provider # A Bearer Token Provider. This can be an instance of any one of the # following classes: @@ -2387,7 +2393,11 @@ def build_request(operation_name, params = {}) operation: config.api.operation(operation_name), client: self, params: params, - config: config) + config: config, + tracer: config.telemetry_provider.tracer_provider.tracer( + Aws::Telemetry.module_to_tracer_name('Aws::STS') + ) + ) context[:gem_name] = 'aws-sdk-core' context[:gem_version] = '3.201.4' Seahorse::Client::Request.new(handlers, context) From c9259c7a168bafd861bcfaa36950b726a4d1720d Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 13 Aug 2024 11:53:02 -0700 Subject: [PATCH 27/70] Update parent span name --- gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index f80995b08da..3f8a312efb7 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -67,7 +67,7 @@ def service_id(context) end def parent_span_name(context, service_id) - "#{service_id}.#{context.operation.name}".strip + "#{service_id}.#{context.operation.name}".delete(' ') end end From 3febd0738b4e8632cbc97c68d8b0ae2cf30b827b Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 13 Aug 2024 12:35:19 -0700 Subject: [PATCH 28/70] Fix syntax --- gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb | 2 +- gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb | 2 +- gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb index 2676f34c76f..7305da1e576 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb @@ -132,7 +132,7 @@ def response_attrs(context) 'http.status_code' => context.http_response.status_code.to_s }.tap do |h| if context.http_response.headers.key?('Content-Length') - h['http.response.content_length'] = + h['http.response_content_length'] = context.http_response.headers['Content-Length'] end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index 3f8a312efb7..6286dc3bdb5 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -48,7 +48,7 @@ def call(context) 'rpc.service' => service_id, 'rpc.method' => context.operation.name, 'code.function' => context.operation_name.to_s, - 'code.namespace' => 'AWS::Plugins::Telemetry' + 'code.namespace' => 'Aws::Plugins::Telemetry' } context.tracer.in_span( parent_span_name(context, service_id), diff --git a/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb b/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb index 6cc7de08a82..446db351fd5 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb @@ -217,7 +217,7 @@ def request_attrs(context) 'net.peer.port' => context.http_request.endpoint.port.to_s }.tap do |h| if context.http_request.headers.key?('Content-Length') - h['http.request_context_length'] = + h['http.request_content_length'] = context.http_request.headers['Content-Length'] end end @@ -228,7 +228,7 @@ def response_attrs(context) 'http.status_code' => context.http_response.status_code.to_s }.tap do |h| if context.http_response.headers.key?('Content-Length') - h['http.response.content_length'] = + h['http.response_content_length'] = context.http_response.headers['Content-Length'] end From 49ff8bbf37a82a4f1a1284e1d4e6df7f29a0686e Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 13 Aug 2024 12:35:43 -0700 Subject: [PATCH 29/70] Add plugin specs --- .../spec/aws/plugins/telemetry_spec.rb | 212 ++++++++++++++++++ 1 file changed, 212 insertions(+) diff --git a/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb b/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb index 00f0822b286..ac18727679b 100644 --- a/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb +++ b/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb @@ -1,10 +1,222 @@ # frozen_string_literal: true require_relative '../../spec_helper' +require 'opentelemetry-sdk' +require 'webmock/rspec' + module Aws module Plugins describe Telemetry do + let(:creds) { Aws::Credentials.new('akid', 'secret') } + let(:client_config) { { credentials: creds, region: 'us-east-1' } } + + TelemetryClient = ApiHelper.sample_service( + metadata: { + 'protocol' => 'rest-xml', + 'serviceId' => 'Telemetry Service' + }, + operations: { + 'SomeOperation' => { + 'http' => { 'method' => 'POST', 'requestUri' => '/some_operation' }, + 'input' => { 'shape' => 'OperationRequest' }, + 'requestcompression' => { + 'encodings' => ['gzip'] + } + }, + 'OperationStreaming' => { + 'http' => { 'method' => 'POST', 'requestUri' => '/' }, + 'input' => { 'shape' => 'OperationStreamingRequest' }, + 'requestcompression' => { + 'encodings' => ['gzip'] + } + } + }, + shapes: { + 'OperationRequest' => { + 'type' => 'structure', + 'members' => { + 'Body' => { 'shape' => 'Body', 'streaming' => false } + }, + 'payload' => 'Body' + }, + 'OperationStreamingRequest' => { + 'type' => 'structure', + 'members' => { + 'Body' => { 'shape' => 'Body', 'streaming' => true } + }, + 'payload' => 'Body' + }, + 'Body' => { 'type' => 'blob' } + } + ).const_get(:Client) + + context 'telemetry_provider option' do + let(:custom_class) { Class.new(Aws::Telemetry::TelemetryProviderBase) } + let(:custom_provider) { custom_class.new } + + it 'defaults to no-op provider' do + client = TelemetryClient.new(client_config) + expect(client.config.telemetry_provider) + .to be_an_instance_of(Aws::Telemetry::NoOpTelemetryProvider) + end + + it 'does not raise error when given an otel provider' do + otel_provider = Aws::Telemetry::OTelProvider.new + client_config[:telemetry_provider] = otel_provider + expect { TelemetryClient.new(client_config) } + .not_to raise_error + end + + it 'does not raise error when given a custom provider' do + client_config[:telemetry_provider] = custom_provider + expect { TelemetryClient.new(client_config) } + .not_to raise_error + end + + it 'raises an argument error when given an invalid provider' do + expect do + TelemetryClient.new(telemetry_provider: 'foo') + end.to raise_error(ArgumentError) + end + end + + describe 'telemetry providers' do + context 'no-op telemetry provider' do + it 'does not raise error when calling an operation' do + client = TelemetryClient.new(stub_responses: true) + expect { client.some_operation }.not_to raise_error + end + end + + context 'otel provider' do + let(:otel_provider) { Aws::Telemetry::OTelProvider.new } + let(:otel_export) { OpenTelemetry::SDK::Trace::Export } + let(:otel_exporter) { otel_export::InMemorySpanExporter.new } + + before do + processor = otel_export::SimpleSpanProcessor.new(otel_exporter) + OpenTelemetry::SDK.configure do |c| + c.add_span_processor(processor) + end + end + + let(:finished_send_span) do + otel_exporter + .finished_spans + .find { |span| span.name == 'Handler.NetHttp' } + end + + let(:finished_op_span) do + otel_exporter + .finished_spans + .find { |span| span.name == 'TelemetryService.SomeOperation' } + end + + let(:client) do + client_config.tap do |c| + c[:telemetry_provider] = otel_provider + c[:endpoint] = 'https://foo.com' + end + TelemetryClient.new(client_config) + end + + it 'raises error when an otel dependency is not required' do + allow(Aws::Telemetry).to receive(:otel_loaded?).and_return(false) + expect { otel_provider } + .to raise_error( + ArgumentError, + 'Requires the `opentelemetry-sdk` gem to use OTel Provider.' + ) + end + + it 'creates spans with all the supplied parameters' do + stub_request(:post, 'https://foo.com/some_operation') + client.some_operation + + # pp otel_exporter.finished_spans + expect(finished_send_span).not_to be_nil + expect(finished_op_span).not_to be_nil + expect(finished_send_span.attributes) + .to include( + 'http.method' => 'POST', + 'net.protocol.name' => 'http', + 'net.protocol.version' => '1.1', + 'net.peer.name' => 'foo.com', + 'net.peer.port' => '443', + 'http.status_code' => '200' + ) + expect(finished_op_span.attributes) + .to include( + 'rpc.service' => 'Telemetry Service', + 'rpc.method' => 'SomeOperation', + 'code.function' => 'some_operation', + 'code.namespace' => 'Aws::Plugins::Telemetry' + ) + expect(finished_send_span.kind).to eq(:internal) + expect(finished_op_span.kind).to eq(:client) + expect(finished_send_span.parent_span_id) + .to eq(finished_op_span.span_id) + end + + it 'applies content-length span attributes when applicable' do + body = 'AAAA' + stub_request(:post, 'https://foo.com/some_operation') + .to_return( + body: body, + headers: { 'Content-Length' => body.size } + ) + client.some_operation(body: body) + + expect(finished_send_span.attributes) + .to include( + 'http.request_content_length' => body.size.to_s, + 'http.response_content_length' => body.size.to_s + ) + end + + it 'populates span data with error when it occurs' do + stub_request(:post, 'https://foo.com/some_operation') + .to_return(status: 500) + server_err = nil + begin + client.some_operation + rescue StandardError => e + server_err = e + end + + expect(finished_op_span.status.code).to eq(2) # err code + expect(finished_op_span.events[0].name).to eq('exception') + expect(finished_op_span.events[0].attributes['exception.type']) + .to eq(server_err.to_s) + end + + context 'stub_responses' do + it 'creates a stub span with all the supplied parameters' do + client = TelemetryClient.new( + stub_responses: true, + telemetry_provider: otel_provider + ) + client.some_operation + + finished_stub_span = + otel_exporter + .finished_spans + .find { |span| span.name == 'Handler.StubResponses' } + expect(finished_stub_span).not_to be_nil + expect(finished_stub_span.attributes) + .to include( + 'http.method' => 'POST', + 'net.protocol.name' => 'http', + 'net.protocol.version' => '1.1' + ) + expect(finished_stub_span.kind).to eq(:internal) + expect(finished_stub_span.parent_span_id) + .to eq(finished_op_span.span_id) + end + end + end + end end end end From 4dae0f6497994b9837e71a9515e6e76cf76086dd Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 13 Aug 2024 13:18:24 -0700 Subject: [PATCH 30/70] Update specs --- .../lib/aws-sdk-core/telemetry.rb | 10 ++--- gems/aws-sdk-core/spec/aws/telemetry_spec.rb | 45 +++++++++++++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 gems/aws-sdk-core/spec/aws/telemetry_spec.rb diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb index 30e28f4ea43..9e96c72a408 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb @@ -27,11 +27,6 @@ module Aws # * {SpanBase} module Telemetry class << self - # @api private - def module_to_tracer_name(module_name) - "#{module_name.gsub('::', '.')}.client".downcase - end - # @api private def otel_loaded? if @use_otel.nil? @@ -45,6 +40,11 @@ def otel_loaded? end @use_otel end + + # @api private + def module_to_tracer_name(module_name) + "#{module_name.gsub('::', '.')}.client".downcase + end end end end diff --git a/gems/aws-sdk-core/spec/aws/telemetry_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry_spec.rb new file mode 100644 index 00000000000..e11e1b05b63 --- /dev/null +++ b/gems/aws-sdk-core/spec/aws/telemetry_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require_relative '../spec_helper' +require 'opentelemetry-sdk' + +module Aws + describe Telemetry do + describe '.otel_loaded?' do + before { Aws::Telemetry.instance_variable_set(:@use_otel, nil) } + + it 'is true when opentelemetry-sdk is available' do + expect(Aws::Telemetry).to receive(:require) + .with('opentelemetry-sdk') + .and_return(true) + expect(Aws::Telemetry.otel_loaded?) + .to be true + end + + it 'returns false when opentelemetry-sdk is not available' do + expect(Aws::Telemetry).to receive(:require) + .with('opentelemetry-sdk') + .and_raise(LoadError) + expect(Aws::Telemetry.otel_loaded?) + .to be false + end + + it 'memoizes its status' do + expect(Aws::Telemetry).to receive(:require) + .once + .with('opentelemetry-sdk') + .and_raise(LoadError) + Aws::Telemetry.otel_loaded? + # second call should not call require again + Aws::Telemetry.otel_loaded? + end + end + + describe '.module_to_tracer_name' do + it 'correctly converts module to tracer name' do + expect(Aws::Telemetry.module_to_tracer_name('Aws::Telemetry')) + .to eq('aws.telemetry.client') + end + end + end +end From aae9d6a960d43a5129842e43e8ff784548cb30a0 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 13 Aug 2024 13:18:44 -0700 Subject: [PATCH 31/70] Import specs --- .../spec/aws/telemetry/base_spec.rb | 75 +++++++++++ .../spec/aws/telemetry/no_op_spec.rb | 71 ++++++++++ .../spec/aws/telemetry/otel_spec.rb | 127 ++++++++++++++++++ .../spec/aws/telemetry/span_status_spec.rb | 30 +++++ 4 files changed, 303 insertions(+) create mode 100644 gems/aws-sdk-core/spec/aws/telemetry/base_spec.rb create mode 100644 gems/aws-sdk-core/spec/aws/telemetry/no_op_spec.rb create mode 100644 gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb create mode 100644 gems/aws-sdk-core/spec/aws/telemetry/span_status_spec.rb diff --git a/gems/aws-sdk-core/spec/aws/telemetry/base_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry/base_spec.rb new file mode 100644 index 00000000000..27a46ec7151 --- /dev/null +++ b/gems/aws-sdk-core/spec/aws/telemetry/base_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require_relative '../../spec_helper' + +module Aws + module Telemetry + describe TracerProviderBase do + it 'defines the interface' do + expect do + subject.tracer + end.to raise_error(NotImplementedError) + end + end + + describe TracerBase do + it 'defines the interface' do + expect do + subject.start_span('foo') + end.to raise_error(NotImplementedError) + + expect do + subject.in_span('foo') + end.to raise_error(NotImplementedError) + end + end + + describe SpanBase do + it 'defines the interface' do + expect do + subject.set_attribute('foo', 'bar') + end.to raise_error(NotImplementedError) + + expect do + subject.add_attributes('foo' => 'bar') + end.to raise_error(NotImplementedError) + + expect do + subject.add_event('foo') + end.to raise_error(NotImplementedError) + + expect do + subject.status = 'foo' + end.to raise_error(NotImplementedError) + + expect do + subject.finish + end.to raise_error(NotImplementedError) + + expect do + subject.record_exception(StandardError.new) + end.to raise_error(NotImplementedError) + end + end + + describe ContextManagerBase do + it 'defines the interface' do + expect do + subject.current + end.to raise_error(NotImplementedError) + + expect do + subject.current_span + end.to raise_error(NotImplementedError) + + expect do + subject.attach('foo') + end.to raise_error(NotImplementedError) + + expect do + subject.detach('foo') + end.to raise_error(NotImplementedError) + end + end + end +end diff --git a/gems/aws-sdk-core/spec/aws/telemetry/no_op_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry/no_op_spec.rb new file mode 100644 index 00000000000..7493775fc16 --- /dev/null +++ b/gems/aws-sdk-core/spec/aws/telemetry/no_op_spec.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +module Hearth + module Telemetry + describe NoOpTelemetryProvider do + describe '#initialize' do + it 'sets up no-op provider' do + expect(subject.tracer_provider) + .to be_a(Hearth::Telemetry::NoOpTracerProvider) + end + end + end + + describe NoOpTracerProvider do + describe '#tracer' do + it 'returns an instance of no-op tracer' do + expect(subject.tracer) + .to be_an_instance_of(Hearth::Telemetry::NoOpTracer) + end + end + end + + describe NoOpTracer do + describe '#start_span' do + it 'yields an instance of no-op span' do + span = subject.start_span('foo') + expect(span).to be_an_instance_of(Hearth::Telemetry::NoOpSpan) + end + end + + describe '#in_span' do + it 'yields an instance of no-op span' do + subject.in_span('wrapper') do |span| + expect(span) + .to be_an_instance_of(Hearth::Telemetry::NoOpSpan) + end + end + end + end + + describe NoOpSpan do + describe '#set_attribute' do + it 'returns itself' do + expect(subject.set_attribute('some_attribute', 'some_value')) + .to be_an_instance_of(Hearth::Telemetry::NoOpSpan) + end + end + + describe '#add_attributes' do + it 'returns itself' do + expect(subject.add_attributes({ 'foo' => 'bar' })) + .to be_an_instance_of(Hearth::Telemetry::NoOpSpan) + end + end + + describe '#add_event' do + it 'returns itself' do + expect(subject.add_event('some_event', attributes: {})) + .to be_an_instance_of(Hearth::Telemetry::NoOpSpan) + end + end + + describe '#finish' do + it 'returns itself' do + expect(subject.finish) + .to be_an_instance_of(Hearth::Telemetry::NoOpSpan) + end + end + end + end +end diff --git a/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb new file mode 100644 index 00000000000..41a13f42154 --- /dev/null +++ b/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb @@ -0,0 +1,127 @@ +# frozen_string_literal: true + +require_relative '../../spec_helper' +require 'opentelemetry-sdk' + +module Aws + module Telemetry + describe OTelProvider do + before do + allow(Aws::Telemetry) + .to receive(:otel_loaded?) + .and_return(true) + end + + let(:otel_provider) { OTelProvider.new } + let(:context_manager) { otel_provider.context_manager } + let(:tracer_provider) { otel_provider.tracer_provider } + let(:tracer) { tracer_provider.tracer('some_tracer') } + + describe '#initialize' do + it 'raises ArgumentError when otel dependency fails to load' do + allow(Aws::Telemetry).to receive(:otel_loaded?).and_return(false) + expect { otel_provider }.to raise_error(ArgumentError) + end + + it 'sets up tracer provider and context manager' do + expect(tracer_provider).to be_a(Aws::Telemetry::OTelTracerProvider) + expect(context_manager).to be_a(Aws::Telemetry::OTelContextManager) + end + end + + describe OTelContextManager do + after { OpenTelemetry::Context.clear } + let(:root_context) { OpenTelemetry::Context::ROOT } + let(:new_context) do + OpenTelemetry::Context.empty.set_value('new', 'context') + end + + describe '#current' do + it 'returns the current context' do + expect(context_manager.current).to eq(root_context) + end + end + + describe '#current_span' do + it 'returns the current span' do + wrapper_span = tracer.start_span('foo') + expect(context_manager.current_span.instance_variable_get(:@span)) + .to eq(wrapper_span.instance_variable_get(:@span)) + end + end + + describe '#attach' do + it 'sets the current context' do + context_manager.attach(new_context) + expect(context_manager.current).to eq(new_context) + end + end + + describe '#detach' do + it 'detaches the previously set context' do + token = context_manager.attach(new_context) + expect(context_manager.current).to eq(new_context) + context_manager.detach(token) + expect(context_manager.current).to eq(root_context) + end + end + end + + describe 'OTelTracerProvider' do + it 'returns a tracer instance' do + expect(tracer).to be_a(Aws::Telemetry::OTelTracer) + end + + context 'tracer' do + let(:otel_export) { OpenTelemetry::SDK::Trace::Export } + let(:otel_exporter) { otel_export::InMemorySpanExporter.new } + before do + processor = otel_export::SimpleSpanProcessor.new(otel_exporter) + OpenTelemetry::SDK.configure do |c| + c.add_span_processor(processor) + end + end + let(:finished_span) { otel_exporter.finished_spans[0] } + + describe '#start_span' do + it 'returns a valid span with supplied parameters' do + span = tracer.start_span('some_span') + span.set_attribute('apple', 'pie') + span.add_event('pizza party') + span.status = Aws::Telemetry::SpanStatus.ok + span.finish + expect(finished_span.name).to eq('some_span') + expect(finished_span.attributes).to include('apple' => 'pie') + expect(finished_span.events[0].name).to eq('pizza party') + expect(finished_span.status) + .to be_an_instance_of(Aws::Telemetry::SpanStatus) + end + end + + describe '#in_span' do + let(:error) { StandardError.new('foo') } + it 'returns a valid span with supplied parameters' do + tracer.in_span('foo') do |span| + span['meat'] = 'pie' + span.add_attributes('durian' => 'pie') + span.status = Aws::Telemetry::SpanStatus.error + span.record_exception(error, attributes: { 'burnt' => 'pie' }) + end + expect(finished_span.name).to eq('foo') + expect(finished_span.attributes) + .to include('meat' => 'pie', 'durian' => 'pie') + expect(finished_span.status.code).to eq(2) + expect(finished_span.events.size).to eq(1) + expect(finished_span.events[0].name).to eq('exception') + expect(finished_span.events[0].attributes['exception.type']) + .to eq(error.class.to_s) + expect(finished_span.events[0].attributes['exception.message']) + .to eq(error.message) + expect(finished_span.events[0].attributes['burnt']).to eq('pie') + end + end + end + end + end + end +end diff --git a/gems/aws-sdk-core/spec/aws/telemetry/span_status_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry/span_status_spec.rb new file mode 100644 index 00000000000..7a7689eecdc --- /dev/null +++ b/gems/aws-sdk-core/spec/aws/telemetry/span_status_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require_relative '../../spec_helper' + +module Aws + module Telemetry + describe SpanStatus do + describe '.unset' do + it 'returns the correct expected code' do + status = Aws::Telemetry::SpanStatus.unset + expect(status.code).to eq(Aws::Telemetry::SpanStatus::UNSET) + end + end + + describe '.ok' do + it 'returns the correct expected code' do + status = Aws::Telemetry::SpanStatus.ok + expect(status.code).to eq(Aws::Telemetry::SpanStatus::OK) + end + end + + describe '.error' do + it 'returns the correct expected code' do + status = Aws::Telemetry::SpanStatus.error + expect(status.code).to eq(Aws::Telemetry::SpanStatus::ERROR) + end + end + end + end +end From 4179326dbe80191f98e21cc67367b6f22f3f20a0 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 13 Aug 2024 14:52:31 -0700 Subject: [PATCH 32/70] Update specs to V3 --- .../spec/aws/telemetry/no_op_spec.rb | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/gems/aws-sdk-core/spec/aws/telemetry/no_op_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry/no_op_spec.rb index 7493775fc16..382da5bb960 100644 --- a/gems/aws-sdk-core/spec/aws/telemetry/no_op_spec.rb +++ b/gems/aws-sdk-core/spec/aws/telemetry/no_op_spec.rb @@ -1,12 +1,14 @@ # frozen_string_literal: true -module Hearth +require_relative '../../spec_helper' + +module Aws module Telemetry describe NoOpTelemetryProvider do describe '#initialize' do it 'sets up no-op provider' do expect(subject.tracer_provider) - .to be_a(Hearth::Telemetry::NoOpTracerProvider) + .to be_a(Aws::Telemetry::NoOpTracerProvider) end end end @@ -15,7 +17,7 @@ module Telemetry describe '#tracer' do it 'returns an instance of no-op tracer' do expect(subject.tracer) - .to be_an_instance_of(Hearth::Telemetry::NoOpTracer) + .to be_an_instance_of(Aws::Telemetry::NoOpTracer) end end end @@ -24,7 +26,7 @@ module Telemetry describe '#start_span' do it 'yields an instance of no-op span' do span = subject.start_span('foo') - expect(span).to be_an_instance_of(Hearth::Telemetry::NoOpSpan) + expect(span).to be_an_instance_of(Aws::Telemetry::NoOpSpan) end end @@ -32,7 +34,7 @@ module Telemetry it 'yields an instance of no-op span' do subject.in_span('wrapper') do |span| expect(span) - .to be_an_instance_of(Hearth::Telemetry::NoOpSpan) + .to be_an_instance_of(Aws::Telemetry::NoOpSpan) end end end @@ -42,28 +44,28 @@ module Telemetry describe '#set_attribute' do it 'returns itself' do expect(subject.set_attribute('some_attribute', 'some_value')) - .to be_an_instance_of(Hearth::Telemetry::NoOpSpan) + .to be_an_instance_of(Aws::Telemetry::NoOpSpan) end end describe '#add_attributes' do it 'returns itself' do expect(subject.add_attributes({ 'foo' => 'bar' })) - .to be_an_instance_of(Hearth::Telemetry::NoOpSpan) + .to be_an_instance_of(Aws::Telemetry::NoOpSpan) end end describe '#add_event' do it 'returns itself' do expect(subject.add_event('some_event', attributes: {})) - .to be_an_instance_of(Hearth::Telemetry::NoOpSpan) + .to be_an_instance_of(Aws::Telemetry::NoOpSpan) end end describe '#finish' do it 'returns itself' do expect(subject.finish) - .to be_an_instance_of(Hearth::Telemetry::NoOpSpan) + .to be_an_instance_of(Aws::Telemetry::NoOpSpan) end end end From d095efe41f99a4b6f2be1cfd8bbc1261bc21e9cf Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Wed, 14 Aug 2024 14:01:34 -0700 Subject: [PATCH 33/70] Add a way to reset opentelemetry sdk configurations --- gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb | 3 ++- gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb | 5 ++++- gems/aws-sdk-core/spec/spec_helper.rb | 9 +++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb b/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb index ac18727679b..6bee3811ffa 100644 --- a/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb +++ b/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb @@ -100,6 +100,8 @@ module Plugins end end + after { SpecHelper.reset_opentelemetry_sdk } + let(:finished_send_span) do otel_exporter .finished_spans @@ -133,7 +135,6 @@ module Plugins stub_request(:post, 'https://foo.com/some_operation') client.some_operation - # pp otel_exporter.finished_spans expect(finished_send_span).not_to be_nil expect(finished_op_span).not_to be_nil expect(finished_send_span.attributes) diff --git a/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb index 41a13f42154..9f9d7f1d926 100644 --- a/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb +++ b/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb @@ -30,7 +30,7 @@ module Telemetry end describe OTelContextManager do - after { OpenTelemetry::Context.clear } + before { OpenTelemetry::Context.clear } let(:root_context) { OpenTelemetry::Context::ROOT } let(:new_context) do OpenTelemetry::Context.empty.set_value('new', 'context') @@ -81,6 +81,8 @@ module Telemetry c.add_span_processor(processor) end end + after { SpecHelper.reset_opentelemetry_sdk } + let(:finished_span) { otel_exporter.finished_spans[0] } describe '#start_span' do @@ -100,6 +102,7 @@ module Telemetry describe '#in_span' do let(:error) { StandardError.new('foo') } + it 'returns a valid span with supplied parameters' do tracer.in_span('foo') do |span| span['meat'] = 'pie' diff --git a/gems/aws-sdk-core/spec/spec_helper.rb b/gems/aws-sdk-core/spec/spec_helper.rb index a6ca9fcdd6e..d237e49e632 100644 --- a/gems/aws-sdk-core/spec/spec_helper.rb +++ b/gems/aws-sdk-core/spec/spec_helper.rb @@ -70,6 +70,15 @@ def client_with_plugin(options = {}, &block) client_class.new(options) end + # clears opentelemetry-sdk configuration state between specs + def reset_opentelemetry_sdk + OpenTelemetry.instance_variable_set( + :@tracer_provider, + OpenTelemetry::Internal::ProxyTracerProvider.new + ) + OpenTelemetry.error_handler = nil + OpenTelemetry.propagation = nil + end end end From 9f939c4dc0ef65a0f60e280c8a3e391a8728cc68 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Wed, 14 Aug 2024 14:06:04 -0700 Subject: [PATCH 34/70] Clean up require --- gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb b/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb index 6bee3811ffa..e937111b8e7 100644 --- a/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb +++ b/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb @@ -2,7 +2,6 @@ require_relative '../../spec_helper' require 'opentelemetry-sdk' -require 'webmock/rspec' module Aws module Plugins From 26be5aa77065c90c5eb881eae12a889bde8ff161 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Fri, 16 Aug 2024 12:30:03 -0700 Subject: [PATCH 35/70] Add tracer to context for async clients --- .../templates/async_client_class.mustache | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build_tools/aws-sdk-code-generator/templates/async_client_class.mustache b/build_tools/aws-sdk-code-generator/templates/async_client_class.mustache index 60f018fdf36..215fc7a27da 100644 --- a/build_tools/aws-sdk-code-generator/templates/async_client_class.mustache +++ b/build_tools/aws-sdk-code-generator/templates/async_client_class.mustache @@ -89,7 +89,11 @@ module {{module_name}} client: self, params: params, http_response: Seahorse::Client::Http::AsyncResponse.new, - config: config) + config: config, + tracer: config.telemetry_provider.tracer_provider.tracer( + Aws::Telemetry.module_to_tracer_name('{{module_name}}') + ) + ) context[:gem_name] = '{{gem_name}}' context[:gem_version] = '{{gem_version}}' Seahorse::Client::Request.new(handlers, context) From ab0c4a76c6af1f07fcef969010616404bc5a3c98 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Fri, 16 Aug 2024 13:07:54 -0700 Subject: [PATCH 36/70] Default to no-op tracer if none set --- gems/aws-sdk-core/lib/seahorse/client/request_context.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gems/aws-sdk-core/lib/seahorse/client/request_context.rb b/gems/aws-sdk-core/lib/seahorse/client/request_context.rb index 57a7f5e4c48..d78b6577ebd 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/request_context.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/request_context.rb @@ -24,7 +24,7 @@ def initialize(options = {}) @http_request = options[:http_request] || Http::Request.new @http_response = options[:http_response] || Http::Response.new @retries = 0 - @tracer = options[:tracer] + @tracer = options[:tracer] || Aws::Telemetry::NoOpTracer.new @metadata = {} end From 74bad6eaf0601c8e93771e6d38456ea088cd9e22 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Fri, 16 Aug 2024 13:10:33 -0700 Subject: [PATCH 37/70] Add helper to reset otel sdk configuration state --- gems/aws-sdk-core/spec/spec_helper.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/gems/aws-sdk-core/spec/spec_helper.rb b/gems/aws-sdk-core/spec/spec_helper.rb index d237e49e632..fb750c3082b 100644 --- a/gems/aws-sdk-core/spec/spec_helper.rb +++ b/gems/aws-sdk-core/spec/spec_helper.rb @@ -71,6 +71,7 @@ def client_with_plugin(options = {}, &block) end # clears opentelemetry-sdk configuration state between specs + # https://github.com/open-telemetry/opentelemetry-ruby/blob/main/test_helpers/lib/opentelemetry/test_helpers.rb#L18 def reset_opentelemetry_sdk OpenTelemetry.instance_variable_set( :@tracer_provider, From 93d328be7f92613c99d5864a917f27519429b9af Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Fri, 16 Aug 2024 13:15:11 -0700 Subject: [PATCH 38/70] Update to handle services without serviceid metadata --- gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index 6286dc3bdb5..3cf6fb27959 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -36,14 +36,9 @@ def resolve_provider(_cfg) end end - # this is the root parent handler - # sets up initial attributes to emit class Handler < Seahorse::Client::Handler def call(context) - # serviceId may not be present in older versions - # do I need to retrieve legacy serviceId? service_id = service_id(context) - attributes = { 'rpc.service' => service_id, 'rpc.method' => context.operation.name, @@ -63,7 +58,8 @@ def call(context) def service_id(context) context.config.api.metadata['serviceId'] || - context.config.api.metadata['serviceAbbreviation'] + context.config.api.metadata['serviceAbbreviation'] || + context.config.api.metadata['serviceFullName'] end def parent_span_name(context, service_id) From a922645a6a46d4caf4d1f4ee37c3b7ce442f982a Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Fri, 16 Aug 2024 13:23:35 -0700 Subject: [PATCH 39/70] Revert "Update existing specs with tracer" This reverts commit c909b47804d9fe781d7124ae0baf525e19cdfa61. --- gems/aws-sdk-core/spec/aws/plugins/request_compression_spec.rb | 3 ++- .../aws-sdk-core/spec/seahorse/client/net_http/handler_spec.rb | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gems/aws-sdk-core/spec/aws/plugins/request_compression_spec.rb b/gems/aws-sdk-core/spec/aws/plugins/request_compression_spec.rb index fb8434d959b..897b1a7ef8a 100644 --- a/gems/aws-sdk-core/spec/aws/plugins/request_compression_spec.rb +++ b/gems/aws-sdk-core/spec/aws/plugins/request_compression_spec.rb @@ -1,12 +1,13 @@ # frozen_string_literal: true require_relative '../../spec_helper' +require 'aws-sdk-core/plugins/request_compression' module Aws module Plugins describe RequestCompression do let(:creds) { Aws::Credentials.new('akid', 'secret') } - let(:client_opts) { { credentials: creds, stub_responses: true } } + let(:client_opts) { { credentials: creds, stub_responses: true } } RequestCompressionClient = ApiHelper.sample_service( metadata: { 'protocol' => 'rest-xml' }, diff --git a/gems/aws-sdk-core/spec/seahorse/client/net_http/handler_spec.rb b/gems/aws-sdk-core/spec/seahorse/client/net_http/handler_spec.rb index 7d1b715bd23..2d71f139acc 100644 --- a/gems/aws-sdk-core/spec/seahorse/client/net_http/handler_spec.rb +++ b/gems/aws-sdk-core/spec/seahorse/client/net_http/handler_spec.rb @@ -19,7 +19,6 @@ module NetHttp RequestContext.new.tap do |context| context.config = config context.http_request.endpoint = endpoint - context.tracer = Aws::Telemetry::NoOpTracer.new end end From 3d3ee30e187261c943992bc7ec71e61bd761b275 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Fri, 16 Aug 2024 13:28:44 -0700 Subject: [PATCH 40/70] Wrap h2 handler --- .../lib/seahorse/client/h2/handler.rb | 114 ++++++++++-------- 1 file changed, 67 insertions(+), 47 deletions(-) diff --git a/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb b/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb index d6001af9b0e..c0c5185be52 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb @@ -27,60 +27,80 @@ module H2 class Handler < Client::Handler def call(context) - stream = nil - begin - conn = context.client.connection - stream = conn.new_stream - - stream_mutex = Mutex.new - close_condition = ConditionVariable.new - sync_queue = Queue.new - - conn.connect(context.http_request.endpoint) - _register_callbacks( - context.http_response, - stream, - stream_mutex, - close_condition, - sync_queue - ) - - conn.debug_output("sending initial request ...") - if input_emitter = context[:input_event_emitter] - _send_initial_headers(context.http_request, stream) - - # prepare for sending events later - input_emitter.stream = stream - # request sigv4 serves as the initial #prior_signature - input_emitter.encoder.prior_signature = - context.http_request.headers['authorization'].split('Signature=').last - input_emitter.validate_event = context.config.validate_params - else - _send_initial_headers(context.http_request, stream) - _send_initial_data(context.http_request, stream) + span_wrapper(context) do + stream = nil + begin + conn = context.client.connection + stream = conn.new_stream + + stream_mutex = Mutex.new + close_condition = ConditionVariable.new + sync_queue = Queue.new + + conn.connect(context.http_request.endpoint) + _register_callbacks( + context.http_response, + stream, + stream_mutex, + close_condition, + sync_queue + ) + + conn.debug_output("sending initial request ...") + if input_emitter = context[:input_event_emitter] + _send_initial_headers(context.http_request, stream) + + # prepare for sending events later + input_emitter.stream = stream + # request sigv4 serves as the initial #prior_signature + input_emitter.encoder.prior_signature = + context.http_request.headers['authorization'].split('Signature=').last + input_emitter.validate_event = context.config.validate_params + else + _send_initial_headers(context.http_request, stream) + _send_initial_data(context.http_request, stream) + end + + conn.start(stream) + rescue *NETWORK_ERRORS => error + error = NetworkingError.new( + error, error_message(context.http_request, error)) + context.http_response.signal_error(error) + rescue => error + conn.debug_output(error.inspect) + # not retryable + context.http_response.signal_error(error) end - conn.start(stream) - rescue *NETWORK_ERRORS => error - error = NetworkingError.new( - error, error_message(context.http_request, error)) - context.http_response.signal_error(error) - rescue => error - conn.debug_output(error.inspect) - # not retryable - context.http_response.signal_error(error) + AsyncResponse.new( + context: context, + stream: stream, + stream_mutex: stream_mutex, + close_condition: close_condition, + sync_queue: sync_queue + ) end + end - AsyncResponse.new( - context: context, - stream: stream, - stream_mutex: stream_mutex, - close_condition: close_condition, - sync_queue: sync_queue + private + + def span_wrapper(context, &block) + context.tracer.in_span( + 'Handler.H2', + attributes: request_attrs(context), + &block ) end - private + def request_attrs(context) + { + 'http.method' => context.http_request.http_method, + 'net.protocol.name' => 'http', + 'net.protocol.version' => '2', + 'net.peer.name' => context.http_request.endpoint.host, + 'net.peer.port' => context.http_request.endpoint.port.to_s + } + end def _register_callbacks(resp, stream, stream_mutex, close_condition, sync_queue) stream.on(:headers) do |headers| From 5f834599938fee405e2af432e6f9d4d60d26f782 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 20 Aug 2024 09:53:00 -0700 Subject: [PATCH 41/70] Refactor request and response attrs --- .../aws-sdk-core/plugins/stub_responses.rb | 35 ++------------- .../lib/aws-sdk-core/telemetry.rb | 44 +++++++++++++++++++ .../lib/seahorse/client/h2/handler.rb | 12 +---- .../lib/seahorse/client/net_http/handler.rb | 38 ++-------------- 4 files changed, 53 insertions(+), 76 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb index 7305da1e576..cb8eb6243ca 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb @@ -106,39 +106,12 @@ def signal_http(stub, http_resp, async_mode = false) def span_wrapper(context, &block) context.tracer.in_span( 'Handler.StubResponses', - attributes: request_attrs(context) + attributes: Aws::Telemetry.http_request_attrs(context) ) do |span| block.call.tap do - span.add_attributes(response_attrs(context)) - end - end - end - - def request_attrs(context) - { - 'http.method' => context.http_request.http_method, - 'net.protocol.name' => 'http', - 'net.protocol.version' => Net::HTTP::HTTPVersion - }.tap do |h| - if context.http_request.headers.key?('Content-Length') - h['http.request_context_length'] = - context.http_request.headers['Content-Length'] - end - end - end - - def response_attrs(context) - { - 'http.status_code' => context.http_response.status_code.to_s - }.tap do |h| - if context.http_response.headers.key?('Content-Length') - h['http.response_content_length'] = - context.http_response.headers['Content-Length'] - end - - if context.http_response.headers.key?('x-amz-request-id') - h['aws.request_id'] = - context.http_response.headers['x-amz-request-id'] + span.add_attributes( + Aws::Telemetry.http_response_attrs(context) + ) end end end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb index 9e96c72a408..2175cf9818b 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb @@ -45,6 +45,50 @@ def otel_loaded? def module_to_tracer_name(module_name) "#{module_name.gsub('::', '.')}.client".downcase end + + # @api private + def http_request_attrs(context) + { + 'http.method' => context.http_request.http_method, + 'net.protocol.name' => 'http' + }.tap do |h| + # determine client to find out http version + h['net.protocol.version'] = + if context.client.is_a? Seahorse::Client::AsyncBase + '2' + else + Net::HTTP::HTTPVersion + end + # add endpoint attrs unless stub responses + unless context.config.stub_responses + h['net.peer.name'] = context.http_request.endpoint.host + h['net.peer.port'] = context.http_request.endpoint.port.to_s + end + + # add content-length attr if it exists on request headers + if context.http_request.headers.key?('Content-Length') + h['http.request_content_length'] = + context.http_request.headers['Content-Length'] + end + end + end + + # @api private + def http_response_attrs(context) + { + 'http.status_code' => context.http_response.status_code.to_s + }.tap do |h| + if context.http_response.headers.key?('Content-Length') + h['http.response_content_length'] = + context.http_response.headers['Content-Length'] + end + + if context.http_response.headers.key?('x-amz-request-id') + h['aws.request_id'] = + context.http_response.headers['x-amz-request-id'] + end + end + end end end end diff --git a/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb b/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb index c0c5185be52..5b90bc408c2 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb @@ -87,21 +87,11 @@ def call(context) def span_wrapper(context, &block) context.tracer.in_span( 'Handler.H2', - attributes: request_attrs(context), + attributes: Aws::Telemetry.http_request_attrs(context), &block ) end - def request_attrs(context) - { - 'http.method' => context.http_request.http_method, - 'net.protocol.name' => 'http', - 'net.protocol.version' => '2', - 'net.peer.name' => context.http_request.endpoint.host, - 'net.peer.port' => context.http_request.endpoint.port.to_s - } - end - def _register_callbacks(resp, stream, stream_mutex, close_condition, sync_queue) stream.on(:headers) do |headers| resp.signal_headers(headers) diff --git a/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb b/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb index 446db351fd5..82c7b3117f6 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/net_http/handler.rb @@ -201,44 +201,14 @@ def extract_headers(response) def span_wrapper(context, &block) context.tracer.in_span( 'Handler.NetHttp', - attributes: request_attrs(context) + attributes: Aws::Telemetry.http_request_attrs(context) ) do |span| block.call - span.add_attributes(response_attrs(context)) - end - end - - def request_attrs(context) - { - 'http.method' => context.http_request.http_method, - 'net.protocol.name' => 'http', - 'net.protocol.version' => Net::HTTP::HTTPVersion, - 'net.peer.name' => context.http_request.endpoint.host, - 'net.peer.port' => context.http_request.endpoint.port.to_s - }.tap do |h| - if context.http_request.headers.key?('Content-Length') - h['http.request_content_length'] = - context.http_request.headers['Content-Length'] - end - end - end - - def response_attrs(context) - { - 'http.status_code' => context.http_response.status_code.to_s - }.tap do |h| - if context.http_response.headers.key?('Content-Length') - h['http.response_content_length'] = - context.http_response.headers['Content-Length'] - end - - if context.http_response.headers.key?('x-amz-request-id') - h['aws.request_id'] = - context.http_response.headers['x-amz-request-id'] - end + span.add_attributes( + Aws::Telemetry.http_response_attrs(context) + ) end end - end end end From 819dac0ede7b83096653b63d66568df9568058f2 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 20 Aug 2024 09:54:14 -0700 Subject: [PATCH 42/70] Reorder wrapper in h2 handler --- .../lib/seahorse/client/h2/handler.rb | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb b/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb index 5b90bc408c2..714a0426a6f 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb @@ -83,15 +83,6 @@ def call(context) end private - - def span_wrapper(context, &block) - context.tracer.in_span( - 'Handler.H2', - attributes: Aws::Telemetry.http_request_attrs(context), - &block - ) - end - def _register_callbacks(resp, stream, stream_mutex, close_condition, sync_queue) stream.on(:headers) do |headers| resp.signal_headers(headers) @@ -156,8 +147,14 @@ def error_message(req, error) end end + def span_wrapper(context, &block) + context.tracer.in_span( + 'Handler.H2', + attributes: Aws::Telemetry.http_request_attrs(context), + &block + ) + end end - end end end From cbf1a0cee90502d41586012bb80d1080c6e2f95d Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 20 Aug 2024 10:14:46 -0700 Subject: [PATCH 43/70] Add rbs files --- .../sig/aws-sdk-core/telemetry/base.rbs | 46 +++++++++++++++++++ .../sig/aws-sdk-core/telemetry/otel.rbs | 22 +++++++++ .../sig/aws-sdk-core/telemetry/span_kind.rbs | 15 ++++++ .../aws-sdk-core/telemetry/span_status.rbs | 24 ++++++++++ 4 files changed, 107 insertions(+) create mode 100644 gems/aws-sdk-core/sig/aws-sdk-core/telemetry/base.rbs create mode 100644 gems/aws-sdk-core/sig/aws-sdk-core/telemetry/otel.rbs create mode 100644 gems/aws-sdk-core/sig/aws-sdk-core/telemetry/span_kind.rbs create mode 100644 gems/aws-sdk-core/sig/aws-sdk-core/telemetry/span_status.rbs diff --git a/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/base.rbs b/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/base.rbs new file mode 100644 index 00000000000..ce9f9e4fb8f --- /dev/null +++ b/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/base.rbs @@ -0,0 +1,46 @@ +module Aws + module Telemetry + class TelemetryProviderBase + def initialize: (?tracer_provider: TracerProviderBase, ?context_manager: ContextManagerBase) -> void + attr_reader tracer_provider: TracerProviderBase + + attr_reader context_manager: ContextManagerBase + end + + class TracerProviderBase + def tracer: (?String name) -> TracerBase + end + + class TracerBase + def start_span: (String name, ?untyped with_parent, ?Hash[String, untyped] attributes, ?SpanKind kind) -> SpanBase + + def in_span: (String name, ?Hash[String, untyped] attributes, ?SpanKind kind) -> SpanBase + end + + class SpanBase + def set_attribute: (String key, untyped value) -> self + alias []= set_attribute + + def add_attributes: (Hash[String, untyped] attributes) -> self + + def add_event: (String name, ?Hash[String, untyped] attributes) -> self + + def status=: (SpanStatus status) -> void + + def finish: (?Time end_timestamp) -> self + + def record_exception: (untyped exception, ?Hash[String, untyped] attributes) -> void + end + + class ContextManagerBase + def current: () -> untyped + + def current_span: () -> SpanBase + + def attach: (untyped context) -> untyped + + def detach: (untyped token) -> bool + end + + end +end diff --git a/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/otel.rbs b/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/otel.rbs new file mode 100644 index 00000000000..131ad2f2a1c --- /dev/null +++ b/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/otel.rbs @@ -0,0 +1,22 @@ +module Aws + module Telemetry + class OTelProvider < TelemetryProviderBase + def initialize: () -> void + end + + class OTelTracerProvider < TracerProviderBase + def initialize: () -> void + end + + class OTelTracer < TracerBase + def initialize: (untyped tracer) -> void + end + + class OTelSpan < SpanBase + def initialize: (untyped span) -> void + end + + class OTelContextManager < ContextManagerBase + end + end +end diff --git a/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/span_kind.rbs b/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/span_kind.rbs new file mode 100644 index 00000000000..38ec29a1936 --- /dev/null +++ b/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/span_kind.rbs @@ -0,0 +1,15 @@ +module Aws + module Telemetry + module SpanKind + INTERNAL: :internal + + SERVER: :server + + CLIENT: :client + + CONSUMER: :consumer + + PRODUCER: :producer + end + end +end diff --git a/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/span_status.rbs b/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/span_status.rbs new file mode 100644 index 00000000000..ec9c42614b4 --- /dev/null +++ b/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/span_status.rbs @@ -0,0 +1,24 @@ +module Aws + module Telemetry + class SpanStatus + + def self.unset: (?::String description) -> SpanStatus + + def self.ok: (?::String description) -> SpanStatus + + def self.error: (?::String description) -> SpanStatus + + def initialize: (Integer code, ?description: ::String) -> void + + attr_reader code: Integer + + attr_reader description: String + + OK: 0 + + UNSET: 1 + + ERROR: 2 + end + end +end From 72510392a29dc33371bd97dbf5806bca20a2d7d5 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 20 Aug 2024 11:36:51 -0700 Subject: [PATCH 44/70] Update docs --- .../lib/aws-sdk-core/plugins/telemetry.rb | 7 +-- .../lib/aws-sdk-core/telemetry.rb | 2 +- .../lib/aws-sdk-core/telemetry/base.rb | 49 ++++++++++--------- .../lib/aws-sdk-core/telemetry/no_op.rb | 10 ++-- .../lib/aws-sdk-core/telemetry/otel.rb | 34 ++++++------- .../lib/aws-sdk-core/telemetry/span_kind.rb | 2 +- .../lib/aws-sdk-core/telemetry/span_status.rb | 6 +-- 7 files changed, 55 insertions(+), 55 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index 3cf6fb27959..55465f15a0e 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -9,9 +9,10 @@ class Telemetry < Seahorse::Client::Plugin default: Aws::Telemetry::NoOpTelemetryProvider, doc_type: Aws::Telemetry::TelemetryProviderBase, rbs_type: 'untyped', - docstring: <<-DOCS) do |cfg| - Allows you to provide a telemetry provider. By default, - will use the NoOpTelemetryProvider. + docstring: <<~DOCS) do |cfg| + Allows you to provide a telemetry provider, which is used + to emit telemetry data. By default, will use `NoOpTelemetryProvider` + which will not record or emit any telemetry data. DOCS resolve_provider(cfg) end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb index 2175cf9818b..17f87df8442 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb @@ -13,7 +13,7 @@ module Aws # a telemetry signal. # # A telemetry provider is used to emit telemetry data. By default, the - # `NoOpTelemetryProvider` will not record or emit any telemetry data. + # {NoOpTelemetryProvider} will not record or emit any telemetry data. # The SDK currently supports OpenTelemetry (OTel) as a provider. See # {OTelProvider} for more information. # diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb index 93377a5d64b..67078c4355e 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb @@ -2,42 +2,43 @@ module Aws module Telemetry - # Base for TelemetryProvider classes. + # Base for `TelemetryProvider` classes. # They are used to emit telemetry data. It needs the - # following class instances to function: - # - `TracerProvider` - A provider that returns a tracer + # following class implementations to function: + # - {TracerProviderBase} - A provider that returns a tracer # instance. Then, a tracer will create spans and those # spans will contain information in that given moment. - # - `ContextManager` - Manages context and used to + # - {ContextManagerBase} - Manages context and used to # return the current context within a trace. class TelemetryProviderBase - # @param [Class] tracer_provider A provider that returns - # a tracer instance. - # @param [Class] context_manager Manages context and - # used to return the current context. + # @param [Aws::Telemetry::TracerBase] tracer_provider A provider + # that returns a tracer instance. + # @param [Aws::Telemetry::ContextManagerBase] context_manager Manages + # context and used to return the current context. def initialize(tracer_provider: nil, context_manager: nil) @tracer_provider = tracer_provider @context_manager = context_manager end - # @return [TracerProvider] + + # @return [Aws::Telemetry::TracerProviderBase] attr_reader :tracer_provider - # @return [ContextManager] + # @return [Aws::Telemetry::ContextManagerBase] attr_reader :context_manager end - # Base for TracerProvider classes. + # Base for `TracerProvider` classes. class TracerProviderBase # Returns a Tracer instance. # # @param [String] name Tracer name - # @return [Tracer] + # @return [Aws::Telemetry::TracerBase] def tracer(name = nil) raise NotImplementedError end end - # Base for Tracer classes. + # Base for `Tracer` classes. class TracerBase # Used when a caller wants to manage the activation/deactivation and # lifecycle of the Span and its parent manually. @@ -46,7 +47,7 @@ class TracerBase # @param [Object] with_parent Parent Context # @param [Hash] attributes Attributes to attach to the span # @param [Aws::Telemetry::SpanKind] kind Type of Span - # @return [Span] + # @return [Aws::Telemetry::SpanBase] def start_span(name, with_parent: nil, attributes: nil, kind: nil) raise NotImplementedError end @@ -60,13 +61,13 @@ def start_span(name, with_parent: nil, attributes: nil, kind: nil) # @param [String] name Span name # @param [Hash] attributes Attributes to attach to the span # @param [Aws::Telemetry::SpanKind] kind Type of Span - # @return [Span] + # @return [Aws::Telemetry::SpanBase] def in_span(name, attributes: nil, kind: nil) raise NotImplementedError end end - # Base for Span classes. + # Base for `Span` classes. class SpanBase # Set attribute. # @@ -74,7 +75,7 @@ class SpanBase # @param [String, Boolean, Numeric, Array] value # Value must be non-nil and (array of) string, boolean or numeric type. # Array values must not contain nil elements and all elements must be of - # the same basic type (string, numeric, boolean). + # the same basic type (string, numeric, boolean) # @return [self] returns itself def set_attribute(key, value) raise NotImplementedError @@ -87,7 +88,7 @@ def set_attribute(key, value) # Boolean>}] attributes Values must be non-nil and (array of) string, # boolean or numeric type. Array values must not contain nil elements # and all elements must be of the same basic type (string, numeric, - # boolean). + # boolean) # @return [self] returns itself def add_attributes(attributes) raise NotImplementedError @@ -95,12 +96,12 @@ def add_attributes(attributes) # Add event to a Span. # - # @param [String] name Name of the event. + # @param [String] name Name of the event # @param [Hash{String => String, Numeric, Boolean, Array}] attributes Values must be non-nil and (array of) # string, boolean or numeric type. Array values must not contain nil # elements and all elements must be of the same basic type (string, - # numeric, boolean). + # numeric, boolean) # @return [self] returns itself def add_event(name, attributes: nil) raise NotImplementedError @@ -109,7 +110,7 @@ def add_event(name, attributes: nil) # Sets the Span status. # # @param [Aws::Telemetry::SpanStatus] status The new status, which - # overrides the default Span status, which is OK. + # overrides the default Span status, which is `OK` # @return [void] def status=(status) raise NotImplementedError @@ -137,7 +138,7 @@ def record_exception(exception, attributes: nil) end end - # Base for all ContextManager classes. + # Base for all `ContextManager` classes. class ContextManagerBase # Returns current context. # @@ -148,7 +149,7 @@ def current # Returns the current span from current context. # - # @return Span + # @return [Aws::Telemetry::SpanBase] def current_span raise NotImplementedError end @@ -167,7 +168,7 @@ def attach(context) # specified Context. # # @param [Object] token The token provided by matching the call to attach - # @return [Boolean] True if the calls matched, False otherwise + # @return [Boolean] `True` if the calls matched, `False` otherwise def detach(token) raise NotImplementedError end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb index 443631f91e6..e0ef1aaa9b4 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb @@ -2,7 +2,7 @@ module Aws module Telemetry - # No-op implementation for TelemetryProvider. + # No-op implementation for {TelemetryProviderBase}. class NoOpTelemetryProvider < TelemetryProviderBase def initialize super( @@ -12,14 +12,14 @@ def initialize end end - # No-op implementation for TracerProvider. + # No-op implementation for {TracerProviderBase}. class NoOpTracerProvider < TracerProviderBase def tracer(name = nil) @tracer ||= NoOpTracer.new end end - # No-op implementation for Tracer. + # No-op implementation for {TracerBase}. class NoOpTracer < TracerBase def start_span(name, with_parent: nil, attributes: nil, kind: nil) NoOpSpan.new @@ -30,7 +30,7 @@ def in_span(name, attributes: nil, kind: nil) end end - # No-op implementation for Span. + # No-op implementation for {SpanBase}. class NoOpSpan < SpanBase def set_attribute(key, value) self @@ -54,7 +54,7 @@ def finish(end_timestamp: nil) def record_exception(exception, attributes: nil); end end - # No-op implementation for ContextManager. + # No-op implementation for {ContextManagerBase}. class NoOpContextManager < ContextManagerBase def current; end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb index b351588f665..828e0a726c7 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb @@ -9,7 +9,6 @@ module Telemetry # telemetry provider in the client config. # # @example Configuration - # # require 'opentelemetry-sdk' # # # sets up the OpenTelemetry SDK with their config defaults @@ -23,7 +22,6 @@ module Telemetry # more information. # # @example Exporting via console - # # require 'opentelemetry-sdk' # # ENV['OTEL_TRACES_EXPORTER'] ||= 'console' @@ -46,7 +44,7 @@ def initialize end end - # OpenTelemetry-based TracerProvider, an entry point for + # OpenTelemetry-based {TracerProviderBase}, an entry point for # creating Tracer instances. class OTelTracerProvider < TracerProviderBase def initialize @@ -57,13 +55,13 @@ def initialize # Returns a Tracer instance. # # @param [optional String] name Tracer name - # @return [Tracer] + # @return [Aws::Telemetry::OTelTracer] def tracer(name = nil) OTelTracer.new(@tracer_provider.tracer(name)) end end - # OpenTelemetry-based Tracer, responsible for creating spans. + # OpenTelemetry-based {TracerBase}, responsible for creating spans. class OTelTracer < TracerBase def initialize(tracer) super() @@ -77,7 +75,7 @@ def initialize(tracer) # @param [Object] with_parent Parent Context # @param [Hash] attributes Attributes to attach to the span # @param [Aws::Telemetry::SpanKind] kind Type of Span - # @return [Span] + # @return [Aws::Telemetry::OTelSpan] def start_span(name, with_parent: nil, attributes: nil, kind: nil) span = @tracer.start_span( name, @@ -97,7 +95,7 @@ def start_span(name, with_parent: nil, attributes: nil, kind: nil) # @param [String] name Span name # @param [Hash] attributes Attributes to attach to the span # @param [Aws::Telemetry::SpanKind] kind Type of Span - # @return [Span] + # @return [Aws::Telemetry::OTelSpan] def in_span(name, attributes: nil, kind: nil, &block) @tracer.in_span(name, attributes: attributes, kind: kind) do |span| block.call(OTelSpan.new(span)) @@ -105,7 +103,7 @@ def in_span(name, attributes: nil, kind: nil, &block) end end - # OpenTelemetry-based Span, represents a single operation + # OpenTelemetry-based {SpanBase}, represents a single operation # within a trace. class OTelSpan < SpanBase def initialize(span) @@ -119,7 +117,7 @@ def initialize(span) # @param [String, Boolean, Numeric, Array] value # Value must be non-nil and (array of) string, boolean or numeric type. # Array values must not contain nil elements and all elements must be of - # the same basic type (string, numeric, boolean). + # the same basic type (string, numeric, boolean) # @return [self] returns itself def set_attribute(key, value) @span.set_attribute(key, value) @@ -132,7 +130,7 @@ def set_attribute(key, value) # Boolean>}] attributes Values must be non-nil and (array of) string, # boolean or numeric type. Array values must not contain nil elements # and all elements must be of the same basic type (string, numeric, - # boolean). + # boolean) # @return [self] returns itself def add_attributes(attributes) @span.add_attributes(attributes) @@ -140,12 +138,12 @@ def add_attributes(attributes) # Add event to a Span. # - # @param [String] name Name of the event. + # @param [String] name Name of the event # @param [Hash{String => String, Numeric, Boolean, Array}] attributes Values must be non-nil and (array of) # string, boolean or numeric type. Array values must not contain nil # elements and all elements must be of the same basic type (string, - # numeric, boolean). + # numeric, boolean) # @return [self] returns itself def add_event(name, attributes: nil) @span.add_event(name, attributes: attributes) @@ -154,7 +152,7 @@ def add_event(name, attributes: nil) # Sets the Span status. # # @param [Aws::Telemetry::Status] status The new status, which - # overrides the default Span status, which is OK. + # overrides the default Span status, which is `OK` # @return [void] def status=(status) @span.status = status @@ -162,7 +160,7 @@ def status=(status) # Finishes the Span. # - # @param [Time] end_timestamp End timestamp for the span. + # @param [Time] end_timestamp End timestamp for the span # @return [self] returns itself def finish(end_timestamp: nil) @span.finish(end_timestamp: end_timestamp) @@ -175,14 +173,14 @@ def finish(end_timestamp: nil) # @param [Hash{String => String, Numeric, Boolean, Array}] attributes One or more key:value pairs, where the # keys must be strings and the values may be (array of) string, boolean - # or numeric type. + # or numeric type # @return [void] def record_exception(exception, attributes: nil) @span.record_exception(exception, attributes: attributes) end end - # OpenTelemetry-based ContextManager, manages context and + # OpenTelemetry-based {ContextManagerBase}, manages context and # used to return the current context within a trace. class OTelContextManager < ContextManagerBase # Returns current context. @@ -194,7 +192,7 @@ def current # Returns the current span from current context. # - # @return Span + # @return [Aws::Telemetry::OTelSpan] def current_span OTelSpan.new(OpenTelemetry::Trace.current_span) end @@ -213,7 +211,7 @@ def attach(context) # specified Context. # # @param [Object] token The token provided by matching the call to attach - # @return [Boolean] True if the calls matched, False otherwise + # @return [Boolean] `True` if the calls matched, `False` otherwise def detach(token) OpenTelemetry::Context.detach(token) end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_kind.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_kind.rb index 7bc43ada256..588985a086a 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_kind.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_kind.rb @@ -12,7 +12,7 @@ module SpanKind # Represents a request to some remote service. CLIENT = :client - # Represents a child of an asynchronous +PRODUCER+ request. + # Represents a child of an asynchronous `PRODUCER` request. CONSUMER = :consumer # Represents an asynchronous request. diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb index baec8bf254d..6790c2b7c4a 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb @@ -7,7 +7,7 @@ class SpanStatus class << self private :new - # Returns a newly created {SpanStatus} with code == +UNSET+ + # Returns a newly created {SpanStatus} with code, +UNSET+ # and an optional description. # # @param [optional String] description @@ -16,7 +16,7 @@ def unset(description = '') new(UNSET, description: description) end - # Returns a newly created {SpanStatus} with code == +OK+ + # Returns a newly created {SpanStatus} with code, +OK+ # and an optional description. # # @param [optional String] description @@ -25,7 +25,7 @@ def ok(description = '') new(OK, description: description) end - # Returns a newly created {SpanStatus} with code == +ERROR+ + # Returns a newly created {SpanStatus} with code, +ERROR+ # and an optional description. # # @param [optional String] description From 4c3c61157f1d3deb6d4044baecbc1c264f61d417 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 20 Aug 2024 12:15:49 -0700 Subject: [PATCH 45/70] Add changelog --- gems/aws-sdk-core/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gems/aws-sdk-core/CHANGELOG.md b/gems/aws-sdk-core/CHANGELOG.md index 54b7503d65b..545e9593d67 100644 --- a/gems/aws-sdk-core/CHANGELOG.md +++ b/gems/aws-sdk-core/CHANGELOG.md @@ -1,6 +1,8 @@ Unreleased Changes ------------------ +* Feature - Add support for Observability. + 3.201.5 (2024-08-15) ------------------ From 61862616fa01a45371f67680d6ec2dcc470662f1 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 20 Aug 2024 13:36:45 -0700 Subject: [PATCH 46/70] Minor updates --- Gemfile | 2 -- gems/aws-sdk-core/lib/aws-sdk-core.rb | 11 ----------- .../lib/aws-sdk-core/plugins/telemetry.rb | 12 ++++++------ gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb | 4 +--- gems/aws-sdk-core/spec/aws/telemetry/base_spec.rb | 2 +- 5 files changed, 8 insertions(+), 23 deletions(-) diff --git a/Gemfile b/Gemfile index 3edd16f06c7..5e17a488f29 100644 --- a/Gemfile +++ b/Gemfile @@ -65,9 +65,7 @@ group :test do gem 'addressable' gem 'cucumber' gem 'webmock' - gem 'multipart-post' gem 'rspec' - gem 'opentelemetry-sdk' end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core.rb b/gems/aws-sdk-core/lib/aws-sdk-core.rb index 2f865304dee..61ccedb47af 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core.rb @@ -7,7 +7,6 @@ require_relative 'aws-sdk-core/deprecations' # credential providers - require_relative 'aws-sdk-core/credential_provider' require_relative 'aws-sdk-core/refreshing_credentials' require_relative 'aws-sdk-core/assume_role_credentials' @@ -30,7 +29,6 @@ require_relative 'aws-sdk-core/plugins/bearer_authorization' # client modules - require_relative 'aws-sdk-core/client_stubs' require_relative 'aws-sdk-core/async_client_stubs' require_relative 'aws-sdk-core/eager_loader' @@ -45,11 +43,9 @@ require_relative 'aws-sdk-core/util' # resource classes - require_relative 'aws-sdk-core/resources/collection' # logging - require_relative 'aws-sdk-core/log/formatter' require_relative 'aws-sdk-core/log/param_filter' require_relative 'aws-sdk-core/log/param_formatter' @@ -58,14 +54,12 @@ require_relative 'aws-sdk-core/telemetry' # stubbing - require_relative 'aws-sdk-core/stubbing/empty_stub' require_relative 'aws-sdk-core/stubbing/data_applicator' require_relative 'aws-sdk-core/stubbing/stub_data' require_relative 'aws-sdk-core/stubbing/xml_error' # stubbing protocols - require_relative 'aws-sdk-core/stubbing/protocols/json' require_relative 'aws-sdk-core/stubbing/protocols/rest' require_relative 'aws-sdk-core/stubbing/protocols/rest_json' @@ -76,7 +70,6 @@ require_relative 'aws-sdk-core/stubbing/protocols/api_gateway' # protocols - require_relative 'aws-sdk-core/error_handler' require_relative 'aws-sdk-core/rest' require_relative 'aws-sdk-core/xml' @@ -85,21 +78,17 @@ require_relative 'aws-sdk-core/rpc_v2' # event stream - require_relative 'aws-sdk-core/binary' require_relative 'aws-sdk-core/event_emitter' # endpoint discovery - require_relative 'aws-sdk-core/endpoint_cache' # client metrics - require_relative 'aws-sdk-core/client_side_monitoring/request_metrics' require_relative 'aws-sdk-core/client_side_monitoring/publisher' # utilities - require_relative 'aws-sdk-core/arn' require_relative 'aws-sdk-core/arn_parser' require_relative 'aws-sdk-core/ec2_metadata' diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index 55465f15a0e..7292fa52180 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -9,12 +9,12 @@ class Telemetry < Seahorse::Client::Plugin default: Aws::Telemetry::NoOpTelemetryProvider, doc_type: Aws::Telemetry::TelemetryProviderBase, rbs_type: 'untyped', - docstring: <<~DOCS) do |cfg| - Allows you to provide a telemetry provider, which is used - to emit telemetry data. By default, will use `NoOpTelemetryProvider` - which will not record or emit any telemetry data. + docstring: <<~DOCS) do |_cfg| +Allows you to provide a telemetry provider, which is used +to emit telemetry data. By default, will use `NoOpTelemetryProvider` +which will not record or emit any telemetry data. DOCS - resolve_provider(cfg) + resolve_provider end def after_initialize(client) @@ -32,7 +32,7 @@ def validate_telemetry_provider(config) class << self private - def resolve_provider(_cfg) + def resolve_provider Aws::Telemetry::NoOpTelemetryProvider.new end end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb index 17f87df8442..e3d1bf07811 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb @@ -52,20 +52,18 @@ def http_request_attrs(context) 'http.method' => context.http_request.http_method, 'net.protocol.name' => 'http' }.tap do |h| - # determine client to find out http version h['net.protocol.version'] = if context.client.is_a? Seahorse::Client::AsyncBase '2' else Net::HTTP::HTTPVersion end - # add endpoint attrs unless stub responses + unless context.config.stub_responses h['net.peer.name'] = context.http_request.endpoint.host h['net.peer.port'] = context.http_request.endpoint.port.to_s end - # add content-length attr if it exists on request headers if context.http_request.headers.key?('Content-Length') h['http.request_content_length'] = context.http_request.headers['Content-Length'] diff --git a/gems/aws-sdk-core/spec/aws/telemetry/base_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry/base_spec.rb index 27a46ec7151..1bf97633e7d 100644 --- a/gems/aws-sdk-core/spec/aws/telemetry/base_spec.rb +++ b/gems/aws-sdk-core/spec/aws/telemetry/base_spec.rb @@ -39,7 +39,7 @@ module Telemetry end.to raise_error(NotImplementedError) expect do - subject.status = 'foo' + subject.status = Aws::Telemetry::SpanStatus.error end.to raise_error(NotImplementedError) expect do From 1c784f7e41180c3fb9d3468d49d4dbd45b405fa7 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 20 Aug 2024 13:36:49 -0700 Subject: [PATCH 47/70] Revert "Update sso and sts to include telemetry plugin" This reverts commit dbbfe502dd74ffe674ded78be194fa9c3983a2a3. --- gems/aws-sdk-core/lib/aws-sdk-sso/client.rb | 12 +----------- gems/aws-sdk-core/lib/aws-sdk-sts/client.rb | 12 +----------- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-sso/client.rb b/gems/aws-sdk-core/lib/aws-sdk-sso/client.rb index b2b44f6bc3c..d24a2062549 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-sso/client.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-sso/client.rb @@ -32,7 +32,6 @@ require 'aws-sdk-core/plugins/request_compression.rb' require 'aws-sdk-core/plugins/defaults_mode.rb' require 'aws-sdk-core/plugins/recursion_detection.rb' -require 'aws-sdk-core/plugins/telemetry.rb' require 'aws-sdk-core/plugins/sign.rb' require 'aws-sdk-core/plugins/protocols/rest_json.rb' @@ -84,7 +83,6 @@ class Client < Seahorse::Client::Base add_plugin(Aws::Plugins::RequestCompression) add_plugin(Aws::Plugins::DefaultsMode) add_plugin(Aws::Plugins::RecursionDetection) - add_plugin(Aws::Plugins::Telemetry) add_plugin(Aws::Plugins::Sign) add_plugin(Aws::Plugins::Protocols::RestJson) add_plugin(Aws::SSO::Plugins::Endpoints) @@ -332,10 +330,6 @@ class Client < Seahorse::Client::Base # ** Please note ** When response stubbing is enabled, no HTTP # requests are made, and retries are disabled. # - # @option options [Aws::Telemetry::TelemetryProviderBase] :telemetry_provider (Aws::Telemetry::NoOpTelemetryProvider) - # Allows you to provide a telemetry provider. By default, - # will use the NoOpTelemetryProvider. - # # @option options [Aws::TokenProvider] :token_provider # A Bearer Token Provider. This can be an instance of any one of the # following classes: @@ -646,11 +640,7 @@ def build_request(operation_name, params = {}) operation: config.api.operation(operation_name), client: self, params: params, - config: config, - tracer: config.telemetry_provider.tracer_provider.tracer( - Aws::Telemetry.module_to_tracer_name('Aws::SSO') - ) - ) + config: config) context[:gem_name] = 'aws-sdk-core' context[:gem_version] = '3.201.5' Seahorse::Client::Request.new(handlers, context) diff --git a/gems/aws-sdk-core/lib/aws-sdk-sts/client.rb b/gems/aws-sdk-core/lib/aws-sdk-sts/client.rb index 2067b45f76f..b06408765c9 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-sts/client.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-sts/client.rb @@ -32,7 +32,6 @@ require 'aws-sdk-core/plugins/request_compression.rb' require 'aws-sdk-core/plugins/defaults_mode.rb' require 'aws-sdk-core/plugins/recursion_detection.rb' -require 'aws-sdk-core/plugins/telemetry.rb' require 'aws-sdk-core/plugins/sign.rb' require 'aws-sdk-core/plugins/protocols/query.rb' require 'aws-sdk-sts/plugins/sts_regional_endpoints.rb' @@ -85,7 +84,6 @@ class Client < Seahorse::Client::Base add_plugin(Aws::Plugins::RequestCompression) add_plugin(Aws::Plugins::DefaultsMode) add_plugin(Aws::Plugins::RecursionDetection) - add_plugin(Aws::Plugins::Telemetry) add_plugin(Aws::Plugins::Sign) add_plugin(Aws::Plugins::Protocols::Query) add_plugin(Aws::STS::Plugins::STSRegionalEndpoints) @@ -339,10 +337,6 @@ class Client < Seahorse::Client::Base # ** Please note ** When response stubbing is enabled, no HTTP # requests are made, and retries are disabled. # - # @option options [Aws::Telemetry::TelemetryProviderBase] :telemetry_provider (Aws::Telemetry::NoOpTelemetryProvider) - # Allows you to provide a telemetry provider. By default, - # will use the NoOpTelemetryProvider. - # # @option options [Aws::TokenProvider] :token_provider # A Bearer Token Provider. This can be an instance of any one of the # following classes: @@ -2393,11 +2387,7 @@ def build_request(operation_name, params = {}) operation: config.api.operation(operation_name), client: self, params: params, - config: config, - tracer: config.telemetry_provider.tracer_provider.tracer( - Aws::Telemetry.module_to_tracer_name('Aws::STS') - ) - ) + config: config) context[:gem_name] = 'aws-sdk-core' context[:gem_version] = '3.201.5' Seahorse::Client::Request.new(handlers, context) From d943a386553214455688720fcd2bc42d4d9ac4de Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Tue, 20 Aug 2024 13:43:31 -0700 Subject: [PATCH 48/70] Update docs --- gems/aws-sdk-core/lib/seahorse/client/request_context.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gems/aws-sdk-core/lib/seahorse/client/request_context.rb b/gems/aws-sdk-core/lib/seahorse/client/request_context.rb index d78b6577ebd..de7650b2ae9 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/request_context.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/request_context.rb @@ -9,11 +9,14 @@ class RequestContext # @option options [required,Symbol] :operation_name (nil) # @option options [required,Model::Operation] :operation (nil) # @option options [Model::Authorizer] :authorizer (nil) + # @option options [Client] :client (nil) # @option options [Hash] :params ({}) # @option options [Configuration] :config (nil) # @option options [Http::Request] :http_request (Http::Request.new) # @option options [Http::Response] :http_response (Http::Response.new) - # and #rewind. + # @option options [Integer] :retries (0) + # @option options [Aws::Telemetry::TracerBase] :tracer (Aws::Telemetry::NoOpTracer.new) + # @options options [Hash] :metadata ({}) def initialize(options = {}) @operation_name = options[:operation_name] @operation = options[:operation] @@ -55,7 +58,7 @@ def initialize(options = {}) # @return [Integer] attr_accessor :retries - # @return [TracerProvider::Tracer] + # @return [Tracer] attr_accessor :tracer # @return [Hash] From a92b80aba415a97b81daab16cf0873da849165ac Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Wed, 21 Aug 2024 10:37:03 -0700 Subject: [PATCH 49/70] Add missing span attri --- gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index 7292fa52180..e4e25a576cb 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -41,6 +41,7 @@ class Handler < Seahorse::Client::Handler def call(context) service_id = service_id(context) attributes = { + 'rpc.system' => 'aws-api', 'rpc.service' => service_id, 'rpc.method' => context.operation.name, 'code.function' => context.operation_name.to_s, From 193d8f6b8a7efac2277ef72e148e2e27e7861b05 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Wed, 21 Aug 2024 10:47:59 -0700 Subject: [PATCH 50/70] Update specs service model --- .../spec/aws/plugins/telemetry_spec.rb | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb b/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb index e937111b8e7..adb6cedc143 100644 --- a/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb +++ b/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb @@ -17,17 +17,7 @@ module Plugins operations: { 'SomeOperation' => { 'http' => { 'method' => 'POST', 'requestUri' => '/some_operation' }, - 'input' => { 'shape' => 'OperationRequest' }, - 'requestcompression' => { - 'encodings' => ['gzip'] - } - }, - 'OperationStreaming' => { - 'http' => { 'method' => 'POST', 'requestUri' => '/' }, - 'input' => { 'shape' => 'OperationStreamingRequest' }, - 'requestcompression' => { - 'encodings' => ['gzip'] - } + 'input' => { 'shape' => 'OperationRequest' } } }, shapes: { @@ -38,13 +28,6 @@ module Plugins }, 'payload' => 'Body' }, - 'OperationStreamingRequest' => { - 'type' => 'structure', - 'members' => { - 'Body' => { 'shape' => 'Body', 'streaming' => true } - }, - 'payload' => 'Body' - }, 'Body' => { 'type' => 'blob' } } ).const_get(:Client) @@ -147,6 +130,7 @@ module Plugins ) expect(finished_op_span.attributes) .to include( + 'rpc.system' => 'aws-api', 'rpc.service' => 'Telemetry Service', 'rpc.method' => 'SomeOperation', 'code.function' => 'some_operation', From 9d9d2552bc2925642efde7b949b7187be7bf38f1 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Wed, 21 Aug 2024 15:06:37 -0700 Subject: [PATCH 51/70] Update telemetry plugin config --- .../lib/aws-sdk-core/plugins/telemetry.rb | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index e4e25a576cb..d548cea0885 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -8,13 +8,13 @@ class Telemetry < Seahorse::Client::Plugin :telemetry_provider, default: Aws::Telemetry::NoOpTelemetryProvider, doc_type: Aws::Telemetry::TelemetryProviderBase, - rbs_type: 'untyped', + rbs_type: Aws::Telemetry::TelemetryProviderBase, docstring: <<~DOCS) do |_cfg| Allows you to provide a telemetry provider, which is used to emit telemetry data. By default, will use `NoOpTelemetryProvider` which will not record or emit any telemetry data. DOCS - resolve_provider + Aws::Telemetry::NoOpTelemetryProvider.new end def after_initialize(client) @@ -29,14 +29,6 @@ def validate_telemetry_provider(config) end end - class << self - private - - def resolve_provider - Aws::Telemetry::NoOpTelemetryProvider.new - end - end - class Handler < Seahorse::Client::Handler def call(context) service_id = service_id(context) From b82661f4a65ed7417e3723bdfbd51488847cbb7f Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Wed, 21 Aug 2024 15:12:28 -0700 Subject: [PATCH 52/70] Add config documentation --- gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index d548cea0885..54c5e4ed616 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -11,8 +11,11 @@ class Telemetry < Seahorse::Client::Plugin rbs_type: Aws::Telemetry::TelemetryProviderBase, docstring: <<~DOCS) do |_cfg| Allows you to provide a telemetry provider, which is used -to emit telemetry data. By default, will use `NoOpTelemetryProvider` -which will not record or emit any telemetry data. +to emit telemetry data. By default, uses `NoOpTelemetryProvider` +which will not record or emit any telemetry data. The SDK currently supports +OpenTelemetry (OTel) as a telemetry provider. To use the OTel provider, +require the`opentelemetry-sdk` gem and then, pass in an instance of a +`Aws::Telemetry::OTelProvider` for telemetry provider. DOCS Aws::Telemetry::NoOpTelemetryProvider.new end From ae1ce0119fae826d170e0c0a5d827d9cd71813ce Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Thu, 22 Aug 2024 09:05:44 -0700 Subject: [PATCH 53/70] Move otel_loaded check to OTelProvider --- .../lib/aws-sdk-core/telemetry.rb | 14 ----- .../lib/aws-sdk-core/telemetry/otel.rb | 18 +++++- .../spec/aws/plugins/telemetry_spec.rb | 3 +- .../spec/aws/telemetry/otel_spec.rb | 3 +- gems/aws-sdk-core/spec/aws/telemetry_spec.rb | 58 +++++++++---------- 5 files changed, 50 insertions(+), 46 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb index e3d1bf07811..2d6452cfd10 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry.rb @@ -27,20 +27,6 @@ module Aws # * {SpanBase} module Telemetry class << self - # @api private - def otel_loaded? - if @use_otel.nil? - @use_otel = - begin - require 'opentelemetry-sdk' - true - rescue LoadError, NameError - false - end - end - @use_otel - end - # @api private def module_to_tracer_name(module_name) "#{module_name.gsub('::', '.')}.client".downcase diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb index 828e0a726c7..7434c33d1ca 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb @@ -33,7 +33,7 @@ module Telemetry # client = Aws::S3::Client.new(telemetry_provider: otel_provider) class OTelProvider < TelemetryProviderBase def initialize - unless Aws::Telemetry.otel_loaded? + unless otel_loaded? raise ArgumentError, 'Requires the `opentelemetry-sdk` gem to use OTel Provider.' end @@ -42,6 +42,22 @@ def initialize context_manager: OTelContextManager.new ) end + + private + + # @api private + def otel_loaded? + if @use_otel.nil? + @use_otel = + begin + require 'opentelemetry-sdk' + true + rescue LoadError, NameError + false + end + end + @use_otel + end end # OpenTelemetry-based {TracerProviderBase}, an entry point for diff --git a/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb b/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb index adb6cedc143..c0af563fd70 100644 --- a/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb +++ b/gems/aws-sdk-core/spec/aws/plugins/telemetry_spec.rb @@ -105,7 +105,8 @@ module Plugins end it 'raises error when an otel dependency is not required' do - allow(Aws::Telemetry).to receive(:otel_loaded?).and_return(false) + allow_any_instance_of(Aws::Telemetry::OTelProvider) + .to receive(:otel_loaded?).and_return(false) expect { otel_provider } .to raise_error( ArgumentError, diff --git a/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb index 9f9d7f1d926..0f849ee6438 100644 --- a/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb +++ b/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb @@ -19,7 +19,8 @@ module Telemetry describe '#initialize' do it 'raises ArgumentError when otel dependency fails to load' do - allow(Aws::Telemetry).to receive(:otel_loaded?).and_return(false) + allow_any_instance_of(Aws::Telemetry::OTelProvider) + .to receive(:otel_loaded?).and_return(false) expect { otel_provider }.to raise_error(ArgumentError) end diff --git a/gems/aws-sdk-core/spec/aws/telemetry_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry_spec.rb index e11e1b05b63..2ee1c397108 100644 --- a/gems/aws-sdk-core/spec/aws/telemetry_spec.rb +++ b/gems/aws-sdk-core/spec/aws/telemetry_spec.rb @@ -5,35 +5,35 @@ module Aws describe Telemetry do - describe '.otel_loaded?' do - before { Aws::Telemetry.instance_variable_set(:@use_otel, nil) } - - it 'is true when opentelemetry-sdk is available' do - expect(Aws::Telemetry).to receive(:require) - .with('opentelemetry-sdk') - .and_return(true) - expect(Aws::Telemetry.otel_loaded?) - .to be true - end - - it 'returns false when opentelemetry-sdk is not available' do - expect(Aws::Telemetry).to receive(:require) - .with('opentelemetry-sdk') - .and_raise(LoadError) - expect(Aws::Telemetry.otel_loaded?) - .to be false - end - - it 'memoizes its status' do - expect(Aws::Telemetry).to receive(:require) - .once - .with('opentelemetry-sdk') - .and_raise(LoadError) - Aws::Telemetry.otel_loaded? - # second call should not call require again - Aws::Telemetry.otel_loaded? - end - end + # describe '.otel_loaded?' do + # before { Aws::Telemetry.instance_variable_set(:@use_otel, nil) } + # + # it 'is true when opentelemetry-sdk is available' do + # expect(Aws::Telemetry).to receive(:require) + # .with('opentelemetry-sdk') + # .and_return(true) + # expect(Aws::Telemetry.otel_loaded?) + # .to be true + # end + # + # it 'returns false when opentelemetry-sdk is not available' do + # expect(Aws::Telemetry).to receive(:require) + # .with('opentelemetry-sdk') + # .and_raise(LoadError) + # expect(Aws::Telemetry.otel_loaded?) + # .to be false + # end + # + # it 'memoizes its status' do + # expect(Aws::Telemetry).to receive(:require) + # .once + # .with('opentelemetry-sdk') + # .and_raise(LoadError) + # Aws::Telemetry.otel_loaded? + # # second call should not call require again + # Aws::Telemetry.otel_loaded? + # end + # end describe '.module_to_tracer_name' do it 'correctly converts module to tracer name' do From 4c2a4efe438d9b16c557fe6e137591dae8fc96ab Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Thu, 22 Aug 2024 09:17:01 -0700 Subject: [PATCH 54/70] Update Changelog --- gems/aws-sdk-core/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gems/aws-sdk-core/CHANGELOG.md b/gems/aws-sdk-core/CHANGELOG.md index 545e9593d67..c50c2ca7731 100644 --- a/gems/aws-sdk-core/CHANGELOG.md +++ b/gems/aws-sdk-core/CHANGELOG.md @@ -1,7 +1,7 @@ Unreleased Changes ------------------ -* Feature - Add support for Observability. +* Feature - Add support for Observability which includes a configuration, `telemetry_provider` and an OpenTelemetry-based telemetry provider. 3.201.5 (2024-08-15) ------------------ From bb404a81f7795da5e29f175a14258285f4cb9b4f Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Thu, 22 Aug 2024 10:01:29 -0700 Subject: [PATCH 55/70] Bump min core --- build_tools/services.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build_tools/services.rb b/build_tools/services.rb index 0a89ffd6fe2..a0f72c51f5f 100644 --- a/build_tools/services.rb +++ b/build_tools/services.rb @@ -10,10 +10,10 @@ class ServiceEnumerator MANIFEST_PATH = File.expand_path('../../services.json', __FILE__) # Minimum `aws-sdk-core` version for new gem builds - MINIMUM_CORE_VERSION = "3.201.0" + MINIMUM_CORE_VERSION = "3.202.0" # Minimum `aws-sdk-core` version for new S3 gem builds - MINIMUM_CORE_VERSION_S3 = "3.201.0" + MINIMUM_CORE_VERSION_S3 = "3.202.0" EVENTSTREAM_PLUGIN = "Aws::Plugins::EventStreamConfiguration" From 1cd2d83f234fab1197ba0a710ae342db1d2fe890 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Thu, 22 Aug 2024 22:57:27 -0700 Subject: [PATCH 56/70] Move current_span to Tracer interface --- .../lib/aws-sdk-core/telemetry/base.rb | 14 ++++---- .../lib/aws-sdk-core/telemetry/no_op.rb | 6 ++-- .../lib/aws-sdk-core/telemetry/otel.rb | 15 ++++----- .../sig/aws-sdk-core/telemetry/base.rbs | 4 +-- .../spec/aws/telemetry/base_spec.rb | 8 ++--- .../spec/aws/telemetry/no_op_spec.rb | 7 ++++ .../spec/aws/telemetry/otel_spec.rb | 33 +++++++++---------- gems/aws-sdk-core/spec/aws/telemetry_spec.rb | 30 ----------------- 8 files changed, 46 insertions(+), 71 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb index 67078c4355e..0ae0b950ec5 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb @@ -65,6 +65,13 @@ def start_span(name, with_parent: nil, attributes: nil, kind: nil) def in_span(name, attributes: nil, kind: nil) raise NotImplementedError end + + # Returns the current active span. + # + # @return [Aws::Telemetry::SpanBase] + def current_span + raise NotImplementedError + end end # Base for `Span` classes. @@ -147,13 +154,6 @@ def current raise NotImplementedError end - # Returns the current span from current context. - # - # @return [Aws::Telemetry::SpanBase] - def current_span - raise NotImplementedError - end - # Associates a Context with the caller’s current execution unit. # Returns a token to be used with the matching call to detach. # diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb index e0ef1aaa9b4..5b7d855b249 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/no_op.rb @@ -28,6 +28,10 @@ def start_span(name, with_parent: nil, attributes: nil, kind: nil) def in_span(name, attributes: nil, kind: nil) yield NoOpSpan.new end + + def current_span + NoOpSpan.new + end end # No-op implementation for {SpanBase}. @@ -58,8 +62,6 @@ def record_exception(exception, attributes: nil); end class NoOpContextManager < ContextManagerBase def current; end - def current_span; end - def attach(context); end def detach(token); end diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb index 7434c33d1ca..72ccc6f8c27 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/otel.rb @@ -45,7 +45,6 @@ def initialize private - # @api private def otel_loaded? if @use_otel.nil? @use_otel = @@ -117,6 +116,13 @@ def in_span(name, attributes: nil, kind: nil, &block) block.call(OTelSpan.new(span)) end end + + # Returns the current active span. + # + # @return [Aws::Telemetry::OTelSpan] + def current_span + OTelSpan.new(OpenTelemetry::Trace.current_span) + end end # OpenTelemetry-based {SpanBase}, represents a single operation @@ -206,13 +212,6 @@ def current OpenTelemetry::Context.current end - # Returns the current span from current context. - # - # @return [Aws::Telemetry::OTelSpan] - def current_span - OTelSpan.new(OpenTelemetry::Trace.current_span) - end - # Associates a Context with the caller’s current execution unit. # Returns a token to be used with the matching call to detach. # diff --git a/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/base.rbs b/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/base.rbs index ce9f9e4fb8f..3050bbc5710 100644 --- a/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/base.rbs +++ b/gems/aws-sdk-core/sig/aws-sdk-core/telemetry/base.rbs @@ -15,6 +15,8 @@ module Aws def start_span: (String name, ?untyped with_parent, ?Hash[String, untyped] attributes, ?SpanKind kind) -> SpanBase def in_span: (String name, ?Hash[String, untyped] attributes, ?SpanKind kind) -> SpanBase + + def current_span: () -> SpanBase end class SpanBase @@ -35,8 +37,6 @@ module Aws class ContextManagerBase def current: () -> untyped - def current_span: () -> SpanBase - def attach: (untyped context) -> untyped def detach: (untyped token) -> bool diff --git a/gems/aws-sdk-core/spec/aws/telemetry/base_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry/base_spec.rb index 1bf97633e7d..bb7ef0fc564 100644 --- a/gems/aws-sdk-core/spec/aws/telemetry/base_spec.rb +++ b/gems/aws-sdk-core/spec/aws/telemetry/base_spec.rb @@ -21,6 +21,10 @@ module Telemetry expect do subject.in_span('foo') end.to raise_error(NotImplementedError) + + expect do + subject.current_span + end.to raise_error(NotImplementedError) end end @@ -58,10 +62,6 @@ module Telemetry subject.current end.to raise_error(NotImplementedError) - expect do - subject.current_span - end.to raise_error(NotImplementedError) - expect do subject.attach('foo') end.to raise_error(NotImplementedError) diff --git a/gems/aws-sdk-core/spec/aws/telemetry/no_op_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry/no_op_spec.rb index 382da5bb960..dec98bf5877 100644 --- a/gems/aws-sdk-core/spec/aws/telemetry/no_op_spec.rb +++ b/gems/aws-sdk-core/spec/aws/telemetry/no_op_spec.rb @@ -38,6 +38,13 @@ module Telemetry end end end + + describe '#current_span' do + it 'returns an instance of no-op span' do + expect(subject.current_span) + .to be_an_instance_of(Aws::Telemetry::NoOpSpan) + end + end end describe NoOpSpan do diff --git a/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb index 0f849ee6438..01502945322 100644 --- a/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb +++ b/gems/aws-sdk-core/spec/aws/telemetry/otel_spec.rb @@ -6,21 +6,14 @@ module Aws module Telemetry describe OTelProvider do - before do - allow(Aws::Telemetry) - .to receive(:otel_loaded?) - .and_return(true) - end - let(:otel_provider) { OTelProvider.new } let(:context_manager) { otel_provider.context_manager } let(:tracer_provider) { otel_provider.tracer_provider } - let(:tracer) { tracer_provider.tracer('some_tracer') } describe '#initialize' do it 'raises ArgumentError when otel dependency fails to load' do allow_any_instance_of(Aws::Telemetry::OTelProvider) - .to receive(:otel_loaded?).and_return(false) + .to receive(:require).with('opentelemetry-sdk').and_raise(LoadError) expect { otel_provider }.to raise_error(ArgumentError) end @@ -43,14 +36,6 @@ module Telemetry end end - describe '#current_span' do - it 'returns the current span' do - wrapper_span = tracer.start_span('foo') - expect(context_manager.current_span.instance_variable_get(:@span)) - .to eq(wrapper_span.instance_variable_get(:@span)) - end - end - describe '#attach' do it 'sets the current context' do context_manager.attach(new_context) @@ -69,6 +54,8 @@ module Telemetry end describe 'OTelTracerProvider' do + let(:tracer) { tracer_provider.tracer('some_tracer') } + it 'returns a tracer instance' do expect(tracer).to be_a(Aws::Telemetry::OTelTracer) end @@ -76,6 +63,8 @@ module Telemetry context 'tracer' do let(:otel_export) { OpenTelemetry::SDK::Trace::Export } let(:otel_exporter) { otel_export::InMemorySpanExporter.new } + let(:finished_span) { otel_exporter.finished_spans[0] } + before do processor = otel_export::SimpleSpanProcessor.new(otel_exporter) OpenTelemetry::SDK.configure do |c| @@ -84,8 +73,6 @@ module Telemetry end after { SpecHelper.reset_opentelemetry_sdk } - let(:finished_span) { otel_exporter.finished_spans[0] } - describe '#start_span' do it 'returns a valid span with supplied parameters' do span = tracer.start_span('some_span') @@ -124,6 +111,16 @@ module Telemetry expect(finished_span.events[0].attributes['burnt']).to eq('pie') end end + + describe '#current_span' do + it 'returns the current span' do + tracer.in_span('foo') do |span| + span['blueberry'] = 'pie' + expect(tracer.current_span.instance_variable_get(:@span)) + .to eq(span.instance_variable_get(:@span)) + end + end + end end end end diff --git a/gems/aws-sdk-core/spec/aws/telemetry_spec.rb b/gems/aws-sdk-core/spec/aws/telemetry_spec.rb index 2ee1c397108..1b4236dfb01 100644 --- a/gems/aws-sdk-core/spec/aws/telemetry_spec.rb +++ b/gems/aws-sdk-core/spec/aws/telemetry_spec.rb @@ -5,36 +5,6 @@ module Aws describe Telemetry do - # describe '.otel_loaded?' do - # before { Aws::Telemetry.instance_variable_set(:@use_otel, nil) } - # - # it 'is true when opentelemetry-sdk is available' do - # expect(Aws::Telemetry).to receive(:require) - # .with('opentelemetry-sdk') - # .and_return(true) - # expect(Aws::Telemetry.otel_loaded?) - # .to be true - # end - # - # it 'returns false when opentelemetry-sdk is not available' do - # expect(Aws::Telemetry).to receive(:require) - # .with('opentelemetry-sdk') - # .and_raise(LoadError) - # expect(Aws::Telemetry.otel_loaded?) - # .to be false - # end - # - # it 'memoizes its status' do - # expect(Aws::Telemetry).to receive(:require) - # .once - # .with('opentelemetry-sdk') - # .and_raise(LoadError) - # Aws::Telemetry.otel_loaded? - # # second call should not call require again - # Aws::Telemetry.otel_loaded? - # end - # end - describe '.module_to_tracer_name' do it 'correctly converts module to tracer name' do expect(Aws::Telemetry.module_to_tracer_name('Aws::Telemetry')) From 0628c327241bd7c7d5454851ce7ea426f925a987 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Fri, 23 Aug 2024 13:58:33 -0700 Subject: [PATCH 57/70] Reorder requires --- gems/aws-sdk-core/lib/aws-sdk-core.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core.rb b/gems/aws-sdk-core/lib/aws-sdk-core.rb index 61ccedb47af..c1c6a45a278 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core.rb @@ -50,9 +50,6 @@ require_relative 'aws-sdk-core/log/param_filter' require_relative 'aws-sdk-core/log/param_formatter' -# telemetry -require_relative 'aws-sdk-core/telemetry' - # stubbing require_relative 'aws-sdk-core/stubbing/empty_stub' require_relative 'aws-sdk-core/stubbing/data_applicator' @@ -84,9 +81,10 @@ # endpoint discovery require_relative 'aws-sdk-core/endpoint_cache' -# client metrics +# client metrics / telemetry require_relative 'aws-sdk-core/client_side_monitoring/request_metrics' require_relative 'aws-sdk-core/client_side_monitoring/publisher' +require_relative 'aws-sdk-core/telemetry' # utilities require_relative 'aws-sdk-core/arn' From 1ef0f62101d8bf715e45a68ef20563b9efe44030 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Fri, 23 Aug 2024 14:28:48 -0700 Subject: [PATCH 58/70] Refactor to private methods --- .../aws-sdk-core/plugins/stub_responses.rb | 40 ++++--- .../lib/seahorse/client/h2/handler.rb | 106 +++++++++--------- 2 files changed, 80 insertions(+), 66 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb index cb8eb6243ca..fb7cdf1e78b 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb @@ -50,27 +50,37 @@ class Handler < Seahorse::Client::Handler def call(context) span_wrapper(context) do - stub = context.client.next_stub(context) - resp = Seahorse::Client::Response.new(context: context) - async_mode = context.client.is_a? Seahorse::Client::AsyncBase - if Hash === stub && stub[:mutex] - stub[:mutex].synchronize { apply_stub(stub, resp, async_mode) } - else - apply_stub(stub, resp, async_mode) - end - async_mode ? Seahorse::Client::AsyncResponse.new( + stub_responses(context) + end + end + + private + + def stub_responses(context) + stub = context.client.next_stub(context) + resp = Seahorse::Client::Response.new(context: context) + async_mode = context.client.is_a? Seahorse::Client::AsyncBase + if Hash === stub && stub[:mutex] + stub[:mutex].synchronize { apply_stub(stub, resp, async_mode: async_mode) } + else + apply_stub(stub, resp, async_mode: async_mode) + end + if async_mode + Seahorse::Client::AsyncResponse.new( context: context, stream: context[:input_event_stream_handler].event_emitter.stream, sync_queue: Queue.new - ) : resp + ) + else + resp end end - def apply_stub(stub, response, async_mode = false) + def apply_stub(stub, response, async_mode: false) http_resp = response.context.http_response case when stub[:error] then signal_error(stub[:error], http_resp) - when stub[:http] then signal_http(stub[:http], http_resp, async_mode) + when stub[:http] then signal_http(stub[:http], http_resp, async_mode: async_mode) when stub[:data] then response.data = stub[:data] end end @@ -86,17 +96,17 @@ def signal_error(error, http_resp) # @param [Seahorse::Client::Http::Response] stub # @param [Seahorse::Client::Http::Response | Seahorse::Client::Http::AsyncResponse] http_resp # @param [Boolean] async_mode - def signal_http(stub, http_resp, async_mode = false) + def signal_http(stub, http_resp, async_mode: false) if async_mode h2_headers = stub.headers.to_h.inject([]) do |arr, (k, v)| arr << [k, v] end - h2_headers << [":status", stub.status_code] + h2_headers << [':status', stub.status_code] http_resp.signal_headers(h2_headers) else http_resp.signal_headers(stub.status_code, stub.headers.to_h) end - while chunk = stub.body.read(1024 * 1024) + while (chunk = stub.body.read(1024 * 1024)) http_resp.signal_data(chunk) end stub.body.rewind diff --git a/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb b/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb index 714a0426a6f..2127c1b664c 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb @@ -27,62 +27,66 @@ module H2 class Handler < Client::Handler def call(context) - span_wrapper(context) do - stream = nil - begin - conn = context.client.connection - stream = conn.new_stream - - stream_mutex = Mutex.new - close_condition = ConditionVariable.new - sync_queue = Queue.new - - conn.connect(context.http_request.endpoint) - _register_callbacks( - context.http_response, - stream, - stream_mutex, - close_condition, - sync_queue - ) - - conn.debug_output("sending initial request ...") - if input_emitter = context[:input_event_emitter] - _send_initial_headers(context.http_request, stream) - - # prepare for sending events later - input_emitter.stream = stream - # request sigv4 serves as the initial #prior_signature - input_emitter.encoder.prior_signature = - context.http_request.headers['authorization'].split('Signature=').last - input_emitter.validate_event = context.config.validate_params - else - _send_initial_headers(context.http_request, stream) - _send_initial_data(context.http_request, stream) - end - - conn.start(stream) - rescue *NETWORK_ERRORS => error - error = NetworkingError.new( - error, error_message(context.http_request, error)) - context.http_response.signal_error(error) - rescue => error - conn.debug_output(error.inspect) - # not retryable - context.http_response.signal_error(error) - end + span_wrapper(context) { _call(context) } + end + + private - AsyncResponse.new( - context: context, - stream: stream, - stream_mutex: stream_mutex, - close_condition: close_condition, - sync_queue: sync_queue + def _call(context) + stream = nil + begin + conn = context.client.connection + stream = conn.new_stream + + stream_mutex = Mutex.new + close_condition = ConditionVariable.new + sync_queue = Queue.new + + conn.connect(context.http_request.endpoint) + _register_callbacks( + context.http_response, + stream, + stream_mutex, + close_condition, + sync_queue ) + + conn.debug_output("sending initial request ...") + if input_emitter = context[:input_event_emitter] + _send_initial_headers(context.http_request, stream) + + # prepare for sending events later + input_emitter.stream = stream + # request sigv4 serves as the initial #prior_signature + input_emitter.encoder.prior_signature = + context.http_request.headers['authorization'].split('Signature=').last + input_emitter.validate_event = context.config.validate_params + else + _send_initial_headers(context.http_request, stream) + _send_initial_data(context.http_request, stream) + end + + conn.start(stream) + rescue *NETWORK_ERRORS => error + error = NetworkingError.new( + error, error_message(context.http_request, error)) + context.http_response.signal_error(error) + rescue => error + conn.debug_output(error.inspect) + # not retryable + context.http_response.signal_error(error) end + + AsyncResponse.new( + context: context, + stream: stream, + stream_mutex: stream_mutex, + close_condition: close_condition, + sync_queue: sync_queue + ) end - private + def _register_callbacks(resp, stream, stream_mutex, close_condition, sync_queue) stream.on(:headers) do |headers| resp.signal_headers(headers) From 4a15734eb63c33380041670b6b1a779b46d965dc Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 26 Aug 2024 07:24:28 -0700 Subject: [PATCH 59/70] Revert "Refactor to private methods" This reverts commit 1ef0f62101d8bf715e45a68ef20563b9efe44030. --- .../aws-sdk-core/plugins/stub_responses.rb | 40 +++---- .../lib/seahorse/client/h2/handler.rb | 106 +++++++++--------- 2 files changed, 66 insertions(+), 80 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb index fb7cdf1e78b..cb8eb6243ca 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb @@ -50,37 +50,27 @@ class Handler < Seahorse::Client::Handler def call(context) span_wrapper(context) do - stub_responses(context) - end - end - - private - - def stub_responses(context) - stub = context.client.next_stub(context) - resp = Seahorse::Client::Response.new(context: context) - async_mode = context.client.is_a? Seahorse::Client::AsyncBase - if Hash === stub && stub[:mutex] - stub[:mutex].synchronize { apply_stub(stub, resp, async_mode: async_mode) } - else - apply_stub(stub, resp, async_mode: async_mode) - end - if async_mode - Seahorse::Client::AsyncResponse.new( + stub = context.client.next_stub(context) + resp = Seahorse::Client::Response.new(context: context) + async_mode = context.client.is_a? Seahorse::Client::AsyncBase + if Hash === stub && stub[:mutex] + stub[:mutex].synchronize { apply_stub(stub, resp, async_mode) } + else + apply_stub(stub, resp, async_mode) + end + async_mode ? Seahorse::Client::AsyncResponse.new( context: context, stream: context[:input_event_stream_handler].event_emitter.stream, sync_queue: Queue.new - ) - else - resp + ) : resp end end - def apply_stub(stub, response, async_mode: false) + def apply_stub(stub, response, async_mode = false) http_resp = response.context.http_response case when stub[:error] then signal_error(stub[:error], http_resp) - when stub[:http] then signal_http(stub[:http], http_resp, async_mode: async_mode) + when stub[:http] then signal_http(stub[:http], http_resp, async_mode) when stub[:data] then response.data = stub[:data] end end @@ -96,17 +86,17 @@ def signal_error(error, http_resp) # @param [Seahorse::Client::Http::Response] stub # @param [Seahorse::Client::Http::Response | Seahorse::Client::Http::AsyncResponse] http_resp # @param [Boolean] async_mode - def signal_http(stub, http_resp, async_mode: false) + def signal_http(stub, http_resp, async_mode = false) if async_mode h2_headers = stub.headers.to_h.inject([]) do |arr, (k, v)| arr << [k, v] end - h2_headers << [':status', stub.status_code] + h2_headers << [":status", stub.status_code] http_resp.signal_headers(h2_headers) else http_resp.signal_headers(stub.status_code, stub.headers.to_h) end - while (chunk = stub.body.read(1024 * 1024)) + while chunk = stub.body.read(1024 * 1024) http_resp.signal_data(chunk) end stub.body.rewind diff --git a/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb b/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb index 2127c1b664c..714a0426a6f 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb @@ -27,66 +27,62 @@ module H2 class Handler < Client::Handler def call(context) - span_wrapper(context) { _call(context) } - end - - private - - def _call(context) - stream = nil - begin - conn = context.client.connection - stream = conn.new_stream - - stream_mutex = Mutex.new - close_condition = ConditionVariable.new - sync_queue = Queue.new - - conn.connect(context.http_request.endpoint) - _register_callbacks( - context.http_response, - stream, - stream_mutex, - close_condition, - sync_queue - ) - - conn.debug_output("sending initial request ...") - if input_emitter = context[:input_event_emitter] - _send_initial_headers(context.http_request, stream) - - # prepare for sending events later - input_emitter.stream = stream - # request sigv4 serves as the initial #prior_signature - input_emitter.encoder.prior_signature = - context.http_request.headers['authorization'].split('Signature=').last - input_emitter.validate_event = context.config.validate_params - else - _send_initial_headers(context.http_request, stream) - _send_initial_data(context.http_request, stream) + span_wrapper(context) do + stream = nil + begin + conn = context.client.connection + stream = conn.new_stream + + stream_mutex = Mutex.new + close_condition = ConditionVariable.new + sync_queue = Queue.new + + conn.connect(context.http_request.endpoint) + _register_callbacks( + context.http_response, + stream, + stream_mutex, + close_condition, + sync_queue + ) + + conn.debug_output("sending initial request ...") + if input_emitter = context[:input_event_emitter] + _send_initial_headers(context.http_request, stream) + + # prepare for sending events later + input_emitter.stream = stream + # request sigv4 serves as the initial #prior_signature + input_emitter.encoder.prior_signature = + context.http_request.headers['authorization'].split('Signature=').last + input_emitter.validate_event = context.config.validate_params + else + _send_initial_headers(context.http_request, stream) + _send_initial_data(context.http_request, stream) + end + + conn.start(stream) + rescue *NETWORK_ERRORS => error + error = NetworkingError.new( + error, error_message(context.http_request, error)) + context.http_response.signal_error(error) + rescue => error + conn.debug_output(error.inspect) + # not retryable + context.http_response.signal_error(error) end - conn.start(stream) - rescue *NETWORK_ERRORS => error - error = NetworkingError.new( - error, error_message(context.http_request, error)) - context.http_response.signal_error(error) - rescue => error - conn.debug_output(error.inspect) - # not retryable - context.http_response.signal_error(error) + AsyncResponse.new( + context: context, + stream: stream, + stream_mutex: stream_mutex, + close_condition: close_condition, + sync_queue: sync_queue + ) end - - AsyncResponse.new( - context: context, - stream: stream, - stream_mutex: stream_mutex, - close_condition: close_condition, - sync_queue: sync_queue - ) end - + private def _register_callbacks(resp, stream, stream_mutex, close_condition, sync_queue) stream.on(:headers) do |headers| resp.signal_headers(headers) From b49b499231fb7b7969ae1806eed3f56769d86059 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 26 Aug 2024 07:30:14 -0700 Subject: [PATCH 60/70] Refactor to private method --- .../aws-sdk-core/plugins/stub_responses.rb | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb index cb8eb6243ca..ef2a27ee348 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb @@ -50,20 +50,26 @@ class Handler < Seahorse::Client::Handler def call(context) span_wrapper(context) do - stub = context.client.next_stub(context) - resp = Seahorse::Client::Response.new(context: context) - async_mode = context.client.is_a? Seahorse::Client::AsyncBase - if Hash === stub && stub[:mutex] - stub[:mutex].synchronize { apply_stub(stub, resp, async_mode) } - else - apply_stub(stub, resp, async_mode) - end - async_mode ? Seahorse::Client::AsyncResponse.new( - context: context, - stream: context[:input_event_stream_handler].event_emitter.stream, - sync_queue: Queue.new - ) : resp + stub_responses(context) + end + end + + private + + def stub_responses(context) + stub = context.client.next_stub(context) + resp = Seahorse::Client::Response.new(context: context) + async_mode = context.client.is_a? Seahorse::Client::AsyncBase + if Hash === stub && stub[:mutex] + stub[:mutex].synchronize { apply_stub(stub, resp, async_mode) } + else + apply_stub(stub, resp, async_mode) end + async_mode ? Seahorse::Client::AsyncResponse.new( + context: context, + stream: context[:input_event_stream_handler].event_emitter.stream, + sync_queue: Queue.new + ) : resp end def apply_stub(stub, response, async_mode = false) From 7018ecb70cf3031a24ebf12092a62ae19ebc2954 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 26 Aug 2024 07:35:05 -0700 Subject: [PATCH 61/70] Add span wrapper --- .../lib/aws-sdk-core/plugins/telemetry.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index 54c5e4ed616..68300a86c6b 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -34,6 +34,12 @@ def validate_telemetry_provider(config) class Handler < Seahorse::Client::Handler def call(context) + span_wrapper(context) { @handler.call(context) } + end + + private + + def span_wrapper(context, &block) service_id = service_id(context) attributes = { 'rpc.system' => 'aws-api', @@ -45,14 +51,11 @@ def call(context) context.tracer.in_span( parent_span_name(context, service_id), attributes: attributes, - kind: Aws::Telemetry::SpanKind::CLIENT - ) do - @handler.call(context) - end + kind: Aws::Telemetry::SpanKind::CLIENT, + &block + ) end - private - def service_id(context) context.config.api.metadata['serviceId'] || context.config.api.metadata['serviceAbbreviation'] || From 2e65821df41154aa0e0e99bf6131aa2beea774fe Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 26 Aug 2024 07:43:04 -0700 Subject: [PATCH 62/70] Refactor to private method --- .../lib/seahorse/client/h2/handler.rb | 105 +++++++++--------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb b/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb index 714a0426a6f..0a70f0970f2 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/h2/handler.rb @@ -27,62 +27,65 @@ module H2 class Handler < Client::Handler def call(context) - span_wrapper(context) do - stream = nil - begin - conn = context.client.connection - stream = conn.new_stream - - stream_mutex = Mutex.new - close_condition = ConditionVariable.new - sync_queue = Queue.new - - conn.connect(context.http_request.endpoint) - _register_callbacks( - context.http_response, - stream, - stream_mutex, - close_condition, - sync_queue - ) - - conn.debug_output("sending initial request ...") - if input_emitter = context[:input_event_emitter] - _send_initial_headers(context.http_request, stream) - - # prepare for sending events later - input_emitter.stream = stream - # request sigv4 serves as the initial #prior_signature - input_emitter.encoder.prior_signature = - context.http_request.headers['authorization'].split('Signature=').last - input_emitter.validate_event = context.config.validate_params - else - _send_initial_headers(context.http_request, stream) - _send_initial_data(context.http_request, stream) - end - - conn.start(stream) - rescue *NETWORK_ERRORS => error - error = NetworkingError.new( - error, error_message(context.http_request, error)) - context.http_response.signal_error(error) - rescue => error - conn.debug_output(error.inspect) - # not retryable - context.http_response.signal_error(error) - end + span_wrapper(context) { _call(context) } + end + + private - AsyncResponse.new( - context: context, - stream: stream, - stream_mutex: stream_mutex, - close_condition: close_condition, - sync_queue: sync_queue + def _call(context) + stream = nil + begin + conn = context.client.connection + stream = conn.new_stream + + stream_mutex = Mutex.new + close_condition = ConditionVariable.new + sync_queue = Queue.new + + conn.connect(context.http_request.endpoint) + _register_callbacks( + context.http_response, + stream, + stream_mutex, + close_condition, + sync_queue ) + + conn.debug_output("sending initial request ...") + if input_emitter = context[:input_event_emitter] + _send_initial_headers(context.http_request, stream) + + # prepare for sending events later + input_emitter.stream = stream + # request sigv4 serves as the initial #prior_signature + input_emitter.encoder.prior_signature = + context.http_request.headers['authorization'].split('Signature=').last + input_emitter.validate_event = context.config.validate_params + else + _send_initial_headers(context.http_request, stream) + _send_initial_data(context.http_request, stream) + end + + conn.start(stream) + rescue *NETWORK_ERRORS => error + error = NetworkingError.new( + error, error_message(context.http_request, error)) + context.http_response.signal_error(error) + rescue => error + conn.debug_output(error.inspect) + # not retryable + context.http_response.signal_error(error) end + + AsyncResponse.new( + context: context, + stream: stream, + stream_mutex: stream_mutex, + close_condition: close_condition, + sync_queue: sync_queue + ) end - private def _register_callbacks(resp, stream, stream_mutex, close_condition, sync_queue) stream.on(:headers) do |headers| resp.signal_headers(headers) From 6c6ad5ced1306b599100c8e9ba04a709074fb72b Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 26 Aug 2024 09:37:17 -0700 Subject: [PATCH 63/70] Update for readability --- .../templates/async_client_class.mustache | 7 ++++--- .../aws-sdk-code-generator/templates/client_class.mustache | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/build_tools/aws-sdk-code-generator/templates/async_client_class.mustache b/build_tools/aws-sdk-code-generator/templates/async_client_class.mustache index 215fc7a27da..42567024795 100644 --- a/build_tools/aws-sdk-code-generator/templates/async_client_class.mustache +++ b/build_tools/aws-sdk-code-generator/templates/async_client_class.mustache @@ -83,6 +83,9 @@ module {{module_name}} # @api private def build_request(operation_name, params = {}) handlers = @handlers.for(operation_name) + tracer = config.telemetry_provider.tracer_provider.tracer( + Aws::Telemetry.module_to_tracer_name('{{module_name}}') + ) context = Seahorse::Client::RequestContext.new( operation_name: operation_name, operation: config.api.operation(operation_name), @@ -90,9 +93,7 @@ module {{module_name}} params: params, http_response: Seahorse::Client::Http::AsyncResponse.new, config: config, - tracer: config.telemetry_provider.tracer_provider.tracer( - Aws::Telemetry.module_to_tracer_name('{{module_name}}') - ) + tracer: tracer ) context[:gem_name] = '{{gem_name}}' context[:gem_version] = '{{gem_version}}' diff --git a/build_tools/aws-sdk-code-generator/templates/client_class.mustache b/build_tools/aws-sdk-code-generator/templates/client_class.mustache index b4d5dbbdec2..4145271b124 100644 --- a/build_tools/aws-sdk-code-generator/templates/client_class.mustache +++ b/build_tools/aws-sdk-code-generator/templates/client_class.mustache @@ -99,6 +99,9 @@ module {{module_name}} end end {{/authorizer?}} + tracer = config.telemetry_provider.tracer_provider.tracer( + Aws::Telemetry.module_to_tracer_name('{{module_name}}') + ) context = Seahorse::Client::RequestContext.new( operation_name: operation_name, operation: config.api.operation(operation_name),{{#authorizer?}} @@ -106,9 +109,7 @@ module {{module_name}} client: self, params: params, config: config, - tracer: config.telemetry_provider.tracer_provider.tracer( - Aws::Telemetry.module_to_tracer_name('{{module_name}}') - ) + tracer: tracer ) context[:gem_name] = '{{gem_name}}' context[:gem_version] = '{{gem_version}}' From bfe5c9935845977cce84d04b8e9ae2a6e09d5aec Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 26 Aug 2024 09:57:18 -0700 Subject: [PATCH 64/70] Update docs --- gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb index 6790c2b7c4a..3c75358e0a1 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/span_status.rb @@ -7,7 +7,7 @@ class SpanStatus class << self private :new - # Returns a newly created {SpanStatus} with code, +UNSET+ + # Returns a newly created {SpanStatus} with code, `UNSET` # and an optional description. # # @param [optional String] description @@ -16,7 +16,7 @@ def unset(description = '') new(UNSET, description: description) end - # Returns a newly created {SpanStatus} with code, +OK+ + # Returns a newly created {SpanStatus} with code, `OK` # and an optional description. # # @param [optional String] description @@ -25,7 +25,7 @@ def ok(description = '') new(OK, description: description) end - # Returns a newly created {SpanStatus} with code, +ERROR+ + # Returns a newly created {SpanStatus} with code, `ERROR` # and an optional description. # # @param [optional String] description From 834cf0c1d664f62baeedc1fa2359854182e09f8b Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Wed, 28 Aug 2024 09:03:03 -0700 Subject: [PATCH 65/70] Add customizations --- build_tools/customizations.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/build_tools/customizations.rb b/build_tools/customizations.rb index 11a8e5f1bff..e1680412137 100644 --- a/build_tools/customizations.rb +++ b/build_tools/customizations.rb @@ -107,6 +107,8 @@ def dynamodb_example_deep_transform(subsegment, keys) end api('ImportExport') do |api| + api['metadata']['serviceId'] = 'importexport' if api['metadata']['serviceId'].nil? + api['operations'].each do |_, operation| operation['http']['requestUri'] = '/' end @@ -115,6 +117,9 @@ def dynamodb_example_deep_transform(subsegment, keys) %w(Lambda LambdaPreview).each do |svc_name| api(svc_name) do |api| api['shapes']['Timestamp']['type'] = 'timestamp' + if (svc_name == 'LambdaPreview') && api['metadata']['serviceId'].nil? + api['metadata']['serviceId'] = 'Lambda Preview' + end end doc('lambda') do |docs| @@ -220,6 +225,8 @@ def dynamodb_example_deep_transform(subsegment, keys) # uses both flattened and locationName. Query protocol is supposed to # ignore location name (xmlName) when flattened (xmlFlattened) is used. api('SimpleDB') do |api| + api['metadata']['serviceId'] = 'SimpleDB' if api['metadata']['serviceId'].nil? + api['shapes'].each do |_, shape| next unless shape['type'] == 'structure' From 071182179faca873426963f666eec0202bef93d0 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Wed, 28 Aug 2024 09:05:33 -0700 Subject: [PATCH 66/70] Bump min core --- build_tools/services.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build_tools/services.rb b/build_tools/services.rb index a0f72c51f5f..0909815bc96 100644 --- a/build_tools/services.rb +++ b/build_tools/services.rb @@ -10,10 +10,10 @@ class ServiceEnumerator MANIFEST_PATH = File.expand_path('../../services.json', __FILE__) # Minimum `aws-sdk-core` version for new gem builds - MINIMUM_CORE_VERSION = "3.202.0" + MINIMUM_CORE_VERSION = "3.203.0" # Minimum `aws-sdk-core` version for new S3 gem builds - MINIMUM_CORE_VERSION_S3 = "3.202.0" + MINIMUM_CORE_VERSION_S3 = "3.203.0" EVENTSTREAM_PLUGIN = "Aws::Plugins::EventStreamConfiguration" From 3d1b994353b403e423cfde8b0b0f55dd283c92ce Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Wed, 28 Aug 2024 09:13:13 -0700 Subject: [PATCH 67/70] Update based on feedback --- build_tools/customizations.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/build_tools/customizations.rb b/build_tools/customizations.rb index e1680412137..41eb5394e6a 100644 --- a/build_tools/customizations.rb +++ b/build_tools/customizations.rb @@ -67,9 +67,7 @@ def dynamodb_example_deep_transform(subsegment, keys) api('CloudFront') do |api| api['shapes'].each do |_, shape| - if shape['members'] && shape['members']['MaxItems'] - shape['members']['MaxItems']['shape'] = 'integer' - end + shape['members']['MaxItems']['shape'] = 'integer' if shape['members'] && shape['members']['MaxItems'] end api['operations'].keys.each do |name| @@ -107,7 +105,7 @@ def dynamodb_example_deep_transform(subsegment, keys) end api('ImportExport') do |api| - api['metadata']['serviceId'] = 'importexport' if api['metadata']['serviceId'].nil? + api['metadata']['serviceId'] ||= 'importexport' api['operations'].each do |_, operation| operation['http']['requestUri'] = '/' @@ -116,10 +114,8 @@ def dynamodb_example_deep_transform(subsegment, keys) %w(Lambda LambdaPreview).each do |svc_name| api(svc_name) do |api| + api['metadata']['serviceId'] ||= 'Lambda Preview' if svc_name == 'LambdaPreview' api['shapes']['Timestamp']['type'] = 'timestamp' - if (svc_name == 'LambdaPreview') && api['metadata']['serviceId'].nil? - api['metadata']['serviceId'] = 'Lambda Preview' - end end doc('lambda') do |docs| @@ -225,7 +221,7 @@ def dynamodb_example_deep_transform(subsegment, keys) # uses both flattened and locationName. Query protocol is supposed to # ignore location name (xmlName) when flattened (xmlFlattened) is used. api('SimpleDB') do |api| - api['metadata']['serviceId'] = 'SimpleDB' if api['metadata']['serviceId'].nil? + api['metadata']['serviceId'] ||= 'SimpleDB' api['shapes'].each do |_, shape| next unless shape['type'] == 'structure' From 3b72c62c6f765ff2efac76b9085071014f827a39 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Wed, 28 Aug 2024 09:27:32 -0700 Subject: [PATCH 68/70] Fix length --- build_tools/customizations.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build_tools/customizations.rb b/build_tools/customizations.rb index 41eb5394e6a..53a23cf0f5b 100644 --- a/build_tools/customizations.rb +++ b/build_tools/customizations.rb @@ -67,7 +67,9 @@ def dynamodb_example_deep_transform(subsegment, keys) api('CloudFront') do |api| api['shapes'].each do |_, shape| - shape['members']['MaxItems']['shape'] = 'integer' if shape['members'] && shape['members']['MaxItems'] + if shape['members'] && shape['members']['MaxItems'] + shape['members']['MaxItems']['shape'] = 'integer' + end end api['operations'].keys.each do |name| From 5d171f62a0ed3529860b1788dd1ee5e5e4a439f5 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Wed, 28 Aug 2024 11:25:58 -0700 Subject: [PATCH 69/70] Update for readability --- .../lib/aws-sdk-core/plugins/stub_responses.rb | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb index ef2a27ee348..e0ee7e9561a 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb @@ -65,11 +65,16 @@ def stub_responses(context) else apply_stub(stub, resp, async_mode) end - async_mode ? Seahorse::Client::AsyncResponse.new( - context: context, - stream: context[:input_event_stream_handler].event_emitter.stream, - sync_queue: Queue.new - ) : resp + + if async_mode + Seahorse::Client::AsyncResponse.new( + context: context, + stream: context[:input_event_stream_handler].event_emitter.stream, + sync_queue: Queue.new + ) + else + resp + end end def apply_stub(stub, response, async_mode = false) From 1b222f958a43ab4e44ebca3bc93a07aabda5a225 Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Wed, 28 Aug 2024 14:43:18 -0700 Subject: [PATCH 70/70] Update based on feedback --- .../lib/aws-sdk-core/plugins/telemetry.rb | 14 ++++++++------ .../lib/aws-sdk-core/telemetry/base.rb | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb index 68300a86c6b..57bfb216713 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/plugins/telemetry.rb @@ -9,12 +9,14 @@ class Telemetry < Seahorse::Client::Plugin default: Aws::Telemetry::NoOpTelemetryProvider, doc_type: Aws::Telemetry::TelemetryProviderBase, rbs_type: Aws::Telemetry::TelemetryProviderBase, - docstring: <<~DOCS) do |_cfg| -Allows you to provide a telemetry provider, which is used -to emit telemetry data. By default, uses `NoOpTelemetryProvider` -which will not record or emit any telemetry data. The SDK currently supports -OpenTelemetry (OTel) as a telemetry provider. To use the OTel provider, -require the`opentelemetry-sdk` gem and then, pass in an instance of a + docstring: <<-DOCS) do |_cfg| +Allows you to provide a telemetry provider, which is used to +emit telemetry data. By default, uses `NoOpTelemetryProvider` which +will not record or emit any telemetry data. The SDK supports the +following telemetry providers: + +* OpenTelemetry (OTel) - To use the OTel provider, install and require the +`opentelemetry-sdk` gem and then, pass in an instance of a `Aws::Telemetry::OTelProvider` for telemetry provider. DOCS Aws::Telemetry::NoOpTelemetryProvider.new diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb index 0ae0b950ec5..fbe011a4363 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/telemetry/base.rb @@ -5,10 +5,10 @@ module Telemetry # Base for `TelemetryProvider` classes. # They are used to emit telemetry data. It needs the # following class implementations to function: - # - {TracerProviderBase} - A provider that returns a tracer + # * {TracerProviderBase} - A provider that returns a tracer # instance. Then, a tracer will create spans and those # spans will contain information in that given moment. - # - {ContextManagerBase} - Manages context and used to + # * {ContextManagerBase} - Manages context and used to # return the current context within a trace. class TelemetryProviderBase # @param [Aws::Telemetry::TracerBase] tracer_provider A provider