背景

如果使用了CSV日志个数,可以使用file_fdw访问数据库的日志(慢日志,错误日志,审计日志),如下:

《使用SQL查询数据库日志 - file_fdw , csvlog , program , find》

日志是什么格式?日志在哪个目录?数据盘在哪个目录?都可以通过参数得到。所以我们不需要记录这些东西,直接使用函数来创建file_fdw外部表来查询日志。

如果不是CSV格式,可以使用pg_read_file函数来读取日志的内容。


1. log_destination

3. Valid values are combinations of "stderr", "syslog", "csvlog", and "eventlog", depending on the platform.

使用psql变量存储日志、数据盘位置,便于你创建fdw表

man psql

variable用法

《PostgreSQL Oracle 兼容性之 psql sqlplus 变量 & set variable》

《使用SQL查询数据库日志 - file_fdw , csvlog , program , find》

psql 客户端,使用gset代替分号,可以将结果存储到psql变量中

如下,创建两个psql变量data_directory,log_directory

1、获取数据库的数据、日志目录位置


1. select setting as data_directory from pg_settings where name='data_directory'
2. \gset

4. select setting as log_directory from pg_settings where name='log_directory'
5. \gset

2、使用pg_ls_logdir()可列出日志文件目录的文件


1. postgres=# select * from pg_ls_logdir() order by modification;
2. name               |  size  |      modification
3. ----------------------------------+--------+------------------------
4. postgresql-2019-03-26_062949.log |    168 | 2019-03-26 14:29:49+08
5. postgresql-2019-03-26_062949.csv |   1800 | 2019-03-26 14:41:48+08
6. postgresql-2019-03-26_064150.csv | 143182 | 2019-03-27 10:03:12+08
7. postgresql-2019-03-26_064150.log |   1734 | 2019-03-27 10:03:12+08
8. postgresql-2019-03-27_020316.log |    168 | 2019-03-27 10:03:16+08
9. postgresql-2019-03-27_020316.csv |  25697 | 2019-05-25 00:11:30+08
10. postgresql-2019-05-28_080838.log |   8085 | 2019-07-10 09:39:33+08
11. postgresql-2019-05-28_080838.csv |  46466 | 2019-07-10 10:08:10+08
12. (8 rows)

14. postgres=# select * from pg_ls_logdir() where name ~ 'csv$' order by modification;
15. name               |  size  |      modification
16. ----------------------------------+--------+------------------------
17. postgresql-2019-03-26_062949.csv |   1800 | 2019-03-26 14:41:48+08
18. postgresql-2019-03-26_064150.csv | 143182 | 2019-03-27 10:03:12+08
19. postgresql-2019-03-27_020316.csv |  25697 | 2019-05-25 00:11:30+08
20. postgresql-2019-05-28_080838.csv |  46466 | 2019-07-10 10:08:10+08
21. (4 rows)

3、将日志文件名记录到变量中


1. select name as log_name from pg_ls_logdir() where name ~ 'csv$' order by modification desc limit 1
2. \gset

4、使用psql变量拼出sql,使用pg_read_file直接读取文件内容,指定从哪个字节读到哪个字节。

注意pg_read_file函数返回的结果不分行,也不区分字段,如下输出0字节到100字节。但是pg_read_file可以读取任意格式的文件。


1. select log from pg_read_file(:'data_directory'||'/'||:'log_directory'||'/'||:'log_name', 0, 100) as log;


1. postgres=# select log from pg_read_file(:'data_directory'||'/'||:'log_directory'||'/'||:'log_name', 0, 10000) as log;
2. log

