1. 程式人生 > >Postgresql - 原始碼 - logger process

Postgresql - 原始碼 - logger process

程式碼位置:

src/backend/postmaster/syslogger.c

 

system logger是從PG 8開始的。通過重定向到管道來捕獲來自Postmaster, backends, 和其他的子程序的所有stderr,並寫入日誌檔案。可以再postgresql.conf 中配置日誌檔案的 size 和 age限制。如果達到了限制,關閉當前日誌檔案,並建立新的日誌檔案。日誌檔案儲存在子目錄子目錄中,可以配置命名方案。

 

 

/* syslogger process 主入口 */

NON_EXEC_STATIC void

SysLoggerMain(int argc, char *argv[])

{

#ifndef WIN32

    char        logbuffer[READ_BUF_SIZE];

    int         bytes_in_logbuffer = 0;

#endif

    char     *currentLogDir;

    char     *currentLogFilename;

    int         currentLogRotationAge;

    pg_time_t   now;

 

    now = MyStartTime;

 

#ifdef EXEC_BACKEND

    syslogger_parseArgs(argc, argv);

#endif                          /* EXEC_BACKEND */

 

    am_syslogger = true;

 

    init_ps_display("logger", "", "", "");

 

    /* 如果重新啟動,我們的STDRR已經被重定向到我們自己的輸入管道中。這當然是無用的,更不用說它干擾檢測管EOF。把stderr只想到 /dev/null 。假設 syslogger 中生成的所有的訊息將通過 elog.c 傳送到 write_syslogger_file 。 */

    if (redirection_done)

    {

        int         fd = open(DEVNULL, O_WRONLY, 0);

 

        /* 關閉可能看起來是多餘,但實際上並不是:我們要確保管道即使開啟失敗也會關閉。我們可以在 stderr 指向任何地方執行,但我們不能負擔額外的管道輸入描述符。由於我們只是試圖將這些重置為DEVNULL,所以在這裡從close/dup2 呼叫中檢查失敗沒有多大意義,如果它們失敗,那麼可能檔案描述符被關閉,並且任何寫入都將進入 bitbucket 。 */

        close(fileno(stdout));

        close(fileno(stderr));

        if (fd != -1)

        {

            (void) dup2(fd, fileno(stdout));

            (void) dup2(fd, fileno(stderr));

            close(fd);

        }

    }

 

    /* Syslogger 自己的stderr 不可能是syslogPipe,所以如果我們不關閉它,就將其設定為文字模式。SubPostmasterMain中設定為二進位制 */

#ifdef WIN32

    else

        _setmode(_fileno(stderr), _O_TEXT);

#endif

 

    /* 也關閉我們的管道的書寫端。這就需要我們能夠正確地檢測管道EOF。(但注意,在重新啟動的情況下,postmaster 已經這樣做了。) */

#ifndef WIN32

    if (syslogPipe[1] >= 0)

        close(syslogPipe[1]);

    syslogPipe[1] = -1;

#else

    if (syslogPipe[1])

        CloseHandle(syslogPipe[1]);

    syslogPipe[1] = 0;

#endif

 

    /* 正確接受或忽略 postmaster 可能傳送給我們的訊號 */

 

    pqsignal(SIGHUP, sigHupHandler);    /* set flag to read config file */

    pqsignal(SIGINT, SIG_IGN);

    pqsignal(SIGTERM, SIG_IGN);

    pqsignal(SIGQUIT, SIG_IGN);

    pqsignal(SIGALRM, SIG_IGN);

    pqsignal(SIGPIPE, SIG_IGN);

    pqsignal(SIGUSR1, sigUsr1Handler);  /* request log rotation */

    pqsignal(SIGUSR2, SIG_IGN);

 

    /* 重置一些由 postmaster 接受但不在這裡接受的訊號 */

    pqsignal(SIGCHLD, SIG_DFL);

    pqsignal(SIGTTIN, SIG_DFL);

    pqsignal(SIGTTOU, SIG_DFL);

    pqsignal(SIGCONT, SIG_DFL);

    pqsignal(SIGWINCH, SIG_DFL);

 

    PG_SETMASK(&UnBlockSig);

 

#ifdef WIN32

    /* 啟動單獨的資料傳輸執行緒 */

    InitializeCriticalSection(&sysloggerSection);

    EnterCriticalSection(&sysloggerSection);

 

    threadHandle = (HANDLE) _beginthreadex(NULL, 0, pipeThread, NULL, 0, NULL);

    if (threadHandle == 0)

        elog(FATAL, "could not create syslogger data transfer thread: %m");

#endif                          /* WIN32 */

 

    /* 記住active logfile的名稱。我們從飲用時間重新計算,因為值傳遞了pg_time_t 比在EXEC_BACKEND情況下傳遞整個檔案路徑要便宜的多。 */

    last_file_name = logfile_getname(first_syslogger_file_time, NULL);

 

    /* 記住活動日誌檔案引數 */

    currentLogDir = pstrdup(Log_directory);

    currentLogFilename = pstrdup(Log_filename);

    currentLogRotationAge = Log_RotationAge;

    /* 設定下一個計劃的旋轉時間 */

    set_next_rotation_time();

    update_metainfo_datafile();

 

    /* 主worker迴圈 */

    for (;;)

    {

        bool        time_based_rotation = false;

        int         size_rotation_for = 0;

        long        cur_timeout;

        int         cur_flags;

 

#ifndef WIN32

        int         rc;

#endif

 

        /* 清除任何已掛起的喚醒 */

        ResetLatch(MyLatch);

 

        /* 處理最近收到的任何請求或訊號。 */

        if (got_SIGHUP)

        {

            got_SIGHUP = false;

            ProcessConfigFile(PGC_SIGHUP);

 

            /* 檢查postgresql.conf中的日誌目錄或檔名模式是否更改。如果是,強制旋轉以確保將日誌檔案寫入正確的位置。 */

            if (strcmp(Log_directory, currentLogDir) != 0)

            {

                pfree(currentLogDir);

                currentLogDir = pstrdup(Log_directory);

                rotation_requested = true;

 

                /* 此外,如果不存在,則建立新目錄;忽略錯誤 */

                (void) MakePGDirectory(Log_directory);

            }

            if (strcmp(Log_filename, currentLogFilename) != 0)

            {

                pfree(currentLogFilename);

                currentLogFilename = pstrdup(Log_filename);

                rotation_requested = true;

            }

 

            /* 如果迴圈時間引數改變,則重置下一個迴圈時間,但不要立即強制旋轉。 */

            if (currentLogRotationAge != Log_RotationAge)

            {

                currentLogRotationAge = Log_RotationAge;

                set_next_rotation_time();

            }

 

            /* 如果我們迴圈禁用失敗,在SIGHUP 之後 重新啟用迴圈嘗試。 */

            if (rotation_disabled)

            {

                rotation_disabled = false;

                rotation_requested = true;

            }

 

            /* 重新載入配置時,重寫最後一個日誌檔名。即使 rotation_requested 是 false 的,log_destination 可能已經改變,我們不希望等待下一個檔案轉換。 */

            update_metainfo_datafile();

        }

 

        if (Log_RotationAge > 0 && !rotation_disabled)

        {

            /* 如果到時間,執行日誌檔案轉換 */

            now = (pg_time_t) time(NULL);

            if (now >= next_rotation_time)

                rotation_requested = time_based_rotation = true;

        }

 

        if (!rotation_requested && Log_RotationSize > 0 && !rotation_disabled)

        {

            /* 如果日誌檔案太大,轉換檔案 */

            if (ftell(syslogFile) >= Log_RotationSize * 1024L)

            {

                rotation_requested = true;

                size_rotation_for |= LOG_DESTINATION_STDERR;

            }

            if (csvlogFile != NULL &&

                ftell(csvlogFile) >= Log_RotationSize * 1024L)

            {

                rotation_requested = true;

                size_rotation_for |= LOG_DESTINATION_CSVLOG;

            }

        }

 

        if (rotation_requested)

        {

            /* 當值都是0的時候強制轉換檔案。意味著請求是由pg_rotate_logfile 傳送的。 */

            if (!time_based_rotation && size_rotation_for == 0)

                size_rotation_for = LOG_DESTINATION_STDERR | LOG_DESTINATION_CSVLOG;

            logfile_rotate(time_based_rotation, size_rotation_for);

        }

 

        /* 計算下一次轉換的時間,這樣我們就不會sleep的更久。我們假設上面得到的“now”值仍然足夠接近。注意,在呼叫 logfile_rotate() 之後,我們不能進行這個計算,因為它將推進next_rotation_time. 還要注意,在計算超時時時需要注意溢位:對於Log_RotationAge 的設定,next_rotation_time 將來可能超過INT_MAX 毫秒。在這種情況下,我們將不再等待INT_MAX 毫秒,然後再試一次。 */

        if (Log_RotationAge > 0 && !rotation_disabled)

        {

            pg_time_t   delay;

 

            delay = next_rotation_time - now;

            if (delay > 0)

            {

                if (delay > INT_MAX / 1000)

                    delay = INT_MAX / 1000;

                cur_timeout = delay * 1000L;    /* msec */

            }

            else

                cur_timeout = 0;

            cur_flags = WL_TIMEOUT;

        }

        else

        {

            cur_timeout = -1L;

            cur_flags = 0;

        }

 

        /* sleep直到有一些事需要去做 */

#ifndef WIN32

        rc = WaitLatchOrSocket(MyLatch,

                             WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,

                             syslogPipe[0],

                             cur_timeout,

                             WAIT_EVENT_SYSLOGGER_MAIN);

 

        if (rc & WL_SOCKET_READABLE)

        {

            int         bytesRead;

 

            bytesRead = read(syslogPipe[0],

                             logbuffer + bytes_in_logbuffer,

                             sizeof(logbuffer) - bytes_in_logbuffer);

            if (bytesRead < 0)

            {

                if (errno != EINTR)

                    ereport(LOG,

                            (errcode_for_socket_access(),

                             errmsg("could not read from logger pipe: %m")));

            }

            else if (bytesRead > 0)

            {

                bytes_in_logbuffer += bytesRead;

                process_pipe_input(logbuffer, &bytes_in_logbuffer);

                continue;

            }

            else

            {

                /* 當select()表示read-ready意味著管道上的EOF時,讀取的位元組為零:也就是說,不再存在管道寫端開啟的程序。因此,postmaster 和所有的後端程序都關閉了,結束了。 */

                pipe_eof_seen = true;

 

                /* 如果有任何資料剩下,現在就強迫它輸出 */

                flush_pipe_input(logbuffer, &bytes_in_logbuffer);

            }

        }

#else                           /* WIN32 */

 

        /* 在Windows上,我們把它留給一個單獨的執行緒來傳輸資料和檢測管道EOF。主執行緒剛剛醒來,以處理SIGHUP 和轉換條件。伺服器程式碼通常不是執行緒安全的,所以我們確保每次只有一個執行緒是活動的,只要我們不sleep,就進入關鍵部分。 */

        LeaveCriticalSection(&sysloggerSection);

 

        (void) WaitLatch(MyLatch,

                         WL_LATCH_SET | cur_flags,

                         cur_timeout,

                         WAIT_EVENT_SYSLOGGER_MAIN);

 

        EnterCriticalSection(&sysloggerSection);

#endif                          /* WIN32 */

 

        if (pipe_eof_seen)

        {

            ereport(DEBUG1,

                    (errmsg("logger shutting down")));

 

            /* 從系統日誌中正常退出。注意,我們故意不在退出之前關閉 syslogFile ;這是為了允許在 proc_exit 中生成elog訊息。規則exit() 將負責 flushing 和關閉STDIO通道。 */

            proc_exit(0);

        }

    }

}