I'm new to the list, so I don't quite know the procedure for doing this
<or even if this is the right list for this>, but I have attached a
program that I think the apache community could benefit from.  The
program is self reliant, and will split the apache log files on the fly
(it will also rotate the logs).

----
History:
  Our web site is huge.  It has one domain and a BUNCH of sub
directories.  Each sub directory needed to act as a separate "virtual
host".  Because I couldn't have the customelog or transferlog within a
<directory> I created this app.  It allows me (using the <directory>
tags) to create a sub directory in www.mycompany.com/subdir and have
it's own logfile.


----
Usage: splitlogs [OPTIONS]... DEFAULT...

  Where OPTIONS can be FILE:DIRECTORY:ROTATION or FILE:DIRECTORY

  DEFAULT is in the format FILE:ROTATION or FILE

    The FILE is the path and name of the actual logfile.  The DIRECTORY
    is the directory you want split into a separate file.  The ROTATION
    is the system time at which the log will be rotated.  If no
    ROTATION time is given, the log will not be rotated and the file
    name will remain unchanged. If rotation is used, the log file will
    be in the format: FILE.2001_Aug_01_17:55.log


Installation:
  Add the following to the httpd.conf:

TransferLog "| splitlogs log-A:/A:86400 log-B:/B log-def


Note: If you have the two directories /a and /a/b, make sure to place
      the /a/b before /a.  Otherwise all of the /a/b requests would
      end up in the wrong logfile.

-- 
Chuck Pierce                Phone: (615) 253-5634
Sr. Systems Administrator   Pager: (888) 569-8085
State TN, OIR/Finance         Fax: (615) 741-7341
/*
 * A program to split the output of a logfile on the fly and rotate (if necessary)
 *
 * Contributed by Chuck Pierce <[EMAIL PROTECTED]>
 *
 * Aug 01, 2001
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>

#define NAME "splitlogs"
#define VERSION "1.0a"
#define BUFSIZE 65536

#ifndef utc_offset
#define utc_offset 0
#endif

struct darray {
  char **data;
  int count;
};

struct options {
  char *dir;   /* Directory in the url */
  char *file;  /* Location of the logfile */
  char *ofile; /* Unmodified name of the file */
  int FOUT;   /* File pointer */
  int rotate; /* Rotation time <set to 0 if none>. */
};

struct darray *SPLIT(char *data, char *delim) {
  /* a function for spliting a string into an array of strings */

  char *ptr = data, *fptr = data, **array = NULL, *string;
  int j, i, len, counter = 0;
  struct darray *output;

  output = calloc(sizeof(struct darray *), counter);
  if (output == NULL) { fprintf(stderr, "Error: out of memory\n"); exit(0); }
  
  /* if there is nothing to split, there is no need to try to split it */
  if (!data) {
    output->count = 0;
    return output;
  }

  /* find out how big we need to be */
  do {
    ptr = strstr(ptr, delim);
    if (ptr) {
      ptr = ptr + strlen(delim);
    }
    counter++;
  } while (ptr);
  
  /* split */
  ptr = data;
  array = calloc(sizeof(char **), counter);
  if (array == NULL) { fprintf(stderr, "Error: out of memory\n"); exit(0); }
  for (j=0; j<counter; j++) {
    /* find where the delimeter is */
    ptr = strstr(ptr, delim);
    if (ptr) { len = ptr - fptr; } else { len = strlen(fptr); }

    /* create new string to hold the new data */
    string = calloc(sizeof(char), len);
    if (string == NULL) { fprintf(stderr, "Error: out of memory\n"); exit(0); }
    /* copy the segment to a new string */
    for (i=0; i<len; i++) {
      string[i] = fptr[i];
    }

    /* move to the next part to be split */
    if (ptr) {
      ptr = ptr + strlen(delim);
      fptr = ptr;
    }

    array[j] = string;
  }
  output->data = array;
  output->count = counter;

  return (output);
}


