Skip to content

Commit a11d8b1

Browse files
yoheykahayworth
andauthored
feat: Add with_attributes context propagation for PG instrumentation (#101)
* feat: Add `with_attributes` context propagation for PG instrumentation * remove context_with_attributes * add test cases Co-authored-by: Andrew Hayworth <[email protected]>
1 parent 67d2294 commit a11d8b1

File tree

4 files changed

+77
-0
lines changed

4 files changed

+77
-0
lines changed

instrumentation/pg/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ OpenTelemetry::SDK.configure do |c|
3030
end
3131
```
3232

33+
The `PG` instrumentation allows the user to supply additional attributes via the `with_attributes` method. This makes it possible to supply additional attributes on PG spans. Attributes supplied in `with_attributes` supersede those automatically generated within `PG`'s automatic instrumentation. If you supply a `db.statement` attribute in `with_attributes`, this library's `:db_statement` configuration will not be applied.
34+
35+
```ruby
36+
require 'opentelemetry/instrumentation/pg'
37+
38+
conn = PG::Connection.open(host: "localhost", user: "root", dbname: "postgres")
39+
OpenTelemetry::Instrumentation::PG.with_attributes('pizzatoppings' => 'mushrooms') do
40+
conn.exec("SELECT 1")
41+
end
42+
```
43+
3344
### Configuration options
3445

3546
```ruby

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,35 @@ module OpenTelemetry
1111
module Instrumentation
1212
# Contains the OpenTelemetry instrumentation for the Pg gem
1313
module PG
14+
extend self
15+
16+
CURRENT_ATTRIBUTES_KEY = Context.create_key('pg-attributes-hash')
17+
18+
private_constant :CURRENT_ATTRIBUTES_KEY
19+
20+
# Returns the attributes hash representing the postgres client context found
21+
# in the optional context or the current context if none is provided.
22+
#
23+
# @param [optional Context] context The context to lookup the current
24+
# attributes hash. Defaults to Context.current
25+
def attributes(context = nil)
26+
context ||= Context.current
27+
context.value(CURRENT_ATTRIBUTES_KEY) || {}
28+
end
29+
30+
# Activates/deactivates the merged attributes hash within the current Context,
31+
# which makes the "current attributes hash" available implicitly.
32+
#
33+
# On exit, the attributes hash that was active before calling this method
34+
# will be reactivated.
35+
#
36+
# @param [Span] span the span to activate
37+
# @yield [Hash, Context] yields attributes hash and a context containing the
38+
# attributes hash to the block.
39+
def with_attributes(attributes_hash)
40+
attributes_hash = attributes.merge(attributes_hash)
41+
Context.with_value(CURRENT_ATTRIBUTES_KEY, attributes_hash) { |c, h| yield h, c }
42+
end
1443
end
1544
end
1645
end

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def span_attrs(kind, *args)
8585

8686
attrs = { 'db.operation' => validated_operation(operation), 'db.postgresql.prepared_statement_name' => statement_name }
8787
attrs['db.statement'] = sql unless config[:db_statement] == :omit
88+
attrs.merge!(OpenTelemetry::Instrumentation::PG.attributes)
8889
attrs.reject! { |_, v| v.nil? }
8990

9091
[span_name(operation), client_attributes.merge(attrs)]

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,42 @@
6868
_(span.attributes['peer.service']).must_equal 'readonly:postgres'
6969
end
7070

71+
describe '.attributes' do
72+
let(:attributes) do
73+
{
74+
'db.name' => 'pg',
75+
'db.statement' => 'foobar',
76+
'db.operation' => 'PREPARE FOR SELECT 1',
77+
'db.postgresql.prepared_statement_name' => 'bar',
78+
'net.peer.ip' => '192.168.0.1',
79+
'peer.service' => 'example:custom'
80+
}
81+
end
82+
83+
it 'returns an empty hash by default' do
84+
_(OpenTelemetry::Instrumentation::PG.attributes).must_equal({})
85+
end
86+
87+
it 'returns the current attributes hash' do
88+
OpenTelemetry::Instrumentation::PG.with_attributes(attributes) do
89+
_(OpenTelemetry::Instrumentation::PG.attributes).must_equal(attributes)
90+
end
91+
end
92+
93+
it 'sets span attributes according to with_attributes hash' do
94+
OpenTelemetry::Instrumentation::PG.with_attributes(attributes) do
95+
client.prepare('foo', 'SELECT 1')
96+
end
97+
98+
_(span.attributes['db.name']).must_equal 'pg'
99+
_(span.attributes['db.statement']).must_equal 'foobar'
100+
_(span.attributes['db.operation']).must_equal 'PREPARE FOR SELECT 1'
101+
_(span.attributes['db.postgresql.prepared_statement_name']).must_equal 'bar'
102+
_(span.attributes['net.peer.ip']).must_equal '192.168.0.1'
103+
_(span.attributes['peer.service']).must_equal 'example:custom'
104+
end
105+
end
106+
71107
%i[exec query sync_exec async_exec].each do |method|
72108
it "after request (with method: #{method})" do
73109
client.send(method, 'SELECT 1')

0 commit comments

Comments
 (0)