Skip to content

asyncpg: report unix socket connections as network.transport=unix - #4925

Open
chuenchen309 wants to merge 2 commits into
open-telemetry:mainfrom
chuenchen309:fix/asyncpg-unix-socket-network-transport
Open

asyncpg: report unix socket connections as network.transport=unix#4925
chuenchen309 wants to merge 2 commits into
open-telemetry:mainfrom
chuenchen309:fix/asyncpg-unix-socket-network-transport

Conversation

@chuenchen309

Copy link
Copy Markdown

Description

_hydrate_span_from_args reports network.transport as pipe when asyncpg's
Connection._addr is a string. asyncpg uses a string _addr exclusively for unix domain
sockets: connect_utils.py builds the .s.PGSQL.<port> path for any host starting with /
("UNIX socket name") and __connect_addr branches on isinstance(addr, str) to call
loop.create_unix_connection().

Semconv defines pipe as "Named or anonymous pipe" and unix as "Unix domain socket", so
every socket-based connection reported the wrong transport under the new semconv. The redis
instrumentation already reports unix for the same case.

Only the new semconv value changes — net.transport keeps other under the default mode,
which also matches redis.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

  • Added test_span_unix_socket_new_semconv. Without the one-line fix it fails with
    AssertionError: 'pipe' != 'unix'; with it, it passes.
  • pytest instrumentation/opentelemetry-instrumentation-asyncpg/tests — 21 passed
    (20 before this PR).
  • ruff check and ruff format --check clean.

The existing test_span_unix_socket_default_semconv only covered the default mode, which is
why this was not caught.

Does This PR Require a Core Repo Change?

  • Yes. - Link to PR:
  • No.

Checklist:

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added
  • Documentation has been updated

_hydrate_span_from_args set network.transport to "pipe" for a string
_addr, which asyncpg uses exclusively for unix domain sockets. Semconv
defines "pipe" as a named or anonymous pipe and "unix" as a unix domain
socket, so the value was wrong for every socket-based connection. The
redis instrumentation already reports "unix" for the same case.

Only the new semconv value changes; net.transport keeps "other" under
the default mode.

Assisted-by: Claude Opus 5
@linux-foundation-easycla

linux-foundation-easycla Bot commented Aug 5, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

@chuenchen309
chuenchen309 requested a review from a team as a code owner August 5, 2026 13:31
@aabmass

aabmass commented Aug 6, 2026

Copy link
Copy Markdown
Member

Thanks for the PR, is there an issue filed for this or a reproducer?

@chuenchen309

Copy link
Copy Markdown
Author

No issue filed — before opening I searched the tracker for both the identifiers (asyncpg + network.transport) and the symptom ("unix socket"), and found nothing open or closed. Happy to file one if you'd rather have it tracked issue-first.

Here's a reproducer against a real server, with the TCP connection as a control group so it's visible that only the unix-socket branch is affected. PostgreSQL 16.2 listening on both a unix socket and 127.0.0.1:55433; asyncpg 0.30.0; this repo at ac3673d2c.

import asyncio, sys
import asyncpg
from opentelemetry.instrumentation.asyncpg import AsyncPGInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter

exporter = InMemorySpanExporter()
provider = TracerProvider()
provider.add_span_processor(SimpleSpanProcessor(exporter))
AsyncPGInstrumentor().instrument(tracer_provider=provider)

async def run(**kw):
    conn = await asyncpg.connect(user="postgres", database="postgres", port=55433, **kw)
    print("  Connection._addr =", repr(conn._addr))
    await conn.execute("SELECT 1")
    await conn.close()

def dump(label):
    for s in exporter.get_finished_spans():
        print(f"  {label}:", {k: v for k, v in s.attributes.items()
                              if k.startswith(("network.", "net.", "server."))})
    exporter.clear()

asyncio.run(run(host=sys.argv[1])); dump("unix")
asyncio.run(run(host="127.0.0.1")); dump("tcp")

OTEL_SEMCONV_STABILITY_OPT_IN=database python repro.py /run/user/1000/pg on main:

  Connection._addr = '/run/user/1000/pg/.s.PGSQL.55433'
  unix: {'server.address': '/run/user/1000/pg/.s.PGSQL.55433', 'network.transport': 'pipe'}
  Connection._addr = ('127.0.0.1', 55433)
  tcp: {'server.address': '127.0.0.1', 'server.port': 55433, 'network.transport': 'tcp'}

Same command with this PR applied — only the unix line moves:

  unix: {'server.address': '/run/user/1000/pg/.s.PGSQL.55433', 'network.transport': 'unix'}
  tcp: {'server.address': '127.0.0.1', 'server.port': 55433, 'network.transport': 'tcp'}

I ran the other two modes on the patched tree as well, since this touches _set_net_transport:

  • default (no opt-in) — byte-identical to main: net.transport = 'other' for the socket, 'ip_tcp' for TCP.
  • database/dup — emits both, net.transport = 'other' alongside network.transport = 'unix'.

pytest instrumentation/opentelemetry-instrumentation-asyncpg/tests is 21 passed.

One thing that turned up while checking, independent of whether this lands: the same "addr is a str ⇒ unix socket" branch exists in three instrumentations and they disagree. redis (util.py, the if "path" in conn_kwargs branch) emits unix; asyncpg and pymemcache (__init__.py, elif isinstance(instance.server, str)) emit pipe. The pymemcache one has a comment on that very branch reading "unix socket path string", so it looks like the same mistake rather than a deliberate difference. I kept this PR to one instrumentation — happy to fold pymemcache in here or open it separately, whichever you prefer.

AI-assisted with agentic coding tools; all changes were reviewed and tested by me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants