Author: timw
Date: Sun Mar  7 20:31:45 2010
New Revision: 920093

URL: http://svn.apache.org/viewvc?rev=920093&view=rev
Log:
Adding basic log rotation functionality to the ISAPI redirector - #48501
Config and functionality is modeled after the Apache HTTPD rotatelogs program 
for consistency.

Modified:
    tomcat/jk/trunk/native/iis/jk_isapi_plugin.c
    tomcat/jk/trunk/xdocs/reference/iis.xml

Modified: tomcat/jk/trunk/native/iis/jk_isapi_plugin.c
URL: 
http://svn.apache.org/viewvc/tomcat/jk/trunk/native/iis/jk_isapi_plugin.c?rev=920093&r1=920092&r2=920093&view=diff
==============================================================================
--- tomcat/jk/trunk/native/iis/jk_isapi_plugin.c (original)
+++ tomcat/jk/trunk/native/iis/jk_isapi_plugin.c Sun Mar  7 20:31:45 2010
@@ -128,6 +128,9 @@
 #define ENABLE_CHUNKED_ENCODING_TAG ("enable_chunked_encoding")
 #define ERROR_PAGE_TAG              ("error_page")
 
+#define LOG_ROTATION_TIME_TAG       ("log_rotationtime")
+#define LOG_FILESIZE_TAG            ("log_filesize")
+
 /* HTTP standard headers */
 #define TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE     ("Transfer-Encoding: 
chunked")
 #define TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE_LEN (26)
@@ -479,6 +482,7 @@
 static jk_map_t *jk_environment_map = NULL;
 
 static jk_logger_t *logger = NULL;
+static JK_CRIT_SEC log_cs;
 static char *SERVER_NAME = "SERVER_NAME";
 static char *SERVER_SOFTWARE = "SERVER_SOFTWARE";
 static char *INSTANCE_ID = "INSTANCE_ID";
@@ -487,7 +491,12 @@
 static char extension_uri[INTERNET_MAX_URL_LENGTH] =
     "/jakarta/isapi_redirect.dll";
 static char log_file[MAX_PATH * 2];
+static char log_file_effective[MAX_PATH * 2];
 static int  log_level = JK_LOG_DEF_LEVEL;
+static long log_rotationtime = 0;
+static time_t log_next_rotate_time = 0;
+static ULONGLONG log_filesize = 0;
+
 static char worker_file[MAX_PATH * 2];
 static char worker_mount_file[MAX_PATH * 2] = {0};
 static int  worker_mount_reload = JK_URIMAP_DEF_RELOAD;
@@ -557,6 +566,10 @@
 
 static int init_jk(char *serverName);
 
+
+static int JK_METHOD iis_log_to_file(jk_logger_t *l, int level,
+                                    int used, char *what);
+
 static BOOL initialize_extension(void);
 
 static int read_registry_init_data(void);
@@ -2273,9 +2286,11 @@
         }
         wc_close(logger);
         jk_shm_close();
+        JK_ENTER_CS(&(log_cs), rc);
         if (logger) {
             jk_close_file_logger(&logger);
         }
+        JK_LEAVE_CS(&(log_cs), rc);
     }
     JK_LEAVE_CS(&(init_cs), rc);
 
@@ -2335,6 +2350,7 @@
         StringCbPrintf(HTTP_WORKER_HEADER_INDEX, MAX_PATH, 
HTTP_HEADER_TEMPLATE, WORKER_HEADER_INDEX_BASE, hInst);
 
         JK_INIT_CS(&init_cs, rc);
+        JK_INIT_CS(&log_cs, rc);
 
     break;
     case DLL_PROCESS_DETACH:
@@ -2344,6 +2360,7 @@
         __except(1) {
         }
         JK_DELETE_CS(&init_cs, rc);
+        JK_DELETE_CS(&log_cs, rc);
         break;
 
     default:
@@ -2387,17 +2404,147 @@
     return 0;
 }
 
