• Main Page
  • Modules
  • Classes
  • Files
  • File List
  • File Members

wvlogfile.cc

00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  *
00005  * A "Log Receiver" that logs messages to a file
00006  */
00007 #include "wvlogfile.h"
00008 #include "wvtimeutils.h"
00009 #include "wvdiriter.h"
00010 #include "strutils.h"
00011 #include "wvdailyevent.h"
00012 #include "wvfork.h"
00013 #include <time.h>
00014 #include <sys/types.h>
00015 #ifndef _WIN32
00016 #include <sys/wait.h>
00017 #endif
00018 
00019 #define MAX_LOGFILE_SZ  1024*1024*100   // 100 Megs
00020 
00021 #ifdef MACOS
00022 #define O_LARGEFILE 00000000 // MAC doesn't need Largefile support, so just make it a dummy value when ORd
00023 #endif
00024 
00025 static time_t gmtoffset()
00026 {
00027     time_t nowgmt = time(NULL);
00028     struct tm gmt = *gmtime(&nowgmt);
00029     struct tm local = *localtime(&nowgmt);
00030     time_t nowantilocal = mktime(&gmt); // mktime assumes gmt
00031     return nowgmt - nowantilocal;
00032 }
00033 
00034 
00035 //----------------------------------- WvLogFileBase ------------------
00036 
00037 WvLogFileBase::WvLogFileBase(WvStringParm _filename, WvLog::LogLevel _max_level)
00038     : WvLogRcv(_max_level),
00039       WvFile(_filename, O_WRONLY|O_APPEND|O_CREAT|O_LARGEFILE, 0644)
00040 {
00041     fsync_every = fsync_count = 0;
00042 }
00043 
00044 
00045 WvLogFileBase::WvLogFileBase(WvLog::LogLevel _max_level) 
00046     : WvLogRcv(_max_level) 
00047 { 
00048     fsync_every = fsync_count = 0;
00049 }
00050 
00051 
00052 void WvLogFileBase::_mid_line(const char *str, size_t len)
00053 {
00054     WvFile::write(str, len);
00055 }
00056 
00057 
00058 void WvLogFileBase::_end_line()
00059 {
00060     if (fsync_every)
00061     {
00062         fsync_count--;
00063         if (fsync_count <= 0 || fsync_count > fsync_every)
00064         {
00065             fsync_count = fsync_every;
00066             //WvFile::print("tick!\n");
00067             WvFile::flush(1000);
00068             fsync(getwfd());
00069         }
00070     }
00071 }
00072 
00073 #ifdef _WIN32
00074 #define TIME_FORMAT "%b %d %H:%M:%S" // timezones in win32 look stupid
00075 #else
00076 #define TIME_FORMAT "%b %d %H:%M:%S %Z"
00077 #endif
00078 
00079 void WvLogFileBase::_make_prefix(time_t timenow)
00080 {
00081     struct tm* tmstamp = localtime(&timenow);
00082     char timestr[30];
00083     strftime(&timestr[0], 30, TIME_FORMAT, tmstamp);
00084 
00085     prefix = WvString("%s: %s<%s>: ", timestr, last_source,
00086         loglevels[last_level]);
00087     prelen = prefix.len();
00088 }
00089 
00090 //----------------------------------- WvLogFile ----------------------
00091 
00092 WvLogFile::WvLogFile(WvStringParm _filename, WvLog::LogLevel _max_level,
00093                      int _keep_for, bool _force_new_line, bool _allow_append)
00094     : WvLogFileBase(_max_level), keep_for(_keep_for), filename(_filename),
00095       allow_append(_allow_append)
00096 {
00097     WvLogRcv::force_new_line = _force_new_line;
00098     // start_log(); // don't open log until the first message gets printed
00099 }
00100 
00101 void WvLogFile::_make_prefix(time_t timenow)
00102 {
00103     if (!WvFile::isok())
00104         start_log();
00105     
00106     // struct tm *tmstamp = localtime(&timenow);
00107     struct stat statbuf;
00108 
00109     // Get the filesize
00110     if (fstat(getfd(), &statbuf) == -1)
00111         statbuf.st_size = 0;
00112 
00113     // Make sure we are calculating last_day in the current time zone.
00114     if (last_day != ((timenow + gmtoffset())/86400) 
00115         || statbuf.st_size > MAX_LOGFILE_SZ)
00116         start_log();
00117 
00118     WvLogFileBase::_make_prefix(timenow);
00119 }
00120 
00121 static void trim_old_logs(WvStringParm filename, WvStringParm base,
00122                           int keep_for)
00123 {
00124     if (!keep_for) return;
00125     WvDirIter i(getdirname(filename), false);
00126     for (i.rewind(); i.next(); )
00127     {
00128         // if it begins with the base name
00129         if (!strncmp(i.ptr()->name, base, strlen(base)))
00130         {
00131             // and it's older than 'keep_for' days
00132             if (i.ptr()->st_mtime < wvtime().tv_sec - keep_for*86400)
00133                 ::unlink(i.ptr()->fullname);
00134         }
00135     }
00136 }
00137 
00138 
00139 WvString WvLogFile::start_log()
00140 {
00141     WvFile::close();
00142 
00143     int num = 0;
00144     struct stat statbuf;
00145     time_t timenow = wvtime().tv_sec;
00146     last_day = (timenow + gmtoffset()) / 86400;
00147     struct tm* tmstamp = localtime(&timenow);
00148     char buf[20];
00149     WvString fullname;
00150     strftime(buf, 20, "%Y-%m-%d", tmstamp);
00151 
00152     // Get the next filename
00153     do
00154         fullname = WvString("%s.%s.%s", filename, buf, num++);
00155     while (stat(fullname, &statbuf) != -1
00156                 && (statbuf.st_size >= MAX_LOGFILE_SZ || !allow_append));
00157 
00158     WvString curname("%s.current", filename);
00159     WvString base = getfilename(filename);
00160 
00161     WvFile::open(fullname, O_WRONLY|O_APPEND|O_CREAT|O_LARGEFILE, 0644);
00162 
00163 #ifndef _WIN32 // no symlinks in win32
00164     // Don't delete the file, unless it's a symlink!
00165     int sym = readlink(curname, buf, 20);
00166     if (sym > 0 || errno == ENOENT)
00167     {
00168         unlink(curname);
00169         symlink(getfilename(fullname), curname);
00170     }
00171 #endif
00172 
00173 #ifndef _WIN32
00174     // We fork here because this can be really slow when the directory has
00175     // (oh, say 32,000 files)
00176     pid_t forky = wvfork();
00177     if (!forky)
00178     {
00179         // ForkTwiceSoTheStupidThingWorksRight
00180         if (!wvfork())
00181         {
00182             // Child will Look for old logs and purge them
00183             trim_old_logs(filename, base, keep_for);
00184             _exit(0);
00185         }
00186         _exit(0);
00187     }
00188     // In case a signal is in the process of being delivered...
00189     pid_t rv;
00190     while ((rv = waitpid(forky, NULL, 0)) != forky)
00191         if (rv == -1 && errno != EINTR)
00192             break;
00193 #else
00194     // just do it in the foreground on Windows
00195     trim_old_logs(filename, base, keep_for);
00196 #endif
00197     
00198     return fullname;
00199 }

Generated on Thu Aug 12 2010 11:33:10 for WvStreams by  doxygen 1.7.1