Skip to content

Commit

Permalink
Observability Adjustments (#217)
Browse files Browse the repository at this point in the history
  • Loading branch information
jterapin authored Aug 23, 2024
1 parent 4e70e58 commit 4853737
Show file tree
Hide file tree
Showing 28 changed files with 165 additions and 153 deletions.
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
{
'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
) 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) && !@body.instance_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

0 comments on commit 4853737

Please sign in to comment.