class OpenTelemetry::Instrumentation::ActionView::SpanSubscriber

The SpanSubscriber is a special ActiveSupport::Notification subscription handler which turns notifications into generic spans, taking care to handle context appropriately.

Constants

ALWAYS_VALID_PAYLOAD_TYPES

Public Class Methods

new(name:, tracer:) click to toggle source
# File lib/opentelemetry/instrumentation/action_view/span_subscriber.rb, line 16
def initialize(name:, tracer:)
  @span_name = name.split('.')[0..1].reverse.join(' ').freeze
  @tracer = tracer
end

Public Instance Methods

finish(_name, _id, payload) click to toggle source
# File lib/opentelemetry/instrumentation/action_view/span_subscriber.rb, line 30
def finish(_name, _id, payload) # rubocop:disable Metrics/AbcSize
  span = payload.delete(:__opentelemetry_span)
  token = payload.delete(:__opentelemetry_ctx_token)
  return unless span && token

  payload = transform_payload(payload)
  attrs = payload.map do |k, v|
    [k.to_s, sanitized_value(v)] if valid_payload_key?(k) && valid_payload_value?(v)
  end
  span.add_attributes(attrs.compact.to_h)

  if (e = payload[:exception_object])
    span.record_exception(e)
    span.status = OpenTelemetry::Trace::Status.error("Unhandled exception of type: #{e.class}")
  end

  span.finish
  OpenTelemetry::Context.detach(token)
end
start(_name, _id, payload) click to toggle source
# File lib/opentelemetry/instrumentation/action_view/span_subscriber.rb, line 21
def start(_name, _id, payload)
  span = @tracer.start_span(@span_name, kind: :internal)
  token = OpenTelemetry::Context.attach(
    OpenTelemetry::Trace.context_with_span(span)
  )

  [span, token]
end

Private Instance Methods

instrumentation_config() click to toggle source
# File lib/opentelemetry/instrumentation/action_view/span_subscriber.rb, line 52
def instrumentation_config
  ActionView::Instrumentation.instance.config
end
sanitized_value(value) click to toggle source

We'll accept symbols as values, but stringify them; and we'll stringify symbols within an array.

# File lib/opentelemetry/instrumentation/action_view/span_subscriber.rb, line 77
def sanitized_value(value)
  if value.is_a?(Array)
    value.map { |v| v.is_a?(Symbol) ? v.to_s : v }
  elsif value.is_a?(Symbol)
    value.to_s
  else
    value
  end
end
transform_payload(payload) click to toggle source
# File lib/opentelemetry/instrumentation/action_view/span_subscriber.rb, line 56
def transform_payload(payload)
  return payload if instrumentation_config[:notification_payload_transform].nil?

  instrumentation_config[:notification_payload_transform].call(payload)
end
valid_payload_key?(key) click to toggle source
# File lib/opentelemetry/instrumentation/action_view/span_subscriber.rb, line 62
def valid_payload_key?(key)
  %i[exception exception_object].none?(key) && instrumentation_config[:disallowed_notification_payload_keys].none?(key)
end
valid_payload_value?(value) click to toggle source
# File lib/opentelemetry/instrumentation/action_view/span_subscriber.rb, line 66
def valid_payload_value?(value)
  if value.is_a?(Array)
    return true if value.empty?

    value.map(&:class).uniq.size == 1 && ALWAYS_VALID_PAYLOAD_TYPES.any? { |t| value.first.is_a?(t) }
  else
    ALWAYS_VALID_PAYLOAD_TYPES.any? { |t| value.is_a?(t) }
  end
end