PostgreSQL csvlog 源码字段分析记录

0.前言

PostgreSQL我们配置好相应的配置文件后,就可以在对应目录下生成csv格式的日志文件,打开后发现csv字段还是挺多的,故自己找源码分析并记录。
PostgreSQL因为公司原因才学几天,肯定有错误见谅

1.各字段分析和实例解析

PostgreSQL csvlog日志格式记录了非常多的信息,足有二十多列,在PostgreSQL文档 中例子具体列如下,各字段翻译参考PostgreSQL中文文档

CREATE TABLE postgres_log
(
  log_time timestamp(3) with time zone,#日志时间,带毫秒的时间戳
  user_name text,# 当前登录数据库的用户名
  database_name text,# 数据库名
  process_id integer,# 进程ID
  connection_from text,# 客户端主机:端口号
  session_id text,# 会话ID 由后台进程启动时间和PID组成
  session_line_num bigint,#每个会话的行号,类似history命令
  command_tag text,# 命令标签
  session_start_time timestamp with time zone,# 会话开始时间
  virtual_transaction_id text,# 虚拟事务ID
  transaction_id bigint,# 事务ID
  error_severity text,# 错误等级
  sql_state_code text,# SQLSTATE 代码
  message text,# 消息
  detail text,# 错误消息详情
  hint text,# 提示
  internal_query text,# 导致错误的内部查询(如果有)
  internal_query_pos integer,# 错误位置所在的字符计数
  context text,#  错误上下文
  query text,# 导致错误的用户查询(如果有且被log_min_error_statement启用)
  query_pos integer,# 错误位置所在的字符计数
  location text,# 在 PostgreSQL 源代码中错误的位置(如果log_error_verbosity被设置为verbose)
  application_name text,# 应用名
  PRIMARY KEY (session_id, session_line_num)
);
  • message这个字段有点意思:在error_severity 为ERROR时里面是错误原因(表XXX已存在了啥的);在error_severity 为LOG时,这里会放查询语句和查询时间啥的
  • 其中sql_state_code(SQL状态码)可以在官方文档附录中搜索到具体的错误情况。

以csv中一条记录为例,当时执行的SQL如下
在这里插入图片描述

2021-09-14 23:51:05.272 CST,“postgres”,“postgres”,1169,“[local]”,6140c438.491,5,“idle”,2021-09-14 23:48:08 CST,3/10,0,ERROR,42601,“subquery in FROM must have an alias”,“For example, FROM (SELECT …) [AS] foo.”,“select * from (select id from company where salary>10000 );”,15,"psql

方便观看我把二十多个字段分了组。

1. 第一组连接信息
字段备注
log_time timestamp2021-09-14 23:51:05.272 CST日志记录时间
user_namepostgres我直接 su - postgres登录的
database_namepostgres默认数据库
process_id1169进程id
connection_from[local ]本机
第一组比较简单没啥好说,需要注意的是connection_from 字段是由客户端主机:端口号组成,我这里因为是本机所以没展现效果
2. 第二组session和事务
字段备注
session_id6140c438.491后台进程的启动时间+PID
session_line_num5第5条命令
command_tagidleidle表示空闲,具体参考下文
session_start_time2021-09-14 23:48:08 CST会话开启时间
virtual_transaction_id3/10虚拟事务id
transaction_id0事务id
  • command_tag的idle解释查询自StackOverflow,在官方文档也有相应解释,command_tag其他类型就比较容易看懂了如:SELECT、SHOW、authentication等。
  • virtual_transaction_id由两个部分组成,分别是backendid和local transaction id,定义于源码的lock.h文件。虚拟事务ID简单说用来标示锁冲突的对象的,具体用途请参考这篇博客,本文略。
3. 第三组查询详情
字段备注
error_severityERROR错误等级,这里是ERROR
sql_state_code42601sql状态码,42601表示语法错误
messagesubquery in FROM must have an alias错误信息,子查询必须取别名
detail细节
hintFor example, FROM (SELECT …) [AS] foo.备注:举例……
internal_query导致错误的内部查询(如果有)
internal_query_pos内部错误位置所在的字符计数
context错误上下文
queryselect * from(select id from company where salary>10000 );完整的查询语句
query_pos15错误位置所在的字符计数
locationPostgreSQL 源代码中错误的位置(如果有)
application_namepsql客户端名称

第三组是最多的,但也比较好懂。

经过上面解释后,可以发现我们最关心的时间、user、connection、SQL语句和错误信息等都可以从csvlog中找到~

2.源码溯源

上一节分析了各字段,现在我们看看这些字段在源码里究竟是怎么赋值的~

PostgreSQL 源码的src/include/utils/elog.h的2762行的write_csvlog方法就是进行csvlog各字段的赋值操作。

