WvStreams
wvlogfile.cc
1/*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * A "Log Receiver" that logs messages to a file
6 */
7#include "wvlogfile.h"
8#include "wvtimeutils.h"
9#include "wvdiriter.h"
10#include "strutils.h"
11#include "wvdailyevent.h"
12#include "wvfork.h"
13#include <time.h>
14#include <sys/types.h>
15#ifndef _WIN32
16#include <sys/wait.h>
17#endif
18
19#define MAX_LOGFILE_SZ 1024*1024*100 // 100 Megs
20
21#ifdef MACOS
22#define O_LARGEFILE 00000000 // MAC doesn't need Largefile support, so just make it a dummy value when ORd
23#endif
24
25static time_t gmtoffset()
26{
27 time_t nowgmt = time(NULL);
28 struct tm gmt = *gmtime(&nowgmt);
29 struct tm local = *localtime(&nowgmt);
30 time_t nowantilocal = mktime(&gmt); // mktime assumes gmt
31 return nowgmt - nowantilocal;
32}
33
34
35//----------------------------------- WvLogFileBase ------------------
36
37WvLogFileBase::WvLogFileBase(WvStringParm _filename, WvLog::LogLevel _max_level)
38 : WvLogRcv(_max_level),
39 WvFile(_filename, O_WRONLY|O_APPEND|O_CREAT|O_LARGEFILE, 0644)
40{
41 fsync_every = fsync_count = 0;
42}
43
44
45WvLogFileBase::WvLogFileBase(WvLog::LogLevel _max_level)
46 : WvLogRcv(_max_level)
47{
48 fsync_every = fsync_count = 0;
49}
50
51
52void WvLogFileBase::_mid_line(const char *str, size_t len)
53{
54 WvFile::write(str, len);
55}
56
57
59{
60 if (fsync_every)
61 {
62 fsync_count--;
63 if (fsync_count <= 0 || fsync_count > fsync_every)
64 {
65 fsync_count = fsync_every;
66 //WvFile::print("tick!\n");
67 WvFile::flush(1000);
68 fsync(getwfd());
69 }
70 }
71}
72
73#ifdef _WIN32
74#define TIME_FORMAT "%b %d %H:%M:%S" // timezones in win32 look stupid
75#else
76#define TIME_FORMAT "%b %d %H:%M:%S %Z"
77#endif
78
79void WvLogFileBase::_make_prefix(time_t timenow)
80{
81 struct tm* tmstamp = localtime(&timenow);
82 char timestr[30];
83 strftime(&timestr[0], 30, TIME_FORMAT, tmstamp);
84
85 prefix = WvString("%s: %s<%s>: ", timestr, last_source,
86 loglevels[last_level]);
87 prelen = prefix.len();
88}
89
90//----------------------------------- WvLogFile ----------------------
91
92WvLogFile::WvLogFile(WvStringParm _filename, WvLog::LogLevel _max_level,
93 int _keep_for, bool _force_new_line, bool _allow_append)
94 : WvLogFileBase(_max_level), keep_for(_keep_for), filename(_filename),
95 allow_append(_allow_append)
96{
97 WvLogRcv::force_new_line = _force_new_line;
98 // start_log(); // don't open log until the first message gets printed
99}
100
101void WvLogFile::_make_prefix(time_t timenow)
102{
103 if (!WvFile::isok())
104 start_log();
105
106 // struct tm *tmstamp = localtime(&timenow);
107 struct stat statbuf;
108
109 // Get the filesize
110 if (fstat(getfd(), &statbuf) == -1)
111 statbuf.st_size = 0;
112
113 // Make sure we are calculating last_day in the current time zone.
114 if (last_day != ((timenow + gmtoffset())/86400)
115 || statbuf.st_size > MAX_LOGFILE_SZ)
116 start_log();
117
119}
120
121static void trim_old_logs(WvStringParm filename, WvStringParm base,
122 int keep_for)
123{
124 if (!keep_for) return;
125 WvDirIter i(getdirname(filename), false);
126 for (i.rewind(); i.next(); )
127 {
128 // if it begins with the base name
129 if (!strncmp(i.ptr()->name, base, strlen(base)))
130 {
131 // and it's older than 'keep_for' days
132 if (i.ptr()->st_mtime < wvtime().tv_sec - keep_for*86400)
133 ::unlink(i.ptr()->fullname);
134 }
135 }
136}
137
138
139WvString WvLogFile::start_log()
140{
142
143 int num = 0;
144 struct stat statbuf;
145 time_t timenow = wvtime().tv_sec;
146 last_day = (timenow + gmtoffset()) / 86400;
147 struct tm* tmstamp = localtime(&timenow);
148 char buf[20];
149 WvString fullname;
150 strftime(buf, 20, "%Y-%m-%d", tmstamp);
151
152 // Get the next filename
153 do
154 fullname = WvString("%s.%s.%s", filename, buf, num++);
155 while (stat(fullname, &statbuf) != -1
156 && (statbuf.st_size >= MAX_LOGFILE_SZ || !allow_append));
157
158 WvString curname("%s.current", filename);
159 WvString base = getfilename(filename);
160
161 WvFile::open(fullname, O_WRONLY|O_APPEND|O_CREAT|O_LARGEFILE, 0644);
162
163#ifndef _WIN32 // no symlinks in win32
164 // Don't delete the file, unless it's a symlink!
165 int sym = readlink(curname, buf, 20);
166 if (sym > 0 || errno == ENOENT)
167 {
168 unlink(curname);
169 symlink(getfilename(fullname), curname);
170 }
171#endif
172
173#ifndef _WIN32
174 // We fork here because this can be really slow when the directory has
175 // (oh, say 32,000 files)
176 pid_t forky = wvfork();
177 if (!forky)
178 {
179 // ForkTwiceSoTheStupidThingWorksRight
180 if (!wvfork())
181 {
182 // Child will Look for old logs and purge them
183 trim_old_logs(filename, base, keep_for);
184 _exit(0);
185 }
186 _exit(0);
187 }
188 // In case a signal is in the process of being delivered...
189 pid_t rv;
190 while ((rv = waitpid(forky, NULL, 0)) != forky)
191 if (rv == -1 && errno != EINTR)
192 break;
193#else
194 // just do it in the foreground on Windows
195 trim_old_logs(filename, base, keep_for);
196#endif
197
198 return fullname;
199}
virtual bool flush(time_t msec_timeout)=0
flush the output buffer, if we can do it without delaying more than msec_timeout milliseconds at a ti...
virtual bool isok() const =0
By default, returns true if geterr() == 0.
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
int getfd() const
Returns the Unix file descriptor for reading and writing.
int getwfd() const
Returns the Unix file descriptor for writing to this stream.
virtual void close()
Closes the file descriptors.
WvFile implements a stream connected to a file or Unix device.
virtual void _make_prefix(time_t now_sec)
Set the Prefix and Prefix Length (size_t prelen)
Definition wvlogfile.cc:79
virtual void _mid_line(const char *str, size_t len)
add text to the current log line.
Definition wvlogfile.cc:52
virtual void _end_line()
End this (Guaranteed NonEmpty) log line.
Definition wvlogfile.cc:58
WvLogRcv adds some intelligence to WvLogRcvBase, to keep track of line-prefix-printing and other form...
virtual size_t write(const void *buf, size_t count)
Write data to the stream.
Definition wvstream.cc:532
WvString is an implementation of a simple and efficient printable-string class.
pid_t wvfork(int dontclose1=-1, int dontclose2=-1)
wvfork() just runs fork(), but it closes all file descriptors that are flagged close-on-exec,...
Definition wvfork.cc:71
WvString getfilename(WvStringParm fullname)
Take a full path/file name and splits it up into respective pathname and filename.
Definition strutils.cc:506