/* * Copyright 2007 IAC Search & Media. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* * Provides timers for the entire request cylce in Apache, including the time * spent logging. * * Configuration is currently done at compile time. Change the variables * bellow: */ #define TIMER_LOG_PATH "/var/tmp/httpd_timer_log" #define TIMER_MAX_LINE_LEN (256) /* * mod_timer works by using several hooks in apache: * - process_connection: Start Connection timer. * - process_request: Start Request timer. * - request_rec->p cleanup: End Request timer. * - conn_rec->p cleanup: End Connection timer. */ /* * mod_timer writes to a log file with the following format: * c:(remote ip):(remote port):(time of start):(duration)\n * r:(remote ip):(remote port):(time of start):(duration)\n * Where 'c' means a connection and 'r' means a request. * Time of start is expressed as an apr_time_t. * Duration is expressed in microns. * For example: * r:1.2.3.4:4331:1:134485 * r:1.2.3.4:4331:2:445 * c:1.2.3.4:4331:1:134930 * This would be a keep alive request where two requests were made inside a * connection. */ #include "httpd.h" #include "http_core.h" #include "http_config.h" #include "http_protocol.h" #include "http_log.h" static int g_logfile = -1; typedef struct timer_point_t { apr_sockaddr_t *client; apr_time_t start; } timer_point_t; static apr_status_t timer_end(timer_point_t *t, char endtype) { /* yes, this is evil. */ if (g_logfile >= 0) { int len; char buf[TIMER_MAX_LINE_LEN]; apr_time_t end = apr_time_now(); len = apr_snprintf(buf, sizeof(buf), "%c:%pI:%"APR_TIME_T_FMT":%"APR_TIME_T_FMT"\n", endtype, t->client, t->start, (end - t->start)); if (len > 0) { write(g_logfile, buf, len); } } return APR_SUCCESS; } static apr_status_t timer_end_conn(void *baton) { timer_point_t *t = (timer_point_t*)baton; return timer_end(t, 'c'); } static apr_status_t timer_end_req(void *baton) { timer_point_t *t = (timer_point_t*)baton; return timer_end(t, 'r'); } int timer_process_conn(conn_rec *c) { if (g_logfile < 0) { return DECLINED; } timer_point_t *t = apr_palloc(c->pool, sizeof(timer_point_t)); t->start = apr_time_now(); t->client = c->remote_addr; apr_pool_cleanup_register(c->pool, t, timer_end_conn, apr_pool_cleanup_null); return DECLINED; } static int timer_create_req(request_rec *r) { if (g_logfile < 0) { return DECLINED; } timer_point_t *t = apr_palloc(r->pool, sizeof(timer_point_t)); t->start = apr_time_now(); t->client = r->connection->remote_addr; apr_pool_cleanup_register(r->pool, t, timer_end_req, apr_pool_cleanup_null); return DECLINED; } void timer_child_init(apr_pool_t *p, server_rec *s) { g_logfile = open(TIMER_LOG_PATH, O_WRONLY|O_APPEND|O_CREAT, 0644); if (g_logfile < 0) { /* XXXXX: hack/abuse/killme */ apr_status_t rv = errno; ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, "[mod_timer] - Failed to open '%s' for writing!!", TIMER_LOG_PATH); } } static void timer_register_hooks(apr_pool_t * p) { ap_hook_child_init(timer_child_init, NULL, NULL, APR_HOOK_FIRST); ap_hook_process_connection(timer_process_conn, NULL, NULL, APR_HOOK_FIRST); ap_hook_create_request(timer_create_req, NULL, NULL, APR_HOOK_FIRST); } /* XXXXX: make the logging path configurable. */ static const command_rec timer_cmds[] = { {NULL} }; module AP_MODULE_DECLARE_DATA timer_module = { STANDARD20_MODULE_STUFF, NULL, NULL, NULL, NULL, timer_cmds, timer_register_hooks, };