LCOV - code coverage report
Current view: top level - src/backend/utils/error - csvlog.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 88.0 % 108 95
Test Date: 2026-03-03 03:15:11 Functions: 100.0 % 2 2
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * csvlog.c
       4              :  *    CSV logging
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  *
      10              :  * IDENTIFICATION
      11              :  *    src/backend/utils/error/csvlog.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              :  */
      15              : 
      16              : #include "postgres.h"
      17              : 
      18              : #include "access/xact.h"
      19              : #include "lib/stringinfo.h"
      20              : #include "libpq/libpq-be.h"
      21              : #include "miscadmin.h"
      22              : #include "postmaster/syslogger.h"
      23              : #include "storage/lock.h"
      24              : #include "storage/proc.h"
      25              : #include "tcop/tcopprot.h"
      26              : #include "utils/backend_status.h"
      27              : #include "utils/guc.h"
      28              : #include "utils/ps_status.h"
      29              : 
      30              : 
      31              : /*
      32              :  * append a CSV'd version of a string to a StringInfo
      33              :  * We use the PostgreSQL defaults for CSV, i.e. quote = escape = '"'
      34              :  * If it's NULL, append nothing.
      35              :  */
      36              : static inline void
      37          169 : appendCSVLiteral(StringInfo buf, const char *data)
      38              : {
      39          169 :     const char *p = data;
      40              :     char        c;
      41              : 
      42              :     /* avoid confusing an empty string with NULL */
      43          169 :     if (p == NULL)
      44           80 :         return;
      45              : 
      46           89 :     appendStringInfoCharMacro(buf, '"');
      47         1787 :     while ((c = *p++) != '\0')
      48              :     {
      49         1698 :         if (c == '"')
      50            6 :             appendStringInfoCharMacro(buf, '"');
      51         1698 :         appendStringInfoCharMacro(buf, c);
      52              :     }
      53           89 :     appendStringInfoCharMacro(buf, '"');
      54              : }
      55              : 
      56              : /*
      57              :  * write_csvlog -- Generate and write CSV log entry
      58              :  *
      59              :  * Constructs the error message, depending on the Errordata it gets, in a CSV
      60              :  * format which is described in doc/src/sgml/config.sgml.
      61              :  */
      62              : void
      63           20 : write_csvlog(ErrorData *edata)
      64              : {
      65              :     StringInfoData buf;
      66           20 :     bool        print_stmt = false;
      67              : 
      68              :     /* static counter for line numbers */
      69              :     static long log_line_number = 0;
      70              : 
      71              :     /* has counter been reset in current process? */
      72              :     static int  log_my_pid = 0;
      73              : 
      74              :     /*
      75              :      * This is one of the few places where we'd rather not inherit a static
      76              :      * variable's value from the postmaster.  But since we will, reset it when
      77              :      * MyProcPid changes.
      78              :      */
      79           20 :     if (log_my_pid != MyProcPid)
      80              :     {
      81           11 :         log_line_number = 0;
      82           11 :         log_my_pid = MyProcPid;
      83           11 :         reset_formatted_start_time();
      84              :     }
      85           20 :     log_line_number++;
      86              : 
      87           20 :     initStringInfo(&buf);
      88              : 
      89              :     /* timestamp with milliseconds */
      90           20 :     appendStringInfoString(&buf, get_formatted_log_time());
      91           20 :     appendStringInfoChar(&buf, ',');
      92              : 
      93              :     /* username */
      94           20 :     if (MyProcPort)
      95            9 :         appendCSVLiteral(&buf, MyProcPort->user_name);
      96           20 :     appendStringInfoChar(&buf, ',');
      97              : 
      98              :     /* database name */
      99           20 :     if (MyProcPort)
     100            9 :         appendCSVLiteral(&buf, MyProcPort->database_name);
     101           20 :     appendStringInfoChar(&buf, ',');
     102              : 
     103              :     /* Process id  */
     104           20 :     if (MyProcPid != 0)
     105           20 :         appendStringInfo(&buf, "%d", MyProcPid);
     106           20 :     appendStringInfoChar(&buf, ',');
     107              : 
     108              :     /* Remote host and port */
     109           20 :     if (MyProcPort && MyProcPort->remote_host)
     110              :     {
     111            9 :         appendStringInfoChar(&buf, '"');
     112            9 :         appendStringInfoString(&buf, MyProcPort->remote_host);
     113            9 :         if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
     114              :         {
     115            0 :             appendStringInfoChar(&buf, ':');
     116            0 :             appendStringInfoString(&buf, MyProcPort->remote_port);
     117              :         }
     118            9 :         appendStringInfoChar(&buf, '"');
     119              :     }
     120           20 :     appendStringInfoChar(&buf, ',');
     121              : 
     122              :     /* session id */
     123           20 :     appendStringInfo(&buf, "%" PRIx64 ".%x", MyStartTime, MyProcPid);
     124           20 :     appendStringInfoChar(&buf, ',');
     125              : 
     126              :     /* Line number */
     127           20 :     appendStringInfo(&buf, "%ld", log_line_number);
     128           20 :     appendStringInfoChar(&buf, ',');
     129              : 
     130              :     /* PS display */
     131           20 :     if (MyProcPort)
     132              :     {
     133              :         StringInfoData msgbuf;
     134              :         const char *psdisp;
     135              :         int         displen;
     136              : 
     137            9 :         initStringInfo(&msgbuf);
     138              : 
     139            9 :         psdisp = get_ps_display(&displen);
     140            9 :         appendBinaryStringInfo(&msgbuf, psdisp, displen);
     141            9 :         appendCSVLiteral(&buf, msgbuf.data);
     142              : 
     143            9 :         pfree(msgbuf.data);
     144              :     }
     145           20 :     appendStringInfoChar(&buf, ',');
     146              : 
     147              :     /* session start timestamp */
     148           20 :     appendStringInfoString(&buf, get_formatted_start_time());
     149           20 :     appendStringInfoChar(&buf, ',');
     150              : 
     151              :     /* Virtual transaction id */
     152              :     /* keep VXID format in sync with lockfuncs.c */
     153           20 :     if (MyProc != NULL && MyProc->vxid.procNumber != INVALID_PROC_NUMBER)
     154            9 :         appendStringInfo(&buf, "%d/%u", MyProc->vxid.procNumber, MyProc->vxid.lxid);
     155           20 :     appendStringInfoChar(&buf, ',');
     156              : 
     157              :     /* Transaction id */
     158           20 :     appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny());
     159           20 :     appendStringInfoChar(&buf, ',');
     160              : 
     161              :     /* Error severity */
     162           20 :     appendStringInfoString(&buf, _(error_severity(edata->elevel)));
     163           20 :     appendStringInfoChar(&buf, ',');
     164              : 
     165              :     /* SQL state code */
     166           20 :     appendStringInfoString(&buf, unpack_sql_state(edata->sqlerrcode));
     167           20 :     appendStringInfoChar(&buf, ',');
     168              : 
     169              :     /* errmessage */
     170           20 :     appendCSVLiteral(&buf, edata->message);
     171           20 :     appendStringInfoChar(&buf, ',');
     172              : 
     173              :     /* errdetail or errdetail_log */
     174           20 :     if (edata->detail_log)
     175            0 :         appendCSVLiteral(&buf, edata->detail_log);
     176              :     else
     177           20 :         appendCSVLiteral(&buf, edata->detail);
     178           20 :     appendStringInfoChar(&buf, ',');
     179              : 
     180              :     /* errhint */
     181           20 :     appendCSVLiteral(&buf, edata->hint);
     182           20 :     appendStringInfoChar(&buf, ',');
     183              : 
     184              :     /* internal query */
     185           20 :     appendCSVLiteral(&buf, edata->internalquery);
     186           20 :     appendStringInfoChar(&buf, ',');
     187              : 
     188              :     /* if printed internal query, print internal pos too */
     189           20 :     if (edata->internalpos > 0 && edata->internalquery != NULL)
     190            0 :         appendStringInfo(&buf, "%d", edata->internalpos);
     191           20 :     appendStringInfoChar(&buf, ',');
     192              : 
     193              :     /* errcontext */
     194           20 :     if (!edata->hide_ctx)
     195           20 :         appendCSVLiteral(&buf, edata->context);
     196           20 :     appendStringInfoChar(&buf, ',');
     197              : 
     198              :     /* user query --- only reported if not disabled by the caller */
     199           20 :     print_stmt = check_log_of_query(edata);
     200           20 :     if (print_stmt)
     201            2 :         appendCSVLiteral(&buf, debug_query_string);
     202           20 :     appendStringInfoChar(&buf, ',');
     203           20 :     if (print_stmt && edata->cursorpos > 0)
     204            1 :         appendStringInfo(&buf, "%d", edata->cursorpos);
     205           20 :     appendStringInfoChar(&buf, ',');
     206              : 
     207              :     /* file error location */
     208           20 :     if (Log_error_verbosity >= PGERROR_VERBOSE)
     209              :     {
     210              :         StringInfoData msgbuf;
     211              : 
     212            0 :         initStringInfo(&msgbuf);
     213              : 
     214            0 :         if (edata->funcname && edata->filename)
     215            0 :             appendStringInfo(&msgbuf, "%s, %s:%d",
     216              :                              edata->funcname, edata->filename,
     217              :                              edata->lineno);
     218            0 :         else if (edata->filename)
     219            0 :             appendStringInfo(&msgbuf, "%s:%d",
     220              :                              edata->filename, edata->lineno);
     221            0 :         appendCSVLiteral(&buf, msgbuf.data);
     222            0 :         pfree(msgbuf.data);
     223              :     }
     224           20 :     appendStringInfoChar(&buf, ',');
     225              : 
     226              :     /* application name */
     227           20 :     if (application_name)
     228           20 :         appendCSVLiteral(&buf, application_name);
     229              : 
     230           20 :     appendStringInfoChar(&buf, ',');
     231              : 
     232              :     /* backend type */
     233           20 :     appendCSVLiteral(&buf, get_backend_type_for_log());
     234           20 :     appendStringInfoChar(&buf, ',');
     235              : 
     236              :     /* leader PID */
     237           20 :     if (MyProc)
     238              :     {
     239           13 :         PGPROC     *leader = MyProc->lockGroupLeader;
     240              : 
     241              :         /*
     242              :          * Show the leader only for active parallel workers.  This leaves out
     243              :          * the leader of a parallel group.
     244              :          */
     245           13 :         if (leader && leader->pid != MyProcPid)
     246            0 :             appendStringInfo(&buf, "%d", leader->pid);
     247              :     }
     248           20 :     appendStringInfoChar(&buf, ',');
     249              : 
     250              :     /* query id */
     251           20 :     appendStringInfo(&buf, "%" PRId64, pgstat_get_my_query_id());
     252              : 
     253           20 :     appendStringInfoChar(&buf, '\n');
     254              : 
     255              :     /* If in the syslogger process, try to write messages direct to file */
     256           20 :     if (MyBackendType == B_LOGGER)
     257            0 :         write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
     258              :     else
     259           20 :         write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
     260              : 
     261           20 :     pfree(buf.data);
     262           20 : }
        

Generated by: LCOV version 2.0-1