/*
  * Constructs the error message, depending on the Errordata it gets, in a CSV
  * format which is described in doc/src/sgml/config.sgml.
  */
 static void
 write_csvlog(ErrorData *edata)
 {
     StringInfoData buf;
     bool        print_stmt = false;
 
     /* static counter for line numbers */
     static long log_line_number = 0;
 
     /* has counter been reset in current process? */
     static int  log_my_pid = 0;
 
     /*
      * This is one of the few places where we'd rather not inherit a static
      * variable's value from the postmaster.  But since we will, reset it when
      * MyProcPid changes.
      */
     if (log_my_pid != MyProcPid)
     {
         log_line_number = 0;
         log_my_pid = MyProcPid;
         formatted_start_time[0] = '\0';
     }
     log_line_number++;
 
     initStringInfo(&buf);
   // 从这里开始,每个字段什么意思都可以看到,每个字段都用appendStringInfoChar(&buf, ',');隔开来了。  
     /*
      * timestamp with milliseconds
      *
      * Check if the timestamp is already calculated for the syslog message,
      * and use it if so.  Otherwise, get the current timestamp.  This is done
      * to put same timestamp in both syslog and csvlog messages.
      */
     if (formatted_log_time[0] == '\0')
         setup_formatted_log_time();
 
     appendStringInfoString(&buf, formatted_log_time);
     appendStringInfoChar(&buf, ',');
 
     /* username */
     if (MyProcPort)
         appendCSVLiteral(&buf, MyProcPort->user_name);
     appendStringInfoChar(&buf, ',');
 
     /* database name */
     if (MyProcPort)
         appendCSVLiteral(&buf, MyProcPort->database_name);
     appendStringInfoChar(&buf, ',');
 
     /* Process id  */
     if (MyProcPid != 0)
         appendStringInfo(&buf, "%d", MyProcPid);
     appendStringInfoChar(&buf, ',');
 
     /* Remote host and port */
     if (MyProcPort && MyProcPort->remote_host)
     {
         appendStringInfoChar(&buf, '"');
         appendStringInfoString(&buf, MyProcPort->remote_host);
         if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
         {
             appendStringInfoChar(&buf, ':');
             appendStringInfoString(&buf, MyProcPort->remote_port);
         }
         appendStringInfoChar(&buf, '"');
     }
     appendStringInfoChar(&buf, ',');
 
     /* session id */
     // session id 是两个字段组成的分别是后台进程的启动时间和PID,所以是唯一的
     appendStringInfo(&buf, "%lx.%x", (long) MyStartTime, MyProcPid);
     appendStringInfoChar(&buf, ',');
 
     /* Line number */
     appendStringInfo(&buf, "%ld", log_line_number);
     appendStringInfoChar(&buf, ',');
 
     /* PS display */
     if (MyProcPort)
     {
         StringInfoData msgbuf;
         const char *psdisp;
         int         displen;
 
         initStringInfo(&msgbuf);
 
         psdisp = get_ps_display(&displen);
         appendBinaryStringInfo(&msgbuf, psdisp, displen);
         appendCSVLiteral(&buf, msgbuf.data);
 
         pfree(msgbuf.data);
     }
     appendStringInfoChar(&buf, ',');
 
     /* session start timestamp */
     if (formatted_start_time[0] == '\0')
         setup_formatted_start_time();
     appendStringInfoString(&buf, formatted_start_time);
     appendStringInfoChar(&buf, ',');
 
     /* Virtual transaction id */
     /* keep VXID format in sync with lockfuncs.c */
     //虚拟事务号由backendid和local transaction id组成
     if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
         appendStringInfo(&buf, "%d/%u", MyProc->backendId, MyProc->lxid);
     appendStringInfoChar(&buf, ',');
 
     /* Transaction id */
     appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny());
     appendStringInfoChar(&buf, ',');
 
     /* Error severity */
     appendStringInfoString(&buf, _(error_severity(edata->elevel)));
     appendStringInfoChar(&buf, ',');
 
     /* SQL state code */
     appendStringInfoString(&buf, unpack_sql_state(edata->sqlerrcode));
     appendStringInfoChar(&buf, ',');
 
     /* errmessage */
     appendCSVLiteral(&buf, edata->message);
     appendStringInfoChar(&buf, ',');
 
     /* errdetail or errdetail_log */
      是否输出代码位置
     if (edata->detail_log)
         appendCSVLiteral(&buf, edata->detail_log);
     else
         appendCSVLiteral(&buf, edata->detail);
     appendStringInfoChar(&buf, ',');
 
     /* errhint */
     appendCSVLiteral(&buf, edata->hint);
     appendStringInfoChar(&buf, ',');
 
     /* internal query */
     appendCSVLiteral(&buf, edata->internalquery);
     appendStringInfoChar(&buf, ',');
 
     /* if printed internal query, print internal pos too */
     if (edata->internalpos > 0 && edata->internalquery != NULL)
         appendStringInfo(&buf, "%d", edata->internalpos);
     appendStringInfoChar(&buf, ',');
 
     /* errcontext */
     if (!edata->hide_ctx)
         appendCSVLiteral(&buf, edata->context);
     appendStringInfoChar(&buf, ',');
 
     /* user query --- only reported if not disabled by the caller */
     if (is_log_level_output(edata->elevel, log_min_error_statement) &&
         debug_query_string != NULL &&
         !edata->hide_stmt)
         print_stmt = true;
     if (print_stmt)
         appendCSVLiteral(&buf, debug_query_string);
     appendStringInfoChar(&buf, ',');
     if (print_stmt && edata->cursorpos > 0)
         appendStringInfo(&buf, "%d", edata->cursorpos);
     appendStringInfoChar(&buf, ',');
 
     /* file error location */
     if (Log_error_verbosity >= PGERROR_VERBOSE)
     {
         StringInfoData msgbuf;
 
         initStringInfo(&msgbuf);
 
         if (edata->funcname && edata->filename)
             appendStringInfo(&msgbuf, "%s, %s:%d",
                              edata->funcname, edata->filename,
                              edata->lineno);
         else if (edata->filename)
             appendStringInfo(&msgbuf, "%s:%d",
                              edata->filename, edata->lineno);
         appendCSVLiteral(&buf, msgbuf.data);
         pfree(msgbuf.data);
     }
     appendStringInfoChar(&buf, ',');
 
     /* application name */
     if (application_name)
         appendCSVLiteral(&buf, application_name);
 
     appendStringInfoChar(&buf, ',');
 
     /* backend type */
     if (MyProcPid == PostmasterPid)
         appendCSVLiteral(&buf, "postmaster");
     else if (MyBackendType == B_BG_WORKER)
         appendCSVLiteral(&buf, MyBgworkerEntry->bgw_type);
     else
         appendCSVLiteral(&buf, GetBackendTypeDesc(MyBackendType));
 
     appendStringInfoChar(&buf, ',');
 
     /* leader PID */
     if (MyProc)
     {
         PGPROC     *leader = MyProc->lockGroupLeader;
 
         /*
          * Show the leader only for active parallel workers.  This leaves out
          * the leader of a parallel group.
          */
         if (leader && leader->pid != MyProcPid)
             appendStringInfo(&buf, "%d", leader->pid);
     }
     appendStringInfoChar(&buf, ',');
 
     /* query id */
     appendStringInfo(&buf, "%lld", (long long) pgstat_get_my_query_id());
 
     appendStringInfoChar(&buf, '\n');
 
     /* If in the syslogger process, try to write messages direct to file */
     if (MyBackendType == B_LOGGER)
         write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
     else
         write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
 
     pfree(buf.data);
 }
  • 源码比较简单,基本上根据原始的注释就能想到具体操作,后面的详细解释,后面来填坑
  • write_log(ErrorData *edata)方法传入ErrorData 这样的结构体定义了elog.h头文件中369行,具体定义如下,根据自带的注释也能理解意思
