Skip to content

Commit 7f0d5ae

Browse files
authored
Merge pull request #188 from lonewolf3739/asyncpg-semantic-conv
2 parents 9334eef + 028ccaa commit 7f0d5ae

File tree

3 files changed

+89
-129
lines changed

3 files changed

+89
-129
lines changed

instrumentation/opentelemetry-instrumentation-asyncpg/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
- Update asyncpg instrumentation to follow semantic conventions
6+
([#188](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/188))
7+
58
## Version 0.12b0
69

710
Released 2020-08-14

instrumentation/opentelemetry-instrumentation-asyncpg/src/opentelemetry/instrumentation/asyncpg/__init__.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,30 @@
4949

5050

5151
def _hydrate_span_from_args(connection, query, parameters) -> dict:
52-
span_attributes = {"db.type": "sql"}
52+
"""Get network and database attributes from connection."""
53+
span_attributes = {"db.system": "postgresql"}
54+
55+
# connection contains _params attribute which is a namedtuple ConnectionParameters.
56+
# https://github.com/MagicStack/asyncpg/blob/master/asyncpg/connection.py#L68
5357

5458
params = getattr(connection, "_params", None)
55-
span_attributes["db.instance"] = getattr(params, "database", None)
56-
span_attributes["db.user"] = getattr(params, "user", None)
59+
dbname = getattr(params, "database", None)
60+
if dbname:
61+
span_attributes["db.name"] = dbname
62+
user = getattr(params, "user", None)
63+
if user:
64+
span_attributes["db.user"] = user
65+
66+
# connection contains _addr attribute which is either a host/port tuple, or unix socket string
67+
# https://magicstack.github.io/asyncpg/current/_modules/asyncpg/connection.html
68+
addr = getattr(connection, "_addr", None)
69+
if isinstance(addr, tuple):
70+
span_attributes["net.peer.name"] = addr[0]
71+
span_attributes["net.peer.ip"] = addr[1]
72+
span_attributes["net.transport"] = "IP.TCP"
73+
elif isinstance(addr, str):
74+
span_attributes["net.peer.name"] = addr
75+
span_attributes["net.transport"] = "Unix"
5776

5877
if query is not None:
5978
span_attributes["db.statement"] = query
@@ -105,10 +124,10 @@ async def _do_execute(self, func, instance, args, kwargs):
105124
tracer = getattr(asyncpg, _APPLIED)
106125

107126
exception = None
127+
params = getattr(instance, "_params", {})
128+
name = args[0] if args[0] else params.get("database", "postgresql")
108129

109-
with tracer.start_as_current_span(
110-
"postgresql", kind=SpanKind.CLIENT
111-
) as span:
130+
with tracer.start_as_current_span(name, kind=SpanKind.CLIENT) as span:
112131
if span.is_recording():
113132
span_attributes = _hydrate_span_from_args(
114133
instance,

tests/opentelemetry-docker-tests/tests/asyncpg/test_asyncpg_functional.py

Lines changed: 61 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
from opentelemetry.test.test_base import TestBase
88
from opentelemetry.trace.status import StatusCode
99

10-
POSTGRES_HOST = os.getenv("POSTGRESQL_HOST ", "localhost")
11-
POSTGRES_PORT = int(os.getenv("POSTGRESQL_PORT ", "5432"))
12-
POSTGRES_DB_NAME = os.getenv("POSTGRESQL_DB_NAME ", "opentelemetry-tests")
13-
POSTGRES_PASSWORD = os.getenv("POSTGRESQL_HOST ", "testpassword")
14-
POSTGRES_USER = os.getenv("POSTGRESQL_HOST ", "testuser")
10+
POSTGRES_HOST = os.getenv("POSTGRESQL_HOST", "localhost")
11+
POSTGRES_PORT = int(os.getenv("POSTGRESQL_PORT", "5432"))
12+
POSTGRES_DB_NAME = os.getenv("POSTGRESQL_DB_NAME", "opentelemetry-tests")
13+
POSTGRES_PASSWORD = os.getenv("POSTGRESQL_PASSWORD", "testpassword")
14+
POSTGRES_USER = os.getenv("POSTGRESQL_USER", "testuser")
1515

1616

1717
def async_call(coro):
@@ -41,34 +41,28 @@ def setUpClass(cls):
4141
def tearDownClass(cls):
4242
AsyncPGInstrumentor().uninstrument()
4343

44+
def check_span(self, span):
45+
self.assertEqual(span.attributes["db.system"], "postgresql")
46+
self.assertEqual(span.attributes["db.name"], POSTGRES_DB_NAME)
47+
self.assertEqual(span.attributes["db.user"], POSTGRES_USER)
48+
self.assertEqual(span.attributes["net.peer.name"], POSTGRES_HOST)
49+
self.assertEqual(span.attributes["net.peer.ip"], POSTGRES_PORT)
50+
4451
def test_instrumented_execute_method_without_arguments(self, *_, **__):
4552
async_call(self._connection.execute("SELECT 42;"))
4653
spans = self.memory_exporter.get_finished_spans()
4754
self.assertEqual(len(spans), 1)
4855
self.assertIs(StatusCode.UNSET, spans[0].status.status_code)
49-
self.assertEqual(
50-
spans[0].attributes,
51-
{
52-
"db.type": "sql",
53-
"db.user": POSTGRES_USER,
54-
"db.instance": POSTGRES_DB_NAME,
55-
"db.statement": "SELECT 42;",
56-
},
57-
)
56+
self.check_span(spans[0])
57+
self.assertEqual(spans[0].name, "SELECT 42;")
58+
self.assertEqual(spans[0].attributes["db.statement"], "SELECT 42;")
5859

5960
def test_instrumented_fetch_method_without_arguments(self, *_, **__):
6061
async_call(self._connection.fetch("SELECT 42;"))
6162
spans = self.memory_exporter.get_finished_spans()
6263
self.assertEqual(len(spans), 1)
63-
self.assertEqual(
64-
spans[0].attributes,
65-
{
66-
"db.type": "sql",
67-
"db.user": POSTGRES_USER,
68-
"db.instance": POSTGRES_DB_NAME,
69-
"db.statement": "SELECT 42;",
70-
},
71-
)
64+
self.check_span(spans[0])
65+
self.assertEqual(spans[0].attributes["db.statement"], "SELECT 42;")
7266

7367
def test_instrumented_transaction_method(self, *_, **__):
7468
async def _transaction_execute():
@@ -79,35 +73,16 @@ async def _transaction_execute():
7973

8074
spans = self.memory_exporter.get_finished_spans()
8175
self.assertEqual(3, len(spans))
82-
self.assertEqual(
83-
{
84-
"db.instance": POSTGRES_DB_NAME,
85-
"db.user": POSTGRES_USER,
86-
"db.type": "sql",
87-
"db.statement": "BEGIN;",
88-
},
89-
spans[0].attributes,
90-
)
76+
self.check_span(spans[0])
77+
self.assertEqual(spans[0].attributes["db.statement"], "BEGIN;")
9178
self.assertIs(StatusCode.UNSET, spans[0].status.status_code)
92-
self.assertEqual(
93-
{
94-
"db.instance": POSTGRES_DB_NAME,
95-
"db.user": POSTGRES_USER,
96-
"db.type": "sql",
97-
"db.statement": "SELECT 42;",
98-
},
99-
spans[1].attributes,
100-
)
79+
80+
self.check_span(spans[1])
81+
self.assertEqual(spans[1].attributes["db.statement"], "SELECT 42;")
10182
self.assertIs(StatusCode.UNSET, spans[1].status.status_code)
102-
self.assertEqual(
103-
{
104-
"db.instance": POSTGRES_DB_NAME,
105-
"db.user": POSTGRES_USER,
106-
"db.type": "sql",
107-
"db.statement": "COMMIT;",
108-
},
109-
spans[2].attributes,
110-
)
83+
84+
self.check_span(spans[2])
85+
self.assertEqual(spans[2].attributes["db.statement"], "COMMIT;")
11186
self.assertIs(StatusCode.UNSET, spans[2].status.status_code)
11287

11388
def test_instrumented_failed_transaction_method(self, *_, **__):
@@ -120,57 +95,28 @@ async def _transaction_execute():
12095

12196
spans = self.memory_exporter.get_finished_spans()
12297
self.assertEqual(3, len(spans))
123-
self.assertEqual(
124-
{
125-
"db.instance": POSTGRES_DB_NAME,
126-
"db.user": POSTGRES_USER,
127-
"db.type": "sql",
128-
"db.statement": "BEGIN;",
129-
},
130-
spans[0].attributes,
131-
)
98+
99+
self.check_span(spans[0])
100+
self.assertEqual(spans[0].attributes["db.statement"], "BEGIN;")
132101
self.assertIs(StatusCode.UNSET, spans[0].status.status_code)
102+
103+
self.check_span(spans[1])
133104
self.assertEqual(
134-
{
135-
"db.instance": POSTGRES_DB_NAME,
136-
"db.user": POSTGRES_USER,
137-
"db.type": "sql",
138-
"db.statement": "SELECT 42::uuid;",
139-
},
140-
spans[1].attributes,
141-
)
142-
self.assertEqual(
143-
StatusCode.ERROR, spans[1].status.status_code,
144-
)
145-
self.assertEqual(
146-
{
147-
"db.instance": POSTGRES_DB_NAME,
148-
"db.user": POSTGRES_USER,
149-
"db.type": "sql",
150-
"db.statement": "ROLLBACK;",
151-
},
152-
spans[2].attributes,
105+
spans[1].attributes["db.statement"], "SELECT 42::uuid;"
153106
)
107+
self.assertEqual(StatusCode.ERROR, spans[1].status.status_code)
108+
109+
self.check_span(spans[2])
110+
self.assertEqual(spans[2].attributes["db.statement"], "ROLLBACK;")
154111
self.assertIs(StatusCode.UNSET, spans[2].status.status_code)
155112

156113
def test_instrumented_method_doesnt_capture_parameters(self, *_, **__):
157114
async_call(self._connection.execute("SELECT $1;", "1"))
158115
spans = self.memory_exporter.get_finished_spans()
159116
self.assertEqual(len(spans), 1)
160117
self.assertIs(StatusCode.UNSET, spans[0].status.status_code)
161-
self.assertEqual(
162-
spans[0].attributes,
163-
{
164-
"db.type": "sql",
165-
"db.user": POSTGRES_USER,
166-
# This shouldn't be set because we don't capture parameters by
167-
# default
168-
#
169-
# "db.statement.parameters": "('1',)",
170-
"db.instance": POSTGRES_DB_NAME,
171-
"db.statement": "SELECT $1;",
172-
},
173-
)
118+
self.check_span(spans[0])
119+
self.assertEqual(spans[0].attributes["db.statement"], "SELECT $1;")
174120

175121

176122
class TestFunctionalAsyncPG_CaptureParameters(TestBase):
@@ -197,64 +143,56 @@ def setUpClass(cls):
197143
def tearDownClass(cls):
198144
AsyncPGInstrumentor().uninstrument()
199145

146+
def check_span(self, span):
147+
self.assertEqual(span.attributes["db.system"], "postgresql")
148+
self.assertEqual(span.attributes["db.name"], POSTGRES_DB_NAME)
149+
self.assertEqual(span.attributes["db.user"], POSTGRES_USER)
150+
self.assertEqual(span.attributes["net.peer.name"], POSTGRES_HOST)
151+
self.assertEqual(span.attributes["net.peer.ip"], POSTGRES_PORT)
152+
200153
def test_instrumented_execute_method_with_arguments(self, *_, **__):
201154
async_call(self._connection.execute("SELECT $1;", "1"))
202155
spans = self.memory_exporter.get_finished_spans()
203156
self.assertEqual(len(spans), 1)
204157
self.assertIs(StatusCode.UNSET, spans[0].status.status_code)
158+
159+
self.check_span(spans[0])
160+
self.assertEqual(spans[0].name, "SELECT $1;")
161+
self.assertEqual(spans[0].attributes["db.statement"], "SELECT $1;")
205162
self.assertEqual(
206-
spans[0].attributes,
207-
{
208-
"db.type": "sql",
209-
"db.user": POSTGRES_USER,
210-
"db.statement.parameters": "('1',)",
211-
"db.instance": POSTGRES_DB_NAME,
212-
"db.statement": "SELECT $1;",
213-
},
163+
spans[0].attributes["db.statement.parameters"], "('1',)"
214164
)
215165

216166
def test_instrumented_fetch_method_with_arguments(self, *_, **__):
217167
async_call(self._connection.fetch("SELECT $1;", "1"))
218168
spans = self.memory_exporter.get_finished_spans()
219169
self.assertEqual(len(spans), 1)
170+
171+
self.check_span(spans[0])
172+
self.assertEqual(spans[0].attributes["db.statement"], "SELECT $1;")
220173
self.assertEqual(
221-
spans[0].attributes,
222-
{
223-
"db.type": "sql",
224-
"db.user": POSTGRES_USER,
225-
"db.statement.parameters": "('1',)",
226-
"db.instance": POSTGRES_DB_NAME,
227-
"db.statement": "SELECT $1;",
228-
},
174+
spans[0].attributes["db.statement.parameters"], "('1',)"
229175
)
230176

231177
def test_instrumented_executemany_method_with_arguments(self, *_, **__):
232178
async_call(self._connection.executemany("SELECT $1;", [["1"], ["2"]]))
233179
spans = self.memory_exporter.get_finished_spans()
234180
self.assertEqual(len(spans), 1)
181+
182+
self.check_span(spans[0])
183+
self.assertEqual(spans[0].attributes["db.statement"], "SELECT $1;")
235184
self.assertEqual(
236-
{
237-
"db.type": "sql",
238-
"db.statement": "SELECT $1;",
239-
"db.statement.parameters": "([['1'], ['2']],)",
240-
"db.user": POSTGRES_USER,
241-
"db.instance": POSTGRES_DB_NAME,
242-
},
243-
spans[0].attributes,
185+
spans[0].attributes["db.statement.parameters"], "([['1'], ['2']],)"
244186
)
245187

246188
def test_instrumented_execute_interface_error_method(self, *_, **__):
247189
with self.assertRaises(asyncpg.InterfaceError):
248190
async_call(self._connection.execute("SELECT 42;", 1, 2, 3))
249191
spans = self.memory_exporter.get_finished_spans()
250192
self.assertEqual(len(spans), 1)
193+
194+
self.check_span(spans[0])
195+
self.assertEqual(spans[0].attributes["db.statement"], "SELECT 42;")
251196
self.assertEqual(
252-
spans[0].attributes,
253-
{
254-
"db.type": "sql",
255-
"db.instance": POSTGRES_DB_NAME,
256-
"db.user": POSTGRES_USER,
257-
"db.statement.parameters": "(1, 2, 3)",
258-
"db.statement": "SELECT 42;",
259-
},
197+
spans[0].attributes["db.statement.parameters"], "(1, 2, 3)"
260198
)

0 commit comments

Comments
 (0)