Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Observability Adjustments #217

Merged
merged 10 commits into from
Aug 23, 2024
3 changes: 2 additions & 1 deletion codegen/projections/white_label/spec/telemetry_spec.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ module WhiteLabel
end

it 'raises error when an otel dependency was not required' do
allow(Hearth::Telemetry).to receive(:otel_loaded?).and_return(false)
allow_any_instance_of(Hearth::Telemetry::OTelProvider)
.to receive(:require).with('opentelemetry-sdk').and_raise(LoadError)
expect { otel_provider }
.to raise_error(
ArgumentError,
Expand Down
1 change: 1 addition & 0 deletions hearth/.rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Metrics/MethodLength:

Metrics/ParameterLists:
Exclude:
- 'lib/hearth/interceptor_context.rb'
- 'lib/hearth/middleware/auth.rb'
- 'lib/hearth/middleware/send.rb'
- 'lib/hearth/middleware/event_streams.rb'
Expand Down
1 change: 1 addition & 0 deletions hearth/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ Unreleased Changes
------------------

* Feature - Add support for Event Streams.
* Feature - Add support for Observability.
* Issue - Remove `Struct` from generated Types, Config, and other places to allow for better RBS typing as well as less reserved words.
* Feature - Add `Hearth::Cbor.encode` and `Hearth::Cbor.decode`.
* Issue - Fix query param `to_s` for empty arrays.
Expand Down
4 changes: 2 additions & 2 deletions hearth/lib/hearth/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ def initialize(options = {})
# @return [Symbol] The name of the API operation called.
attr_reader :operation_name

# @return [HTTP::Request]
# @return [Request]
attr_reader :request

# @return [HTTP::Response]
# @return [Response]
attr_reader :response

# @return [Configuration] An instance of operation configuration.
Expand Down
17 changes: 17 additions & 0 deletions hearth/lib/hearth/http/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,23 @@ def remove_query_param(name)
def prefix_host(prefix)
uri.host = prefix + uri.host
end

# Contains attributes for Telemetry span to emit.
# @return [Hash]
def span_attributes
jterapin marked this conversation as resolved.
Show resolved Hide resolved
{
'http.method' => http_method,
'net.protocol.name' => 'http',
'net.protocol.version' => Net::HTTP::HTTPVersion,
'net.peer.name' => uri.host,
'net.peer.port' => uri.port
}.tap do |h|
if headers.key?('Content-Length')
h['http.request_content_length'] =
headers['Content-Length']
end
end
end
end
end
end
13 changes: 13 additions & 0 deletions hearth/lib/hearth/http/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ def reset
@fields.clear
super
end

# Contains attributes for Telemetry span to emit.
# @return [Hash]
def span_attributes
{
'http.status_code' => status
}.tap do |h|
if headers.key?('Content-Length')
h['http.response_content_length'] =
headers['Content-Length']
end
end
end
end
end
end
6 changes: 5 additions & 1 deletion hearth/lib/hearth/interceptor_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ class InterceptorContext
# @param [Response] response
# @param [Output] output
# @param [Configuration] config
def initialize(input:, request:, response:, output:, config:)
def initialize(input:, request:, response:, output:, config:, tracer:)
@input = input
@request = request
@response = response
@output = output
@config = config
@tracer = tracer
@attributes = {}
end

Expand All @@ -34,6 +35,9 @@ def initialize(input:, request:, response:, output:, config:)
# @return [Configuration] config
attr_reader :config

# @return [Tracer] tracer
attr_reader :tracer

# @return [Hash] attributes Additional interceptor data
attr_reader :attributes
end
Expand Down
3 changes: 2 additions & 1 deletion hearth/lib/hearth/interceptors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ def interceptor_context(input, context, output)
request: context.request,
response: context.response,
output: output,
config: context.config
config: context.config,
tracer: context.tracer
)
end

Expand Down
37 changes: 4 additions & 33 deletions hearth/lib/hearth/middleware/send.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def call(input, context)
private

def stub_response(input, context, output)
span_wrapper(context, stub_response: true) do
span_wrapper(context) do
stub = @stubs.next(context.operation_name)
log_debug(context, "Stubbing response with stub: #{stub}")
if @response_events
Expand Down Expand Up @@ -109,42 +109,13 @@ def send_request(context, output)
end
end

def span_wrapper(context, stub_response: false, &block)
def span_wrapper(context, &block)
context.tracer.in_span(
'Middleware.Send',
attributes: request_attrs(context, stub_response: stub_response)
attributes: context.request.span_attributes
jterapin marked this conversation as resolved.
Show resolved Hide resolved
) do |span|
block.call
span.add_attributes(response_attrs(context))
end
end

