Skip to content

Commit

Permalink
feat: add option to configure span name (#222)
Browse files Browse the repository at this point in the history
* feat: add option to configure span name

* Fix existing tests

* Appease the cop

* Add some tests

* Cop caught my extra whitespace :-(
  • Loading branch information
fbogsany authored Dec 20, 2022
1 parent 512e441 commit 99026b1
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base
option :peer_service, default: nil, validate: :string
option :enable_sql_obfuscation, default: false, validate: :boolean
option :db_statement, default: :include, validate: %I[omit include obfuscate]
option :span_name, default: :statement_type, validate: %I[statement_type db_name db_operation_and_name]

private

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,23 @@ def detect_unmatched_pairs(obfuscated)
%r{'|"|\/\*|\*\/}.match(obfuscated)
end

def database_span_name(sql)
# Setting span name to the SQL query without obfuscation would
# result in PII + cardinality issues.
# First attempt to infer the statement type then fallback to
# current Otel approach {database.component_name}.{database_instance_name}
# /~https://github.com/open-telemetry/opentelemetry-python/blob/39fa078312e6f41c403aa8cad1868264011f7546/ext/opentelemetry-ext-dbapi/tests/test_dbapi_integration.py#L53
# This creates span names like mysql.default, mysql.replica, postgresql.staging etc.

statement_type = extract_statement_type(sql)

return statement_type unless statement_type.nil?

# fallback
database_name ? "mysql.#{database_name}" : 'mysql'
def database_span_name(sql) # rubocop:disable Metrics/CyclomaticComplexity
case config[:span_name]
when :statement_type
extract_statement_type(sql)
when :db_name
database_name
when :db_operation_and_name
op = OpenTelemetry::Instrumentation::Mysql2.attributes['db.operation']
name = database_name
if op && name
"#{op} #{name}"
elsif op
op
elsif name
name
end
end || 'mysql'
end

def database_name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
client.query('DESELECT 1')
end.must_raise Mysql2::Error

_(span.name).must_equal 'mysql.mysql'
_(span.name).must_equal 'mysql'
_(span.attributes['db.system']).must_equal 'mysql'
_(span.attributes['db.name']).must_equal 'mysql'
_(span.attributes['db.statement']).must_equal 'DESELECT 1'
Expand Down Expand Up @@ -219,7 +219,7 @@
client.query(sql)
end.must_raise Mysql2::Error

_(span.name).must_equal 'mysql.mysql'
_(span.name).must_equal 'mysql'
_(span.attributes['db.statement']).must_equal obfuscated_sql
end
end
Expand Down Expand Up @@ -309,6 +309,136 @@
end
end
end

describe 'when span_name is set as statement_type' do
it 'sets span name to statement type' do
OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_INSTRUMENTATION_MYSQL2_CONFIG_OPTS' => 'span_name=statement_type') do
instrumentation.instance_variable_set(:@installed, false)
instrumentation.install

sql = "SELECT * from users where users.id = 1 and users.email = 'test@test.com'"
expect do
client.query(sql)
end.must_raise Mysql2::Error

_(span.name).must_equal 'select'
end
end

it 'sets span name to mysql when statement type is not recognized' do
OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_INSTRUMENTATION_MYSQL2_CONFIG_OPTS' => 'span_name=statement_type') do
instrumentation.instance_variable_set(:@installed, false)
instrumentation.install

sql = 'DESELECT 1'
expect do
client.query(sql)
end.must_raise Mysql2::Error

_(span.name).must_equal 'mysql'
end
end
end

describe 'when span_name is set as db_name' do
it 'sets span name to db name' do
OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_INSTRUMENTATION_MYSQL2_CONFIG_OPTS' => 'span_name=db_name') do
instrumentation.instance_variable_set(:@installed, false)
instrumentation.install

sql = "SELECT * from users where users.id = 1 and users.email = 'test@test.com'"
expect do
client.query(sql)
end.must_raise Mysql2::Error

_(span.name).must_equal 'mysql' # TODO: change the db name so we can distinguish it from the default
end
end

describe 'when db name is nil' do
let(:database) { nil }

it 'sets span name to mysql' do
OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_INSTRUMENTATION_MYSQL2_CONFIG_OPTS' => 'span_name=db_name') do
instrumentation.instance_variable_set(:@installed, false)
instrumentation.install

sql = "SELECT * from users where users.id = 1 and users.email = 'test@test.com'"
expect do
client.query(sql)
end.must_raise Mysql2::Error

_(span.name).must_equal 'mysql'
end
end
end
end

describe 'when span_name is set as db_operation_and_name' do
it 'sets span name to db operation and name' do
OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_INSTRUMENTATION_MYSQL2_CONFIG_OPTS' => 'span_name=db_operation_and_name') do
instrumentation.instance_variable_set(:@installed, false)
instrumentation.install

sql = "SELECT * from users where users.id = 1 and users.email = 'test@test.com'"
OpenTelemetry::Instrumentation::Mysql2.with_attributes('db.operation' => 'foo') do
expect do
client.query(sql)
end.must_raise Mysql2::Error
end

_(span.name).must_equal 'foo mysql'
end
end

it 'sets span name to db name when db.operation is not set' do
OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_INSTRUMENTATION_MYSQL2_CONFIG_OPTS' => 'span_name=db_operation_and_name') do
instrumentation.instance_variable_set(:@installed, false)
instrumentation.install

sql = "SELECT * from users where users.id = 1 and users.email = 'test@test.com'"
expect do
client.query(sql)
end.must_raise Mysql2::Error

_(span.name).must_equal 'mysql'
end
end

describe 'when db name is nil' do
let(:database) { nil }

it 'sets span name to db operation' do
OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_INSTRUMENTATION_MYSQL2_CONFIG_OPTS' => 'span_name=db_operation_and_name') do
instrumentation.instance_variable_set(:@installed, false)
instrumentation.install

sql = "SELECT * from users where users.id = 1 and users.email = 'test@test.com'"
OpenTelemetry::Instrumentation::Mysql2.with_attributes('db.operation' => 'foo') do
expect do
client.query(sql)
end.must_raise Mysql2::Error
end

_(span.name).must_equal 'foo'
end
end

it 'sets span name to mysql when db.operation is not set' do
OpenTelemetry::TestHelpers.with_env('OTEL_RUBY_INSTRUMENTATION_MYSQL2_CONFIG_OPTS' => 'span_name=db_name') do
instrumentation.instance_variable_set(:@installed, false)
instrumentation.install

sql = "SELECT * from users where users.id = 1 and users.email = 'test@test.com'"
expect do
client.query(sql)
end.must_raise Mysql2::Error

_(span.name).must_equal 'mysql'
end
end
end
end
end
end unless ENV['OMIT_SERVICES']
end

0 comments on commit 99026b1

Please sign in to comment.