Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1n9NFF-0007qy-Jc for pgsql-hackers@arkaria.postgresql.org; Mon, 17 Jan 2022 08:20:06 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1n9NEh-0002Ir-Sz for pgsql-hackers@arkaria.postgresql.org; Mon, 17 Jan 2022 08:19:31 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1n9NEh-0002Ii-99 for pgsql-hackers@lists.postgresql.org; Mon, 17 Jan 2022 08:19:31 +0000 Received: from mail-wm1-x32a.google.com ([2a00:1450:4864:20::32a]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1n9NEZ-0005Ly-KB for pgsql-hackers@lists.postgresql.org; Mon, 17 Jan 2022 08:19:30 +0000 Received: by mail-wm1-x32a.google.com with SMTP id ay4-20020a05600c1e0400b0034a81a94607so19885525wmb.1 for ; Mon, 17 Jan 2022 00:19:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aiven.io; s=google; h=from:to:cc:subject:date:message-id:organization:in-reply-to :references:mime-version:content-transfer-encoding; bh=EfjzWUcFkm5cFbZh7U8+I6S3a2fn0hDvmzMViXRC+rM=; b=rbHEdhcVq5fo7kUff6Y8DpNSVp1Y6IdjQigJzULrmH50XxPz0RtoeyfA2iXk4qi9IO Rzfp1LJTR70n1jT/yb22ZQ3XPTwnWAOiOdonmkOLRIGoSYdNulDgelYyo3+ZnDBPirsz gFchsHo5ee1JB5N+AJSbudVImGa7VaVd+px4g= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:organization :in-reply-to:references:mime-version:content-transfer-encoding; bh=EfjzWUcFkm5cFbZh7U8+I6S3a2fn0hDvmzMViXRC+rM=; b=gfRqu0wTqrptev44fQwmiErs9rsgbLcxmf96QEKllfyt1rw3gy4Asbn0MF2X00DbRw DpFRoB7g+B+Vr0DNbXPxeom2rR/TLYlfGJjnlL0tuEEi9ZoZRk2+H1/7w2+K9VsFORz1 Gio+fFYru5WRJXA1A3ikPkluixy291w0okMcopNy30t2KkU1PHi7bkCic447AmJ0qsxW PeKpCXO/6O8MgWpusyhDmljel2lp/5ybCWmn/syvmmSaM4a5vz5wNLo0BzHwnpRvLSy8 Y5dHnbdIatmjZeVerUruwa7ysUEf5K5/2J4ilA/j1DKc3z1g12d/rh6HuFoC+j0AWw10 EAQw== X-Gm-Message-State: AOAM532W9bPyROn84mYNcDIKe29xVyikr0ex0fRiDZ1pUz/ZSHdykkuK INSlkg4rYZUEjqHtXnsZuZytyg== X-Google-Smtp-Source: ABdhPJwNaLq0/fprir+/dDbQYcBBE91mUKkE5hUILISTDVBauV/9P5Ej4Rz2PIz0giJWEHmgh6G4sA== X-Received: by 2002:a05:600c:3b28:: with SMTP id m40mr19213046wms.192.1642407561404; Mon, 17 Jan 2022 00:19:21 -0800 (PST) Received: from aivenronan.localnet (static-176-158-121-96.ftth.abo.bbox.fr. [176.158.121.96]) by smtp.gmail.com with ESMTPSA id o15sm9333868wri.71.2022.01.17.00.19.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 Jan 2022 00:19:20 -0800 (PST) From: Ronan Dunklau To: Julien Rouhaud Cc: Justin Pryzby , Alvaro Herrera , pgsql-hackers@lists.postgresql.org Subject: Re: Proposal: More structured logging Date: Mon, 17 Jan 2022 09:18:04 +0100 Message-ID: <2614931.mvXUDI8C0e@aivenronan> Organization: aiven In-Reply-To: <20220115060959.5j3yw4rvc4ujzdg7@jrouhaud> References: <202108191304.u7g5wwuepwpe@alvherre.pgsql> <10995044.nUPlyArG6x@aivenronan> <20220115060959.5j3yw4rvc4ujzdg7@jrouhaud> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="nextPart11905294.O9o76ZdvQC" Content-Transfer-Encoding: 7Bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk This is a multi-part message in MIME format. --nextPart11905294.O9o76ZdvQC Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="iso-8859-1" Le samedi 15 janvier 2022, 07:09:59 CET Julien Rouhaud a =E9crit : > Hi, >=20 > 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. >=20 > The cfbot reports that the patchset doesn't apply anymore: >=20 > http://cfbot.cputube.org/patch_36_3293.log > =3D=3D=3D Applying patches on top of PostgreSQL commit ID > 43c2175121c829c8591fc5117b725f1f22bfb670 =3D=3D=3D [...] > =3D=3D=3D 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 >=20 > 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= =20 destination has been introduced. I rebased to fix the first patch, and adde= d a=20 new patch to add support for tags in jsonlog output. Best regards, =2D-=20 Ronan Dunklau --nextPart11905294.O9o76ZdvQC Content-Disposition: attachment; filename="v5-0001-Add-ErrorTag-support.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="v5-0001-Add-ErrorTag-support.patch" From 8071760e2a826b4ec88d2580c7bfc76e9bd2ff6e Mon Sep 17 00:00:00 2001 From: Ronan Dunklau Date: Fri, 13 Aug 2021 15:03:18 +0200 Subject: [PATCH v5 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 +#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 --nextPart11905294.O9o76ZdvQC Content-Disposition: attachment; filename="v5-0002-Add-test-module-for-the-new-tag-functionality.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="v5-0002-Add-test-module-for-the-new-tag-functionality.patch" From ca893c36fa4572c91c366280aa4fd5f17ebfe332 Mon Sep 17 00:00:00 2001 From: Ronan Dunklau Date: Fri, 20 Aug 2021 10:46:20 +0200 Subject: [PATCH v5 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 +#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 --nextPart11905294.O9o76ZdvQC Content-Disposition: attachment; filename="v5-0003-Output-error-tags-in-CSV-logs.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="v5-0003-Output-error-tags-in-CSV-logs.patch" From bb746e18cb9ddb7a7c0928e3c3830fc94015bdcb Mon Sep 17 00:00:00 2001 From: Ronan Dunklau Date: Mon, 17 Jan 2022 08:26:46 +0100 Subject: [PATCH v5 3/5] Output error tags in CSV logs --- src/backend/utils/error/csvlog.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/backend/utils/error/csvlog.c b/src/backend/utils/error/csvlog.c index 89f78b447d..999839713c 100644 --- a/src/backend/utils/error/csvlog.c +++ b/src/backend/utils/error/csvlog.c @@ -252,6 +252,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 --nextPart11905294.O9o76ZdvQC Content-Disposition: attachment; filename="v5-0005-Add-error-tags-to-json-output.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="v5-0005-Add-error-tags-to-json-output.patch" From aca65ffb8d1b1642884adb582730b06cc6be93fe Mon Sep 17 00:00:00 2001 From: Ronan Dunklau Date: Mon, 17 Jan 2022 08:56:30 +0100 Subject: [PATCH v5 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 --nextPart11905294.O9o76ZdvQC Content-Disposition: attachment; filename="v5-0004-Add-comment-in-config.sgml-as-a-reminder-to-also-.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="UTF-8"; name="v5-0004-Add-comment-in-config.sgml-as-a-reminder-to-also-.patch" From 1db1172eb698c08504729c1f3a2e9685dab6a374 Mon Sep 17 00:00:00 2001 From: Ronan Dunklau Date: Tue, 11 Jan 2022 10:16:53 +0100 Subject: [PATCH v5 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: - + CREATE TABLE postgres_log ( -- 2.34.1 --nextPart11905294.O9o76ZdvQC--