diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index b3b78d2..1ee8945 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -17925,6 +17925,15 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup()); Return information about a file. + + + pg_report_log(eleveltext, message anyelement, ishidestmtboolean, detail text, hint text, context text) + + void + + Write message into log file as per log level. + + @@ -17993,6 +18002,24 @@ SELECT (pg_stat_file('filename')).modification; + + pg_report_log + + + pg_report_log is useful to write custom messages + into current log destination and returns void. + This function don't support the PANIC, FATAL log levels due to their unique internal DB usage, which may cause the database instability. Using ishidestmt, function can write or ignore the current SQL statement into the log file. Also, we can have DETAIL, HINT, CONTEXT log messages by provding detail, hint and context as function arguments. By default, all these parameter values are EMPTY. + Typical usages include: + +postgres=# SELECT pg_report_log('NOTICE', 'Custom Message', true); +NOTICE: Custom Message + pg_report_log +--------------- + +(1 row) + + + diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index ccc030f..4105252 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -940,3 +940,18 @@ RETURNS jsonb LANGUAGE INTERNAL STRICT IMMUTABLE AS 'jsonb_set'; + +CREATE OR REPLACE FUNCTION pg_report_log(elevel TEXT, message TEXT, ishidestmt BOOLEAN DEFAULT TRUE, detail TEXT DEFAULT '', hint TEXT DEFAULT '', context TEXT DEFAULT '') +RETURNS VOID +LANGUAGE INTERNAL +VOLATILE +AS 'pg_report_log'; + +CREATE OR REPLACE FUNCTION pg_report_log(elevel TEXT, message anyelement, ishidestmt BOOLEAN DEFAULT TRUE, detail TEXT DEFAULT '', hint TEXT DEFAULT '', context TEXT DEFAULT '') +RETURNS VOID +VOLATILE +AS +$$ +SELECT pg_report_log($1::pg_catalog.text, $2::pg_catalog.text, $3::pg_catalog.bool, $4::pg_catalog.text, $5::pg_catalog.text, $6::pg_catalog.text) +$$ +LANGUAGE SQL; diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index c0495d9..be25422 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -76,6 +76,103 @@ current_query(PG_FUNCTION_ARGS) } /* + * pg_report_log() + * + * Printing custom log messages in log file. + */ + +typedef struct +{ + int ecode; + char *level; +} errorlevels; + +Datum +pg_report_log(PG_FUNCTION_ARGS) +{ + + /* + * Do not add FATAL, PANIC log levels to the below list. + */ + errorlevels elevels[]={ + {DEBUG5, "DEBUG5"}, {DEBUG4, "DEBUG4"}, {DEBUG3, "DEBUG3"}, + {DEBUG2, "DEBUG2"}, {DEBUG1, "DEBUG1"}, {LOG, "LOG"}, + {COMMERROR, "COMMERROR"}, {INFO, "INFO"}, {NOTICE, "NOTICE"}, + {WARNING, "WARNING"}, {ERROR, "ERROR"} + /* + * Adding PGERROR to elevels if WIN32 + */ + #ifdef WIN32 + ,{PGERROR, "PGERROR"} + #endif + }; + + int itr = 0; + bool ishidestmt = false; + int noelevel = (int) sizeof(elevels)/sizeof(*elevels); + char *level, *detail, *hint, *cntxt; + + level = text_to_cstring(PG_GETARG_TEXT_P(0)); + detail = text_to_cstring(PG_GETARG_TEXT_P(3)); + hint = text_to_cstring(PG_GETARG_TEXT_P(4)); + cntxt = text_to_cstring(PG_GETARG_TEXT_P(5)); + + ishidestmt = PG_GETARG_BOOL(2); + + /* + * Do not expose FATAL, PANIC log levels to outer world. + */ + if(pg_strcasecmp("FATAL", level) == 0) + ereport(ERROR, + (errmsg("%s is an unsupported report log level.", level))); + + else if(pg_strcasecmp("PANIC", level) == 0) + ereport(ERROR, + (errmsg("%s is an unsupported report log level.", level))); + else + { + while (itr < noelevel) + { + if (pg_strcasecmp(elevels[itr].level, level) == 0) + { + /* + * Is errhide statement true + */ + if (ishidestmt == true) + { + ereport(elevels[itr].ecode, + (errmsg("%s", text_to_cstring(PG_GETARG_TEXT_P(1))), + (pg_strcasecmp(detail, "") != 0) ? errdetail("%s", detail) : 0, + (pg_strcasecmp(hint, "") != 0) ? errhint("%s", hint) : 0, + (pg_strcasecmp(cntxt, "") != 0) ? errcontext_msg("%s", cntxt) : 0, + errhidestmt(true) + )); + break; + } + else + { + ereport(elevels[itr].ecode, + (errmsg("%s", text_to_cstring(PG_GETARG_TEXT_P(1))), + (pg_strcasecmp(detail, "") != 0) ? errdetail("%s", detail) : 0, + (pg_strcasecmp(hint, "") != 0) ? errhint("%s", hint) : 0, + (pg_strcasecmp(cntxt, "") != 0) ? errcontext_msg("%s", cntxt) : 0 + )); + break; + } + } + itr++; + } + + /* Invalid log level */ + if (itr == noelevel) + ereport(ERROR, + (errmsg("Unknown log level \"%s\"", level))); + else + PG_RETURN_VOID(); + } +} + +/* * Send a signal to another backend. * * The signal is delivered if the user is either a superuser or the same diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index ddf7c67..da772e2 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -5349,6 +5349,14 @@ DESCR("row security for current context active on table by table oid"); DATA(insert OID = 3299 ( row_security_active PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "25" _null_ _null_ _null_ _null_ _null_ row_security_active_name _null_ _null_ _null_ )); DESCR("row security for current context active on table by table name"); +/* Logging function */ + +DATA(insert OID = 6015 ( pg_report_log PGNSP PGUID 12 1 0 0 0 f f f f t f v 6 0 2278 "25 25 16 25 25 25" _null_ _null_ "{elevel, message, ishidestmt, detail, hint, context}" _null_ _null_ pg_report_log _null_ _null_ _null_ )); +DESCR("write message to log file"); +DATA(insert OID = 6016 ( pg_report_log PGNSP PGUID 14 1 0 0 0 f f f f t f v 6 0 2278 "25 2283 16 25 25 25" _null_ _null_ "{elevel, message, ishidestmt, detail, hint, context}" _null_ _null_ "SELECT pg_report_log($1::pg_catalog.text, $2::pg_catalog.text, $3::pg_catalog.bool, $4::pg_catalog.text, $5::pg_catalog.text, $6::pg_catalog.text)" _null_ _null_ _null_ )); +DESCR("write message to log file"); + + /* * Symbolic values for proargmodes column. Note that these must agree with * the FunctionParameterMode enum in parsenodes.h; we declare them here to diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index fc1679e..0dd1425 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -495,6 +495,7 @@ extern Datum pg_typeof(PG_FUNCTION_ARGS); extern Datum pg_collation_for(PG_FUNCTION_ARGS); extern Datum pg_relation_is_updatable(PG_FUNCTION_ARGS); extern Datum pg_column_is_updatable(PG_FUNCTION_ARGS); +extern Datum pg_report_log(PG_FUNCTION_ARGS); /* oid.c */ extern Datum oidin(PG_FUNCTION_ARGS); diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 7684717..fcb7218 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -16,6 +16,13 @@ #include +/* + * XXX + * If you are adding another elevel, make sure you update the + * pg_report_log() in src/backend/utils/adt/misc.c, with the + * new elevel + */ + /* Error level codes */ #define DEBUG5 10 /* Debugging messages, in categories of * decreasing detail. */ diff --git a/src/test/regress/expected/reportlog.out b/src/test/regress/expected/reportlog.out new file mode 100644 index 0000000..a352564 --- /dev/null +++ b/src/test/regress/expected/reportlog.out @@ -0,0 +1,35 @@ +-- +-- Test for Report Log With WARNING +-- +SELECT pg_catalog.pg_report_log('WARNING', 'Custom Message'); --OK +WARNING: Custom Message + pg_report_log +--------------- + +(1 row) + +-- +-- Test for ERROR with default ishidestmt +-- +SELECT pg_catalog.pg_report_log('ERROR', 'Custom Message'); --ERROR +ERROR: Custom Message +-- +-- Test for ERROR with ishidestmt +-- +SELECT pg_catalog.pg_report_log('ERROR', 'Custom Message', true); --ERROR +ERROR: Custom Message +-- +-- Test for anyelement +-- +SELECT pg_catalog.pg_report_log('WARNING', -1234.34); --OK +WARNING: -1234.34 + pg_report_log +--------------- + +(1 row) + +-- +-- Test for denial of FATAL +-- +SELECT pg_catalog.pg_report_log('FATAL', 'Fatal Message'); --OK +ERROR: FATAL is an unsupported report log level. diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 6fc5d1e..4cd193d 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -97,7 +97,7 @@ test: rules # ---------- # Another group of parallel tests # ---------- -test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb indirect_toast equivclass +test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb indirect_toast reportlog equivclass # ---------- # Another group of parallel tests # NB: temp.sql does a reconnect which transiently uses 2 connections, diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 2ae51cf..6c9f5c3 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -158,3 +158,4 @@ test: with test: xml test: event_trigger test: stats +test: reportlog diff --git a/src/test/regress/sql/reportlog.sql b/src/test/regress/sql/reportlog.sql new file mode 100644 index 0000000..eb9ee90 --- /dev/null +++ b/src/test/regress/sql/reportlog.sql @@ -0,0 +1,24 @@ +-- +-- Test for Report Log With WARNING +-- +SELECT pg_catalog.pg_report_log('WARNING', 'Custom Message'); --OK + +-- +-- Test for ERROR with default ishidestmt +-- +SELECT pg_catalog.pg_report_log('ERROR', 'Custom Message'); --ERROR + +-- +-- Test for ERROR with ishidestmt +-- +SELECT pg_catalog.pg_report_log('ERROR', 'Custom Message', true); --ERROR + +-- +-- Test for anyelement +-- +SELECT pg_catalog.pg_report_log('WARNING', -1234.34); --OK + +-- +-- Test for denial of FATAL +-- +SELECT pg_catalog.pg_report_log('FATAL', 'Fatal Message'); --OK