/* * Add all the autogenerated exceptions as subclasses of SPIError */ static void PLy_generate_spi_exceptions(PyObject *mod, PyObject *base) { int i; for (i = 0; exception_map[i].name != NULL; i++) { bool found; PyObject *exc; PLyExceptionEntry *entry; PyObject *sqlstate; PyObject *dict = PyDict_New(); if (dict == NULL) PLy_elog(ERROR, "could not generate SPI exceptions"); sqlstate = PyString_FromString(unpack_sql_state(exception_map[i].sqlstate)); if (sqlstate == NULL) PLy_elog(ERROR, "could not generate SPI exceptions"); PyDict_SetItemString(dict, "sqlstate", sqlstate); Py_DECREF(sqlstate); exc = PyErr_NewException(exception_map[i].name, base, dict); PyModule_AddObject(mod, exception_map[i].classname, exc); entry = hash_search(PLy_spi_exceptions, &exception_map[i].sqlstate, HASH_ENTER, &found); entry->exc = exc; Assert(!found); } }
/* set attributes of the given exception to details from ErrorData */ void PLy_exception_set_with_details(PyObject *excclass, ErrorData *edata) { PyObject *args = NULL; PyObject *error = NULL; args = Py_BuildValue("(s)", edata->message); if (!args) goto failure; /* create a new exception with the error message as the parameter */ error = PyObject_CallObject(excclass, args); if (!error) goto failure; if (!set_string_attr(error, "sqlstate", unpack_sql_state(edata->sqlerrcode))) goto failure; if (!set_string_attr(error, "detail", edata->detail)) goto failure; if (!set_string_attr(error, "hint", edata->hint)) goto failure; if (!set_string_attr(error, "query", edata->internalquery)) goto failure; if (!set_string_attr(error, "schema_name", edata->schema_name)) goto failure; if (!set_string_attr(error, "table_name", edata->table_name)) goto failure; if (!set_string_attr(error, "column_name", edata->column_name)) goto failure; if (!set_string_attr(error, "datatype_name", edata->datatype_name)) goto failure; if (!set_string_attr(error, "constraint_name", edata->constraint_name)) goto failure; PyErr_SetObject(excclass, error); Py_DECREF(args); Py_DECREF(error); return; failure: Py_XDECREF(args); Py_XDECREF(error); elog(ERROR, "could not convert error to Python exception"); }
/* * write_jsonlog * Write logs in json format. */ static void write_jsonlog(ErrorData *edata) { StringInfoData buf; TransactionId txid = GetTopTransactionIdIfAny(); initStringInfo(&buf); /* Initialize string */ appendStringInfoChar(&buf, '{'); /* Timestamp */ if (log_time[0] == '\0') setup_formatted_log_time(); appendJSONLiteral(&buf, "timestamp", log_time, true); /* Username */ if (MyProcPort) appendJSONLiteral(&buf, "user", MyProcPort->user_name, true); /* Database name */ if (MyProcPort) appendJSONLiteral(&buf, "dbname", MyProcPort->database_name, true); /* Process ID */ if (MyProcPid != 0) appendStringInfo(&buf, "\"pid\":%d,", MyProcPid); /* Remote host and port */ if (MyProcPort && MyProcPort->remote_host) { appendJSONLiteral(&buf, "remote_host", MyProcPort->remote_host, true); if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0') appendJSONLiteral(&buf, "remote_port", MyProcPort->remote_port, true); } /* Session id */ if (MyProcPid != 0) appendStringInfo(&buf, "\"session_id\":\"%lx.%x\",", (long) MyStartTime, MyProcPid); /* Virtual transaction id */ /* keep VXID format in sync with lockfuncs.c */ if (MyProc != NULL && MyProc->backendId != InvalidBackendId) appendStringInfo(&buf, "\"vxid\":\"%d/%u\",", MyProc->backendId, MyProc->lxid); /* Transaction id */ if (txid != InvalidTransactionId) appendStringInfo(&buf, "\"txid\":%u,", GetTopTransactionIdIfAny()); /* Error severity */ appendJSONLiteral(&buf, "error_severity", (char *) error_severity(edata->elevel), true); /* SQL state code */ if (edata->sqlerrcode != ERRCODE_SUCCESSFUL_COMPLETION) appendJSONLiteral(&buf, "state_code", unpack_sql_state(edata->sqlerrcode), true); /* Error detail or Error detail log */ if (edata->detail_log) appendJSONLiteral(&buf, "detail_log", edata->detail_log, true); else if (edata->detail) appendJSONLiteral(&buf, "detail", edata->detail, true); /* Error hint */ if (edata->hint) appendJSONLiteral(&buf, "hint", edata->hint, true); /* Internal query */ if (edata->internalquery) appendJSONLiteral(&buf, "internal_query", edata->internalquery, true); /* Error context */ if (edata->context) appendJSONLiteral(&buf, "context", edata->context, true); /* File error location */ if (Log_error_verbosity >= PGERROR_VERBOSE) { StringInfoData msgbuf; initStringInfo(&msgbuf); if (edata->funcname && edata->filename) appendStringInfo(&msgbuf, "%s, %s:%d", edata->funcname, edata->filename, edata->lineno); else if (edata->filename) appendStringInfo(&msgbuf, "%s:%d", edata->filename, edata->lineno); appendJSONLiteral(&buf, "file_location", msgbuf.data, true); pfree(msgbuf.data); } /* Application name */ if (application_name && application_name[0] != '\0') appendJSONLiteral(&buf, "application_name", application_name, true); /* Error message */ appendJSONLiteral(&buf, "message", edata->message, false); /* Finish string */ appendStringInfoChar(&buf, '}'); appendStringInfoChar(&buf, '\n'); /* If in the syslogger process, try to write messages direct to file */ if (am_syslogger) write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_STDERR); else write_pipe_chunks(buf.data, buf.len); /* Cleanup */ pfree(buf.data); /* Continue chain to previous hook */ if (prev_log_hook) (*prev_log_hook) (edata); }
/* * redis_log_hook * Hook for shipping log events to Redis * (based on jsonlog.c) */ static void redis_log_hook(ErrorData *edata) { StringInfoData buf; TransactionId txid = GetTopTransactionIdIfAny(); bool print_stmt = false; bool send_status = false; /* static counter for line numbers */ static long log_line_number = 0; /* * This is one of the few places where we'd rather not inherit a static * variable's value from the postmaster. But since we will, reset it when * MyProcPid changes. */ if (lastPid != MyProcPid) { log_line_number = 0; lastPid = MyProcPid; formatted_start_time[0] = '\0'; redis_close_connection(); } /* * Check if the log has to be written, if not just exit. */ if (!is_log_level_output(edata->elevel, Redislog_min_messages)) { goto quickExit; } log_line_number++; initStringInfo(&buf); /* Initialize string */ appendStringInfoChar(&buf, '{'); /* Timestamp */ setup_formatted_log_time(); append_json_literal(&buf, "@timestamp", formatted_log_time, true); /* Username */ if (MyProcPort) append_json_literal(&buf, "user_name", MyProcPort->user_name, true); /* Database name */ if (MyProcPort) append_json_literal(&buf, "database_name", MyProcPort->database_name, true); /* Process ID */ if (MyProcPid != 0) appendStringInfo(&buf, "\"process_id\":%d,", MyProcPid); /* Remote host and port */ if (MyProcPort && MyProcPort->remote_host) { append_json_literal(&buf, "remote_host", MyProcPort->remote_host, true); if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0') append_json_literal(&buf, "remote_port", MyProcPort->remote_port, true); } /* Session id */ if (MyProcPid != 0) appendStringInfo(&buf, "\"session_id\":\"%lx.%x\",", (long) MyStartTime, MyProcPid); /* Process ID */ if (MyProcPid != 0) appendStringInfo(&buf, "\"session_line_num\":%ld,", log_line_number); /* PS display */ if (MyProcPort) { StringInfoData msgbuf; const char *psdisp; int displen; initStringInfo(&msgbuf); psdisp = get_ps_display(&displen); appendBinaryStringInfo(&msgbuf, psdisp, displen); append_json_literal(&buf, "command_tag", msgbuf.data, true); pfree(msgbuf.data); } /* session start timestamp */ if (formatted_start_time[0] == '\0') setup_formatted_start_time(); append_json_literal(&buf, "session_start_time", formatted_start_time, true); /* Virtual transaction id */ /* keep VXID format in sync with lockfuncs.c */ if (MyProc != NULL && MyProc->backendId != InvalidBackendId) appendStringInfo(&buf, "\"virtual_transaction_id\":\"%d/%u\",", MyProc->backendId, MyProc->lxid); /* Transaction id */ if (txid != InvalidTransactionId) appendStringInfo(&buf, "\"transaction_id\":%u,", GetTopTransactionIdIfAny()); /* Error severity */ append_json_literal(&buf, "error_severity", (char *) error_severity(edata->elevel), true); /* SQL state code */ if (edata->sqlerrcode != ERRCODE_SUCCESSFUL_COMPLETION) append_json_literal(&buf, "sql_state_code", unpack_sql_state(edata->sqlerrcode), true); /* Error detail or Error detail log */ if (edata->detail_log) append_json_literal(&buf, "detail_log", edata->detail_log, true); else if (edata->detail) append_json_literal(&buf, "detail", edata->detail, true); /* Error hint */ if (edata->hint) append_json_literal(&buf, "hint", edata->hint, true); /* Internal query */ if (edata->internalquery) append_json_literal(&buf, "internal_query", edata->internalquery, true); /* if printed internal query, print internal pos too */ if (edata->internalpos > 0 && edata->internalquery != NULL) appendStringInfo(&buf, "\"internal_query_pos\":%d,", edata->internalpos); /* Error context */ if (edata->context) append_json_literal(&buf, "context", edata->context, true); /* user query --- only reported if not disabled by the caller */ if (is_log_level_output(edata->elevel, Redislog_min_error_statement) && debug_query_string != NULL && !edata->hide_stmt) print_stmt = true; if (print_stmt) append_json_literal(&buf, "query", debug_query_string, true); /* user query position -- only reposted if not disabled by the caller */ if (print_stmt && edata->cursorpos > 0) appendStringInfo(&buf, "\"query_pos\":%d,", edata->cursorpos); /* File error location */ if (Log_error_verbosity >= PGERROR_VERBOSE) { StringInfoData msgbuf; initStringInfo(&msgbuf); if (edata->funcname && edata->filename) appendStringInfo(&msgbuf, "%s, %s:%d", edata->funcname, edata->filename, edata->lineno); else if (edata->filename) appendStringInfo(&msgbuf, "%s:%d", edata->filename, edata->lineno); append_json_literal(&buf, "file_location", msgbuf.data, true); pfree(msgbuf.data); } /* Application name */ if (application_name && application_name[0] != '\0') append_json_literal(&buf, "application_name", application_name, true); /* Error message */ append_json_literal(&buf, "message", edata->message, false); /* Finish string */ appendStringInfoChar(&buf, '}'); appendStringInfoChar(&buf, '\n'); /* Send the data to Redis */ send_status = redis_log_shipper(buf.data, buf.len); /* Skip sending the event to the server, if it was correctly * shipped to Redis and if 'ship_to_redis_only' is set to true */ if (Redislog_ship_to_redis_only && send_status) { edata->output_to_server = false; } /* Cleanup */ pfree(buf.data); quickExit: /* Continue chain to previous hook */ if (prev_log_hook) (*prev_log_hook) (edata); }
/* * write_jsonlog * Write logs in json format. */ static void write_jsonlog(ErrorData *edata) { StringInfoData buf; TransactionId txid = GetTopTransactionIdIfAny(); /* * Disable logs to server, we don't want duplicate entries in * the server. */ edata->output_to_server = false; /* Determine whether message is enabled for server log output */ if (!is_log_level_output(edata->elevel, log_min_messages)) return; initStringInfo(&buf); /* Initialize string */ appendStringInfoChar(&buf, '{'); /* Timestamp */ setup_formatted_log_time(); appendJSONLiteral(&buf, "timestamp", formatted_log_time, true); /* Username */ if (MyProcPort && MyProcPort->user_name) appendJSONLiteral(&buf, "user", MyProcPort->user_name, true); /* Database name */ if (MyProcPort && MyProcPort->database_name) appendJSONLiteral(&buf, "dbname", MyProcPort->database_name, true); /* Process ID */ if (MyProcPid != 0) appendStringInfo(&buf, "\"pid\":%d,", MyProcPid); /* Remote host and port */ if (MyProcPort && MyProcPort->remote_host) { appendJSONLiteral(&buf, "remote_host", MyProcPort->remote_host, true); if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0') appendJSONLiteral(&buf, "remote_port", MyProcPort->remote_port, true); } /* Session id */ if (MyProcPid != 0) appendStringInfo(&buf, "\"session_id\":\"%lx.%x\",", (long) MyStartTime, MyProcPid); /* Virtual transaction id */ /* keep VXID format in sync with lockfuncs.c */ if (MyProc != NULL && MyProc->backendId != InvalidBackendId) appendStringInfo(&buf, "\"vxid\":\"%d/%u\",", MyProc->backendId, MyProc->lxid); /* Transaction id */ if (txid != InvalidTransactionId) appendStringInfo(&buf, "\"txid\":%u,", GetTopTransactionIdIfAny()); /* Error severity */ appendJSONLiteral(&buf, "error_severity", (char *) error_severity(edata->elevel), true); /* SQL state code */ if (edata->sqlerrcode != ERRCODE_SUCCESSFUL_COMPLETION) appendJSONLiteral(&buf, "state_code", unpack_sql_state(edata->sqlerrcode), true); /* Error detail or Error detail log */ if (edata->detail_log) appendJSONLiteral(&buf, "detail_log", edata->detail_log, true); else if (edata->detail) appendJSONLiteral(&buf, "detail", edata->detail, true); /* Error hint */ if (edata->hint) appendJSONLiteral(&buf, "hint", edata->hint, true); /* Internal query */ if (edata->internalquery) appendJSONLiteral(&buf, "internal_query", edata->internalquery, true); /* Error context */ if (edata->context) appendJSONLiteral(&buf, "context", edata->context, true); /* user query --- only reported if not disabled by the caller */ if (is_log_level_output(edata->elevel, log_min_error_statement) && debug_query_string != NULL && !edata->hide_stmt) { appendJSONLiteral(&buf, "statement", debug_query_string, true); if (edata->cursorpos > 0) appendStringInfo(&buf, "\"cursor_position\":%d,", edata->cursorpos); else if (edata->internalpos > 0) appendStringInfo(&buf, "\"internal_position\":%d,", edata->internalpos); } /* File error location */ if (Log_error_verbosity >= PGERROR_VERBOSE) { StringInfoData msgbuf; initStringInfo(&msgbuf); if (edata->funcname && edata->filename) appendStringInfo(&msgbuf, "%s, %s:%d", edata->funcname, edata->filename, edata->lineno); else if (edata->filename) appendStringInfo(&msgbuf, "%s:%d", edata->filename, edata->lineno); appendJSONLiteral(&buf, "file_location", msgbuf.data, true); pfree(msgbuf.data); } /* Application name */ if (application_name && application_name[0] != '\0') appendJSONLiteral(&buf, "application_name", application_name, true); /* Error message */ appendJSONLiteral(&buf, "message", edata->message, false); /* Finish string */ appendStringInfoChar(&buf, '}'); appendStringInfoChar(&buf, '\n'); /* Write to stderr, if enabled */ if ((Log_destination & LOG_DESTINATION_STDERR) != 0) { if (Logging_collector && redirection_done && !am_syslogger) write_pipe_chunks(buf.data, buf.len); else write_console(buf.data, buf.len); } /* If in the syslogger process, try to write messages direct to file */ if (am_syslogger) write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_STDERR); /* Cleanup */ pfree(buf.data); /* Continue chain to previous hook */ if (prev_log_hook) (*prev_log_hook) (edata); }
static void fmtLogMsg(StringInfo dst, ErrorData *edata) { { char formattedLogTime[FORMATTED_TS_LEN]; /* timestamp with milliseconds */ formatNow(formattedLogTime, sizeof formattedLogTime); /* * Always present, non-nullable; don't need to write the N/P * header. */ appendStringInfoString(dst, formattedLogTime); appendStringInfoChar(dst, '\0'); } /* username */ if (MyProcPort) appendStringInfoPtr(dst, MyProcPort->user_name); else appendStringInfoPtr(dst, NULL); /* database name */ if (MyProcPort) appendStringInfoPtr(dst, MyProcPort->database_name); else appendStringInfoPtr(dst, NULL); /* Process id */ { uint32_t nPid = htobe32(savedPid); appendBinaryStringInfo(dst, (void *) &nPid, sizeof nPid); } /* Remote host and port */ if (MyProcPort && MyProcPort->remote_host) { /* 'present' string header, since this string is nullable */ appendStringInfoChar(dst, 'P'); appendStringInfoString(dst, MyProcPort->remote_host); if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0') { appendStringInfoChar(dst, ':'); appendStringInfoString(dst, MyProcPort->remote_port); } appendStringInfoChar(dst, '\0'); } else appendStringInfoPtr(dst, NULL); /* session id; non-nullable */ appendStringInfo(dst, "%lx.%x", (long) MyStartTime, MyProcPid); appendStringInfoChar(dst, '\0'); /* Line number */ { uint64_t nSeqNum = htobe64(seqNum); appendBinaryStringInfo(dst, (void *) &nSeqNum, sizeof nSeqNum); } /* PS display */ if (MyProcPort) { StringInfoData msgbuf; const char *psdisp; int displen; initStringInfo(&msgbuf); psdisp = get_ps_display(&displen); appendBinaryStringInfo(&msgbuf, psdisp, displen); appendStringInfoChar(dst, 'P'); appendStringInfoString(dst, msgbuf.data); appendStringInfoChar(dst, '\0'); pfree(msgbuf.data); } else appendStringInfoPtr(dst, NULL); /* session start timestamp */ if (cachedBackendStartTime[0] == '\0') { /* Rebuild the cache if it was blown */ reCacheBackendStartTime(); } /* backend start time; non-nullable string */ appendStringInfoString(dst, cachedBackendStartTime); appendStringInfoChar(dst, '\0'); /* * Virtual transaction id * * keep VXID format in sync with lockfuncs.c */ if (MyProc != NULL && MyProc->backendId != InvalidBackendId) { appendStringInfoChar(dst, 'P'); appendStringInfo(dst, "%d/%u", MyProc->backendId, MyProc->lxid); appendStringInfoChar(dst, '\0'); } else appendStringInfoPtr(dst, NULL); /* * Transaction id * * This seems to be a mistake both here and in elog.c; in particular, it's * not clear how the epoch would get added here. However, leave room in * the protocol to fix this later by upcasting. */ { uint64_t nTxid = htobe64((uint64) GetTopTransactionIdIfAny()); appendBinaryStringInfo(dst, (void *) &nTxid, sizeof nTxid); } /* Error severity */ { uint32_t nelevel = htobe32(edata->elevel); appendBinaryStringInfo(dst, (void *) &nelevel, sizeof nelevel); } /* SQL state code */ appendStringInfoPtr(dst, unpack_sql_state(edata->sqlerrcode)); /* errmessage */ appendStringInfoPtr(dst, edata->message); /* errdetail or errdetail_log */ if (edata->detail_log) appendStringInfoPtr(dst, edata->detail_log); else appendStringInfoPtr(dst, edata->detail); /* errhint */ appendStringInfoPtr(dst, edata->hint); /* internal query */ appendStringInfoPtr(dst, edata->internalquery); /* if printed internal query, print internal pos too */ if (edata->internalpos > 0 && edata->internalquery != NULL) { uint32_t ninternalpos = htobe32(edata->internalpos); appendBinaryStringInfo(dst, (void *) &ninternalpos, sizeof ninternalpos); } else { uint32_t ninternalpos = htobe32(-1); appendBinaryStringInfo(dst, (void *) &ninternalpos, sizeof ninternalpos); } /* errcontext */ appendStringInfoPtr(dst, edata->context); /* * user query --- only reported if not disabled by the caller. * * Also include query position. */ if (isLogLevelOutput(edata->elevel, log_min_error_statement) && debug_query_string != NULL && !edata->hide_stmt) { uint32_t nCursorPos = htobe32(edata->cursorpos); appendStringInfoPtr(dst, debug_query_string); appendBinaryStringInfo(dst, (void *) &nCursorPos, sizeof nCursorPos); } else { uint32_t nCursorPos = htobe32(-1); appendStringInfoPtr(dst, NULL); appendBinaryStringInfo(dst, (void *) &nCursorPos, sizeof nCursorPos); } /* file error location */ if (Log_error_verbosity >= PGERROR_VERBOSE) { StringInfoData msgbuf; initStringInfo(&msgbuf); if (edata->funcname && edata->filename) appendStringInfo(&msgbuf, "%s, %s:%d", edata->funcname, edata->filename, edata->lineno); else if (edata->filename) appendStringInfo(&msgbuf, "%s:%d", edata->filename, edata->lineno); appendStringInfoChar(dst, 'P'); appendStringInfoString(dst, msgbuf.data); appendStringInfoChar(dst, '\0'); pfree(msgbuf.data); } else appendStringInfoPtr(dst, NULL); /* application name */ appendStringInfoPtr(dst, application_name); }