4. --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5. ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
6. 2019-07-20 00:02:14.771 CST,,,10766,,5d2aea65.2a0e,19,,2019-07-14 16:40:05 CST,,0,LOG,00000,"checkpoint starting: time",,,,,,,,"LogCheckpointStart, xlog.c:8340",""
7. +
8. 2019-07-20 00:02:27.258 CST,,,10766,,5d2aea65.2a0e,20,,2019-07-14 16:40:05 CST,,0,LOG,00000,"checkpoint complete: wrote 124 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=12.
9. 484 s, sync=0.000 s, total=12.487 s; sync files=48, longest=0.000 s, average=0.000 s; distance=218 kB, estimate=1753715 kB",,,,,,,,"LogCheckpointEnd, xlog.c:8422",""+
10. 2019-07-20 00:30:09.784 CST,,,65215,"[local]",5d31f011.febf,1,"",2019-07-20 00:30:09 CST,,0,LOG,00000,"connection received: host=[local]",,,,,,,,"BackendInitialize, postmaster.c:4294",""
11. +
12. 2019-07-20 00:30:09.785 CST,"postgres","postgres",65215,"[local]",5d31f011.febf,2,"authentication",2019-07-20 00:30:09 CST,3/20405,0,LOG,00000,"connection authorized: user=postgres database=pos
13. tgres application_name=psql",,,,,,,,"PerformAuthentication, postinit.c:303",""                                                                                       +
14. 2019-07-20 00:37:14.358 CST,,,10766,,5d2aea65.2a0e,21,,2019-07-14 16:40:05 CST,,0,LOG,00000,"checkpoint starting: time",,,,,,,,"LogCheckpointStart, xlog.c:8340",""
15. +
16. 2019-07-20 00:37:14.624 CST,,,10766,,5d2aea65.2a0e,22,,2019-07-14 16:40:05 CST,,0,LOG,00000,"checkpoint complete: wrote 2 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.264
17. s, sync=0.000 s, total=0.266 s; sync files=2, longest=0.000 s, average=0.000 s; distance=5 kB, estimate=1578344 kB",,,,,,,,"LogCheckpointEnd, xlog.c:8422",""       +

19. (1 row)

5、使用regexp_split_to_table可以分行,但是如果sql也是分行的不太好处理

https://www.postgresql.org/docs/12/functions-matching.html#POSIX-EMBEDDED-OPTIONS-TABLE


1. postgres=# select regexp_split_to_table(log,'\n') with ordinality from pg_read_file(:'data_directory'||'/'||:'log_directory'||'/'||:'log_name', 0, 1000000) as log;
2. regexp_split_to_table

4. --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5. ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
6. 2019-07-20 00:02:14.771 CST,,,10766,,5d2aea65.2a0e,19,,2019-07-14 16:40:05 CST,,0,LOG,00000,"checkpoint starting: time",,,,,,,,"LogCheckpointStart, xlog.c:8340",""
7. 2019-07-20 00:02:27.258 CST,,,10766,,5d2aea65.2a0e,20,,2019-07-14 16:40:05 CST,,0,LOG,00000,"checkpoint complete: wrote 124 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=12.
8. 484 s, sync=0.000 s, total=12.487 s; sync files=48, longest=0.000 s, average=0.000 s; distance=218 kB, estimate=1753715 kB",,,,,,,,"LogCheckpointEnd, xlog.c:8422",""
9. 2019-07-20 00:30:09.784 CST,,,65215,"[local]",5d31f011.febf,1,"",2019-07-20 00:30:09 CST,,0,LOG,00000,"connection received: host=[local]",,,,,,,,"BackendInitialize, postmaster.c:4294",""
10. 2019-07-20 00:30:09.785 CST,"postgres","postgres",65215,"[local]",5d31f011.febf,2,"authentication",2019-07-20 00:30:09 CST,3/20405,0,LOG,00000,"connection authorized: user=postgres database=pos
11. tgres application_name=psql",,,,,,,,"PerformAuthentication, postinit.c:303",""
12. 2019-07-20 00:37:14.358 CST,,,10766,,5d2aea65.2a0e,21,,2019-07-14 16:40:05 CST,,0,LOG,00000,"checkpoint starting: time",,,,,,,,"LogCheckpointStart, xlog.c:8340",""
13. 2019-07-20 00:37:14.624 CST,,,10766,,5d2aea65.2a0e,22,,2019-07-14 16:40:05 CST,,0,LOG,00000,"checkpoint complete: wrote 2 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.264
14. s, sync=0.000 s, total=0.266 s; sync files=2, longest=0.000 s, average=0.000 s; distance=5 kB, estimate=1578344 kB",,,,,,,,"LogCheckpointEnd, xlog.c:8422",""

