Coverage Report

Created: 2025-07-03 06:49

/src/postgres/src/backend/utils/error/csvlog.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * csvlog.c
4
 *    CSV logging
5
 *
6
 * Portions Copyright (c) 1996-2025, 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
appendCSVLiteral(StringInfo buf, const char *data)
38
0
{
39
0
  const char *p = data;
40
0
  char    c;
41
42
  /* avoid confusing an empty string with NULL */
43
0
  if (p == NULL)
44
0
    return;
45
46
0
  appendStringInfoCharMacro(buf, '"');
47
0
  while ((c = *p++) != '\0')
48
0
  {
49
0
    if (c == '"')
50
0
      appendStringInfoCharMacro(buf, '"');
51
0
    appendStringInfoCharMacro(buf, c);
52
0
  }
53
0
  appendStringInfoCharMacro(buf, '"');
54
0
}
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
write_csvlog(ErrorData *edata)
64
0
{
65
0
  StringInfoData buf;
66
0
  bool    print_stmt = false;
67
68
  /* static counter for line numbers */
69
0
  static long log_line_number = 0;
70
71
  /* has counter been reset in current process? */
72
0
  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
0
  if (log_my_pid != MyProcPid)
80
0
  {
81
0
    log_line_number = 0;
82
0
    log_my_pid = MyProcPid;
83
0
    reset_formatted_start_time();
84
0
  }
85
0
  log_line_number++;
86
87
0
  initStringInfo(&buf);
88
89
  /* timestamp with milliseconds */
90
0
  appendStringInfoString(&buf, get_formatted_log_time());
91
0
  appendStringInfoChar(&buf, ',');
92
93
  /* username */
94
0
  if (MyProcPort)
95
0
    appendCSVLiteral(&buf, MyProcPort->user_name);
96
0
  appendStringInfoChar(&buf, ',');
97
98
  /* database name */
99
0
  if (MyProcPort)
100
0
    appendCSVLiteral(&buf, MyProcPort->database_name);
101
0
  appendStringInfoChar(&buf, ',');
102
103
  /* Process id  */
104
0
  if (MyProcPid != 0)
105
0
    appendStringInfo(&buf, "%d", MyProcPid);
106
0
  appendStringInfoChar(&buf, ',');
107
108
  /* Remote host and port */
109
0
  if (MyProcPort && MyProcPort->remote_host)
110
0
  {
111
0
    appendStringInfoChar(&buf, '"');
112
0
    appendStringInfoString(&buf, MyProcPort->remote_host);
113
0
    if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
114
0
    {
115
0
      appendStringInfoChar(&buf, ':');
116
0
      appendStringInfoString(&buf, MyProcPort->remote_port);
117
0
    }
118
0
    appendStringInfoChar(&buf, '"');
119
0
  }
120
0
  appendStringInfoChar(&buf, ',');
121
122
  /* session id */
123
0
  appendStringInfo(&buf, INT64_HEX_FORMAT ".%x", MyStartTime, MyProcPid);
124
0
  appendStringInfoChar(&buf, ',');
125
126
  /* Line number */
127
0
  appendStringInfo(&buf, "%ld", log_line_number);
128
0
  appendStringInfoChar(&buf, ',');
129
130
  /* PS display */
131
0
  if (MyProcPort)
132
0
  {
133
0
    StringInfoData msgbuf;
134
0
    const char *psdisp;
135
0
    int     displen;
136
137
0
    initStringInfo(&msgbuf);
138
139
0
    psdisp = get_ps_display(&displen);
140
0
    appendBinaryStringInfo(&msgbuf, psdisp, displen);
141
0
    appendCSVLiteral(&buf, msgbuf.data);
142
143
0
    pfree(msgbuf.data);
144
0
  }
145
0
  appendStringInfoChar(&buf, ',');
146
147
  /* session start timestamp */
148
0
  appendStringInfoString(&buf, get_formatted_start_time());
149
0
  appendStringInfoChar(&buf, ',');
150
151
  /* Virtual transaction id */
152
  /* keep VXID format in sync with lockfuncs.c */
153
0
  if (MyProc != NULL && MyProc->vxid.procNumber != INVALID_PROC_NUMBER)
154
0
    appendStringInfo(&buf, "%d/%u", MyProc->vxid.procNumber, MyProc->vxid.lxid);
155
0
  appendStringInfoChar(&buf, ',');
156
157
  /* Transaction id */
158
0
  appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny());
