Le lundi 17 janvier 2022, 09:18:04 CET Ronan Dunklau a écrit :
> Le samedi 15 janvier 2022, 07:09:59 CET Julien Rouhaud a écrit :
> > Hi,
> >
> > On Tue, Jan 11, 2022 at 11:05:26AM +0100, Ronan Dunklau wrote:
> > > Done, and I added anoher commit per your suggestion to add this comment.
> >
> > The cfbot reports that the patchset doesn't apply anymore:
> >
> > http://cfbot.cputube.org/patch_36_3293.log
> > === Applying patches on top of PostgreSQL commit ID
> > 43c2175121c829c8591fc5117b725f1f22bfb670 === [...]
> > === applying patch ./v4-0003-Output-error-tags-in-CSV-logs.patch
> > [...]
> > patching file src/backend/utils/error/elog.c
> > Hunk #2 FAILED at 3014.
> > 1 out of 2 hunks FAILED -- saving rejects to file
> > src/backend/utils/error/elog.c.rej
> >
> > Could you send a rebased version? In the meantime I will switch the cf
> > entry to Waiting on Author.
>
> Thank you for this notification !
>
> The csvlog has been refactored since I wrote the patch, and the new jsonlog
> destination has been introduced. I rebased to fix the first patch, and added
> a new patch to add support for tags in jsonlog output.
>
> Best regards,
Hum, there was a missing import in csvlog.c from the fix above. Sorry about
that.
--
Ronan Dunklau
>From 8071760e2a826b4ec88d2580c7bfc76e9bd2ff6e Mon Sep 17 00:00:00 2001
From: Ronan Dunklau <ronan.dunk...@aiven.io>
Date: Fri, 13 Aug 2021 15:03:18 +0200
Subject: [PATCH v6 1/5] Add ErrorTag support
---
src/backend/utils/error/elog.c | 48 ++++++++++++++++++++++++++++++++++
src/include/utils/elog.h | 10 +++++++
2 files changed, 58 insertions(+)
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 7402696986..4fb4c67c3f 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -460,6 +460,7 @@ errstart(int elevel, const char *domain)
edata->sqlerrcode = ERRCODE_SUCCESSFUL_COMPLETION;
/* errno is saved here so that error parameter eval can't change it */
edata->saved_errno = errno;
+ edata->tags = NIL;
/*
* Any allocations for this error state level should go into ErrorContext
@@ -511,6 +512,7 @@ errfinish(const char *filename, int lineno, const char *funcname)
int elevel;
MemoryContext oldcontext;
ErrorContextCallback *econtext;
+ ListCell *lc;
recursion_depth++;
CHECK_STACK_DEPTH();
@@ -616,7 +618,18 @@ errfinish(const char *filename, int lineno, const char *funcname)
pfree(edata->constraint_name);
if (edata->internalquery)
pfree(edata->internalquery);
+ /* Every tag should have been palloc'ed */
+ if (edata->tags != NIL)
+ {
+ foreach(lc, edata->tags)
+ {
+ ErrorTag *tag = (ErrorTag *) lfirst(lc);
+ pfree(tag->tagvalue);
+ pfree(tag);
+ }
+ pfree(edata->tags);
+ }
errordata_stack_depth--;
/* Exit error-handling context */
@@ -1187,6 +1200,41 @@ errhint_plural(const char *fmt_singular, const char *fmt_plural,
return 0; /* return value does not matter */
}
+int
+errtag(const char *tag, const char *fmt_value,...)
+{
+ ErrorData *edata = &errordata[errordata_stack_depth];
+ ErrorTag *etag;
+ MemoryContext oldcontext;
+ StringInfoData buf;
+
+ recursion_depth++;
+ CHECK_STACK_DEPTH();
+ oldcontext = MemoryContextSwitchTo(edata->assoc_context);
+ etag = palloc(sizeof(ErrorTag));
+ etag->tagname = tag;
+ initStringInfo(&buf);
+ for (;;)
+ {
+ va_list args;
+ int needed;
+
+ errno = edata->saved_errno;
+ va_start(args, fmt_value);
+ needed = appendStringInfoVA(&buf, fmt_value, args);
+ va_end(args);
+ if (needed == 0)
+ break;
+ enlargeStringInfo(&buf, needed);
+ }
+ etag->tagvalue = pstrdup(buf.data);
+ edata->tags = lappend(edata->tags, etag);
+ pfree(buf.data);
+ MemoryContextSwitchTo(oldcontext);
+ recursion_depth--;
+ return 0;
+}
+
/*
* errcontext_msg --- add a context error message text to the current error
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 3eb8de3966..615fae47ef 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -15,6 +15,7 @@
#define ELOG_H
#include <setjmp.h>
+#include "nodes/pg_list.h"
/* Error level codes */
#define DEBUG5 10 /* Debugging messages, in categories of
@@ -193,6 +194,8 @@ extern int errhint(const char *fmt,...) pg_attribute_printf(1, 2);
extern int errhint_plural(const char *fmt_singular, const char *fmt_plural,
unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4);
+extern int errtag(const char *tag, const char *fmt_value,...) pg_attribute_printf(2, 3);
+
/*
* errcontext() is typically called in error context callback functions, not
* within an ereport() invocation. The callback function can be in a different
@@ -395,11 +398,18 @@ typedef struct ErrorData
int internalpos; /* cursor index into internalquery */
char *internalquery; /* text of internally-generated query */
int saved_errno; /* errno at entry */
+ List *tags; /* List of error tags */
/* context containing associated non-constant strings */
struct MemoryContextData *assoc_context;
} ErrorData;
+typedef struct ErrorTag
+{
+ const char *tagname;
+ char *tagvalue;
+} ErrorTag;
+
extern void EmitErrorReport(void);
extern ErrorData *CopyErrorData(void);
extern void FreeErrorData(ErrorData *edata);
--
2.34.1
>From ca893c36fa4572c91c366280aa4fd5f17ebfe332 Mon Sep 17 00:00:00 2001
From: Ronan Dunklau <ronan.dunk...@aiven.io>
Date: Fri, 20 Aug 2021 10:46:20 +0200
Subject: [PATCH v6 2/5] Add test module for the new tag functionality.
This test module consists of an extension exposing a function which just
logs things, and a hook which adds the tags to the detail field in order
to be tested in "regular" output.
---
src/test/modules/test_logging/Makefile | 20 +++
.../test_logging/expected/test_logging.out | 18 +++
.../modules/test_logging/sql/test_logging.sql | 5 +
.../test_logging/test_logging--1.0.sql | 4 +
src/test/modules/test_logging/test_logging.c | 120 ++++++++++++++++++
.../modules/test_logging/test_logging.control | 5 +
6 files changed, 172 insertions(+)
create mode 100644 src/test/modules/test_logging/Makefile
create mode 100644 src/test/modules/test_logging/expected/test_logging.out
create mode 100644 src/test/modules/test_logging/sql/test_logging.sql
create mode 100644 src/test/modules/test_logging/test_logging--1.0.sql
create mode 100644 src/test/modules/test_logging/test_logging.c
create mode 100644 src/test/modules/test_logging/test_logging.control
diff --git a/src/test/modules/test_logging/Makefile b/src/test/modules/test_logging/Makefile
new file mode 100644
index 0000000000..1191886e43
--- /dev/null
+++ b/src/test/modules/test_logging/Makefile
@@ -0,0 +1,20 @@
+# src/test/modules/test_logging/Makefile
+
+MODULES = test_logging
+
+EXTENSION = test_logging
+DATA = test_logging--1.0.sql
+PGFILEDESC = "test_logging - simple log hook test"
+
+REGRESS = test_logging
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_logging
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_logging/expected/test_logging.out b/src/test/modules/test_logging/expected/test_logging.out
new file mode 100644
index 0000000000..41423da535
--- /dev/null
+++ b/src/test/modules/test_logging/expected/test_logging.out
@@ -0,0 +1,18 @@
+CREATE EXTENSION test_logging;
+SET log_min_messages = NOTICE;
+SELECT test_logging(18, 'This is a test message', NULL);
+NOTICE: This is a test message
+DETAIL: NB TAGS: 0 TAGS:
+ test_logging
+--------------
+
+(1 row)
+
+SELECT test_logging(18, 'This is a test message', '{"tag1": "value1", "tag2": "value2"}');
+NOTICE: This is a test message
+DETAIL: NB TAGS: 2 TAGS: tag1: value1 tag2: value2
+ test_logging
+--------------
+
+(1 row)
+
diff --git a/src/test/modules/test_logging/sql/test_logging.sql b/src/test/modules/test_logging/sql/test_logging.sql
new file mode 100644
index 0000000000..bb964b4b63
--- /dev/null
+++ b/src/test/modules/test_logging/sql/test_logging.sql
@@ -0,0 +1,5 @@
+CREATE EXTENSION test_logging;
+SET log_min_messages = NOTICE;
+SELECT test_logging(18, 'This is a test message', NULL);
+SELECT test_logging(18, 'This is a test message', '{"tag1": "value1", "tag2": "value2"}');
+
diff --git a/src/test/modules/test_logging/test_logging--1.0.sql b/src/test/modules/test_logging/test_logging--1.0.sql
new file mode 100644
index 0000000000..663af2db8f
--- /dev/null
+++ b/src/test/modules/test_logging/test_logging--1.0.sql
@@ -0,0 +1,4 @@
+CREATE FUNCTION test_logging(level int, message text, tags jsonb)
+RETURNS VOID
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
diff --git a/src/test/modules/test_logging/test_logging.c b/src/test/modules/test_logging/test_logging.c
new file mode 100644
index 0000000000..18cd39b8e2
--- /dev/null
+++ b/src/test/modules/test_logging/test_logging.c
@@ -0,0 +1,120 @@
+/*--------------------------------------------------------------------------
+ *
+ * test_logging.c
+ * Test logging functions
+ *
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/test/modules/test_logging/test_logging.c
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include <unistd.h>
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/jsonb.h"
+
+PG_MODULE_MAGIC;
+
+static emit_log_hook_type prev_log_hook = NULL;
+
+void _PG_init(void);
+void _PG_fini(void);
+
+static void move_tags_to_detail(ErrorData *edata);
+
+static char *
+jsonb_value_to_tagvalue(JsonbValue *v)
+{
+ switch (v->type)
+ {
+ case jbvNull:
+ return "";
+ case jbvString:
+ return pnstrdup(v->val.string.val, v->val.string.len);
+ default:
+ elog(ERROR, "jsonb type not allowed here: %d", (int) v->type);
+ }
+}
+
+static void
+jsonb_tags_to_key_value_text_pairs(Jsonb *tags, List **keys, List **values)
+{
+ JsonbValue v;
+ JsonbIterator *it;
+ JsonbIteratorToken r;
+ bool skipNested = false;
+
+ it = JsonbIteratorInit(&tags->root);
+ *keys = *values = NIL;
+ while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
+ {
+ skipNested = true;
+ if (r == WJB_KEY)
+ {
+ *keys = lappend(*keys, pnstrdup(v.val.string.val, v.val.string.len));
+ r = JsonbIteratorNext(&it, &v, skipNested);
+ Assert(r != WJB_DONE);
+ *values = lappend(*values, jsonb_value_to_tagvalue(&v));
+ }
+ }
+}
+
+PG_FUNCTION_INFO_V1(test_logging);
+Datum
+test_logging(PG_FUNCTION_ARGS)
+{
+ int level = PG_GETARG_INT32(0);
+ char *message = "";
+ List *keys = NIL,
+ *values = NIL;
+ ListCell *lk,
+ *lv;
+
+ if (!PG_ARGISNULL(1))
+ {
+ message = text_to_cstring(PG_GETARG_TEXT_PP(1));
+ }
+ if (!PG_ARGISNULL(2))
+ {
+ jsonb_tags_to_key_value_text_pairs(PG_GETARG_JSONB_P(2), &keys, &values);
+ }
+ errstart(level, TEXTDOMAIN);
+ errmsg("%s", message);
+ forboth(lk, keys, lv, values)
+ {
+ errtag(lfirst(lk), "%s", (char *) lfirst(lv));
+ }
+ errfinish(__FILE__, __LINE__, PG_FUNCNAME_MACRO);
+
+ PG_RETURN_VOID();
+}
+
+void
+move_tags_to_detail(ErrorData *edata)
+{
+ StringInfoData buf;
+ ListCell *lc;
+
+ initStringInfo(&buf);
+ foreach(lc, edata->tags)
+ {
+ ErrorTag *tag = (ErrorTag *) lfirst(lc);
+
+ appendStringInfo(&buf, "%s: %s ", tag->tagname, tag->tagvalue);
+ }
+ errdetail("NB TAGS: %d TAGS: %s", list_length(edata->tags), buf.data);
+ pfree(buf.data);
+}
+
+void
+_PG_init(void)
+{
+ prev_log_hook = emit_log_hook;
+ emit_log_hook = move_tags_to_detail;
+}
diff --git a/src/test/modules/test_logging/test_logging.control b/src/test/modules/test_logging/test_logging.control
new file mode 100644
index 0000000000..b1596b4b1d
--- /dev/null
+++ b/src/test/modules/test_logging/test_logging.control
@@ -0,0 +1,5 @@
+# test_logging extension
+comment = 'test_logging - simple extension for emitting logs'
+default_version = '1.0'
+module_pathname = '$libdir/test_logging'
+relocatable = true
--
2.34.1
>From 8d2d03f5f8efb7aa8bfa17e6205010b3d26e5cda Mon Sep 17 00:00:00 2001
From: Ronan Dunklau <ronan.dunk...@aiven.io>
Date: Mon, 17 Jan 2022 08:26:46 +0100
Subject: [PATCH v6 3/5] Output error tags in CSV logs
---
src/backend/utils/error/csvlog.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/src/backend/utils/error/csvlog.c b/src/backend/utils/error/csvlog.c
index 89f78b447d..e84cd9c202 100644
--- a/src/backend/utils/error/csvlog.c
+++ b/src/backend/utils/error/csvlog.c
@@ -27,6 +27,7 @@
#include "utils/backend_status.h"
#include "utils/elog.h"
#include "utils/guc.h"
+#include "utils/json.h"
#include "utils/ps_status.h"
@@ -252,6 +253,31 @@ write_csvlog(ErrorData *edata)
/* query id */
appendStringInfo(&buf, "%lld", (long long) pgstat_get_my_query_id());
+ /* tags */
+ if (edata->tags != NIL)
+ {
+ StringInfoData tagbuf;
+ ListCell *lc;
+ bool first = true;
+
+ initStringInfo(&tagbuf);
+ appendStringInfoChar(&tagbuf, '{');
+ foreach(lc, edata->tags)
+ {
+ ErrorTag *etag = lfirst(lc);
+
+ if (!first)
+ appendStringInfoChar(&tagbuf, ',');
+ escape_json(&tagbuf, etag->tagname);
+ appendStringInfoChar(&tagbuf, ':');
+ escape_json(&tagbuf, etag->tagvalue);
+ first = false;
+ }
+ appendStringInfoChar(&tagbuf, '}');
+ appendCSVLiteral(&buf, tagbuf.data);
+ pfree(tagbuf.data);
+ }
+
appendStringInfoChar(&buf, '\n');
/* If in the syslogger process, try to write messages direct to file */
--
2.34.1
>From 3606848935fb6d5f05e6c1e73a05a7de489c5ad8 Mon Sep 17 00:00:00 2001
From: Ronan Dunklau <ronan.dunk...@aiven.io>
Date: Tue, 11 Jan 2022 10:16:53 +0100
Subject: [PATCH v6 4/5] Add comment in config.sgml as a reminder to also
update file_fdw.sgml
---
doc/src/sgml/config.sgml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 4cd9818acf..0ff358233f 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7392,7 +7392,7 @@ log_line_prefix = '%m [%p] %q%u@%d/%a '
application name, backend type, process ID of parallel group leader,
and query id.
Here is a sample table definition for storing CSV-format log output:
-
+<!-- Don't forget to also update file_fdw.sgml if you change this table definition -->
<programlisting>
CREATE TABLE postgres_log
(
--
2.34.1
>From 9dad84190e1f7063e888e616143664e64403f476 Mon Sep 17 00:00:00 2001
From: Ronan Dunklau <ronan.dunk...@aiven.io>
Date: Mon, 17 Jan 2022 08:56:30 +0100
Subject: [PATCH v6 5/5] Add error tags to json output
---
src/backend/utils/error/jsonlog.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/src/backend/utils/error/jsonlog.c b/src/backend/utils/error/jsonlog.c
index 843641c865..70a62c1ec6 100644
--- a/src/backend/utils/error/jsonlog.c
+++ b/src/backend/utils/error/jsonlog.c
@@ -289,6 +289,32 @@ write_jsonlog(ErrorData *edata)
appendJSONKeyValueFmt(&buf, "query_id", false, "%lld",
(long long) pgstat_get_my_query_id());
+ /* tags */
+ if (edata->tags != NIL)
+ {
+ StringInfoData tagbuf;
+ ListCell *lc;
+ bool first = true;
+
+
+ initStringInfo(&tagbuf);
+ appendStringInfoChar(&tagbuf, '{');
+ foreach(lc, edata->tags)
+ {
+ ErrorTag *etag = lfirst(lc);
+ if (!first)
+ appendStringInfoChar(&tagbuf, ',');
+ escape_json(&tagbuf, etag->tagname);
+ appendStringInfoChar(&tagbuf, ':');
+ escape_json(&tagbuf, etag->tagvalue);
+ first = false;
+ }
+ appendStringInfoChar(&tagbuf, '}');
+ appendJSONKeyValue(&buf, "tags", tagbuf.data, false);
+ pfree(tagbuf.data);
+ }
+
+
/* Finish string */
appendStringInfoChar(&buf, '}');
appendStringInfoChar(&buf, '\n');
--
2.34.1