16. (7 rows)

6、使用file_fdw查看日志内容,很完美的呈现。


1. create schema _sys;
2. set search_path=_sys,public,"$user";
3. create extension file_fdw with schema _sys;
4. CREATE SERVER pglog FOREIGN DATA WRAPPER file_fdw;

为了让file_fdw可以读取所有日志文件,使用program来读取文件内容。将外部表中的program的内容存入变量


1. \set pro '''find ':data_directory'/':log_directory' -type f -name "*.csv" -exec cat {} \\;'', format ''csv'''

4. postgres=# \set pro '''find ':data_directory'/':log_directory' -type f -name "*.csv" -exec cat {} \\;'', format ''csv'''
5. postgres=# \echo :pro
6. 'find /data01/pg12/pg_root12000/log -type f -name "*.csv" -exec cat {} \;', format 'csv'

7、建立fdw表如下


1. CREATE FOREIGN TABLE pglog (
2. log_time timestamp(3) with time zone,
3. user_name text,
4. database_name text,
5. process_id integer,
6. connection_from text,
7. session_id text,
8. session_line_num bigint,
9. command_tag text,
10. session_start_time timestamp with time zone,
11. virtual_transaction_id text,
12. transaction_id bigint,
13. error_severity text,
14. sql_state_code text,
15. message text,
16. detail text,
17. hint text,
18. internal_query text,
19. internal_query_pos integer,
20. context text,
21. query text,
22. query_pos integer,
23. location text,
24. application_name text
25. ) SERVER pglog
26. OPTIONS ( program :pro );

8、如果想过滤掉一些记录,可以创建视图


1. create view v_log as select * from pglog where ... -- 过滤不想输出的内容
2. grant select on v_log to public;

9、查询例子


1. postgres=# select * from pglog limit 2;
2. -[ RECORD 1 ]----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3. -----------------------------------------------------
4. log_time               | 2019-07-20 14:02:14.771+08
5. user_name              |
6. database_name          |
7. process_id             | 10766
8. connection_from        |
9. session_id             | 5d2aea65.2a0e
10. session_line_num       | 19
11. command_tag            |
12. session_start_time     | 2019-07-15 06:40:05+08
13. virtual_transaction_id |
14. transaction_id         | 0
15. error_severity         | LOG
16. sql_state_code         | 00000
17. message                | checkpoint starting: time
18. detail                 |
19. hint                   |
20. internal_query         |
21. internal_query_pos     |
22. context                |
23. query                  |
24. query_pos              |
25. location               | LogCheckpointStart, xlog.c:8340
26. application_name       |
27. -[ RECORD 2 ]----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
28. -----------------------------------------------------
29. log_time               | 2019-07-20 14:02:27.258+08
30. user_name              |
31. database_name          |
32. process_id             | 10766
33. connection_from        |
34. session_id             | 5d2aea65.2a0e
35. session_line_num       | 20
36. command_tag            |
37. session_start_time     | 2019-07-15 06:40:05+08
38. virtual_transaction_id |
39. transaction_id         | 0
40. error_severity         | LOG
41. sql_state_code         | 00000
42. message                | checkpoint complete: wrote 124 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=12.484 s, sync=0.000 s, total=12.487 s; sync files=48, longest=0.000 s,
43. average=0.000 s; distance=218 kB, estimate=1753715 kB
44. detail                 |
45. hint                   |
46. internal_query         |
47. internal_query_pos     |
48. context                |
49. query                  |
50. query_pos              |
51. location               | LogCheckpointEnd, xlog.c:8422
52. application_name       |

