Skip to content

Commit d2fc214

Browse files
committed
wip
1 parent 24a4a7c commit d2fc214

File tree

3 files changed

+134
-0
lines changed

3 files changed

+134
-0
lines changed

instrumentation/pg/lib/opentelemetry/instrumentation/pg/instrumentation.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def require_dependencies
4040

4141
def patch_client
4242
::PG::Connection.prepend(Patches::Connection)
43+
::PG::Connection.singleton_class.prepend(Patches::ClassMethods)
4344
end
4445
end
4546
end

instrumentation/pg/lib/opentelemetry/instrumentation/pg/patches/connection.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,43 @@ module OpenTelemetry
1212
module Instrumentation
1313
module PG
1414
module Patches
15+
# Module to prepend to PG::Connection singleton class for connection initialization
16+
module ClassMethods
17+
def new(*args, &block)
18+
tracer = PG::Instrumentation.instance.tracer
19+
config = PG::Instrumentation.instance.config
20+
21+
tracer.in_span('connect', kind: :client) do |span|
22+
conn = super
23+
24+
# Build attributes from the established connection
25+
attributes = {
26+
'db.system' => 'postgresql',
27+
'db.name' => conn.db,
28+
'db.user' => conn.user
29+
}
30+
attributes['peer.service'] = config[:peer_service] if config[:peer_service]
31+
32+
# Add transport attributes
33+
h = conn.host
34+
if h&.start_with?('/')
35+
attributes['net.sock.family'] = 'unix'
36+
attributes['net.peer.name'] = h
37+
else
38+
attributes['net.transport'] = 'ip_tcp'
39+
attributes['net.peer.name'] = h
40+
attributes['net.peer.port'] = conn.port if defined?(::PG::DEF_PGPORT)
41+
end
42+
43+
attributes.merge!(OpenTelemetry::Instrumentation::PG.attributes)
44+
attributes.compact!
45+
46+
span.set_attributes(attributes)
47+
conn
48+
end
49+
end
50+
end
51+
1552
# Module to prepend to PG::Connection for instrumentation
1653
module Connection # rubocop:disable Metrics/ModuleLength
1754
# Capture the first word (including letters, digits, underscores, & '.', ) that follows common table commands

instrumentation/pg/test/opentelemetry/instrumentation/pg/instrumentation_test.rb

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,102 @@
6464
_(exporter.finished_spans.size).must_equal 0
6565
end
6666

67+
describe 'connection initialization' do
68+
it 'creates a connect span when establishing a connection' do
69+
PG::Connection.open(
70+
host: host,
71+
port: port,
72+
user: user,
73+
dbname: dbname,
74+
password: password
75+
).close
76+
77+
_(span.name).must_equal 'connect'
78+
_(span.kind).must_equal :client
79+
_(span.attributes['db.system']).must_equal 'postgresql'
80+
_(span.attributes['db.name']).must_equal dbname
81+
_(span.attributes['db.user']).must_equal user
82+
_(span.attributes['net.peer.name']).must_equal host
83+
_(span.attributes['net.transport']).must_equal 'ip_tcp'
84+
end
85+
86+
it 'creates a connect span using PG::Connection.new' do
87+
PG::Connection.new(
88+
host: host,
89+
port: port,
90+
user: user,
91+
dbname: dbname,
92+
password: password
93+
).close
94+
95+
_(span.name).must_equal 'connect'
96+
_(span.kind).must_equal :client
97+
_(span.attributes['db.system']).must_equal 'postgresql'
98+
_(span.attributes['db.name']).must_equal dbname
99+
end
100+
101+
it 'creates a connect span using PG.connect' do
102+
PG.connect(
103+
host: host,
104+
port: port,
105+
user: user,
106+
dbname: dbname,
107+
password: password
108+
).close
109+
110+
_(span.name).must_equal 'connect'
111+
_(span.kind).must_equal :client
112+
_(span.attributes['db.system']).must_equal 'postgresql'
113+
end
114+
115+
it 'accepts peer service name from config for connection' do
116+
instrumentation.instance_variable_set(:@installed, false)
117+
instrumentation.install(peer_service: 'readonly:postgres')
118+
119+
PG::Connection.open(
120+
host: host,
121+
port: port,
122+
user: user,
123+
dbname: dbname,
124+
password: password
125+
).close
126+
127+
_(span.attributes['peer.service']).must_equal 'readonly:postgres'
128+
end
129+
130+
it 'sets unix socket attributes when connecting via socket' do
131+
skip 'socket test requires no host/port' if host || port
132+
133+
PG::Connection.open(
134+
user: user,
135+
dbname: dbname,
136+
password: password
137+
).close
138+
139+
_(span.name).must_equal 'connect'
140+
_(span.attributes['db.system']).must_equal 'postgresql'
141+
_(span.attributes['net.sock.family']).must_equal 'unix'
142+
_(span.attributes['net.peer.name']).must_match %r{^/}
143+
end
144+
145+
it 'records connection errors' do
146+
expect do
147+
PG::Connection.open(
148+
host: host,
149+
port: port,
150+
user: 'invalid_user',
151+
dbname: dbname,
152+
password: 'wrong_password'
153+
)
154+
end.must_raise PG::ConnectionBad
155+
156+
_(span.name).must_equal 'connect'
157+
_(span.status.code).must_equal OpenTelemetry::Trace::Status::ERROR
158+
_(span.events.first.name).must_equal 'exception'
159+
_(span.events.first.attributes['exception.type']).must_equal 'PG::ConnectionBad'
160+
end
161+
end
162+
67163
it 'accepts peer service name from config' do
68164
instrumentation.instance_variable_set(:@installed, false)
69165
instrumentation.install(peer_service: 'readonly:postgres')

0 commit comments

Comments
 (0)