summaryrefslogtreecommitdiff
path: root/sapi/fpm/fpm/fpm_log.c
diff options
context:
space:
mode:
Diffstat (limited to 'sapi/fpm/fpm/fpm_log.c')
-rw-r--r--sapi/fpm/fpm/fpm_log.c466
1 files changed, 466 insertions, 0 deletions
diff --git a/sapi/fpm/fpm/fpm_log.c b/sapi/fpm/fpm/fpm_log.c
new file mode 100644
index 0000000..6b014b5
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_log.c
@@ -0,0 +1,466 @@
+
+ /* $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;
+ }
+
+ if (reopen) {
+ dup2(fd, wp->log_fd);
+ close(fd);
+ fd = wp->log_fd;
+ fpm_pctl_kill_all(SIGQUIT);
+ } else {
+ wp->log_fd = fd;
+ }
+
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
+ }
+
+ 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 ? 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 ? 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 ? proc.query_string : "");
+ }
+ break;
+
+ case 'Q': /* '?' */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.query_string && *proc.query_string ? "?" : "");
+ }
+ break;
+
+ case 'r': /* request URI */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.request_uri ? proc.request_uri : "-");
+ }
+ break;
+
+ case 'R': /* remote IP address */
+ if (!test) {
+ 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 ? 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;
+}
+/* }}} */