src/include/utils/elog.h
/*
 * ErrorData holds the data accumulated during any one ereport() cycle.
 * Any non-NULL pointers must point to palloc'd data.
 * (The const pointers are an exception; we assume they point at non-freeable
 * constant strings.)
 */
typedef struct ErrorData
{
    int            elevel;            /* error level */
    bool        output_to_server;        /* will report to server log? */
    bool        output_to_client;        /* will report to client? */
    bool        show_funcname;    /* true to force funcname inclusion */
    bool        hide_stmt;        /* true to prevent STATEMENT: inclusion */
    const char *filename;        /* __FILE__ of ereport() call */
    int            lineno;            /* __LINE__ of ereport() call */
    const char *funcname;        /* __func__ of ereport() call */
    const char *domain;            /* message domain */
    const char *context_domain; /* message domain for context message */
    int            sqlerrcode;        /* encoded ERRSTATE */
    char       *message;        /* primary error message */
    char       *detail;            /* detail error message */
    char       *detail_log;        /* detail error message for server log only */
    char       *hint;            /* hint message */
    char       *context;        /* context message */
    char       *schema_name;    /* name of schema */
    char       *table_name;        /* name of table */
    char       *column_name;    /* name of column */
    char       *datatype_name;    /* name of datatype */
    char       *constraint_name;    /* name of constraint */
    int            cursorpos;        /* cursor index into query string */
    int            internalpos;    /* cursor index into internalquery */
    char       *internalquery;    /* text of internally-generated query */
    int            saved_errno;    /* errno at entry */

    /* context containing associated non-constant strings */
    struct MemoryContextData *assoc_context;
} ErrorData;

源码比较简洁
后面再填坑吧我好累/(ㄒoㄒ)/~~

3. 参考

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值