Skip to content

Commit

Permalink
Normalize span names for easier aggregation analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
ravangen committed Jan 24, 2023
1 parent d853703 commit b6b0e13
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 5 deletions.
5 changes: 5 additions & 0 deletions instrumentation/graphql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ OpenTelemetry::SDK.configure do |c|
enable_platform_authorized: false,
# enable_platform_resolve_type maps to the resolve_type and resolve_type_lazy keys
enable_platform_resolve_type: false

# Controls if platform tracing (field/authorized/resolve_type)
# should use the legacy span names (e.g. "MyType.myField") or the
# new normalized span names (e.g. "graphql.execute_field").
legacy_platform_span_names: false
}
end
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,19 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base
# The enable_platform_resolve_type key expects a boolean value,
# and enables the tracing of "resolve_type" and "resolve_type_lazy".
#
# The legacy_platform_span_names key expects a boolean value,
# and controls if platform tracing (field/authorized/resolve_type)
# should use the legacy span names (e.g. "MyType.myField") or the
# new normalized span names (e.g. "graphql.execute_field").
#
# The schemas key expects an array of Schemas, and is used to specify
# which schemas are to be instrumented. If this value is not supplied
# the default behaviour is to instrument all schemas.
option :schemas, default: [], validate: :array
option :enable_platform_field, default: false, validate: :boolean
option :enable_platform_authorized, default: false, validate: :boolean
option :enable_platform_resolve_type, default: false, validate: :boolean
option :legacy_platform_span_names, default: true, validate: :boolean

private

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,31 @@ def platform_trace(platform_key, key, data) # rubocop:disable Metrics/Cyclomatic
def platform_field_key(type, field)
return unless config[:enable_platform_field]

"#{type.graphql_name}.#{field.graphql_name}"
if config[:legacy_platform_span_names]
"#{type.graphql_name}.#{field.graphql_name}"
else
'graphql.execute_field'
end
end

def platform_authorized_key(type)
return unless config[:enable_platform_authorized]

"#{type.graphql_name}.authorized"
if config[:legacy_platform_span_names]
"#{type.graphql_name}.authorized"
else
'graphql.authorized'
end
end

def platform_resolve_type_key(type)
return unless config[:enable_platform_resolve_type]

"#{type.graphql_name}.resolve_type"
if config[:legacy_platform_span_names]
"#{type.graphql_name}.resolve_type"
else
'graphql.resolve_type'
end
end

private
Expand All @@ -73,6 +85,16 @@ def config
def attributes_for(key, data)
attributes = {}
case key
when 'execute_field', 'execute_field_lazy'
attributes['graphql.field.parent'] = data[:owner].graphql_name # owner is the concrete type, not interface
attributes['graphql.field.name'] = data[:field].graphql_name
attributes['graphql.lazy'] = key == 'execute_field_lazy'
when 'authorized', 'authorized_lazy'
attributes['graphql.type.name'] = data.fetch(:type).graphql_name
attributes['graphql.lazy'] = key == 'authorized_lazy'
when 'resolve_type', 'resolve_type_lazy'
attributes['graphql.type.name'] = data.fetch(:type).graphql_name
attributes['graphql.lazy'] = key == 'resolve_type_lazy'
when 'execute_query'
attributes['graphql.operation.name'] = data[:query].selected_operation_name if data[:query].selected_operation_name
attributes['graphql.operation.type'] = data[:query].selected_operation.operation_type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,35 @@
end
end

describe 'when platform_field is enabled without legacy naming' do
let(:config) { { enable_platform_field: true, legacy_platform_span_names: false } }

it 'traces execute_field' do
SomeGraphQLAppSchema.execute(query_string, variables: { id: 1 })

span = spans.find do |s|
s.name == 'graphql.execute_field' &&
s.attributes['graphql.field.parent'] == 'Query' &&
s.attributes['graphql.field.name'] == 'resolvedField'
end
_(span).wont_be_nil
end

it 'includes attributes' do
expected_attributes = {
'graphql.field.parent' => 'Car', # type name, not interface
'graphql.field.name' => 'model',
'graphql.lazy' => false
}

SomeGraphQLAppSchema.execute('{ vehicle { model } }')

span = spans.find { |s| s.name == 'graphql.execute_field' && s.attributes['graphql.field.name'] == 'model' }
_(span).wont_be_nil
_(span.attributes.to_h).must_equal(expected_attributes)
end
end

describe 'when platform_authorized is enabled' do
let(:config) { { enable_platform_authorized: true } }

Expand All @@ -143,6 +172,41 @@
end
end

describe 'when platform_authorized is enabled without legacy naming' do
let(:config) { { enable_platform_authorized: true, legacy_platform_span_names: false } }

it 'traces .authorized' do
skip unless supports_authorized_and_resolved_types?
SomeGraphQLAppSchema.execute(query_string, variables: { id: 1 })

span = spans.find do |s|
s.name == 'graphql.authorized' &&
s.attributes['graphql.type.name'] == 'Query'
end
_(span).wont_be_nil

span = spans.find do |s|
s.name == 'graphql.authorized' &&
s.attributes['graphql.type.name'] == 'SlightlyComplex'
end
_(span).wont_be_nil
end

it 'includes attributes' do
skip unless supports_authorized_and_resolved_types?
expected_attributes = {
'graphql.type.name' => 'OtherQuery',
'graphql.lazy' => false
}

SomeOtherGraphQLAppSchema.execute('{ simpleField }')

span = spans.find { |s| s.name == 'graphql.authorized' }
_(span).wont_be_nil
_(span.attributes.to_h).must_equal(expected_attributes)
end
end

describe 'when platform_resolve_type is enabled' do
let(:config) { { enable_platform_resolve_type: true } }

Expand All @@ -155,6 +219,32 @@
end
end

describe 'when platform_resolve_type is enabled without legacy naming' do
let(:config) { { enable_platform_resolve_type: true, legacy_platform_span_names: false } }

it 'traces .resolve_type' do
skip unless supports_authorized_and_resolved_types?
SomeGraphQLAppSchema.execute('{ vehicle { __typename } }')

span = spans.find { |s| s.name == 'graphql.resolve_type' && s.attributes['graphql.type.name'] == 'Vehicle' }
_(span).wont_be_nil
end

it 'includes attributes' do
skip unless supports_authorized_and_resolved_types?
expected_attributes = {
'graphql.type.name' => 'Vehicle',
'graphql.lazy' => false
}

SomeGraphQLAppSchema.execute('{ vehicle { __typename } }')

span = spans.find { |s| s.name == 'graphql.resolve_type' }
_(span).wont_be_nil
_(span.attributes.to_h).must_equal(expected_attributes)
end
end

it 'traces validate with events' do
SomeGraphQLAppSchema.execute(
<<-GRAPHQL
Expand Down Expand Up @@ -182,11 +272,13 @@ def supports_authorized_and_resolved_types?
end

module Old
Truck = Struct.new(:price)
Truck = Struct.new(:price, :model)
end

module Vehicle
include GraphQL::Schema::Interface

field :model, String, null: true, trace: true # Allow for this scalar to be traced
end

class Car < GraphQL::Schema::Object
Expand Down Expand Up @@ -222,7 +314,7 @@ class QueryType < GraphQL::Schema::Object
field :vehicle, Vehicle, null: true

def vehicle
Old::Truck.new(50)
Old::Truck.new(50, 'Model T')
end

def simple_field
Expand Down

0 comments on commit b6b0e13

Please sign in to comment.