/*------------------------------------------------------------------------- * * csvlog.c * CSV logging * * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/utils/error/csvlog.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/xact.h" #include "libpq/libpq.h" #include "lib/stringinfo.h" #include "miscadmin.h" #include "postmaster/bgworker.h" #include "postmaster/syslogger.h" #include "storage/lock.h" #include "storage/proc.h" #include "tcop/tcopprot.h" #include "utils/backend_status.h" #include "utils/elog.h" #include "utils/guc.h" #include "utils/ps_status.h" /* * append a CSV'd version of a string to a StringInfo * We use the PostgreSQL defaults for CSV, i.e. quote = escape = '"' * If it's NULL, append nothing. */ static inline void appendCSVLiteral(StringInfo buf, const char *data) { const char *p = data; char c; /* avoid confusing an empty string with NULL */ if (p == NULL) return; appendStringInfoCharMacro(buf, '"'); while ((c = *p++) != '\0') { if (c == '"') appendStringInfoCharMacro(buf, '"'); appendStringInfoCharMacro(buf, c); } appendStringInfoCharMacro(buf, '"'); } /* * write_csvlog -- Generate and write CSV log entry * * Constructs the error message, depending on the Errordata it gets, in a CSV * format which is described in doc/src/sgml/config.sgml. */ void write_csvlog(ErrorData *edata) { StringInfoData buf; bool print_stmt = false; /* static counter for line numbers */ static long log_line_number = 0; /* has counter been reset in current process? */ static int log_my_pid = 0; /* * This is one of the few places where we'd rather not inherit a static * variable's value from the postmaster. But since we will, reset it when * MyProcPid changes. */ if (log_my_pid != MyProcPid) { log_line_number = 0; log_my_pid = MyProcPid; reset_formatted_start_time(); } log_line_number++; initStringInfo(&buf); /* timestamp with milliseconds */ appendStringInfoString(&buf, get_formatted_log_time()); appendStringInfoChar(&buf, ','); /* username */ if (MyProcPort) appendCSVLiteral(&buf, MyProcPort->user_name); appendStringInfoChar(&buf, ','); /* database name */ if (MyProcPort) appendCSVLiteral(&buf, MyProcPort->database_name); appendStringInfoChar(&buf, ','); /* Process id */ if (MyProcPid != 0) appendStringInfo(&buf, "%d", MyProcPid); appendStringInfoChar(&buf, ','); /* Remote host and port */ if (MyProcPort && MyProcPort->remote_host) { appendStringInfoChar(&buf, '"'); appendStringInfoString(&buf, MyProcPort->remote_host); if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0') { appendStringInfoChar(&buf, ':'); appendStringInfoString(&buf, MyProcPort->remote_port); } appendStringInfoChar(&buf, '"'); } appendStringInfoChar(&buf, ','); /* session id */ appendStringInfo(&buf, "%lx.%x", (long) MyStartTime, MyProcPid); appendStringInfoChar(&buf, ','); /* Line number */ appendStringInfo(&buf, "%ld", log_line_number); appendStringInfoChar(&buf, ','); /* PS display */ if (MyProcPort) { StringInfoData msgbuf; const char *psdisp; int displen; initStringInfo(&msgbuf); psdisp = get_ps_display(&displen); appendBinaryStringInfo(&msgbuf, psdisp, displen); appendCSVLiteral(&buf, msgbuf.data); pfree(msgbuf.data); } appendStringInfoChar(&buf, ','); /* session start timestamp */ appendStringInfoString(&buf, get_formatted_start_time()); appendStringInfoChar(&buf, ','); /* Virtual transaction id */ /* keep VXID format in sync with lockfuncs.c */ if (MyProc != NULL && MyProc->backendId != InvalidBackendId) appendStringInfo(&buf, "%d/%u", MyProc->backendId, MyProc->lxid); appendStringInfoChar(&buf, ','); /* Transaction id */ appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny()); appendStringInfoChar(&buf, ','); /* Error severity */ appendStringInfoString(&buf, _(error_severity(edata->elevel))); appendStringInfoChar(&buf, ','); /* SQL state code */ appendStringInfoString(&buf, unpack_sql_state(edata->sqlerrcode)); appendStringInfoChar(&buf, ','); /* errmessage */ appendCSVLiteral(&buf, edata->message); appendStringInfoChar(&buf, ','); /* errdetail or errdetail_log */ if (edata->detail_log) appendCSVLiteral(&buf, edata->detail_log); else appendCSVLiteral(&buf, edata->detail); appendStringInfoChar(&buf, ','); /* errhint */ appendCSVLiteral(&buf, edata->hint); appendStringInfoChar(&buf, ','); /* internal query */ appendCSVLiteral(&buf, edata->internalquery); appendStringInfoChar(&buf, ','); /* if printed internal query, print internal pos too */ if (edata->internalpos > 0 && edata->internalquery != NULL) appendStringInfo(&buf, "%d", edata->internalpos); appendStringInfoChar(&buf, ','); /* errcontext */ if (!edata->hide_ctx) appendCSVLiteral(&buf, edata->context); appendStringInfoChar(&buf, ','); /* user query --- only reported if not disabled by the caller */ print_stmt = check_log_of_query(edata); if (print_stmt) appendCSVLiteral(&buf, debug_query_string); appendStringInfoChar(&buf, ','); if (print_stmt && edata->cursorpos > 0) appendStringInfo(&buf, "%d", edata->cursorpos); appendStringInfoChar(&buf, ','); /* file error location */ if (Log_error_verbosity >= PGERROR_VERBOSE) { StringInfoData msgbuf; initStringInfo(&msgbuf); if (edata->funcname && edata->filename) appendStringInfo(&msgbuf, "%s, %s:%d", edata->funcname, edata->filename, edata->lineno); else if (edata->filename) appendStringInfo(&msgbuf, "%s:%d", edata->filename, edata->lineno); appendCSVLiteral(&buf, msgbuf.data); pfree(msgbuf.data); } appendStringInfoChar(&buf, ','); /* application name */ if (application_name) appendCSVLiteral(&buf, application_name); appendStringInfoChar(&buf, ','); /* backend type */ appendCSVLiteral(&buf, get_backend_type_for_log()); appendStringInfoChar(&buf, ','); /* leader PID */ if (MyProc) { PGPROC *leader = MyProc->lockGroupLeader; /* * Show the leader only for active parallel workers. This leaves out * the leader of a parallel group. */ if (leader && leader->pid != MyProcPid) appendStringInfo(&buf, "%d", leader->pid); } appendStringInfoChar(&buf, ','); /* query id */ appendStringInfo(&buf, "%lld", (long long) pgstat_get_my_query_id()); appendStringInfoChar(&buf, '\n'); /* If in the syslogger process, try to write messages direct to file */ if (MyBackendType == B_LOGGER) write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_CSVLOG); else write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_CSVLOG); pfree(buf.data); }