def request_attrs(context, stub_response: false)
{
'http.method' => context.request.http_method,
'net.protocol.name' => 'http',
'net.protocol.version' => Net::HTTP::HTTPVersion
}.tap do |h|
unless stub_response
h['net.peer.name'] = context.request.uri.host
h['net.peer.port'] = context.request.uri.port
end

if context.request.headers.key?('Content-Length')
h['http.request_content_length'] =
context.request.headers['Content-Length']
end
end
end

def response_attrs(context)
{
'http.status_code' => context.response.status
}.tap do |h|
if context.response.headers.key?('Content-Length')
h['http.response_content_length'] =
context.response.headers['Content-Length']
end
span.add_attributes(context.response.span_attributes)
end
end

Expand Down
6 changes: 6 additions & 0 deletions hearth/lib/hearth/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,11 @@ def initialize(uri: URI(''), body: StringIO.new)

# @return [IO, StringIO]
attr_accessor :body

# Contains attributes for Telemetry span to emit.
# @return [Hash]
def span_attributes
{}
end
end
end
6 changes: 6 additions & 0 deletions hearth/lib/hearth/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,11 @@ def reset
@body.rewind if @body.respond_to?(:rewind) && [email protected]_of?(IO)
self
end

# Contains attributes for Telemetry span to emit.
# @return [Hash]
def span_attributes
{}
end
end
end
19 changes: 2 additions & 17 deletions hearth/lib/hearth/telemetry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
module Hearth
# 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.
# as Telemetry. Hearth 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.
Expand All @@ -25,19 +24,5 @@ module Hearth
# * {TracerProviderBase}
# * {TracerBase}
# * {SpanBase}
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
module Telemetry; end
end
14 changes: 7 additions & 7 deletions hearth/lib/hearth/telemetry/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,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 Span
def current_span
raise NotImplementedError
end
end

# Base for Span classes.
Expand Down Expand Up @@ -146,13 +153,6 @@ 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.
#
Expand Down
8 changes: 4 additions & 4 deletions hearth/lib/hearth/telemetry/no_op.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,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 Span.
Expand Down Expand Up @@ -61,10 +65,6 @@ def record_exception(exception, attributes: nil); end
class NoOpContextManager < ContextManagerBase
def current; end

def current_span
NoOpSpan.new
end

def attach(context); end

def detach(token); end
Expand Down
40 changes: 28 additions & 12 deletions hearth/lib/hearth/telemetry/otel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module Telemetry
# client = Service::Client.new(telemetry_provider: otel_provider)
class OTelProvider < TelemetryProviderBase
def initialize
unless Hearth::Telemetry.otel_loaded?
unless otel_loaded?
raise ArgumentError,
'Requires the `opentelemetry-sdk` gem to use OTel Provider.'
end
Expand All @@ -44,6 +44,21 @@ def initialize
context_manager: OTelContextManager.new
)
end

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 Tracer Provider, an entry point for
Expand All @@ -57,13 +72,14 @@ def initialize
# Returns a Tracer instance.
#
# @param [optional String] name Tracer name
# @return [Tracer]
# @return [Hearth::Telemetry::OTelTracer]
def tracer(name = nil)
OTelTracer.new(@tracer_provider.tracer(name))
end
end

# OpenTelemetry-based Tracer, responsible for creating spans.
# OpenTelemetry-based Tracer, responsible for creating spans
# and retrieving the current active span.
class OTelTracer < TracerBase
def initialize(tracer)
super()
Expand All @@ -77,7 +93,7 @@ def initialize(tracer)
# @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]
# @return [Hearth::Telemetry::OTelSpan]
def start_span(name, with_parent: nil, attributes: nil, kind: nil)
span = @tracer.start_span(
name,
Expand All @@ -97,12 +113,19 @@ 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
# @return [Span]
# @return [Hearth::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))
end
end

# Returns the current active span.
#
# @return [Hearth::Telemetry::OTelSpan]
def current_span
OTelSpan.new(OpenTelemetry::Trace.current_span)
end
end

# OpenTelemetry-based Span, represents a single operation
Expand Down Expand Up @@ -192,13 +215,6 @@ def current
OpenTelemetry::Context.current
end

# Returns the current span from current context.
#
# @return Span
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.
#
Expand Down
2 changes: 2 additions & 0 deletions hearth/sig/lib/hearth/http/request.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ module Hearth
def remove_query_param: (String name) -> void

def prefix_host: (String prefix) -> void

def span_attributes: () -> Hash[String,String]
end
end
end
2 changes: 2 additions & 0 deletions hearth/sig/lib/hearth/http/response.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ module Hearth
def replace: (Response other) -> self

def reset: () -> void

def span_attributes: () -> Hash[String,String]
end
end
end
Loading
Loading