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 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);
00027 return nowgmt - nowantilocal;
00028 }
00029
00030
00031
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
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
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
00095 }
00096
00097 void WvLogFile::_make_prefix(time_t timenow)
00098 {
00099 if (!WvFile::isok())
00100 start_log();
00101
00102
00103 struct stat statbuf;
00104
00105
00106 if (fstat(getfd(), &statbuf) == -1)
00107 statbuf.st_size = 0;
00108
00109
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
00125 if (!strncmp(i.ptr()->name, base, strlen(base)))
00126 {
00127
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
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
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
00171
00172 pid_t forky = wvfork();
00173 if (!forky)
00174 {
00175
00176 if (!wvfork())
00177 {
00178
00179 trim_old_logs(filename, base, keep_for);
00180 _exit(0);
00181 }
00182 _exit(0);
00183 }
00184
00185 pid_t rv;
00186 while ((rv = waitpid(forky, NULL, 0)) != forky)
00187 if (rv == -1 && errno != EINTR)
00188 break;
00189 #else
00190
00191 trim_old_logs(filename, base, keep_for);
00192 #endif
00193
00194 return fullname;
00195 }