diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index d2e5b08..080d24c 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1589,6 +1589,42 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
+
+
+ logdir
+
+
+ Path of directory where log file written. When both logdir
+ and logsize is set, logging is enabled.
+
+
+
+
+
+ logsize
+
+
+ Maximum log size. The unit is megabyte. When the log file size exceeds
+ to logsize, the log is output to another file.
+ When both logdir and logsize is set,
+ logging is enabled.
+
+
+
+
+
+ logminlevel
+
+
+ Level of the log. level1 and level2
+ can be set, level1 is the default.
+ When this parameter is level1,it outputs the time
+ in the function and connection time. level2 outputs
+ information on the protocol being exchanged in addition to
+ level1 information.
+
+
+
@@ -7472,6 +7508,36 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
linkend="libpq-connect-target-session-attrs"/> connection parameter.
+
+
+
+
+ PGLOGDIR
+
+ PGLOGDIR behaves the same as the connection parameter.
+
+
+
+
+
+
+ PGLOGSIZE
+
+ PGLOGSIZE behaves the same as the connection parameter.
+
+
+
+
+
+
+ PGLOGMINLEVEL
+
+ PGLOGMINLEVEL behaves the same as the connection parameter.
+
+
@@ -8300,6 +8366,38 @@ int PQisthreadsafe();
+
+ Logging
+
+
+ trace log
+
+
+ logging
+
+
+
+ Libpq trace log can trace information about SQL queries which issued by
+ the libpq application. This output help determine whether the couse is
+ on backend side or application side and solve issues with the libpq Driver
+ when it use. The log without requiring recompile of the libpq application.
+
+
+
+ You can activate this log by setting both logdir and
+ logsize of the connection string, or by setting both
+ the environment variables PGLOGDIR and PGLOGSIZE.
+ The log trace file is written in a directory setting by PGLOGDIR
+ or logdir.
+ Information to be output to the log file can be controlled by setting
+ logminlevel or PGLOGMINLEVEL.
+ The file name is determined as libpq-%ProcessID-%Y-%m-%d_%H%M%S.log.
+ If the setting of the file path by the connection string or the environment variable is
+ incorrect, the log file is not created in the intended location.
+ The maximum log file size and log level you set is output to the beginning of the file,
+ so you can check it.
+
+
Building libpq Programs
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index bc456fe..2b441be 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -128,6 +128,7 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
#define DefaultSSLMode "prefer"
#else
#define DefaultSSLMode "disable"
+#define DefaultLogMinLevel "level1"
#endif
/* ----------
@@ -325,6 +326,19 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"Target-Session-Attrs", "", 11, /* sizeof("read-write") = 11 */
offsetof(struct pg_conn, target_session_attrs)},
+ /* libpq trace log options */
+ {"logdir", "PGLOGDIR", NULL, NULL,
+ "Logdir", "", MAXPGPATH - 4,
+ offsetof(struct pg_conn, logdir)},
+
+ {"logsize", "PGLOGSIZE", NULL, NULL,
+ "Logsize", "", 5,
+ offsetof(struct pg_conn, logsize_str)},
+
+ {"logminlevel", "PGLOGMINLEVEL", DefaultLogMinLevel, NULL,
+ "LogMinlevel", "", 7,
+ offsetof(struct pg_conn, logminlevel_str)},
+
/* Terminating entry --- MUST BE LAST */
{NULL, NULL, NULL, NULL,
NULL, NULL, 0}
@@ -1128,6 +1142,26 @@ connectOptions2(PGconn *conn)
}
/*
+ * If both size and directory of trace log was given,
+ * initialize a trace log.
+ */
+ if (conn->logsize_str && conn->logsize_str[0] != '\0')
+ conn->logsize = atoi(conn->logsize_str);
+
+ if (conn->logdir != NULL && conn->logsize > 0 && conn->logsize < 2048)
+ {
+ conn->logsize = conn->logsize * 1024 * 1024;
+
+ if(strcmp(conn->logminlevel_str, "level1") == 0)
+ conn->logminlevel = LEVEL1;
+
+ if(strcmp(conn->logminlevel_str, "level2") == 0)
+ conn->logminlevel = LEVEL2;
+
+ initTraceLog(conn);
+ }
+
+ /*
* If password was not given, try to look it up in password file. Note
* that the result might be different for each host/port pair.
*/
@@ -3716,6 +3750,16 @@ freePGconn(PGconn *conn)
termPQExpBuffer(&conn->errorMessage);
termPQExpBuffer(&conn->workBuffer);
+ /* clean up libpq trace log structures */
+ if (conn->logsize_str)
+ free(conn->logsize_str);
+ if (conn->logdir)
+ free(conn->logdir);
+ if (conn->logminlevel_str)
+ free(conn->logminlevel_str);
+ if (conn->traceDebug)
+ fclose(conn->traceDebug);
+
free(conn);
#ifdef WIN32
@@ -3751,6 +3795,7 @@ sendTerminateConn(PGconn *conn)
*/
if (conn->sock != PGINVALID_SOCKET && conn->status == CONNECTION_OK)
{
+ traceLog_fprintf(conn, LEVEL1, "Send connection terminate message to backend: ");
/*
* Try to send "close connection" message to backend. Ignore any
* error.
@@ -4153,6 +4198,8 @@ int
pqPacketSend(PGconn *conn, char pack_type,
const void *buf, size_t buf_len)
{
+ traceLog_fprintf(conn, LEVEL1, "Send connection start message to backend: ");
+
/* Start the message. */
if (pqPutMsgStart(pack_type, true, conn))
return STATUS_ERROR;
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 6aed8c8..42279a6 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -879,7 +879,7 @@ pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, false);
if (res->errMsg)
{
- sprintf(res->errMsg, "%s\n", msgBuf);
+ sprintf(res->errMsg, "NOTICE: %s\n", msgBuf);
/*
* Pass to receiver, then free it.
@@ -991,9 +991,8 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
pgParameterStatus *pstatus;
pgParameterStatus *prev;
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "pqSaveParameterStatus: '%s' = '%s'\n",
- name, value);
+ traceLog_fprintf(conn, LEVEL2, "pqSaveParameterStatus: '%s' = '%s'\n",
+ name, value);
/*
* Forget any old information about the parameter
@@ -1208,6 +1207,8 @@ fail:
int
PQsendQuery(PGconn *conn, const char *query)
{
+ traceLog_fprintf(conn, LEVEL1, "PQsendQuery start :");
+
if (!PQsendQueryStart(conn))
return 0;
@@ -1219,6 +1220,8 @@ PQsendQuery(PGconn *conn, const char *query)
return 0;
}
+ traceLog_fprintf(conn, LEVEL1, "Query: %s \n",query);
+
/* construct the outgoing Query message */
if (pqPutMsgStart('Q', false, conn) < 0 ||
pqPuts(query, conn) < 0 ||
@@ -1249,6 +1252,9 @@ PQsendQuery(PGconn *conn, const char *query)
/* OK, it's launched! */
conn->asyncStatus = PGASYNC_BUSY;
+
+ traceLog_fprintf(conn, LEVEL1, "PQsendQuery end :");
+
return 1;
}
@@ -1266,6 +1272,8 @@ PQsendQueryParams(PGconn *conn,
const int *paramFormats,
int resultFormat)
{
+ traceLog_fprintf(conn, LEVEL1, "PQsendQueryParams start :");
+
if (!PQsendQueryStart(conn))
return 0;
@@ -1283,6 +1291,8 @@ PQsendQueryParams(PGconn *conn,
return 0;
}
+ traceLog_fprintf(conn, LEVEL1, "PQsendQueryParams end :");
+
return PQsendQueryGuts(conn,
command,
"", /* use unnamed statement */
@@ -1306,6 +1316,8 @@ PQsendPrepare(PGconn *conn,
const char *stmtName, const char *query,
int nParams, const Oid *paramTypes)
{
+ traceLog_fprintf(conn, LEVEL1, "PQsendPrepare start :");
+
if (!PQsendQueryStart(conn))
return 0;
@@ -1337,6 +1349,8 @@ PQsendPrepare(PGconn *conn,
return 0;
}
+ traceLog_fprintf(conn, LEVEL1, "Statement name: %s, Query: %s \n",stmtName, query);
+
/* construct the Parse message */
if (pqPutMsgStart('P', false, conn) < 0 ||
pqPuts(stmtName, conn) < 0 ||
@@ -1386,6 +1400,9 @@ PQsendPrepare(PGconn *conn,
/* OK, it's launched! */
conn->asyncStatus = PGASYNC_BUSY;
+
+ traceLog_fprintf(conn, LEVEL1, "PQsendPrepare end :");
+
return 1;
sendFailed:
@@ -1492,6 +1509,8 @@ PQsendQueryGuts(PGconn *conn,
{
int i;
+ traceLog_fprintf(conn, LEVEL1, "PQsendQueryGuts start :");
+
/* This isn't gonna work on a 2.0 server */
if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
{
@@ -1507,6 +1526,8 @@ PQsendQueryGuts(PGconn *conn,
if (command)
{
+ traceLog_fprintf(conn, LEVEL1, "Statement name: %s, Command: %s \n",stmtName, command);
+
/* construct the Parse message */
if (pqPutMsgStart('P', false, conn) < 0 ||
pqPuts(stmtName, conn) < 0 ||
@@ -1638,6 +1659,9 @@ PQsendQueryGuts(PGconn *conn,
/* OK, it's launched! */
conn->asyncStatus = PGASYNC_BUSY;
+
+ traceLog_fprintf(conn, LEVEL1, "PQsendQueryGuts end :");
+
return 1;
sendFailed:
@@ -1780,6 +1804,8 @@ PQgetResult(PGconn *conn)
{
PGresult *res;
+ traceLog_fprintf(conn, LEVEL1, "PQgetResult start :");
+
if (!conn)
return NULL;
@@ -1820,6 +1846,7 @@ PQgetResult(PGconn *conn)
/* Parse it. */
parseInput(conn);
+
}
/* Return the appropriate thing. */
@@ -1874,6 +1901,8 @@ PQgetResult(PGconn *conn)
}
}
+ traceLog_fprintf(conn, LEVEL1, "PQgetResult end :");
+
return res;
}
@@ -2203,6 +2232,8 @@ PQsendDescribePortal(PGconn *conn, const char *portal)
static int
PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
{
+ traceLog_fprintf(conn, LEVEL1, "PQsendDescribe start :");
+
/* Treat null desc_target as empty string */
if (!desc_target)
desc_target = "";
@@ -2218,6 +2249,8 @@ PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
return 0;
}
+ traceLog_fprintf(conn, LEVEL1, "Describe type: %c, target: %s \n",desc_type, desc_target);
+
/* construct the Describe message */
if (pqPutMsgStart('D', false, conn) < 0 ||
pqPutc(desc_type, conn) < 0 ||
@@ -2249,6 +2282,9 @@ PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
/* OK, it's launched! */
conn->asyncStatus = PGASYNC_BUSY;
+
+ traceLog_fprintf(conn, LEVEL1, "PQsendDescribe end :");
+
return 1;
sendFailed:
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 46ece1a..4c77749 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -35,6 +35,7 @@
#ifdef WIN32
#include "win32.h"
+#include
#else
#include
#include
@@ -45,6 +46,7 @@
#endif
#ifdef HAVE_SYS_SELECT_H
#include
+#include
#endif
#include "libpq-fe.h"
@@ -60,6 +62,186 @@ static int pqSocketCheck(PGconn *conn, int forRead, int forWrite,
time_t end_time);
static int pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time);
+static void getTraceLogFilename(PGconn *conn,char* filename);
+static void traceLog_fputnbytes(PGconn *conn, int loglevel ,const char *head, const char *str, size_t n);
+static void fputnbytes(FILE *f, const char *str, size_t n);
+static void getCurrentTime(char* currenttime,int type);
+#define TRACELOG_TIME_SIZE 28
+
+/*
+ * getCurrentTime: get current time for trace log output
+ *
+ * type=0 currenttime formate %Y-%m-%d_%H%M%S
+ * type=1 currenttime formate %Y/%m/%d %H:%M:%S.%Milliseconds
+ */
+static void
+getCurrentTime(char* currenttime,int type)
+{
+#ifdef WIN32
+ SYSTEMTIME localTime;
+ GetLocalTime(&localTime);
+ if(type==0)
+ snprintf(currenttime,TRACELOG_TIME_SIZE,"%4d-%02d-%02d_%02d%02d%02d",
+ localTime.wYear,localTime.wMonth,localTime.wDay,
+ localTime.wHour,localTime.wMinute,localTime.wSecond);
+ else if(type==1)
+ snprintf(currenttime,TRACELOG_TIME_SIZE,"%4d/%02d/%02d %02d:%02d:%02d.%03d",
+ localTime.wYear,localTime.wMonth,localTime.wDay,
+ localTime.wHour,localTime.wMinute,localTime.wSecond,
+ localTime.wMilliseconds);
+#else
+ struct timeb localTime;
+ struct tm *tm;
+ ftime(&localTime);
+ tm = localtime(&localTime.time);
+ if(type == 0)
+ snprintf(currenttime, TRACELOG_TIME_SIZE,"%4d-%02d-%02d_%02d%02d%02d",
+ 1900+ tm->tm_year,1 + tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ else if(type==1)
+ snprintf(currenttime, TRACELOG_TIME_SIZE,"%4d/%02d/%02d %02d:%02d:%02d.%03d",
+ 1900+ tm->tm_year,1 + tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, localTime.millitm);
+#endif
+}
+
+/*
+ * getTraceLogFilename: build trace log file name
+ * The name is libpq-%ProcessID-%Y-%m-%d_%H%M%S.log.
+ */
+static void
+getTraceLogFilename(PGconn *conn,char* filename)
+{
+ char currenttime[TRACELOG_TIME_SIZE]; /* %Y-%m-%d_%H%M%S */
+ getCurrentTime(currenttime,0);
+
+#ifdef WIN32
+ snprintf(filename, MAXPGPATH, "%s\\libpq-%d-%s.log", conn->logdir,getpid(),currenttime);
+#else
+ snprintf(filename, MAXPGPATH, "%s/libpq-%d-%s.log", conn->logdir,getpid(),currenttime);
+#endif
+}
+
+/*
+ * initTraceLog: initialize a trace log file
+ */
+void
+initTraceLog(PGconn *conn)
+{
+ char logfilename[MAXPGPATH];
+ getTraceLogFilename(conn,logfilename);
+ conn->traceDebug=fopen(logfilename,"w");
+ fprintf(conn->traceDebug, "Max log size is %sB, log min level is %s\n",
+ conn->logsize_str, conn->logminlevel_str);
+}
+
+/*
+ * traceLog_fprintf: output trace log to file
+ * If PQtrace() is called, PQtrace() is output followed by libpq trace log.
+ */
+void
+traceLog_fprintf(PGconn *conn, int loglevel, const char *fmt,...)
+{
+ char logfilename[MAXPGPATH];
+ char msgBuf[MAXPGPATH];
+ va_list args;
+ int ret;
+ char currenttime[TRACELOG_TIME_SIZE];
+ bool output_tracelog = true;
+
+ va_start(args, fmt);
+ vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
+ msgBuf[sizeof(msgBuf) - 1] = '\0';
+ va_end(args);
+
+ if(conn->logminlevel < loglevel)
+ output_tracelog = false;
+
+ if(conn->Pfdebug)
+ {
+ fprintf(conn->Pfdebug, "%s", msgBuf);
+ }
+ else if(conn->traceDebug && output_tracelog)
+ {
+ if((int)ftell(conn->traceDebug) >= conn->logsize)
+ {
+ fclose(conn->traceDebug);
+ getTraceLogFilename(conn,logfilename);
+ conn->traceDebug = fopen(logfilename,"w");
+ if(conn->traceDebug == NULL)
+ return;
+ fprintf(conn->traceDebug, "Max log size is %sB, log min level is %s\n",
+ conn->logsize_str, conn->logminlevel_str);
+ }
+
+ /* Select trace log message style */
+ if(loglevel == LEVEL1)
+ {
+ getCurrentTime(currenttime,1);
+ ret = fprintf(conn->traceDebug, "%s %s\n", currenttime, msgBuf);
+ }
+
+ if(loglevel == LEVEL2)
+ {
+ ret = fprintf(conn->traceDebug, "%s",msgBuf);
+ }
+
+ if(ret < 0)
+ {
+ fclose(conn->traceDebug);
+ conn->traceDebug = NULL;
+ return;
+ }
+ fflush(conn->traceDebug);
+ }
+}
+/*
+ * traceLog_fputnbytes: output trace log to file using fputnbytes()
+ */
+static void
+traceLog_fputnbytes(PGconn *conn,int loglevel, const char *head, const char *str, size_t n)
+{
+ char logfilename[MAXPGPATH];
+ int ret;
+ bool output_tracelog = true;
+
+ if(conn->logminlevel < loglevel)
+ output_tracelog = false;
+
+ if (conn->Pfdebug)
+ {
+ fprintf(conn->Pfdebug, "%s", head);
+ fputnbytes(conn->Pfdebug,str, n);
+ fprintf(conn->Pfdebug, "\n");
+ }
+ else if(conn->traceDebug && output_tracelog)
+ {
+ if(conn->logminlevel < loglevel)
+ {
+ return;
+ }
+
+ if((int)ftell(conn->traceDebug) >= conn->logsize)
+ {
+ fclose(conn->traceDebug);
+ getTraceLogFilename(conn,logfilename);
+ conn->traceDebug = fopen(logfilename,"w");
+ if(conn->traceDebug == NULL)
+ return;
+ }
+ ret = fprintf(conn->traceDebug, "%s", head);
+ if(ret < 0)
+ {
+ fclose(conn->traceDebug);
+ conn->traceDebug = NULL;
+ return;
+ }
+ fputnbytes(conn->traceDebug,str,n);
+ fprintf(conn->traceDebug,"\n");
+ fflush(conn->traceDebug);
+ }
+}
+
/*
* PQlibVersion: return the libpq version number
*/
@@ -98,8 +280,7 @@ pqGetc(char *result, PGconn *conn)
*result = conn->inBuffer[conn->inCursor++];
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend> %c\n", *result);
+ traceLog_fprintf(conn, LEVEL2, "From backend> %c\n", *result);
return 0;
}
@@ -114,8 +295,7 @@ pqPutc(char c, PGconn *conn)
if (pqPutMsgBytes(&c, 1, conn))
return EOF;
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> %c\n", c);
+ traceLog_fprintf(conn, LEVEL2, "To backend> %c\n", c);
return 0;
}
@@ -152,9 +332,8 @@ pqGets_internal(PQExpBuffer buf, PGconn *conn, bool resetbuffer)
conn->inCursor = ++inCursor;
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend> \"%s\"\n",
- buf->data);
+ traceLog_fprintf(conn, LEVEL2, "From backend> \"%s\"\n",
+ buf->data);
return 0;
}
@@ -181,8 +360,7 @@ pqPuts(const char *s, PGconn *conn)
if (pqPutMsgBytes(s, strlen(s) + 1, conn))
return EOF;
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> \"%s\"\n", s);
+ traceLog_fprintf(conn, LEVEL2, "To backend> \"%s\"\n", s);
return 0;
}
@@ -194,6 +372,7 @@ pqPuts(const char *s, PGconn *conn)
int
pqGetnchar(char *s, size_t len, PGconn *conn)
{
+ char buf[100];
if (len > (size_t) (conn->inEnd - conn->inCursor))
return EOF;
@@ -202,12 +381,8 @@ pqGetnchar(char *s, size_t len, PGconn *conn)
conn->inCursor += len;
- if (conn->Pfdebug)
- {
- fprintf(conn->Pfdebug, "From backend (%lu)> ", (unsigned long) len);
- fputnbytes(conn->Pfdebug, s, len);
- fprintf(conn->Pfdebug, "\n");
- }
+ sprintf(buf, "From backend (%lu)> ", (unsigned long) len);
+ traceLog_fputnbytes(conn, LEVEL2, buf, s, len);
return 0;
}
@@ -223,15 +398,12 @@ pqGetnchar(char *s, size_t len, PGconn *conn)
int
pqSkipnchar(size_t len, PGconn *conn)
{
+ char buf[100];
if (len > (size_t) (conn->inEnd - conn->inCursor))
return EOF;
- if (conn->Pfdebug)
- {
- fprintf(conn->Pfdebug, "From backend (%lu)> ", (unsigned long) len);
- fputnbytes(conn->Pfdebug, conn->inBuffer + conn->inCursor, len);
- fprintf(conn->Pfdebug, "\n");
- }
+ sprintf(buf, "From backend (%lu)> ", (unsigned long) len);
+ traceLog_fputnbytes(conn, LEVEL2, buf, conn->inBuffer + conn->inCursor, len);
conn->inCursor += len;
@@ -245,15 +417,12 @@ pqSkipnchar(size_t len, PGconn *conn)
int
pqPutnchar(const char *s, size_t len, PGconn *conn)
{
+ char buf[100];
if (pqPutMsgBytes(s, len, conn))
return EOF;
- if (conn->Pfdebug)
- {
- fprintf(conn->Pfdebug, "To backend> ");
- fputnbytes(conn->Pfdebug, s, len);
- fprintf(conn->Pfdebug, "\n");
- }
+ sprintf(buf,"To backend (%lu)> ", (unsigned long) len);
+ traceLog_fputnbytes(conn, LEVEL2, buf, s, len);
return 0;
}
@@ -292,8 +461,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn)
return EOF;
}
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend (#%lu)> %d\n", (unsigned long) bytes, *result);
+ traceLog_fprintf(conn, LEVEL2, "From backend (#%lu)> %d\n", (unsigned long) bytes, *result);
return 0;
}
@@ -328,8 +496,7 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
return EOF;
}
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend (%lu#)> %d\n", (unsigned long) bytes, value);
+ traceLog_fprintf(conn, LEVEL2, "To backend (%lu#)> %d\n", (unsigned long) bytes, value);
return 0;
}
@@ -548,9 +715,8 @@ pqPutMsgStart(char msg_type, bool force_len, PGconn *conn)
conn->outMsgEnd = endPos;
/* length word, if needed, will be filled in by pqPutMsgEnd */
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> Msg %c\n",
- msg_type ? msg_type : ' ');
+ traceLog_fprintf(conn, LEVEL2, "To backend> Msg %c\n",
+ msg_type ? msg_type : ' ');
return 0;
}
@@ -586,9 +752,8 @@ pqPutMsgBytes(const void *buf, size_t len, PGconn *conn)
int
pqPutMsgEnd(PGconn *conn)
{
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n",
- conn->outMsgEnd - conn->outCount);
+ traceLog_fprintf(conn, LEVEL2, "To backend> Msg complete, length %u\n",
+ conn->outMsgEnd - conn->outCount);
/* Fill in length word if needed */
if (conn->outMsgStart >= 0)
@@ -677,9 +842,15 @@ pqReadData(PGconn *conn)
}
/* OK, try to read some data */
+
retry3:
+ traceLog_fprintf(conn, LEVEL1, "Start receiving message from backend:");
+
nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd,
conn->inBufSize - conn->inEnd);
+
+ traceLog_fprintf(conn, LEVEL1, "End receiving message from backend:");
+
if (nread < 0)
{
if (SOCK_ERRNO == EINTR)
@@ -767,9 +938,14 @@ retry3:
* Still not sure that it's EOF, because some data could have just
* arrived.
*/
+
+ traceLog_fprintf(conn, LEVEL1, "Start receiving message from backend:");
retry4:
nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd,
conn->inBufSize - conn->inEnd);
+
+ traceLog_fprintf(conn, LEVEL1, "End receiving message from backend:");
+
if (nread < 0)
{
if (SOCK_ERRNO == EINTR)
@@ -846,6 +1022,8 @@ pqSendSome(PGconn *conn, int len)
{
int sent;
+ traceLog_fprintf(conn, LEVEL1, "Start sending message to backend:");
+
#ifndef WIN32
sent = pqsecure_write(conn, ptr, len);
#else
@@ -858,6 +1036,8 @@ pqSendSome(PGconn *conn, int len)
sent = pqsecure_write(conn, ptr, Min(len, 65536));
#endif
+ traceLog_fprintf(conn, LEVEL1, "End sending message to backend:");
+
if (sent < 0)
{
/* Anything except EAGAIN/EWOULDBLOCK/EINTR is trouble */
@@ -963,6 +1143,9 @@ pqFlush(PGconn *conn)
if (conn->Pfdebug)
fflush(conn->Pfdebug);
+ if (conn->traceDebug)
+ fflush(conn->traceDebug);
+
if (conn->outCount > 0)
return pqSendSome(conn, conn->outCount);
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 3f13ddf..dd8edf3 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -36,6 +36,10 @@ extern "C"
#define PG_COPYRES_EVENTS 0x04
#define PG_COPYRES_NOTICEHOOKS 0x08
+/* trace log level */
+#define LEVEL1 10 /* Time and process (by default) */
+#define LEVEL2 11 /* Server and Client exchange messages */
+
/* Application-visible enum types */
/*
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 66fd317..4f9726a 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -500,6 +500,13 @@ struct pg_conn
/* Buffer for receiving various parts of messages */
PQExpBufferData workBuffer; /* expansible string */
+
+ char *logdir; /* Trace log directory to save log */
+ char *logsize_str; /* Trace log maximum size (string) */
+ char *logminlevel_str; /* Trace log level (string)*/
+ int logsize; /* Trace log maximum size */
+ int logminlevel; /* Trace log level( "level1"(default) or "level2") */
+ FILE *traceDebug; /* Trace log file to write trace info */
};
/* PGcancel stores all data necessary to cancel a connection. A copy of this
@@ -666,6 +673,10 @@ extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending,
bool got_epipe);
#endif
+/* libpq trace log */
+extern void initTraceLog(PGconn *conn);
+extern void traceLog_fprintf(PGconn *conn, int loglevel, const char *fmt,...) pg_attribute_printf(3, 4);
+
/* === SSL === */
/*