Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ module Constants
async_exec_prepared
sync_exec_prepared
].freeze

CONNECTION_METHODS = %i[
connect
open
async_connect
].freeze
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def require_dependencies

def patch_client
::PG::Connection.prepend(Patches::Connection)
::PG::Connection.singleton_class.prepend(Patches::Connect)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,65 @@ module OpenTelemetry
module Instrumentation
module PG
module Patches
# Utility methods for setting connection attributes from Connect module
module ConnectionHelper
module_function

def set_connection_attributes(span, conn, config)
attributes = {
'db.system' => 'postgresql',
'db.name' => conn.db,
'db.user' => conn.user
}
attributes['peer.service'] = config[:peer_service] if config[:peer_service]

h = conn.host
if h&.start_with?('/')
attributes['net.sock.family'] = 'unix'
attributes['net.peer.name'] = h
else
attributes['net.transport'] = 'ip_tcp'
attributes['net.peer.name'] = h
attributes['net.peer.port'] = conn.port if defined?(::PG::DEF_PGPORT)
end

attributes.merge!(OpenTelemetry::Instrumentation::PG.attributes)
attributes.compact!

span.add_attributes(attributes)
end
end

# Module to prepend to PG::Connection singleton class for connection initialization
# We override `new` instead of `initialize` because PG::Connection.new is implemented
# as a Ruby method that calls the C-level connect_start, bypassing initialize.
# We also need to override the aliases (open, connect, async_connect) because they
# were aliased before our prepend, so they point to the original method.
# See: https://github.com/ged/ruby-pg/blob/master/lib/pg/connection.rb#L870
module Connect
def new(...)
tracer = OpenTelemetry::Instrumentation::PG::Instrumentation.instance.tracer
config = OpenTelemetry::Instrumentation::PG::Instrumentation.instance.config

tracer.in_span('connect', kind: :client) do |span|
if block_given?
Copy link
Contributor

Choose a reason for hiding this comment

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

Just wanted to confirm that there wasn't a way to simply this.

Does the block not get forwarded when calling super?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It does get forwarded, but we want a special path for block_given? to be able to set span attributes.

                conn = super
                ConnectionHelper.set_connection_attributes(span, conn, config)
                conn

^ the above only returns conn when there's no block given. When the block is given, it doesn't "leak" the connection object outside of the block.

super do |conn|
ConnectionHelper.set_connection_attributes(span, conn, config)
yield conn
end
else
conn = super
ConnectionHelper.set_connection_attributes(span, conn, config)
conn
end
end
end

PG::Constants::CONNECTION_METHODS.each do |method|
alias_method method, :new
end
end

# Module to prepend to PG::Connection for instrumentation
module Connection # rubocop:disable Metrics/ModuleLength
# Capture the first word (including letters, digits, underscores, & '.', ) that follows common table commands
Expand Down
Loading