Age Owner Branch data TLA 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
823 michael@paquier.xyz 37 :CBC 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 [ + + ]: 1709 : while ((c = *p++) != '\0')
48 : : {
49 [ + + ]: 1620 : if (c == '"')
50 [ - + ]: 6 : appendStringInfoCharMacro(buf, '"');
51 [ - + ]: 1620 : 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 : : {
823 michael@paquier.xyz 115 :UBC 0 : appendStringInfoChar(&buf, ':');
116 : 0 : appendStringInfoString(&buf, MyProcPort->remote_port);
117 : : }
823 michael@paquier.xyz 118 :CBC 9 : appendStringInfoChar(&buf, '"');
119 : : }
120 : 20 : appendStringInfoChar(&buf, ',');
121 : :
122 : : /* session id */
123 : 20 : appendStringInfo(&buf, "%lx.%x", (long) 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 */
42 heikki.linnakangas@i 153 [ + + + + ]:GNC 20 : if (MyProc != NULL && MyProc->vxid.procNumber != INVALID_PROC_NUMBER)
154 : 9 : appendStringInfo(&buf, "%d/%u", MyProc->vxid.procNumber, MyProc->vxid.lxid);
823 michael@paquier.xyz 155 :CBC 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)
823 michael@paquier.xyz 175 :UBC 0 : appendCSVLiteral(&buf, edata->detail_log);
176 : : else
823 michael@paquier.xyz 177 :CBC 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)
823 michael@paquier.xyz 190 :UBC 0 : appendStringInfo(&buf, "%d", edata->internalpos);
823 michael@paquier.xyz 191 :CBC 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 : :
823 michael@paquier.xyz 212 :UBC 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 : : }
823 michael@paquier.xyz 224 :CBC 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)
823 michael@paquier.xyz 246 :UBC 0 : appendStringInfo(&buf, "%d", leader->pid);
247 : : }
823 michael@paquier.xyz 248 :CBC 20 : appendStringInfoChar(&buf, ',');
249 : :
250 : : /* query id */
251 : 20 : appendStringInfo(&buf, "%lld", (long long) 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)
823 michael@paquier.xyz 257 :UBC 0 : write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
258 : : else
823 michael@paquier.xyz 259 :CBC 20 : write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
260 : :
261 : 20 : pfree(buf.data);
262 : 20 : }
|