root/sapi/fpm/fpm/fpm_log.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fpm_log_open
  2. fpm_log_init_child
  3. fpm_log_write


        /* $Id: fpm_status.c 312262 2011-06-18 17:41:56Z felipe $ */
        /* (c) 2009 Jerome Loyet */

#include "php.h"
#include "SAPI.h"
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

#ifdef HAVE_TIMES
#include <sys/times.h>
#endif

#include "fpm_config.h"
#include "fpm_log.h"
#include "fpm_clock.h"
#include "fpm_process_ctl.h"
#include "fpm_signals.h"
#include "fpm_scoreboard.h"
#include "fastcgi.h"
#include "zlog.h"

#ifdef MAX_LINE_LENGTH
# define FPM_LOG_BUFFER MAX_LINE_LENGTH
#else
# define FPM_LOG_BUFFER 1024
#endif

static char *fpm_log_format = NULL;
static int fpm_log_fd = -1;

int fpm_log_open(int reopen) /* {{{ */
{
        struct fpm_worker_pool_s *wp;
        int ret = 1;
        
        int fd;
        for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
                if (!wp->config->access_log) {
                        continue;
                }
                ret = 0;
                
                fd = open(wp->config->access_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
                if (0 > fd) {
                        zlog(ZLOG_SYSERROR, "failed to open access log (%s)", wp->config->access_log);
                        return -1;
                } else {
                        zlog(ZLOG_DEBUG, "open access log (%s)", wp->config->access_log);
                }

                if (reopen) {
                        dup2(fd, wp->log_fd);
                        close(fd);
                        fd = wp->log_fd;
                        fpm_pctl_kill_all(SIGQUIT);
                } else {
                        wp->log_fd = fd;
                }

                if (0 > fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)) {
                        zlog(ZLOG_WARNING, "failed to change attribute of access_log");
                }
        }

        return ret;
}
/* }}} */

/* }}} */
int fpm_log_init_child(struct fpm_worker_pool_s *wp)  /* {{{ */
{
        if (!wp || !wp->config) {
                return -1;
        }

        if (wp->config->access_log && *wp->config->access_log) {
                if (wp->config->access_format) {
                        fpm_log_format = strdup(wp->config->access_format);
                }
        }

        if (fpm_log_fd == -1) {
                fpm_log_fd = wp->log_fd;
        }


        for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
                if (wp->log_fd > -1 && wp->log_fd != fpm_log_fd) {
                        close(wp->log_fd);
                        wp->log_fd = -1;
                }
        }

        return 0;
}
/* }}} */

