From 50177ff65743cfdb5f4d2b713ce1b22ef496232c Mon Sep 17 00:00:00 2001 From: Juli Tera Date: Mon, 29 Jul 2024 15:02:01 -0700 Subject: [PATCH] Create base classes for all --- .../rails_json/lib/rails_json/config.rb | 6 +- .../rails_json/sig/rails_json/client.rbs | 2 +- .../rails_json/sig/rails_json/config.rbs | 2 +- .../rpcv2_cbor/lib/rpcv2_cbor/config.rb | 6 +- .../rpcv2_cbor/sig/rpcv2_cbor/client.rbs | 2 +- .../rpcv2_cbor/sig/rpcv2_cbor/config.rbs | 2 +- .../white_label/lib/white_label/config.rb | 6 +- .../white_label/sig/white_label/client.rbs | 2 +- .../white_label/sig/white_label/config.rbs | 2 +- .../white_label/spec/telemetry_spec.rb | 12 +- .../integration-specs/telemetry_spec.rb | 12 +- .../codegen/middleware/MiddlewareBuilder.java | 2 +- hearth/lib/hearth/telemetry.rb | 15 +- hearth/lib/hearth/telemetry/base.rb | 169 ++++++++++++++++++ hearth/lib/hearth/telemetry/no_op.rb | 102 ++--------- hearth/lib/hearth/telemetry/otel.rb | 10 +- .../hearth/telemetry/telemetry_provider.rb | 22 --- .../{telemetry.rbs => telemetry/base.rbs} | 26 ++- hearth/sig/lib/hearth/telemetry/otel.rbs | 5 +- .../hearth/telemetry/telemetry_provider.rbs | 11 -- hearth/spec/hearth/telemetry/base_spec.rb | 73 ++++++++ 21 files changed, 316 insertions(+), 173 deletions(-) create mode 100644 hearth/lib/hearth/telemetry/base.rb delete mode 100644 hearth/lib/hearth/telemetry/telemetry_provider.rb rename hearth/sig/lib/hearth/{telemetry.rbs => telemetry/base.rbs} (58%) delete mode 100644 hearth/sig/lib/hearth/telemetry/telemetry_provider.rbs create mode 100644 hearth/spec/hearth/telemetry/base_spec.rb diff --git a/codegen/projections/rails_json/lib/rails_json/config.rb b/codegen/projections/rails_json/lib/rails_json/config.rb index ec19f1c75..19e02077e 100644 --- a/codegen/projections/rails_json/lib/rails_json/config.rb +++ b/codegen/projections/rails_json/lib/rails_json/config.rb @@ -59,7 +59,7 @@ module RailsJson # Enable response stubbing for testing. See {Hearth::ClientStubs#stub_responses}. # @option args [Hearth::Stubs] :stubs (Hearth::Stubs.new) # Enable response stubbing for testing. See {Hearth::ClientStubs#stub_responses}. - # @option args [Hearth::Telemetry::TelemetryProvider] :telemetry_provider (Hearth::Telemetry::NoOpTelemetryProvider.new) + # @option args [Hearth::Telemetry::TelemetryProviderBase] :telemetry_provider (Hearth::Telemetry::NoOpTelemetryProvider.new) # 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. To use @@ -96,7 +96,7 @@ module RailsJson # @!attribute stubs # @return [Hearth::Stubs] # @!attribute telemetry_provider - # @return [Hearth::Telemetry::TelemetryProvider] + # @return [Hearth::Telemetry::TelemetryProviderBase] # @!attribute validate_input # @return [Boolean] class Config @@ -140,7 +140,7 @@ def validate! Hearth::Validator.validate_responds_to!(retry_strategy, :acquire_initial_retry_token, :refresh_retry_token, :record_success, context: 'config[:retry_strategy]') Hearth::Validator.validate_types!(stub_responses, TrueClass, FalseClass, context: 'config[:stub_responses]') Hearth::Validator.validate_types!(stubs, Hearth::Stubs, context: 'config[:stubs]') - Hearth::Validator.validate_types!(telemetry_provider, Hearth::Telemetry::TelemetryProvider, context: 'config[:telemetry_provider]') + Hearth::Validator.validate_types!(telemetry_provider, Hearth::Telemetry::TelemetryProviderBase, context: 'config[:telemetry_provider]') Hearth::Validator.validate_types!(validate_input, TrueClass, FalseClass, context: 'config[:validate_input]') end diff --git a/codegen/projections/rails_json/sig/rails_json/client.rbs b/codegen/projections/rails_json/sig/rails_json/client.rbs index 20f0c7e54..073dc283a 100644 --- a/codegen/projections/rails_json/sig/rails_json/client.rbs +++ b/codegen/projections/rails_json/sig/rails_json/client.rbs @@ -28,7 +28,7 @@ module RailsJson ?retry_strategy: Hearth::_RetryStrategy, ?stub_responses: bool, ?stubs: Hearth::Stubs, - ?telemetry_provider: Hearth::Telemetry::TelemetryProvider, + ?telemetry_provider: Hearth::Telemetry::TelemetryProviderBase, ?validate_input: bool ) -> void diff --git a/codegen/projections/rails_json/sig/rails_json/config.rbs b/codegen/projections/rails_json/sig/rails_json/config.rbs index ecd9cb396..d4fab4f17 100644 --- a/codegen/projections/rails_json/sig/rails_json/config.rbs +++ b/codegen/projections/rails_json/sig/rails_json/config.rbs @@ -15,7 +15,7 @@ module RailsJson attr_accessor retry_strategy (): Hearth::_RetryStrategy? attr_accessor stub_responses (): bool? attr_accessor stubs (): Hearth::Stubs? - attr_accessor telemetry_provider (): Hearth::Telemetry::TelemetryProvider? + attr_accessor telemetry_provider (): Hearth::Telemetry::TelemetryProviderBase? attr_accessor validate_input (): bool? def validate!: () -> void diff --git a/codegen/projections/rpcv2_cbor/lib/rpcv2_cbor/config.rb b/codegen/projections/rpcv2_cbor/lib/rpcv2_cbor/config.rb index e46515652..7e1fc4b50 100644 --- a/codegen/projections/rpcv2_cbor/lib/rpcv2_cbor/config.rb +++ b/codegen/projections/rpcv2_cbor/lib/rpcv2_cbor/config.rb @@ -54,7 +54,7 @@ module Rpcv2Cbor # Enable response stubbing for testing. See {Hearth::ClientStubs#stub_responses}. # @option args [Hearth::Stubs] :stubs (Hearth::Stubs.new) # Enable response stubbing for testing. See {Hearth::ClientStubs#stub_responses}. - # @option args [Hearth::Telemetry::TelemetryProvider] :telemetry_provider (Hearth::Telemetry::NoOpTelemetryProvider.new) + # @option args [Hearth::Telemetry::TelemetryProviderBase] :telemetry_provider (Hearth::Telemetry::NoOpTelemetryProvider.new) # 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. To use @@ -87,7 +87,7 @@ module Rpcv2Cbor # @!attribute stubs # @return [Hearth::Stubs] # @!attribute telemetry_provider - # @return [Hearth::Telemetry::TelemetryProvider] + # @return [Hearth::Telemetry::TelemetryProviderBase] # @!attribute validate_input # @return [Boolean] class Config @@ -126,7 +126,7 @@ def validate! Hearth::Validator.validate_responds_to!(retry_strategy, :acquire_initial_retry_token, :refresh_retry_token, :record_success, context: 'config[:retry_strategy]') Hearth::Validator.validate_types!(stub_responses, TrueClass, FalseClass, context: 'config[:stub_responses]') Hearth::Validator.validate_types!(stubs, Hearth::Stubs, context: 'config[:stubs]') - Hearth::Validator.validate_types!(telemetry_provider, Hearth::Telemetry::TelemetryProvider, context: 'config[:telemetry_provider]') + Hearth::Validator.validate_types!(telemetry_provider, Hearth::Telemetry::TelemetryProviderBase, context: 'config[:telemetry_provider]') Hearth::Validator.validate_types!(validate_input, TrueClass, FalseClass, context: 'config[:validate_input]') end diff --git a/codegen/projections/rpcv2_cbor/sig/rpcv2_cbor/client.rbs b/codegen/projections/rpcv2_cbor/sig/rpcv2_cbor/client.rbs index 112d47815..009f11e4f 100644 --- a/codegen/projections/rpcv2_cbor/sig/rpcv2_cbor/client.rbs +++ b/codegen/projections/rpcv2_cbor/sig/rpcv2_cbor/client.rbs @@ -26,7 +26,7 @@ module Rpcv2Cbor ?retry_strategy: Hearth::_RetryStrategy, ?stub_responses: bool, ?stubs: Hearth::Stubs, - ?telemetry_provider: Hearth::Telemetry::TelemetryProvider, + ?telemetry_provider: Hearth::Telemetry::TelemetryProviderBase, ?validate_input: bool ) -> void diff --git a/codegen/projections/rpcv2_cbor/sig/rpcv2_cbor/config.rbs b/codegen/projections/rpcv2_cbor/sig/rpcv2_cbor/config.rbs index 8c300b199..e40aa7d9f 100644 --- a/codegen/projections/rpcv2_cbor/sig/rpcv2_cbor/config.rbs +++ b/codegen/projections/rpcv2_cbor/sig/rpcv2_cbor/config.rbs @@ -13,7 +13,7 @@ module Rpcv2Cbor attr_accessor retry_strategy (): Hearth::_RetryStrategy? attr_accessor stub_responses (): bool? attr_accessor stubs (): Hearth::Stubs? - attr_accessor telemetry_provider (): Hearth::Telemetry::TelemetryProvider? + attr_accessor telemetry_provider (): Hearth::Telemetry::TelemetryProviderBase? attr_accessor validate_input (): bool? def validate!: () -> void diff --git a/codegen/projections/white_label/lib/white_label/config.rb b/codegen/projections/white_label/lib/white_label/config.rb index 7cbe24594..7dbb4489b 100644 --- a/codegen/projections/white_label/lib/white_label/config.rb +++ b/codegen/projections/white_label/lib/white_label/config.rb @@ -69,7 +69,7 @@ module WhiteLabel # Enable response stubbing for testing. See {Hearth::ClientStubs#stub_responses}. # @option args [Hearth::Stubs] :stubs (Hearth::Stubs.new) # Enable response stubbing for testing. See {Hearth::ClientStubs#stub_responses}. - # @option args [Hearth::Telemetry::TelemetryProvider] :telemetry_provider (Hearth::Telemetry::NoOpTelemetryProvider.new) + # @option args [Hearth::Telemetry::TelemetryProviderBase] :telemetry_provider (Hearth::Telemetry::NoOpTelemetryProvider.new) # 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. To use @@ -118,7 +118,7 @@ module WhiteLabel # @!attribute stubs # @return [Hearth::Stubs] # @!attribute telemetry_provider - # @return [Hearth::Telemetry::TelemetryProvider] + # @return [Hearth::Telemetry::TelemetryProviderBase] # @!attribute test_config # @return [String] # @!attribute validate_input @@ -175,7 +175,7 @@ def validate! Hearth::Validator.validate_types!(stage, String, context: 'config[:stage]') Hearth::Validator.validate_types!(stub_responses, TrueClass, FalseClass, context: 'config[:stub_responses]') Hearth::Validator.validate_types!(stubs, Hearth::Stubs, context: 'config[:stubs]') - Hearth::Validator.validate_types!(telemetry_provider, Hearth::Telemetry::TelemetryProvider, context: 'config[:telemetry_provider]') + Hearth::Validator.validate_types!(telemetry_provider, Hearth::Telemetry::TelemetryProviderBase, context: 'config[:telemetry_provider]') Hearth::Validator.validate_types!(test_config, String, context: 'config[:test_config]') Hearth::Validator.validate_types!(validate_input, TrueClass, FalseClass, context: 'config[:validate_input]') end diff --git a/codegen/projections/white_label/sig/white_label/client.rbs b/codegen/projections/white_label/sig/white_label/client.rbs index aaecdaa26..7fa533f5b 100644 --- a/codegen/projections/white_label/sig/white_label/client.rbs +++ b/codegen/projections/white_label/sig/white_label/client.rbs @@ -33,7 +33,7 @@ module WhiteLabel ?stage: String, ?stub_responses: bool, ?stubs: Hearth::Stubs, - ?telemetry_provider: Hearth::Telemetry::TelemetryProvider, + ?telemetry_provider: Hearth::Telemetry::TelemetryProviderBase, ?test_config: untyped, ?validate_input: bool ) -> void diff --git a/codegen/projections/white_label/sig/white_label/config.rbs b/codegen/projections/white_label/sig/white_label/config.rbs index 525c82e7a..cd06e61bd 100644 --- a/codegen/projections/white_label/sig/white_label/config.rbs +++ b/codegen/projections/white_label/sig/white_label/config.rbs @@ -20,7 +20,7 @@ module WhiteLabel attr_accessor stage (): String? attr_accessor stub_responses (): bool? attr_accessor stubs (): Hearth::Stubs? - attr_accessor telemetry_provider (): Hearth::Telemetry::TelemetryProvider? + attr_accessor telemetry_provider (): Hearth::Telemetry::TelemetryProviderBase? attr_accessor test_config (): untyped? attr_accessor validate_input (): bool? diff --git a/codegen/projections/white_label/spec/telemetry_spec.rb b/codegen/projections/white_label/spec/telemetry_spec.rb index 07d1873fd..ae2ea8e1e 100644 --- a/codegen/projections/white_label/spec/telemetry_spec.rb +++ b/codegen/projections/white_label/spec/telemetry_spec.rb @@ -7,12 +7,18 @@ module WhiteLabel describe Config do context 'telemetry_provider' do + let(:custom_class) do + Class.new(Hearth::Telemetry::TelemetryProviderBase) + end + + let(:custom_provider) { custom_class.new } + it 'raises error when given an invalid input', rbs_test: :skip do expect { Config.new(telemetry_provider: 'foo').validate! } .to raise_error( ArgumentError, 'Expected config[:telemetry_provider] to be in ' \ - '[Hearth::Telemetry::TelemetryProvider], got String.' + '[Hearth::Telemetry::TelemetryProviderBase], got String.' ) end @@ -23,10 +29,6 @@ module WhiteLabel end it 'does not raise error when given a custom provider' do - custom_provider = Hearth::Telemetry::TelemetryProvider.new( - tracer_provider: Hearth::Telemetry::NoOpTracerProvider.new, - context_manager: Hearth::Telemetry::NoOpContextManager.new - ) expect { Config.new(telemetry_provider: custom_provider).validate! } .not_to raise_error end diff --git a/codegen/smithy-ruby-codegen-test/integration-specs/telemetry_spec.rb b/codegen/smithy-ruby-codegen-test/integration-specs/telemetry_spec.rb index 07d1873fd..ae2ea8e1e 100644 --- a/codegen/smithy-ruby-codegen-test/integration-specs/telemetry_spec.rb +++ b/codegen/smithy-ruby-codegen-test/integration-specs/telemetry_spec.rb @@ -7,12 +7,18 @@ module WhiteLabel describe Config do context 'telemetry_provider' do + let(:custom_class) do + Class.new(Hearth::Telemetry::TelemetryProviderBase) + end + + let(:custom_provider) { custom_class.new } + it 'raises error when given an invalid input', rbs_test: :skip do expect { Config.new(telemetry_provider: 'foo').validate! } .to raise_error( ArgumentError, 'Expected config[:telemetry_provider] to be in ' \ - '[Hearth::Telemetry::TelemetryProvider], got String.' + '[Hearth::Telemetry::TelemetryProviderBase], got String.' ) end @@ -23,10 +29,6 @@ module WhiteLabel end it 'does not raise error when given a custom provider' do - custom_provider = Hearth::Telemetry::TelemetryProvider.new( - tracer_provider: Hearth::Telemetry::NoOpTracerProvider.new, - context_manager: Hearth::Telemetry::NoOpContextManager.new - ) expect { Config.new(telemetry_provider: custom_provider).validate! } .not_to raise_error end diff --git a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/middleware/MiddlewareBuilder.java b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/middleware/MiddlewareBuilder.java index 7abebb904..f2176ecc0 100644 --- a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/middleware/MiddlewareBuilder.java +++ b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/middleware/MiddlewareBuilder.java @@ -251,7 +251,7 @@ The SDK currently supports OpenTelemetry (OTel) as a provider. To use .name("telemetry_provider") .defaultValue("Hearth::Telemetry::NoOpTelemetryProvider.new") .documentation(telemetryProviderDocumentation) - .documentationRbsAndValidationType("Hearth::Telemetry::TelemetryProvider") + .documentationRbsAndValidationType("Hearth::Telemetry::TelemetryProviderBase") .documentationDefaultValue("Hearth::Telemetry::NoOpTelemetryProvider.new") .build(); diff --git a/hearth/lib/hearth/telemetry.rb b/hearth/lib/hearth/telemetry.rb index e2232ef73..a7ee9cf4b 100644 --- a/hearth/lib/hearth/telemetry.rb +++ b/hearth/lib/hearth/telemetry.rb @@ -1,16 +1,15 @@ # frozen_string_literal: true -require_relative 'telemetry/telemetry_provider' +require_relative 'telemetry/base' require_relative 'telemetry/span_kind' require_relative 'telemetry/span_status' require_relative 'telemetry/no_op' require_relative 'telemetry/otel' module Hearth - # # Telemetry # 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 + # 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 @@ -18,8 +17,14 @@ module Hearth # 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 support by - # following the telemetry interfaces defined in RBS. + # If a provider isn't supported, you can implement your own provider by + # following by inheriting the following base classes and implementing + # interface defined: + # * {TelemetryProviderBase} + # * {ContextManagerBase} + # * {TracerProviderBase} + # * {TracerBase} + # * {SpanBase} module Telemetry # @api private def self.otel_loaded? diff --git a/hearth/lib/hearth/telemetry/base.rb b/hearth/lib/hearth/telemetry/base.rb new file mode 100644 index 000000000..1f7464e3c --- /dev/null +++ b/hearth/lib/hearth/telemetry/base.rb @@ -0,0 +1,169 @@ +# frozen_string_literal: true + +module Hearth + module Telemetry + # Base for TelemetryProvider classes. + 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 [optional 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 [optional Context] with_parent Parent Context + # @param [optional Hash] attributes Attributes to attach to the span + # @param [optional 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 [optional Hash] attributes Attributes to attach to the span + # @param [optional 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 [optional 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 [optional 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 [optional 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 [Context] 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/hearth/lib/hearth/telemetry/no_op.rb b/hearth/lib/hearth/telemetry/no_op.rb index e4cf833dd..08c3c15d6 100644 --- a/hearth/lib/hearth/telemetry/no_op.rb +++ b/hearth/lib/hearth/telemetry/no_op.rb @@ -2,8 +2,8 @@ module Hearth module Telemetry - # No-op implementation for TelemetryProvider - class NoOpTelemetryProvider < TelemetryProvider + # No-op implementation for TelemetryProvider. + class NoOpTelemetryProvider < TelemetryProviderBase def initialize super( tracer_provider: NoOpTracerProvider.new, @@ -12,140 +12,58 @@ def initialize end end - # No-op implementation for TracerProvider + # No-op implementation for TracerProvider. # rubocop:disable Lint/UnusedMethodArgument - class NoOpTracerProvider - # Returns a Tracer instance. - # - # @param [optional String] name Tracer name - # @return [Tracer] + class NoOpTracerProvider < TracerProviderBase def tracer(name = nil) @tracer ||= NoOpTracer.new end end - # No-op implementation for Tracer - class NoOpTracer - # 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 [optional Context] with_parent Parent Context - # @param [optional Hash] attributes Attributes to attach to the span - # @param [optional Hearth::Telemetry::SpanKind] kind Type of Span - # @return [Span] + # No-op implementation for Tracer. + class NoOpTracer < TracerBase def start_span(name, with_parent: nil, attributes: nil, kind: nil) NoOpSpan.new 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 [optional Hash] attributes Attributes to attach to the span - # @param [optional Hearth::Telemetry::SpanKind] kind Type of Span - # @return [Span] def in_span(name, attributes: nil, kind: nil) yield NoOpSpan.new end end - # No-op implementation for Span - class NoOpSpan - # 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 + # No-op implementation for Span. + class NoOpSpan < SpanBase def set_attribute(key, value) self 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 end - # Add event to a Span. - # - # @param [String] name Name of the event. - # @param [optional 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 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); end - # Finishes the Span. - # - # @param [optional Time] end_timestamp End timestamp for the span. - # @return [self] returns itself def finish(end_timestamp: nil) self 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 [optional 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); end end # rubocop:enable Lint/UnusedMethodArgument - # No-op implementation for ContextManager - class NoOpContextManager - # Returns current context. - # - # @return [Context] + # No-op implementation for ContextManager. + class NoOpContextManager < ContextManagerBase def current; end - # Returns the current span from current context. - # - # @return Span def 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. - # - # @param [Context] context The new context - # @return [Object] token A token to be used when detaching def attach(context); 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); end end end diff --git a/hearth/lib/hearth/telemetry/otel.rb b/hearth/lib/hearth/telemetry/otel.rb index 23058bf6e..358a1783a 100644 --- a/hearth/lib/hearth/telemetry/otel.rb +++ b/hearth/lib/hearth/telemetry/otel.rb @@ -8,7 +8,7 @@ module Telemetry # pass in an instance of a `Hearth::Telemetry::OTelProvider` as the # telemetry provider in the client config. # - # Configuration example + # @example Configuration # # require 'opentelemetry-sdk' # @@ -19,10 +19,10 @@ module Telemetry # client = Service::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 + # See {https://opentelemetry.io/docs/languages/ruby/exporters here} for # more information. # - # To demonstrate, we could choose to export through the console: + # @example Exporting via console # # require 'opentelemetry-sdk' # @@ -33,7 +33,7 @@ module Telemetry # # otel_provider = Hearth::Telemetry::OTelProvider.new # client = Service::Client.new(telemetry_provider: otel_provider) - class OTelProvider < TelemetryProvider + class OTelProvider < TelemetryProviderBase def initialize unless Hearth::Telemetry.otel_loaded? raise ArgumentError, @@ -47,7 +47,7 @@ def initialize end # OpenTelemetry-based ContextManager - class OTelContextManager + class OTelContextManager < ContextManagerBase # Returns current context. # # @return [Context] diff --git a/hearth/lib/hearth/telemetry/telemetry_provider.rb b/hearth/lib/hearth/telemetry/telemetry_provider.rb deleted file mode 100644 index 48f14f2f6..000000000 --- a/hearth/lib/hearth/telemetry/telemetry_provider.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -module Hearth - module Telemetry - # Base for all TelemetryProvider classes - class TelemetryProvider - # @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 - end -end diff --git a/hearth/sig/lib/hearth/telemetry.rbs b/hearth/sig/lib/hearth/telemetry/base.rbs similarity index 58% rename from hearth/sig/lib/hearth/telemetry.rbs rename to hearth/sig/lib/hearth/telemetry/base.rbs index 5f4fa5001..12c2b7071 100644 --- a/hearth/sig/lib/hearth/telemetry.rbs +++ b/hearth/sig/lib/hearth/telemetry/base.rbs @@ -1,16 +1,23 @@ module Hearth module Telemetry - interface _TracerProvider - def tracer: (?String name) -> _Tracer + 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 - interface _Tracer - def start_span: (String name, ?untyped with_parent, ?Hash[String, untyped] attributes, ?untyped kind) -> _Span + class TracerBase + def start_span: (String name, ?untyped with_parent, ?Hash[String, untyped] attributes, ?untyped kind) -> SpanBase - def in_span: (String name, ?Hash[String, untyped] attributes, ?untyped kind) -> _Span + def in_span: (String name, ?Hash[String, untyped] attributes, ?untyped kind) -> SpanBase end - interface _Span + class SpanBase def set_attribute: (String key, untyped value) -> self alias []= set_attribute @@ -25,14 +32,15 @@ module Hearth def record_exception: (untyped exception, ?Hash[String, untyped] attributes) -> void end - interface _ContextManager + class ContextManagerBase def current: () -> untyped - def current_span: () -> _Span + def current_span: () -> SpanBase def attach: (untyped context) -> untyped def detach: (untyped token) -> bool end + end -end \ No newline at end of file +end diff --git a/hearth/sig/lib/hearth/telemetry/otel.rbs b/hearth/sig/lib/hearth/telemetry/otel.rbs index e9935c9b0..69636a67f 100644 --- a/hearth/sig/lib/hearth/telemetry/otel.rbs +++ b/hearth/sig/lib/hearth/telemetry/otel.rbs @@ -1,11 +1,10 @@ module Hearth module Telemetry - class OTelProvider < TelemetryProvider + class OTelProvider < TelemetryProviderBase def initialize: () -> void end - class OTelContextManager - include _ContextManager + class OTelContextManager < ContextManagerBase end end end diff --git a/hearth/sig/lib/hearth/telemetry/telemetry_provider.rbs b/hearth/sig/lib/hearth/telemetry/telemetry_provider.rbs deleted file mode 100644 index e1a5e12e9..000000000 --- a/hearth/sig/lib/hearth/telemetry/telemetry_provider.rbs +++ /dev/null @@ -1,11 +0,0 @@ -module Hearth - module Telemetry - class TelemetryProvider - def initialize: (?tracer_provider: _TracerProvider, ?context_manager: _ContextManager) -> void - - attr_reader tracer_provider: _TracerProvider - - attr_reader context_manager: _ContextManager - end - end -end diff --git a/hearth/spec/hearth/telemetry/base_spec.rb b/hearth/spec/hearth/telemetry/base_spec.rb new file mode 100644 index 000000000..35b069db9 --- /dev/null +++ b/hearth/spec/hearth/telemetry/base_spec.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +module Hearth + 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