Skip to content

Rewrite NotificationsTrace#5270

Merged
rmosolgo merged 16 commits intomasterfrom
update-asn
Mar 24, 2025
Merged

Rewrite NotificationsTrace#5270
rmosolgo merged 16 commits intomasterfrom
update-asn

Conversation

@rmosolgo
Copy link
Copy Markdown
Owner

@rmosolgo rmosolgo commented Mar 10, 2025

Trying to update this to handle fiber starts/stops better.

This PR covers ActiveSupport::Notifications, Dry::Notifications, New Relic, Data Dog, and Sentry. I'll address the others in a later PR.

Breaking change to DataDog trace: prepare_span now receives an object instead of a Hash.

  • Do I really need begin_/finish_ methods for parts of execution that don't call out to app code? (no)
  • Update NotificationsTrace
  • Add a test for AS::N trace
  • Migrate other traces to this system - Started with New Relic and Sentry.
  • What about AS::N payloads? (And Dry::Monitor) I want avoid creating that Hash by default, but those will need it for backwards compat.
  • Make sure platform-specific traces still work when there are multiple

end

def instrument(keyword, payload, &block)
raise "Implement #{self.class}#instrument to measure the block"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method instrument missing tests for line 19 (coverage: 0.0)

@platform_resolve_type_key_cache[payload]
when :dataloader_source
@platform_source_class_key_cache[payload.class]
when :parse then self.class::PARSE_NAME
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method name_for missing tests for lines 56, 57, 62, 56, 57, 61 (coverage: 0.75)

attr_reader :keyword, :payload

def start
raise "Implement #{self.class}#start to begin a new event (#{inspect})"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method start missing tests for line 76 (coverage: 0.0)

end

def finish
raise "Implement #{self.class}#finish to end this event (#{inspect})"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method finish missing tests for line 80 (coverage: 0.0)

}.compare_by_identity

def instrument(keyword, payload, &block)
name = EVENT_NAMES.fetch(keyword)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method instrument missing tests for lines 99, 100 (coverage: 0.0)


class Event < NotificationsTrace::Engine::Event
def start
@name = EVENT_NAMES.fetch(@keyword)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method start missing tests for lines 105, 106 (coverage: 0.0)

RUBY

def finish
Dry::Monitor.stop(@name, @payload)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method finish missing tests for line 110 (coverage: 0.0)

# @param trace_resolve_type [Boolean] If `false`, skip tracing `resolve_type?` calls
def initialize(engine:, set_transaction_name: false, trace_scalars: false, trace_authorized: true, trace_resolve_type: true, **rest)
if defined?(Dry::Monitor) && engine == Dry::Monitor
# Backwards compat
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method initialize missing tests for lines 123, 122 (coverage: 0.75)


def lex(query_string:)
@notifications_engine.instrument(:lex, query_string) do
super
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block block missing tests for line 141 (coverage: 0.0)

operation_names = queries.map{|q| operation_name(q) }
span.set_description(operation_names.join(", "))

if queries.size == 1
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block block (span) missing tests for lines 33, 33, 33 (coverage: 0.9444)

selected_op = query.selected_operation
if selected_op
[selected_op.operation_type, selected_op.name].compact.join(' ')
else
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method operation_name missing tests for lines 66, 65 (coverage: 0.6)

def instrument_sentry_execution(platform_key, trace_method, data=nil, &block)
return yield unless Sentry.initialized?
def platform_resolve_type_key(type)
"graphql.resolve_type.#{type.graphql_name}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method platform_resolve_type_key missing tests for line 79 (coverage: 0.0)

result = yield
return result unless span
def platform_source_class_key(source_class)
"graphql.source.#{source_class.name.gsub("::", ".")}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method platform_source_class_key missing tests for line 83 (coverage: 0.0)

end

def instrument(keyword, object, &block)
raise "Implement #{self.class}#instrument to measure the block"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method instrument missing tests for line 20 (coverage: 0.0)