int fpm_log_write(char *log_format TSRMLS_DC) /* {{{ */
{
        char *s, *b;
        char buffer[FPM_LOG_BUFFER+1];
        int token, test;
        size_t len, len2;
        struct fpm_scoreboard_proc_s proc, *proc_p;
        struct fpm_scoreboard_s *scoreboard;
        char tmp[129];
        char format[129];
        time_t now_epoch;
#ifdef HAVE_TIMES
        clock_t tms_total;
#endif

        if (!log_format && (!fpm_log_format || fpm_log_fd == -1)) {
                return -1;
        }

        if (!log_format) {
                log_format = fpm_log_format;
                test = 0;
        } else {
                test = 1;
        }

        now_epoch = time(NULL);

        if (!test) {
                scoreboard = fpm_scoreboard_get();
                if (!scoreboard) {
                        zlog(ZLOG_WARNING, "unable to get scoreboard while preparing the access log");
                        return -1;
                }
                proc_p = fpm_scoreboard_proc_acquire(NULL, -1, 0);
                if (!proc_p) {
                        zlog(ZLOG_WARNING, "[pool %s] Unable to acquire shm slot while preparing the access log", scoreboard->pool);
                        return -1;
                }
                proc = *proc_p;
                fpm_scoreboard_proc_release(proc_p);
        }

        token = 0;

        memset(buffer, '\0', sizeof(buffer));
        b = buffer;
        len = 0;


        s = log_format;

        while (*s != '\0') {
                /* Test is we have place for 1 more char. */
                if (len >= FPM_LOG_BUFFER) {
                        zlog(ZLOG_NOTICE, "the log buffer is full (%d). The access log request has been truncated.", FPM_LOG_BUFFER);
                        len = FPM_LOG_BUFFER;
                        break;
                }

                if (!token && *s == '%') {
                        token = 1;
                        memset(format, '\0', sizeof(format)); /* reset format */
                        s++;
                        continue;
                }

                if (token) {
                        token = 0;
                        len2 = 0;
                        switch (*s) {

                                case '%': /* '%' */
                                        *b = '%';
                                        len2 = 1;
                                        break;

#ifdef HAVE_TIMES
                                case 'C': /* %CPU */
                                        if (format[0] == '\0' || !strcasecmp(format, "total")) {
                                                if (!test) {
                                                        tms_total = proc.last_request_cpu.tms_utime + proc.last_request_cpu.tms_stime + proc.last_request_cpu.tms_cutime + proc.last_request_cpu.tms_cstime;
                                                }
                                        } else if (!strcasecmp(format, "user")) {
                                                if (!test) {
                                                        tms_total = proc.last_request_cpu.tms_utime + proc.last_request_cpu.tms_cutime;
                                                }
                                        } else if (!strcasecmp(format, "system")) {
                                                if (!test) {
                                                        tms_total = proc.last_request_cpu.tms_stime + proc.last_request_cpu.tms_cstime;
                                                }
                                        } else {
                                                zlog(ZLOG_WARNING, "only 'total', 'user' or 'system' are allowed as a modifier for %%%c ('%s')", *s, format);
                                                return -1;
                                        }

                                        format[0] = '\0';
                                        if (!test) {
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.2f", tms_total / fpm_scoreboard_get_tick() / (proc.cpu_duration.tv_sec + proc.cpu_duration.tv_usec / 1000000.) * 100.);
                                        }
                                        break;
#endif

                                case 'd': /* duration µs */
                                        /* seconds */
                                        if (format[0] == '\0' || !strcasecmp(format, "seconds")) {
                                                if (!test) {
                                                        len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.3f", proc.duration.tv_sec + proc.duration.tv_usec / 1000000.);
                                                }

                                        /* miliseconds */
                                        } else if (!strcasecmp(format, "miliseconds") || !strcasecmp(format, "mili")) {
                                                if (!test) {
                                                        len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.3f", proc.duration.tv_sec * 1000. + proc.duration.tv_usec / 1000.);
                                                }

                                        /* microseconds */
                                        } else if (!strcasecmp(format, "microseconds") || !strcasecmp(format, "micro")) {
                                                if (!test) {
                                                        len2 = snprintf(b, FPM_LOG_BUFFER - len, "%lu", proc.duration.tv_sec * 1000000UL + proc.duration.tv_usec);
                                                }

                                        } else {
                                                zlog(ZLOG_WARNING, "only 'seconds', 'mili', 'miliseconds', 'micro' or 'microseconds' are allowed as a modifier for %%%c ('%s')", *s, format);
                                                return -1;
                                        }
                                        format[0] = '\0';
                                        break;

                                case 'e': /* fastcgi env  */
                                        if (format[0] == '\0') {
                                                zlog(ZLOG_WARNING, "the name of the environment variable must be set between embraces for %%%c", *s);
                                                return -1;
                                        }

                                        if (!test) {
                                                char *env = fcgi_getenv((fcgi_request*) SG(server_context), format, strlen(format));
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", env ? env : "-");
                                        }
                                        format[0] = '\0';
                                        break;

                                case 'f': /* script */
                                        if (!test) {
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s",  *proc.script_filename ? proc.script_filename : "-");
                                        }
                                        break;

                                case 'l': /* content length */
                                        if (!test) {
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%zu", proc.content_length);
                                        }
                                        break;

                                case 'm': /* method */
                                        if (!test) {
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", *proc.request_method ? proc.request_method : "-");
                                        }
                                        break;

                                case 'M': /* memory */
                                        /* seconds */
                                        if (format[0] == '\0' || !strcasecmp(format, "bytes")) {
                                                if (!test) {
                                                        len2 = snprintf(b, FPM_LOG_BUFFER - len, "%zu", proc.memory);
                                                }

                                        /* kilobytes */
                                        } else if (!strcasecmp(format, "kilobytes") || !strcasecmp(format, "kilo")) {
                                                if (!test) {
                                                        len2 = snprintf(b, FPM_LOG_BUFFER - len, "%lu", proc.memory / 1024);
                                                }

                                        /* megabytes */
                                        } else if (!strcasecmp(format, "megabytes") || !strcasecmp(format, "mega")) {
                                                if (!test) {
                                                        len2 = snprintf(b, FPM_LOG_BUFFER - len, "%lu", proc.memory / 1024 / 1024);
                                                }

                                        } else {
                                                zlog(ZLOG_WARNING, "only 'bytes', 'kilo', 'kilobytes', 'mega' or 'megabytes' are allowed as a modifier for %%%c ('%s')", *s, format);
                                                return -1;
                                        }
                                        format[0] = '\0';
                                        break;

                                case 'n': /* pool name */
                                        if (!test) {
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", scoreboard->pool[0] ? scoreboard->pool : "-");
                                        }
                                        break;

                                case 'o': /* header output  */
                                        if (format[0] == '\0') {
                                                zlog(ZLOG_WARNING, "the name of the header must be set between embraces for %%%c", *s);
                                                return -1;
                                        }
                                        if (!test) {
                                                sapi_header_struct *h;
                                                zend_llist_position pos;
                                                sapi_headers_struct *sapi_headers = &SG(sapi_headers);
                                                size_t format_len = strlen(format);

                                                h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
                                                while (h) {
                                                        char *header;
                                                        if (!h->header_len) {
                                                                h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
                                                                continue;
                                                        }
                                                        if (!strstr(h->header, format)) {
                                                                h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
                                                                continue;
                                                        }

                                                        /* test if enought char after the header name + ': ' */
                                                        if (h->header_len <= format_len + 2) {
                                                                h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
                                                                continue;
                                                        }

                                                        if (h->header[format_len] != ':' || h->header[format_len + 1] != ' ') {
                                                                h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
                                                                continue;
                                                        }

                                                        header = h->header + format_len + 2;
                                                        len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", header && *header ? header : "-");

                                                        /* found, done */
                                                        break;
                                                }
                                                if (!len2) {
                                                        len2 = 1;
                                                        *b = '-';
                                                }
                                        }
                                        format[0] = '\0';
                                        break;

                                case 'p': /* PID */
                                        if (!test) {
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%ld", (long)getpid());
                                        }
                                        break;

                                case 'P': /* PID */
                                        if (!test) {
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%ld", (long)getppid());
                                        }
                                        break;

                                case 'q': /* query_string */
                                        if (!test) {
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.query_string);
                                        }
                                        break;

                                case 'Q': /* '?' */
                                        if (!test) {
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", *proc.query_string  ? "?" : "");
                                        }
                                        break;

                                case 'r': /* request URI */
                                        if (!test) {
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.request_uri);
                                        }
                                        break;

                                case 'R': /* remote IP address */
                                        if (!test) {
                                                const char *tmp = fcgi_get_last_client_ip();
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", tmp ? tmp : "-");
                                        }
                                        break;

                                case 's': /* status */
                                        if (!test) {
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%d", SG(sapi_headers).http_response_code);
                                        }
                                        break;

                                case 'T':
                                case 't': /* time */
                                        if (!test) {
                                                time_t *t;
                                                if (*s == 't') {
                                                        t = &proc.accepted_epoch;
                                                } else {
                                                        t = &now_epoch;
                                                }
                                                if (format[0] == '\0') {
                                                        strftime(tmp, sizeof(tmp) - 1, "%d/%b/%Y:%H:%M:%S %z", localtime(t));
                                                } else {
                                                        strftime(tmp, sizeof(tmp) - 1, format, localtime(t));
                                                }
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", tmp);
                                        }
                                        format[0] = '\0';
                                        break;

                                case 'u': /* remote user */
                                        if (!test) {
                                                len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.auth_user);
                                        }
                                        break;

                                case '{': /* complex var */
                                        token = 1;
                                        {
                                                char *start;
                                                size_t l;
                                                
                                                start = ++s;

                                                while (*s != '\0') {
                                                        if (*s == '}') {
                                                                l = s - start;

                                                                if (l >= sizeof(format) - 1) {
                                                                        l = sizeof(format) - 1;
                                                                }

                                                                memcpy(format, start, l);
                                                                format[l] = '\0';
                                                                break;
                                                        }
                                                        s++;
                                                }
                                                if (s[1] == '\0') {
                                                        zlog(ZLOG_WARNING, "missing closing embrace in the access.format");
                                                        return -1;
                                                }
                                        }
                                        break;

                                default:
                                        zlog(ZLOG_WARNING, "Invalid token in the access.format (%%%c)", *s);
                                        return -1;
                        }

                        if (*s != '}' && format[0] != '\0') {
                                zlog(ZLOG_WARNING, "embrace is not allowed for modifier %%%c", *s);
                                return -1;
                        }
                        s++;
                        if (!test) {
                                b += len2;
                                len += len2;
                        }
                        continue;
                }

                if (!test) {
                        // push the normal char to the output buffer
                        *b = *s;
                        b++;
                        len++;
                }
                s++;
        }

        if (!test && strlen(buffer) > 0) {
                buffer[len] = '\n';
                write(fpm_log_fd, buffer, len + 1);
        }

        return 0;
}
/* }}} */

/* [<][>][^][v][top][bottom][index][help] */