int main (int argc, char **argv) {
  char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", 
"Oct", "Nov", "Dec" };
  /* Get the date/time */
  time_t timep = time(NULL) + utc_offset, logtimep = timep;
  struct tm *date = localtime(&timep);
  char line[BUFSIZE], *bptr, *fptr;
  int i = 0, nread, nwrite;
  struct options **list, *item;
  struct darray *parced;


  if ((argc < 2) || !strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || 
!strcmp(argv[1], "--help")) {
    fprintf(stderr,
      "%s, version %s\n\n"
      "Usage: %s [OPTIONS]... DEFAULT...\n"
      "\n"
      "  Where OPTIONS can be FILE:DIRECTORY:ROTATION or FILE:DIRECTORY\n"
      "\n"
      "  DEFAULT is in the format FILE:ROTATION or FILE\n"
      "\n"
      "    The FILE is the path and name of the actual logfile.  The DIRECTORY\n"
      "    is the directory you want split into a separate file.  The ROTATION\n"
      "    is the system time at which the log will be rotated.  If no\n"
      "    ROTATION time is given, the log will not be rotated and the file\n"
      "    name will remain unchanged. If rotation is used, the log file will\n"
      "    be in the format: FILE.%0.4d_%s_%0.2d_%0.2d:%0.2d.log\n"
      "\n"
      "\n"
      "Installation:\n"
      "  Add the following to the httpd.conf:\n"
      "\n"
      "TransferLog \"|%s log-A:/A:86400 log-B:/B log-def\n"
      "\n"
      "\n"
      "Note: If you have the two directories /a and /a/b, make sure to place\n"
      "      the /a/b before /a.  Otherwise all of the /a/b requests would\n"
      "      end up in the wrong logfile.\n"
      , NAME, VERSION, NAME, date->tm_year + 1900, months[date->tm_mon], 
date->tm_mday, date->tm_hour, date->tm_min, argv[0]);

    exit(1);
  }

  list = calloc(sizeof(struct options **), argc);
  if (list == NULL) { fprintf(stderr, "Error: out of memory\n"); exit(0); }

  /* Build the array of options, fill the list and open the files */
  for (i=0; i<argc-1; i++) {
    item = calloc(sizeof(struct options *), 1);
    if (item == NULL) { fprintf(stderr, "Error: out of memory\n"); exit(0); }
    parced = SPLIT(argv[i+1], ":");
    if (parced->data[0] && strlen(parced->data[0])) {
      item->ofile = parced->data[0];
    } else {
      fprintf(stderr, "Invalad usage.  Try using %s --help\n", argv[0]);
      exit(1);
    }
    if (parced->data[1] && strlen(parced->data[1]) && (i != (argc - 1))) {
      item->dir = calloc(sizeof(char *), strlen(parced->data[1]) + 6);
      sprintf(item->dir, " \"GET %s", parced->data[1]);
    } else {
      item->dir = NULL;
    }
    if (parced->data[2]) {
      item->file = calloc(sizeof(char *), strlen(item->ofile) + 21);
      sprintf(item->file, "%s.%0.4d_%s_%0.2d_%0.2d:%0.2d.log", item->ofile, 
date->tm_year + 1900, months[date->tm_mon], date->tm_mday, date->tm_hour, 
date->tm_min);
      item->rotate = atoi(parced->data[2]);
    } else {
      if ((i == (argc - 2)) && parced->data[1] && strlen(parced->data[1])) {
        item->file = calloc(sizeof(char *), strlen(item->ofile) + 21);
        sprintf(item->file, "%s.%0.4d_%s_%0.2d_%0.2d:%0.2d.log", item->ofile, 
date->tm_year + 1900, months[date->tm_mon], date->tm_mday, date->tm_hour, 
date->tm_min);
        item->rotate = atoi(parced->data[1]);
      } else {
        item->file = item->ofile;
        item->rotate = 0;
      }
    }
    item->FOUT = open(item->file, O_WRONLY | O_CREAT | O_APPEND, 0640);
    if (item->FOUT < 0) { fprintf(stderr, "Error opening file %s\n", item->file); 
exit(2); }

    list[i] = item;
  }

  /* Main Loop */
  for (;;) {
    /* Read the log entery */
    nread = read(0, line, sizeof line);

    if (nread <= 0) {
      /* If this hapens, the program needs to quit */
      exit(3);
    }
    timep = time(NULL) + utc_offset;

    /* Go through each directory and see which one gets the log entry */
    for (i=0; i<argc-1; i++) {
      if ((list[i]->dir && strstr(line, list[i]->dir)) || (i == (argc - 2))) {
        if (list[i]->rotate && (timep >= (logtimep + list[i]->rotate))) {
          /* It's time to rotate the log */
          close(list[i]->FOUT);
          logtimep = timep;
          date = localtime(&timep);
          sprintf(list[i]->file, "%s.%0.4d_%s_%0.2d_%0.2d:%0.2d.log", list[i]->ofile, 
date->tm_year + 1900, months[date->tm_mon], date->tm_mday, date->tm_hour, 
date->tm_min);
          list[i]->FOUT = open(list[i]->file, O_WRONLY | O_CREAT | O_APPEND, 0640);
          if (list[i]->FOUT < 0) { fprintf(stderr, "Error opening file %s\n", 
list[i]->file); exit(2); }
        }
        do {
          nwrite = write(list[i]->FOUT, line, nread);
        } while (nwrite < 0);
        if (nread != nwrite) { fprintf(stderr, "Error writing to file %s.", 
list[i]->file); exit(2); }
        break;
      }
    }
  }
  /* Suppress compiler warning */
  return (0);
}

Reply via email to