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 <application>libpq</application> 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 === */ /*