From 55b5042b7ad4da23b4512e5d70347dbe96c86c74 Mon Sep 17 00:00:00 2001 From: Anthonin Bonnefoy Date: Fri, 26 Jun 2026 11:06:16 +0200 Subject: Add PQTRACE env to enable protocol tracing in libpq Add 2 env vars, PQTRACE and PQTRACEFLAGS. When PQTRACE is set, PQtrace will be called when connection options are processed. This allows enabling protocol tracing on applications using libpq without requiring specific code. Setting PQTRACE to "-" will output protocol tracing on stdout. PQTRACEFLAGS is the bitwise value of the PQTRACE_SUPPRESS_TIMESTAMPS(1) and PQTRACE_REGRESS_MODE(2) flags. --- doc/src/sgml/libpq.sgml | 48 +++++++++++++++++++++++ src/interfaces/libpq/fe-connect.c | 65 ++++++++++++++++++++++++++++++- src/interfaces/libpq/libpq-int.h | 4 ++ 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 68487a3954f..204191753b9 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -2407,6 +2407,34 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname + + trace_file + + + If set, enables tracing of the client/server protocol exchange to + the named file. The special value - sends the + trace to stdout. Multiple connections using + the same trace file is currently not supported. + + + + + + trace_flags + + + Specifies the trace flags applied when + is set. The value is + an integer that is the bitwise OR of the flag values accepted by + : + 1 for PQTRACE_SUPPRESS_TIMESTAMPS, + 2 for PQTRACE_REGRESS_MODE. + The default is 0. This option has no effect + unless trace_file is also set. + + + + load_balance_hosts @@ -9322,6 +9350,26 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) linkend="libpq-connect-oauth-ca-file"/> connection parameter. + + + + + PQTRACE + + PQTRACE behaves the same as the connection parameter. + + + + + + + PQTRACEFLAGS + + PQTRACEFLAGS behaves the same as the connection parameter. + + diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 2ce128da157..f6f67a14d6c 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -384,6 +384,15 @@ static const internalPQconninfoOption PQconninfoOptions[] = { "Target-Session-Attrs", "", 15, /* sizeof("prefer-standby") = 15 */ offsetof(struct pg_conn, target_session_attrs)}, + {"trace_file", "PQTRACE", + NULL, NULL, + "Trace-File", "D", 64, + offsetof(struct pg_conn, trace_file_str)}, + + {"trace_flags", "PQTRACEFLAGS", NULL, NULL, + "Trace-Flags", "D", 1, + offsetof(struct pg_conn, trace_flags_str)}, + {"load_balance_hosts", "PGLOADBALANCEHOSTS", DefaultLoadBalanceHosts, NULL, "Load-Balance-Hosts", "", 8, /* sizeof("disable") = 8 */ @@ -2060,6 +2069,45 @@ pqConnectOptions2(PGconn *conn) } } + /* + * If a trace file was provided and there's no active Pfdebug, enable + * PQtrace + */ + if (conn->trace_file_str && !conn->Pfdebug) + { + int trace_flags = 0; + + if (conn->trace_flags_str) + { + if (!pqParseIntParam(conn->trace_flags_str, &trace_flags, conn, + "trace_flags")) + return false; + } + + if (strcmp(conn->trace_file_str, "-") == 0) + conn->trace_file = stdout; + + /* + * If the connection is created for a cancel request, and trace_file + * is not stdout, don't try to trace it as the source connection + * already has an open fd on this trace file. + */ + else if (!conn->cancelRequest) + { + conn->trace_file = fopen(conn->trace_file_str, "w"); + if (conn->trace_file == NULL) + { + libpq_append_conn_error(conn, "could not open trace file \"%s\": %m", conn->trace_file_str); + return false; + } + /* Make it line-buffered */ + setvbuf(conn->trace_file, NULL, PG_IOLBF, 0); + } + + PQtrace(conn, conn->trace_file); + PQsetTraceFlags(conn, trace_flags); + } + if (conn->scram_server_key) { int len; @@ -5023,6 +5071,7 @@ pqMakeEmptyPGconn(void) conn->sock = PGINVALID_SOCKET; conn->altsock = PGINVALID_SOCKET; conn->Pfdebug = NULL; + conn->trace_file = NULL; /* * We try to send at least 8K at a time, which is the usual size of pipe @@ -5144,7 +5193,21 @@ freePGconn(PGconn *conn) free(conn->oauth_client_secret); free(conn->oauth_ca_file); free(conn->oauth_scope); - /* Note that conn->Pfdebug is not ours to close or free */ + free(conn->trace_file_str); + free(conn->trace_flags_str); + + /* Untrace the connection to flush Pfdebug */ + PQuntrace(conn); + + /* + * Note that conn->Pfdebug is not ours to close or free if provided + * externally. However, if tracing was enabled through PQTRACE, we need to + * close conn->trace_file. + */ + if (conn->trace_file && conn->trace_file != stdout) + { + fclose(conn->trace_file); + } free(conn->events); pqReleaseConnHosts(conn); free(conn->connip); diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 933ba0d99d5..f172b27e164 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -449,6 +449,10 @@ struct pg_conn char *oauth_ca_file; /* CA file path */ bool oauth_want_retry; /* should we retry on failure? */ + FILE *trace_file; + char *trace_file_str; + char *trace_flags_str; + /* Optional file to write trace info to */ FILE *Pfdebug; int traceFlags; -- 2.50.1 (Apple Git-155)