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