使用plpgsql创建file_fdw表,更加简便


1. do language plpgsql $$
2. declare
3. v_datadir text;
4. v_logdir text;
5. begin
6. select setting into v_datadir from pg_settings where name='data_directory';
7. select setting into v_logdir  from pg_settings where name='log_directory';

9. create schema IF NOT EXISTS _sys ;

11. set search_path=_sys,public,"$user";

13. create extension IF NOT EXISTS file_fdw with schema _sys;

15. CREATE SERVER IF NOT EXISTS pglog FOREIGN DATA WRAPPER file_fdw;

17. execute format('CREATE FOREIGN TABLE IF NOT EXISTS pglog (
18. log_time timestamp(3) with time zone,
19. user_name text,
20. database_name text,
21. process_id integer,
22. connection_from text,
23. session_id text,
24. session_line_num bigint,
25. command_tag text,
26. session_start_time timestamp with time zone,
27. virtual_transaction_id text,
28. transaction_id bigint,
29. error_severity text,
30. sql_state_code text,
31. message text,
32. detail text,
33. hint text,
34. internal_query text,
35. internal_query_pos integer,
36. context text,
37. query text,
38. query_pos integer,
39. location text,
40. application_name text
41. ) SERVER pglog
42. OPTIONS ( program %L , format ''csv'')' ,
43. format('find %s/%s -type f -name "*.csv" -exec cat {} \;', v_datadir, v_logdir)
44. );

46. end;
47. $$;

是不是很方便呢?以上执行完,自动创建pglog外部表,查询它,可以看到日志的内容。换行没有问题。


1. -[ RECORD 15 ]---------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2. -----------------------------------------------------
3. log_time               | 2019-07-20 14:47:35.418+08
4. user_name              | postgres
5. database_name          | postgres
6. process_id             | 65215
7. connection_from        | [local]
8. session_id             | 5d31f011.febf
9. session_line_num       | 11
10. command_tag            | idle
11. session_start_time     | 2019-07-20 14:30:09+08
12. virtual_transaction_id | 3/20443
13. transaction_id         | 0
14. error_severity         | LOG
15. sql_state_code         | 00000
16. message                | statement: SELECT n.nspname as "Schema",
17. +
18. |   p.proname as "Name",
19. +
20. |   pg_catalog.pg_get_function_result(p.oid) as "Result data type",
21. +
22. |   pg_catalog.pg_get_function_arguments(p.oid) as "Argument data types",
23. +
24. |  CASE p.prokind
25. +
26. |   WHEN 'a' THEN 'agg'
27. +
28. |   WHEN 'w' THEN 'window'
29. +
30. |   WHEN 'p' THEN 'proc'
31. +
32. |   ELSE 'func'
33. +
34. |  END as "Type"
35. +
36. | FROM pg_catalog.pg_proc p
37. +
38. |      LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
39. +
40. | WHERE p.proname OPERATOR(pg_catalog.~) '^(.*ordi.*)$' COLLATE pg_catalog.default
41. +
42. | ORDER BY 1, 2, 4;
43. detail                 |
44. hint                   |
45. internal_query         |
46. internal_query_pos     |
47. context                |
48. query                  |
49. query_pos              |
50. location               | exec_simple_query, postgres.c:1045
51. application_name       | psql

参考

man find

man psql

《PostgreSQL Oracle 兼容性之 psql sqlplus 变量 & set variable》

《使用SQL查询数据库日志 - file_fdw , csvlog , program , find》

https://www.postgresql.org/docs/12/runtime-config-logging.html#RUNTIME-CONFIG-LOGGING-CSVLOG

https://www.postgresql.org/docs/12/file-fdw.html

https://www.postgresql.org/docs/12/functions-matching.html#POSIX-EMBEDDED-OPTIONS-TABLE

原文:http://mysql.taobao.org/monthly/2019/07/09/