#############################################################################
# Copyright (c) 2023 Balazs Scheidler <balazs.scheidler@axoflow.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
# As an additional exemption you are allowed to compile & link against the
# OpenSSL libraries as published by the OpenSSL project. See the file
# COPYING for details.
#
#############################################################################

#
# PostgreSQL csvlog
# (https://www.postgresql.org/docs/current/runtime-config-logging.html)
#
# NOTE the message sample below is a multi-line message with embedded NL
# characters.  All of this is a single, multi line log entry that starts
# with the timestamp.
#
# 2023-08-08 12:05:52.805 UTC,,,22113,,64d22fa0.5661,1,,2023-08-08 12:05:52 UTC,23/74060,0,LOG,00000,"automatic vacuum of table ""tablename"": index scans: 0
# pages: 0 removed, 4 remain, 0 skipped due to pins, 0 skipped frozen
# tuples: 114 removed, 268 remain, 0 are dead but not yet removable, oldest xmin: 149738000
# buffer usage: 97 hits, 0 misses, 6 dirtied
# avg read rate: 0.000 MB/s, avg write rate: 114.609 MB/s
# system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s",,,,,,,,,""

block parser postgresql-csvlog-parser(internal(yes) prefix(".pgsql.") on-type-error("drop-property")) {
    channel {
        parser { csv-parser(
            internal(yes)
            columns(
                "timestamp",
                "username",
                "database",
                int("pid"),
                "connection_from",
                "session_id",
                int("session_line_num"),
                "command_tag",
                "session_start_time",
                "virtual_transaction_id",
                int("transaction_id"),
                "severity",
                "sql_state_code",
                "message",
                "detail",
                "hint",
                "internal_query",
                int("internal_query_pos"),
                "context",
                "query",
                int("query_pos"),
                "location",
                "application_name",
                # Available from vesion 13+
                "backend_type",
                # Available from vesion 14+
                int("leader_pid"),
                int("query_id"),
            )
            delimiters(",")
            dialect(escape-double-char)
            flags(strip-whitespace)
            prefix(`prefix`)
            quote-pairs('""')
            on-type-error(`on-type-error`)
        );
        };
        # Extract values into built-in macros
        parser {
          date-parser(
            internal(yes)
            # Extract without the specified timezone as it seems to be not standard compilant.
            format("%Y-%m-%d %H:%M:%S.%f")
            template("$(substr ${`prefix`timestamp} 0 23)")
            );
        };
         rewrite {
            set(int("${`prefix`pid}") value("PID") internal(yes));
            set(string("$(if ('${`prefix`connection_from}' ne '') ${`prefix`connection_from} $HOST_FROM)") value("`prefix`connection_from") internal(yes));
            set(string("${`prefix`message}") value("MESSAGE") internal(yes));
        };

        # Map severity to match https://www.postgresql.org/docs/current/runtime-config-logging.html#RUNTIME-CONFIG-SEVERITY-LEVELS
	# for some reason PgSQL decided to map its internal levels in a
        # shifted fashion above and including "warning". "panic" itself is
	# shifted by two, whereas the other values are shifted by one.
        if (match("DEBUG" value('`prefix`severity') type(string) flags(prefix))) {
            rewrite {
                set-severity("DEBUG");
            };
        } else {
            rewrite {
                set-severity("${`prefix`severity}");

		# shift panic to alert (needs to be shifted by 2)
                set-severity("$(+ $SEVERITY_NUM 1)" condition($SEVERITY_NUM == 0));

                # shift alert..warning (warning=>notice, error=>warning, fatal=>error, alert=>crit)
                set-severity("$(+ $SEVERITY_NUM 1)" condition($SEVERITY_NUM <= 4));
            };
        };
    };
};