159
0
  appendStringInfoChar(&buf, ',');
160
161
  /* Error severity */
162
0
  appendStringInfoString(&buf, _(error_severity(edata->elevel)));
163
0
  appendStringInfoChar(&buf, ',');
164
165
  /* SQL state code */
166
0
  appendStringInfoString(&buf, unpack_sql_state(edata->sqlerrcode));
167
0
  appendStringInfoChar(&buf, ',');
168
169
  /* errmessage */
170
0
  appendCSVLiteral(&buf, edata->message);
171
0
  appendStringInfoChar(&buf, ',');
172
173
  /* errdetail or errdetail_log */
174
0
  if (edata->detail_log)
175
0
    appendCSVLiteral(&buf, edata->detail_log);
176
0
  else
177
0
    appendCSVLiteral(&buf, edata->detail);
178
0
  appendStringInfoChar(&buf, ',');
179
180
  /* errhint */
181
0
  appendCSVLiteral(&buf, edata->hint);
182
0
  appendStringInfoChar(&buf, ',');
183
184
  /* internal query */
185
0
  appendCSVLiteral(&buf, edata->internalquery);
186
0
  appendStringInfoChar(&buf, ',');
187
188
  /* if printed internal query, print internal pos too */
189
0
  if (edata->internalpos > 0 && edata->internalquery != NULL)
190
0
    appendStringInfo(&buf, "%d", edata->internalpos);
191
0
  appendStringInfoChar(&buf, ',');
192
193
  /* errcontext */
194
0
  if (!edata->hide_ctx)
195
0
    appendCSVLiteral(&buf, edata->context);
196
0
  appendStringInfoChar(&buf, ',');
197
198
  /* user query --- only reported if not disabled by the caller */
199
0
  print_stmt = check_log_of_query(edata);
200
0
  if (print_stmt)
201
0
    appendCSVLiteral(&buf, debug_query_string);
202
0
  appendStringInfoChar(&buf, ',');
203
0
  if (print_stmt && edata->cursorpos > 0)
204
0
    appendStringInfo(&buf, "%d", edata->cursorpos);
205
0
  appendStringInfoChar(&buf, ',');
206
207
  /* file error location */
208
0
  if (Log_error_verbosity >= PGERROR_VERBOSE)
209
0
  {
210
0
    StringInfoData msgbuf;
211
212
0
    initStringInfo(&msgbuf);
213
214
0
    if (edata->funcname && edata->filename)
215
0
      appendStringInfo(&msgbuf, "%s, %s:%d",
216
0
               edata->funcname, edata->filename,
217
0
               edata->lineno);
218
0
    else if (edata->filename)
219
0
      appendStringInfo(&msgbuf, "%s:%d",
220
0
               edata->filename, edata->lineno);
221
0
    appendCSVLiteral(&buf, msgbuf.data);
222
0
    pfree(msgbuf.data);
223
0
  }
224
0
  appendStringInfoChar(&buf, ',');
225
226
  /* application name */
227
0
  if (application_name)
228
0
    appendCSVLiteral(&buf, application_name);
229
230
0
  appendStringInfoChar(&buf, ',');
231
232
  /* backend type */
233
0
  appendCSVLiteral(&buf, get_backend_type_for_log());
234
0
  appendStringInfoChar(&buf, ',');
235
236
  /* leader PID */
237
0
  if (MyProc)
238
0
  {
239
0
    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
0
    if (leader && leader->pid != MyProcPid)
246
0
      appendStringInfo(&buf, "%d", leader->pid);
247
0
  }
248
0
  appendStringInfoChar(&buf, ',');
249
250
  /* query id */
251
0
  appendStringInfo(&buf, "%" PRId64, pgstat_get_my_query_id());
252
253
0
  appendStringInfoChar(&buf, '\n');
254
255
  /* If in the syslogger process, try to write messages direct to file */
256
0
  if (MyBackendType == B_LOGGER)
257
0
    write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
258
0
  else
259
0
    write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
260
261
0
  pfree(buf.data);
262
0
}