+/*
+ * Reinitializes the logger, formatting the log file name if rotation is 
enabled,
+ * and calculating the next rotation time if applicable.
+ */
+static int init_logger(int rotate, jk_logger_t **l)
+{
+    int rc = JK_TRUE;
+    int log_open = rotate;  /* log is assumed open if a rotate is requested */
+    char *log_file_name;
+    char log_file_name_buf[MAX_PATH*2];
+
+    /* If log rotation is enabled, format the log filename */
+    if ((log_rotationtime > 0) || (log_filesize > 0)) {
+        time_t t;
+        t = time(NULL);
+
+        if (log_rotationtime > 0) {
+            /* Align time to rotationtime intervals */
+            t = (t / log_rotationtime) * log_rotationtime;
+
+            /* Calculate rotate time */
+            log_next_rotate_time = t + log_rotationtime;
+        }
+
+        log_file_name = log_file_name_buf;
+        if (strchr(log_file, '%')) {
+            struct tm *tm_now;
+
+            /* If there are %s in the log file name, treat it as a sprintf 
format */
+            tm_now = localtime(&t);
+            strftime(log_file_name, sizeof(log_file_name_buf), log_file, 
tm_now);
+        } else {
+            /* Otherwise append the number of seconds to the base name */      
  
+            sprintf_s(log_file_name, sizeof(log_file_name_buf), "%s.%d", 
log_file, (long)t);
+        }
+    } else {
+        log_file_name = log_file;
+    }
+
+    /* Close the current log file if required, and the effective log file name 
has changed */
+    if (log_open && strncmp(log_file_name, log_file_effective, 
strlen(log_file_name)) != 0) {
+        FILE* lf = ((jk_file_logger_t* )logger->logger_private)->logfile;
+        fprintf_s(lf, "Log rotated to %s\r\n", log_file_name);
+        fflush(lf);
+
+        rc = jk_close_file_logger(&logger);
+        log_open = JK_FALSE;
+    }
+
+    if (!log_open) {
+        if (jk_open_file_logger(&logger, log_file_name, log_level)) {
+            logger->log = iis_log_to_file;
+
+            /* Remember the current log file name for the next potential 
rotate */
+            strcpy_s(log_file_effective, sizeof(log_file_effective), 
log_file_name);
+            rc = JK_TRUE;
+        } else {
+            logger = NULL;
+            rc = JK_FALSE;
+        }
+    }
+
+    /* Update logger being used for this log call so it occurs on new file */
+    (*l) = logger;
+    return rc;
+}
+
+/*
+ * Checks whether the log needs to be rotated. Must be called while holding 
the log lock.
+ * The behaviour here is based on the Apache rotatelogs program.
+ * http://httpd.apache.org/docs/2.0/programs/rotatelogs.html
+ */
+static int JK_METHOD rotate_log_file(jk_logger_t **l)
+{
+    int rc = JK_TRUE;
+    int rotate = JK_FALSE;
+
+    if (log_rotationtime > 0) {
+        time_t t = time(NULL);
+        
+        if (t >= log_next_rotate_time) {
+            rotate = JK_TRUE;
+        }
+    } else if (log_filesize > 0) {
+        LARGE_INTEGER filesize;
+        HANDLE h = (HANDLE)_get_osfhandle(fileno(((jk_file_logger_t 
*)(*l)->logger_private)->logfile));
+        GetFileSizeEx(h, &filesize);
+
+        if ((ULONGLONG)filesize.QuadPart >= log_filesize) {
+            rotate = JK_TRUE;
+        }
+    }
+    if (rotate) {
+        rc = init_logger(JK_TRUE, l);
+    }
+    return rc;
+}
+
+/*
+ * Log messages to the log file, rotating the log when required.
+ */
+static int JK_METHOD iis_log_to_file(jk_logger_t *l, int level,
+                                    int used, char *what)
+{
+    int rc = JK_FALSE;
+
+    if (l &&
+        (l->level <= level || level == JK_LOG_REQUEST_LEVEL) &&
+        l->logger_private && what && used > 0) {
+        jk_file_logger_t *p = l->logger_private;
+        rc = JK_TRUE;
+
+        if (p->logfile) {
+            what[used++] = '\r';
+            what[used++] = '\n';
+            what[used] = '\0';
+    
+            /* Perform logging within critical section to protect rotation */
+            JK_ENTER_CS(&(log_cs), rc);
+            if (rc && rotate_log_file(&l)) {
+                /* The rotation process will reallocate the jk_logger_t 
structure, so refetch */ 
+                FILE *rotated = ((jk_file_logger_t 
*)l->logger_private)->logfile;
+                fputs(what, rotated);
+                fflush(rotated);
+                JK_LEAVE_CS(&(log_cs), rc);
+            }
+        }
+    }
+    return rc;
+}
+
 static int init_jk(char *serverName)
 {
     char shm_name[MAX_PATH];
     int rc = JK_FALSE;
+    
+    init_logger(JK_FALSE, &logger);
+    /* TODO: Use System logging to notify the user that
+     *       we cannot open the configured log file.
+     */
 
-    if (!jk_open_file_logger(&logger, log_file, log_level)) {
-        /* TODO: Use System logging to notify the user that
-         *       we cannot open the configured log file.
-         */
-        logger = NULL;
-    }
     StringCbCopy(shm_name, MAX_PATH, SHM_DEF_NAME);
 
     jk_log(logger, JK_LOG_INFO, "Starting %s", (VERSION_STRING));
@@ -2428,6 +2575,9 @@
 
         jk_log(logger, JK_LOG_DEBUG, "Using log file %s.", log_file);
         jk_log(logger, JK_LOG_DEBUG, "Using log level %d.", log_level);
+        jk_log(logger, JK_LOG_DEBUG, "Using log rotation time %d seconds.", 
log_rotationtime);
+        jk_log(logger, JK_LOG_DEBUG, "Using log file size %d bytes.", 
log_filesize);
+
         jk_log(logger, JK_LOG_DEBUG, "Using extension uri %s.", extension_uri);
         jk_log(logger, JK_LOG_DEBUG, "Using worker file %s.", worker_file);
         jk_log(logger, JK_LOG_DEBUG, "Using worker mount file %s.",
@@ -2456,6 +2606,12 @@
                                      DEFAULT_WORKER_THREADS);
     }
 
