00001
00002
00003
00004
00005
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);
00031 return nowgmt - nowantilocal;
00032 }
00033
00034
00035
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
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(×tr[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
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
00099 }
00100
00101 void WvLogFile::_make_prefix(time_t timenow)
00102 {
00103 if (!WvFile::isok())
00104 start_log();
00105
00106
00107 struct stat statbuf;
00108
00109
00110 if (fstat(getfd(), &statbuf) == -1)
00111 statbuf.st_size = 0;
00112
00113
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
00129 if (!strncmp(i.ptr()->name, base, strlen(base)))
00130 {
00131
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
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
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
00175
00176 pid_t forky = wvfork();
00177 if (!forky)
00178 {
00179
00180 if (!wvfork())
00181 {
00182
00183 trim_old_logs(filename, base, keep_for);
00184 _exit(0);
00185 }
00186 _exit(0);
00187 }
00188
00189 pid_t rv;
00190 while ((rv = waitpid(forky, NULL, 0)) != forky)
00191 if (rv == -1 && errno != EINTR)
00192 break;
00193 #else
00194
00195 trim_old_logs(filename, base, keep_for);
00196 #endif
00197
00198 return fullname;
00199 }