php日志记录源码解析

时间:2024-02-16 14:39:16

php日志记录顺序是怎么一回事呢。

出于好奇,我翻看了一下php的源码(基于php5.6.5),

我是通过error_log来顺腾摸瓜的:

PHP_FUNCTION(error_log)
{
    char *message, *opt = NULL, *headers = NULL;
    int message_len, opt_len = 0, headers_len = 0;
    int opt_err = 0, argc = ZEND_NUM_ARGS();
    long erropt = 0;

    if (zend_parse_parameters(argc TSRMLS_CC, "s|lps", &message, &message_len, &erropt, &opt, &opt_len, &headers, &headers_len) == FAILURE) {
        return;
    }

    if (argc > 1) {
        opt_err = erropt;
    }

    if (_php_error_log_ex(opt_err, message, message_len, opt, headers TSRMLS_CC) == FAILURE) {
        RETURN_FALSE;
    }

    RETURN_TRUE;
}
View Code

这里看到php_error主要是通过_php_error_log_ex这个函数

那么继续看下_php_error_log_ex

PHPAPI int _php_error_log_ex(int opt_err, char *message, int message_len, char *opt, char *headers TSRMLS_DC) /* {{{ */
{
    php_stream *stream = NULL;

    switch (opt_err)
    {
        case 1:        /*send an email */
            if (!php_mail(opt, "PHP error_log message", message, headers, NULL TSRMLS_CC)) {
                return FAILURE;
            }
            break;

        case 2:        /*send to an address */
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "TCP/IP option not available!");
            return FAILURE;
            break;

        case 3:        /*save to a file */
            stream = php_stream_open_wrapper(opt, "a", IGNORE_URL_WIN | REPORT_ERRORS, NULL);
            if (!stream) {
                return FAILURE;
            }
            php_stream_write(stream, message, message_len);
            php_stream_close(stream);
            break;

        case 4: /* send to SAPI */
            if (sapi_module.log_message) {
                sapi_module.log_message(message TSRMLS_CC);
            } else {
                return FAILURE;
            }
            break;

        default:
            php_log_err(message TSRMLS_CC);
            break;
    }
    return SUCCESS;
}
View Code

因为我们没有填opt_err选项,所以我们默认就走到了php_log_err

好了,真正的记录是在这里啊,我们继续看下php_log_err

PHPAPI void php_log_err(char *log_message TSRMLS_DC)
{
    int fd = -1;
    time_t error_time;

    if (PG(in_error_log)) {
        /* prevent recursive invocation */
        return;
    }
    PG(in_error_log) = 1;

    /* Try to use the specified logging location. */
    if (PG(error_log) != NULL) {
#ifdef HAVE_SYSLOG_H
        if (!strcmp(PG(error_log), "syslog")) {
            php_syslog(LOG_NOTICE, "%s", log_message);
            PG(in_error_log) = 0;
            return;
        }
#endif
        fd = VCWD_OPEN_MODE(PG(error_log), O_CREAT | O_APPEND | O_WRONLY, 0644);
        
        if (fd != -1) {
            char *tmp;
            int len;
            char *error_time_str;

            time(&error_time);
#ifdef ZTS
            if (!php_during_module_startup()) {
                error_time_str = php_format_date("d-M-Y H:i:s e", 13, error_time, 1 TSRMLS_CC);
            } else {
                error_time_str = php_format_date("d-M-Y H:i:s e", 13, error_time, 0 TSRMLS_CC);
            }
#else
            error_time_str = php_format_date("d-M-Y H:i:s e", 13, error_time, 1 TSRMLS_CC);
#endif
            len = spprintf(&tmp, 0, "[%s] %s%s", error_time_str, log_message, PHP_EOL);
#ifdef PHP_WIN32
            php_flock(fd, 2);
#endif
            php_ignore_value(write(fd, tmp, len));
            efree(tmp);
            efree(error_time_str);
            close(fd);
            PG(in_error_log) = 0;
            return;
        }
    }

    /* Otherwise fall back to the default logging location, if we have one */

    if (sapi_module.log_message) {
        sapi_module.log_message(log_message TSRMLS_CC);
    }
    PG(in_error_log) = 0;
}

好了。这里一切都简单了。。