+    if ((log_rotationtime > 0) && (log_filesize > 0)) {
+        jk_log(logger, JK_LOG_WARNING, 
+            "%s is defined in configuration, but will be ignored because %s is 
set. ",
+            LOG_FILESIZE_TAG, LOG_ROTATION_TIME_TAG);
+    }
+
     if (rewrite_rule_file[0] && jk_map_alloc(&rewrite_map)) {
         if (jk_map_read_properties(rewrite_map, NULL, rewrite_rule_file,
                                    NULL, JK_MAP_HANDLE_RAW, logger)) {
@@ -2640,6 +2796,28 @@
     if (get_config_parameter(src, JK_LOG_LEVEL_TAG, tmpbuf, sizeof(tmpbuf))) {
         log_level = jk_parse_log_level(tmpbuf);
     }
+    if (get_config_parameter(src, LOG_ROTATION_TIME_TAG, tmpbuf, 
sizeof(tmpbuf))) {
+        log_rotationtime = atol(tmpbuf);
+        if (log_rotationtime < 0) {
+            log_rotationtime = 0;
+        }
+    }
+    if (get_config_parameter(src, LOG_FILESIZE_TAG, tmpbuf, sizeof(tmpbuf))) {
+        size_t tl = strlen(tmpbuf);
+        if (tl > 0) {
+            /* rotatelogs has an 'M' suffix on filesize, which we optionally 
support for consistency */
+            if (tmpbuf[tl - 1] == 'M') {
+                tmpbuf[tl - 1] = '\0';
+            }
+            log_filesize = atol(tmpbuf);
+            if (log_filesize < 0) {
+                log_filesize = 0;
+            }
+            /* Convert to MB as per Apache rotatelogs */
+            log_filesize *= (1000 * 1000);
+        }
+    }
+
     ok = ok && get_config_parameter(src, EXTENSION_URI_TAG, extension_uri, 
sizeof(extension_uri));
     ok = ok && get_config_parameter(src, JK_WORKER_FILE_TAG, worker_file, 
sizeof(worker_file));
     ok = ok && get_config_parameter(src, JK_MOUNT_FILE_TAG, worker_mount_file, 
sizeof(worker_mount_file));

Modified: tomcat/jk/trunk/xdocs/reference/iis.xml
URL: 
http://svn.apache.org/viewvc/tomcat/jk/trunk/xdocs/reference/iis.xml?rev=920093&r1=920092&r2=920093&view=diff
==============================================================================
--- tomcat/jk/trunk/xdocs/reference/iis.xml (original)
+++ tomcat/jk/trunk/xdocs/reference/iis.xml Sun Mar  7 20:31:45 2010
@@ -75,11 +75,24 @@
 <attribute name="log_file" required="false"><p>
 A value pointing to location where log file will be created.
 (for example <b>c:\tomcat\logs\isapi.log</b>)
+<br/>If one of the log rotation settings (<b>log_rotationtime</b> or 
<b>log_filesize</b>) are specified then the actual log file name is based on 
this setting.
+If the log file name includes any '%' characters, then it is treated as a 
format string for <code>strftime(3)</code>, 
+e.g. <b>c:\tomcat\logs\isapi-%Y-%m-%d-%H_%M_%S.log</b>. Otherwise, the suffix 
<em>.nnnnnnnnnn</em> is automatically added and is the time in seconds.
+A full list of format string substitutions can be found in the <a 
href="http://httpd.apache.org/docs/2.0/programs/rotatelogs.html";>Apache 
rotatelogs documentation</a>
 </p></attribute>
 <attribute name="log_level" required="false"><p>
 A string value for log level 
 (can be debug, info, warn, error or trace).
 </p></attribute>
+<attribute name="log_rotationtime" required="false"><p>
+The time between log file rotations in seconds.
+Setting this to 0 (the default) disables log rotation based on time. 
+</p></attribute>
+<attribute name="log_filesize" required="false"><p>
+The maximum log file size in megabytes, after which the log file will be 
rotated. Setting this to 0 (the default) disables log rotation based on file 
size.
+<br>The value can have an optional <b>M</b> suffix, i.e. both <b>5</b> and 
<b>5M</b> will rotate the log file when it grows to 5MB.
+<br>If <b>log_rotationtime</b> is specified, then this setting is ignored.  
+</p></attribute>
 <attribute name="worker_file" required="true"><p>
 A string value which is the full path to workers.properties file
 (for example <b>c:\tomcat\conf\workers.properties</b>)
@@ -289,6 +302,58 @@
 </source></p>
 </section>
 
+<section name="Log file rotation">
+<p>
+The ISAPI redirector with version 1.2.31 can perform log rotation, with 
configuration and behaviour similar to the 
+<a 
href="http://httpd.apache.org/docs/2.0/programs/rotatelogs.html";>rotatelogs</a> 
program provided with Apache HTTP Server.
+</p>
+<p>
+To configure log rotation, configure a <b>log_file</b>, and one of the 
<b>log_rotationtime</b> or <b>log_filesize</b> options.
+If both are specified, the <b>log_rotationtime</b> will take precedence, and 
<b>log_filesize</b> will be ignored. 
+<br/>For example, to configure daily rotation of the log file:
+</p>
+<source>
+# Configuration file for the Jakarta ISAPI Redirector
+...
+
+# Full path to the log file for the ISAPI Redirector
+log_file=c:\tomcat\logs\isapi_redirect.%Y-%m-%d.log
+
+# Log level (debug, info, warn, error or trace)
+log_level=info
+
+# Rotate the log file every day
+log_rotationtime=86400
+
+...
+</source>
+<p>
+Or to configure rotation of the log file when it reaches 5MB in size:
+</p>
+<source>
+# Configuration file for the Jakarta ISAPI Redirector
+...
+
+# Full path to the log file for the ISAPI Redirector
+log_file=c:\tomcat\logs\isapi_redirect.%Y-%m-%d-%H.log
+
+# Log level (debug, info, warn, error or trace)
+log_level=info
+
+# Rotate the log file at 5 MB
+log_filesize=5M
+
+...
+</source>
+<p>
+The log will be rotated whenever the configured limit is reached, but only if 
the log file name would change. If you configure
+ a log file name with <code>strftime(3)</code> format codes in it, then ensure 
it specifies the same granularity
+ as the rotation time configured, e.g. <b>%Y-%m-%d</b> if rotating daily 
(<b>log_rotationtime=86400</b>).
+<br/>See the <a 
href="http://httpd.apache.org/docs/2.0/programs/rotatelogs.html";>rotatelogs</a> 
documentation for more examples.
+</p>
+
+</section>
+
 <section name="Using a simple rewrite rules">
 <p>
 The ISAPI redirector with version 1.2.16 can do a simple URL rewriting. 
Although not



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to