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