@platform_resolve_type_key_cache[object]
when :dataloader_source
@platform_source_class_key_cache[object.class]
when :parse then self.class::PARSE_NAME
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method name_for missing tests for lines 57, 58, 63, 57, 58, 62 (coverage: 0.75)

attr_reader :keyword, :object

def start
raise "Implement #{self.class}#start to begin a new event (#{inspect})"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method start missing tests for line 77 (coverage: 0.0)

end

def finish
raise "Implement #{self.class}#finish to end this event (#{inspect})"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method finish missing tests for line 81 (coverage: 0.0)

end

def self.create_module(monitor_name)
if !monitor_name.match?(/[a-z]+/)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class method create_module missing tests for lines 87, 88, 87, 87 (coverage: 0.6667)

# @api private
class Adapter
def instrument(keyword, payload, &block)
raise "Implement #{self.class}#instrument to measure the block"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method instrument missing tests for line 13 (coverage: 0.0)

attr_reader :name, :payload

def start
raise "Implement #{self.class}#start to begin a new event (#{inspect})"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method start missing tests for line 31 (coverage: 0.0)

end

def finish
raise "Implement #{self.class}#finish to end this event (#{inspect})"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method finish missing tests for line 35 (coverage: 0.0)

# @api private
class DryMonitorAdapter < Adapter
def instrument(...)
Dry::Monitor.instrument(...)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method instrument missing tests for line 43 (coverage: 0.0)


class Event < Adapter::Event
def start
Dry::Monitor.start(@name, @payload)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method start missing tests for line 48 (coverage: 0.0)

end

def finish
Dry::Monitor.stop(@name, @payload)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method finish missing tests for line 52 (coverage: 0.0)


# @param engine [Class] The notifications engine to use, eg `Dry::Monitor` or `ActiveSupport::Notifications`
def initialize(engine:, **rest)
adapter = if defined?(Dry::Monitor) && engine == Dry::Monitor
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method initialize missing tests for lines 78, 79, 78, 79 (coverage: 0.75)


def lex(**payload)
@notifications.instrument("lex.graphql", payload) do
super
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block block missing tests for line 97 (coverage: 0.0)

end

def finish_notifications_event
if ev = Fiber[CURRENT_EV_KEY]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method finish_notifications_event missing tests for lines 184, 184, 184 (coverage: 0.75)

operation_names = queries.map{|q| operation_name(q) }
span.set_description(operation_names.join(", "))

if queries.size == 1
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block block (span) missing tests for lines 32, 32, 32 (coverage: 0.9444)

selected_op = query.selected_operation
if selected_op
[selected_op.operation_type, selected_op.name].compact.join(' ')
else
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method operation_name missing tests for lines 65, 64 (coverage: 0.6)

def instrument_sentry_execution(platform_key, trace_method, data=nil, &block)
return yield unless Sentry.initialized?
def platform_resolve_type_key(type)
"graphql.resolve_type.#{type.graphql_name}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method platform_resolve_type_key missing tests for line 78 (coverage: 0.0)

result = yield
return result unless span
def platform_source_class_key(source_class)
"graphql.source.#{source_class.name.gsub("::", ".")}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance method platform_source_class_key missing tests for line 82 (coverage: 0.0)

@rmosolgo rmosolgo added this to the 2.4.16 milestone Mar 24, 2025
@rmosolgo rmosolgo merged commit 48d110e into master Mar 24, 2025
15 checks passed
@rmosolgo rmosolgo removed this from the 2.4.16 milestone Mar 25, 2025
@rmosolgo rmosolgo deleted the update-asn branch March 25, 2025 11:00
@rmosolgo rmosolgo restored the update-asn branch March 25, 2025 11:01
@zvkemp
Copy link
Copy Markdown
Contributor

zvkemp commented Aug 11, 2025

Were the execute_query, execute_query_lazy, and analyze_query instrumentations intentionally omitted from this refactor? We've noticed those are no longer published to our ActiveSupport::Notifications subscriber.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants