diff --git a/.gitignore b/.gitignore index 485dee6..c5e7447 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ .idea +results/* +log/* +logerrors.o +logerrors.so +regression-test/* diff --git a/Makefile b/Makefile index 7b90062..baa2bd9 100644 --- a/Makefile +++ b/Makefile @@ -4,4 +4,6 @@ DATA = logerrors--1.0.sql logerrors--1.0--1.1.sql logerrors--1.1--2.0.sql logerr OBJS = logerrors.o PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) -include $(PGXS) +REGRESS = logerrors +REGRESS_OPTS = --create-role=postgres --temp-config logerrors.conf --load-extension=logerrors --temp-instance=./temp-check +include $(PGXS) diff --git a/README.md b/README.md index dcf0aee..c3f4e93 100644 --- a/README.md +++ b/README.md @@ -13,26 +13,64 @@ Run psql command: $ CREATE EXTENSION logerrors; +## Tests + +The extension uses standard pgxs regression tests. Run `make installcheck` to run all psql scripts defined in `sql` directory. Output of each is then evaluated by `diff` with corresponding expected output stored in the `expected` directory. + +``` + $ make installcheck + +++ regress install-check in +++ + ============== creating temporary instance ============== + ============== initializing database system ============== + ============== starting postmaster ============== + running on port 51698 with PID 472134 + ============== creating database "contrib_regression" ============== + CREATE DATABASE + ALTER DATABASE + ALTER DATABASE + ALTER DATABASE + ALTER DATABASE + ALTER DATABASE + ALTER DATABASE + ============== installing logerrors ============== + CREATE EXTENSION + ============== creating role "postgres" ============== + CREATE ROLE + GRANT + ============== running regression test queries ============== + test logerrors ... ok 15017 ms + ============== shutting down postmaster ============== + ============== removing temporary instance ============== + +===================== + All 1 tests passed. +===================== + +``` + ## Usage After creating extension you can call `pg_log_errors_stats()` function in psql (without any arguments). ``` postgres=# select * from pg_log_errors_stats(); - time_interval | type | message | count - ---------------+---------+----------------------+------- - | WARNING | TOTAL | 0 - | ERROR | TOTAL | 3 - 600 | ERROR | ERRCODE_SYNTAX_ERROR | 3 - 5 | ERROR | ERRCODE_SYNTAX_ERROR | 2 - | FATAL | TOTAL | 0 + time_interval | type | message | count | username | database | sqlstate + ---------------+---------+----------------------+-------+----------+----------+---------- + | WARNING | TOTAL | 0 | | | + | ERROR | TOTAL | 1 | | | + | FATAL | TOTAL | 0 | | | + 5 | ERROR | ERRCODE_SYNTAX_ERROR | 1 | postgres | postgres | 42601 + 600 | ERROR | ERRCODE_SYNTAX_ERROR | 1 | postgres | postgres | 42601 ``` -In output you can see 4 columns: +In output you can see 7 columns: time_interval: how long (in seconds) has statistics been collected. type: postgresql type of message (now supports only these: warning, error, fatal). message: code of message from log_hook. (or 'TOTAL' for total count of that type messages) count: count of messages of this type at this time_interval in log. + username: effective role causing the message + database: database where the message comes from + sqlstate: code of the message transformed to the form of sqlstate To get number of lines in slow log call `pg_slow_log_stats()`: diff --git a/expected/logerrors.out b/expected/logerrors.out new file mode 100644 index 0000000..ad7e9da --- /dev/null +++ b/expected/logerrors.out @@ -0,0 +1,60 @@ +SET ROLE postgres; +SELECT pg_log_errors_reset(); + pg_log_errors_reset +--------------------- + +(1 row) + +SELECT blah(); +ERROR: function blah() does not exist +LINE 1: SELECT blah(); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT pg_sleep(10); + pg_sleep +---------- + +(1 row) + +SELECT * FROM pg_log_errors_stats(); + time_interval | type | message | count | username | database | sqlstate +---------------+---------+----------------------------+-------+----------+--------------------+---------- + | WARNING | TOTAL | 0 | | | + | ERROR | TOTAL | 1 | | | + | FATAL | TOTAL | 0 | | | + 600 | ERROR | ERRCODE_UNDEFINED_FUNCTION | 1 | postgres | contrib_regression | 42883 +(4 rows) + +DO LANGUAGE plpgsql $$ +BEGIN + RAISE SQLSTATE 'XXXXX'; +END; +$$; +ERROR: XXXXX +CONTEXT: PL/pgSQL function inline_code_block line 3 at RAISE +DO LANGUAGE plpgsql $$ +BEGIN + RAISE SQLSTATE 'XXXXY'; +END; +$$; +ERROR: XXXXY +CONTEXT: PL/pgSQL function inline_code_block line 3 at RAISE +SELECT pg_sleep(5); + pg_sleep +---------- + +(1 row) + +SELECT * FROM pg_log_errors_stats(); + time_interval | type | message | count | username | database | sqlstate +---------------+---------+----------------------------+-------+----------+--------------------+---------- + | WARNING | TOTAL | 0 | | | + | ERROR | TOTAL | 3 | | | + | FATAL | TOTAL | 0 | | | + 5 | ERROR | NOT_KNOWN_ERROR | 1 | postgres | contrib_regression | XXXXX + 5 | ERROR | NOT_KNOWN_ERROR | 1 | postgres | contrib_regression | XXXXY + 600 | ERROR | ERRCODE_UNDEFINED_FUNCTION | 1 | postgres | contrib_regression | 42883 + 600 | ERROR | NOT_KNOWN_ERROR | 1 | postgres | contrib_regression | XXXXX + 600 | ERROR | NOT_KNOWN_ERROR | 1 | postgres | contrib_regression | XXXXY +(8 rows) + diff --git a/logerrors.c b/logerrors.c index 69e233f..5aaed76 100644 --- a/logerrors.c +++ b/logerrors.c @@ -556,7 +556,7 @@ put_values_to_tuple( if (found) long_interval_values[2] = CStringGetTextDatum(err_name->name); else { - sprintf(err_name_str, "NOT_KNOWN_ERROR: %d", key.error_code); + sprintf(err_name_str, "NOT_KNOWN_ERROR"); long_interval_values[2] = CStringGetTextDatum(err_name_str); } /* Count */ @@ -575,12 +575,7 @@ put_values_to_tuple( long_interval_values[5] = CStringGetTextDatum(db_name); /* SQLState */ - if (found) { - long_interval_values[6] = CStringGetTextDatum(unpack_sql_state(err_code.num)); - } - else { - long_interval_nulls[6] = true; - } + long_interval_values[6] = CStringGetTextDatum(unpack_sql_state(err_code.num)); if (elem->counter > 0) { tuplestore_putvalues(tupstore, tupdesc, long_interval_values, long_interval_nulls); diff --git a/logerrors.conf b/logerrors.conf new file mode 100644 index 0000000..3899aaf --- /dev/null +++ b/logerrors.conf @@ -0,0 +1 @@ +shared_preload_libraries='logerrors' diff --git a/sql/logerrors.sql b/sql/logerrors.sql new file mode 100644 index 0000000..51a006e --- /dev/null +++ b/sql/logerrors.sql @@ -0,0 +1,17 @@ +SET ROLE postgres; +SELECT pg_log_errors_reset(); +SELECT blah(); +SELECT pg_sleep(10); +SELECT * FROM pg_log_errors_stats(); +DO LANGUAGE plpgsql $$ +BEGIN + RAISE SQLSTATE 'XXXXX'; +END; +$$; +DO LANGUAGE plpgsql $$ +BEGIN + RAISE SQLSTATE 'XXXXY'; +END; +$$; +SELECT pg_sleep(5); +SELECT * FROM pg_log_errors_stats();