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