.el .ne 3
.IP "\\$1" \\$2
..
-.TH "ROTATELOGS" 8 "2013-06-12" "Apache HTTP Server" "rotatelogs"
+.TH "ROTATELOGS" 8 "2013-08-19" "Apache HTTP Server" "rotatelogs"
.SH NAME
rotatelogs \- Piped logging program to rotate Apache logs
.SH "SYNOPSIS"
.PP
-\fBrotatelogs\fR [ -\fBl\fR ] [ -\fBL\fR \fIlinkname\fR ] [ -\fBp\fR \fIprogram\fR ] [ -\fBf\fR ] [ -\fBv\fR ] [ -\fBe\fR ] [ -\fBc\fR ] [ -\fBn\fR \fInumber-of-files\fR ] \fIlogfile\fR \fIrotationtime\fR|\fIfilesize\fR(B|K|M|G) [ \fIoffset\fR ]
+\fBrotatelogs\fR [ -\fBl\fR ] [ -\fBL\fR \fIlinkname\fR ] [ -\fBp\fR \fIprogram\fR ] [ -\fBf\fR ] [ -\fBd\fR ] [ -\fBv\fR ] [ -\fBe\fR ] [ -\fBc\fR ] [ -\fBn\fR \fInumber-of-files\fR ] \fIlogfile\fR \fIrotationtime\fR|\fIfilesize\fR(B|K|M|G) [ \fIoffset\fR ]
.SH "SUMMARY"
-n \fInumber-of-files\fR
Use a circular list of filenames without timestamps\&. With -n 3, the series of log files opened would be "logfile", "logfile\&.1", "logfile\&.2", then overwriting "logfile"\&.
.TP
+-d
+Creates the parent directories that the log file will be placed in if they do not already exist\&. This allows strftime(3) formatting to be used in the path and not just the filename\&.
+.TP
\fIlogfile\fR
.PP The path plus basename of the logfile\&. If \fIlogfile\fR includes any '%' characters, it is treated as a format string for strftime(3)\&. Otherwise, the suffix \fI\&.nnnnnnnnnn\fR is automatically added and is the time in seconds (unless the -t option is used)\&. Both formats compute the start time from the beginning of the current period\&. For example, if a rotation time of 86400 is specified, the hour, minute, and second fields created from the strftime(3) format will all be zero, referring to the beginning of the current 24-hour period (midnight)\&. .PP When using strftime(3) filename formatting, be sure the log file format has enough granularity to produce a different file name each time the logs are rotated\&. Otherwise rotation will overwrite the same file instead of starting a new one\&. For example, if \fIlogfile\fR was /var/logs/errorlog\&.%Y-%m-%d with log rotation at 5 megabytes, but 5 megabytes was reached twice in the same day, the same log file name would be produced and log rotation would keep writing to the same file\&.
.TP
int force_open;
int verbose;
int echo;
- const char *szLogRoot;
+ char *szLogRoot;
int truncate;
const char *linkfile;
const char *postrotate_prog;
int create_empty;
#endif
int num_files;
+ int create_path;
};
typedef struct rotate_status rotate_status_t;
}
fprintf(stderr,
#if APR_FILES_AS_SOCKETS
- "Usage: %s [-v] [-l] [-L linkname] [-p prog] [-f] [-t] [-e] [-c] [-n number] <logfile> "
+ "Usage: %s [-v] [-l] [-L linkname] [-p prog] [-f] [-d] [-t] [-e] [-c] [-n number] <logfile> "
#else
- "Usage: %s [-v] [-l] [-L linkname] [-p prog] [-f] [-t] [-e] [-n number] <logfile> "
+ "Usage: %s [-v] [-l] [-L linkname] [-p prog] [-f] [-d] [-t] [-e] [-n number] <logfile> "
#endif
"{<rotation time in seconds>|<rotation size>(B|K|M|G)} "
"[offset minutes from UTC]\n\n",
" -L path Create hard link from current log to specified path.\n"
" -p prog Run specified program after opening a new log file. See below.\n"
" -f Force opening of log on program start.\n"
+ " -d Create parent directories of log file.\n"
" -t Truncate logfile instead of rotating, tail friendly.\n"
" -e Echo log to stdout for further processing.\n"
#if APR_FILES_AS_SOCKETS
fprintf(stderr, "Rotation based on localtime: %12s\n", config->use_localtime ? "yes" : "no");
fprintf(stderr, "Rotation file date pattern: %12s\n", config->use_strftime ? "yes" : "no");
fprintf(stderr, "Rotation file forced open: %12s\n", config->force_open ? "yes" : "no");
+ fprintf(stderr, "Create parent directories: %12s\n", config->create_path ? "yes" : "no");
fprintf(stderr, "Rotation verbose: %12s\n", config->verbose ? "yes" : "no");
#if APR_FILES_AS_SOCKETS
fprintf(stderr, "Rotation create empty logs: %12s\n", config->create_empty ? "yes" : "no");
}
}
apr_pool_create(&newlog.pool, status->pool);
+ if (config->create_path) {
+ char *ptr = strrchr(newlog.name, '/');
+ if (ptr && ptr > newlog.name) {
+ char *path = apr_pstrmemdup(newlog.pool, newlog.name, ptr - newlog.name);
+ if (config->verbose) {
+ fprintf(stderr, "Creating directory tree %s\n", path);
+ }
+ rv = apr_dir_make_recursive(path, APR_FPROT_OS_DEFAULT, newlog.pool);
+ if (rv != APR_SUCCESS) {
+ char error[120];
+
+ apr_strerror(rv, error, sizeof error);
+ fprintf(stderr, "Could not create directory '%s' (%s)\n", path, error);
+ exit(2);
+ }
+ }
+ }
if (config->verbose) {
fprintf(stderr, "Opening file %s\n", newlog.name);
}
apr_pool_create(&status.pool, NULL);
apr_getopt_init(&opt, status.pool, argc, argv);
#if APR_FILES_AS_SOCKETS
- while ((rv = apr_getopt(opt, "lL:p:ftvecn:", &c, &opt_arg)) == APR_SUCCESS) {
+ while ((rv = apr_getopt(opt, "lL:p:fdtvecn:", &c, &opt_arg)) == APR_SUCCESS) {
#else
- while ((rv = apr_getopt(opt, "lL:p:ftven:", &c, &opt_arg)) == APR_SUCCESS) {
+ while ((rv = apr_getopt(opt, "lL:p:fdtven:", &c, &opt_arg)) == APR_SUCCESS) {
#endif
switch (c) {
case 'l':
case 'f':
config.force_open = 1;
break;
+ case 'd':
+ config.create_path = 1;
+ break;
case 't':
config.truncate = 1;
break;
usage(argv[0], "Incorrect number of arguments");
}
- config.szLogRoot = argv[opt->ind++];
+ rv = apr_filepath_merge(&config.szLogRoot, "", argv[opt->ind++],
+ APR_FILEPATH_TRUENAME, status.pool);
+ if (rv != APR_SUCCESS && rv != APR_EPATHWILD) {
+ usage(argv[0], "Invalid filename given");
+ }
/* Read in the remaining flags, namely time, size and UTC offset. */
for(; opt->ind < argc; opt->ind++) {