Skip to content

Commit

Permalink
feat: instrument mysql2 prepare statement (#862)
Browse files Browse the repository at this point in the history
* feat: instrument prepare for mysql2 lib

* revision
  • Loading branch information
xuan-cao-swi authored Feb 14, 2024
1 parent c048b14 commit 5d40562
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,40 @@ module Patches
# Module to prepend to Mysql2::Client for instrumentation
module Client
def query(sql, options = {})
attributes = client_attributes
tracer.in_span(
_otel_span_name(sql),
attributes: _otel_span_attributes(sql),
kind: :client
) do
super(sql, options)
end
end

def prepare(sql)
tracer.in_span(
_otel_span_name(sql),
attributes: _otel_span_attributes(sql),
kind: :client
) do
super(sql)
end
end

private

def _otel_span_name(sql)
OpenTelemetry::Helpers::MySQL.database_span_name(
sql,
OpenTelemetry::Instrumentation::Mysql2.attributes[
SemanticConventions::Trace::DB_OPERATION
],
_otel_database_name,
config
)
end

def _otel_span_attributes(sql)
attributes = _otel_client_attributes
case config[:db_statement]
when :include
attributes[SemanticConventions::Trace::DB_STATEMENT] = sql
Expand All @@ -24,30 +57,18 @@ def query(sql, options = {})
sql, obfuscation_limit: config[:obfuscation_limit], adapter: :mysql
)
end
tracer.in_span(
OpenTelemetry::Helpers::MySQL.database_span_name(
sql,
OpenTelemetry::Instrumentation::Mysql2.attributes[
SemanticConventions::Trace::DB_OPERATION
],
database_name,
config
),
attributes: attributes.merge!(OpenTelemetry::Instrumentation::Mysql2.attributes),
kind: :client
) do
super(sql, options)
end
end

private
attributes.merge!(OpenTelemetry::Instrumentation::Mysql2.attributes)
attributes.compact!
attributes
end

def database_name
def _otel_database_name
# https://github.com/brianmario/mysql2/blob/ca08712c6c8ea672df658bb25b931fea22555f27/lib/mysql2/client.rb#L78
(query_options[:database] || query_options[:dbname] || query_options[:db])&.to_s
end

def client_attributes
def _otel_client_attributes
# The client specific attributes can be found via the query_options instance variable
# exposed on the mysql2 Client
# https://github.com/brianmario/mysql2/blob/ca08712c6c8ea672df658bb25b931fea22555f27/lib/mysql2/client.rb#L25-L26
Expand All @@ -59,8 +80,9 @@ def client_attributes
SemanticConventions::Trace::NET_PEER_NAME => host,
SemanticConventions::Trace::NET_PEER_PORT => port
}
attributes[SemanticConventions::Trace::DB_NAME] = database_name if database_name
attributes[SemanticConventions::Trace::PEER_SERVICE] = config[:peer_service] if config[:peer_service]

attributes[SemanticConventions::Trace::DB_NAME] = _otel_database_name
attributes[SemanticConventions::Trace::PEER_SERVICE] = config[:peer_service]
attributes
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,53 @@
end
end

describe 'prepare statement' do
it 'after requests with prepare' do
client.prepare('SELECT 1')

_(span.name).must_equal 'select'
_(span.attributes['db.system']).must_equal 'mysql'
_(span.attributes['db.name']).must_equal 'mysql'
_(span.attributes['db.statement']).must_equal 'SELECT 1'
_(span.attributes['net.peer.name']).must_equal host.to_s
_(span.attributes['net.peer.port']).must_equal port.to_s
end

it 'after requests with prepare select ?' do
client.prepare('SELECT ?')

_(span.name).must_equal 'select'
_(span.attributes['db.system']).must_equal 'mysql'
_(span.attributes['db.name']).must_equal 'mysql'
_(span.attributes['db.statement']).must_equal 'SELECT ?'
_(span.attributes['net.peer.name']).must_equal host.to_s
_(span.attributes['net.peer.port']).must_equal port.to_s
end

it 'query ? sequences for db.statement with prepare' do
sql = 'SELECT * from users where users.id = ? and users.email = ?'
expect do
client.prepare(sql)
end.must_raise Mysql2::Error

_(span.attributes['db.system']).must_equal 'mysql'
_(span.attributes['db.name']).must_equal 'mysql'
_(span.name).must_equal 'select'
_(span.attributes['db.statement']).must_equal sql
_(span.attributes['net.peer.name']).must_equal host.to_s
_(span.attributes['net.peer.port']).must_equal port.to_s
end

it 'query invalid byte sequences for db.statement without prepare' do
sql = 'SELECT * from users where users.id = ? and users.email = ?'
expect do
client.query(sql)
end.must_raise Mysql2::Error

_(span.events[0].attributes['exception.message'].slice(0, 37)).must_equal 'You have an error in your SQL syntax;'
end
end

it 'after requests' do
client.query('SELECT 1')

Expand Down

0 comments on commit 5d40562

Please sign in to comment.