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 
25 static 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 
37 WvLogFileBase::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 
45 WvLogFileBase::WvLogFileBase(WvLog::LogLevel _max_level)
46  : WvLogRcv(_max_level)
47 {
48  fsync_every = fsync_count = 0;
49 }
50 
51 
52 void 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 
79 void 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 
92 WvLogFile::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 
101 void 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 
121 static 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 
139 WvString WvLogFile::start_log()
140 {
141  WvFile::close();
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 }
WvStream::write
virtual size_t write(const void *buf, size_t count)
Write data to the stream.
Definition: wvstream.cc:532
WvFdStream::isok
virtual bool isok() const
return true if the stream is actually usable right now
Definition: wvfdstream.cc:134
getfilename
WvString getfilename(WvStringParm fullname)
Take a full path/file name and splits it up into respective pathname and filename.
Definition: strutils.cc:506
WvFile
WvFile implements a stream connected to a file or Unix device.
Definition: wvfile.h:28
WvStream::flush
virtual bool flush(time_t msec_timeout)
flush the output buffer, if we can do it without delaying more than msec_timeout milliseconds at a ti...
Definition: wvstream.cc:707
wvfork
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
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
WvDirIter
Definition: wvdiriter.h:31
WvLogFileBase::_make_prefix
virtual void _make_prefix(time_t now_sec)
Set the Prefix and Prefix Length (size_t prelen)
Definition: wvlogfile.cc:79
WvFdStream::getfd
int getfd() const
Returns the Unix file descriptor for reading and writing.
Definition: wvfdstream.h:81
WvLogFileBase
Basic WvLogRcv that logs to a file.
Definition: wvlogfile.h:16
WvLogRcv
WvLogRcv adds some intelligence to WvLogRcvBase, to keep track of line-prefix-printing and other form...
Definition: wvlogrcv.h:28
wvfork.h
WvFdStream::close
virtual void close()
Closes the file descriptors.
Definition: wvfdstream.cc:117
WvFdStream::getwfd
int getwfd() const
Returns the Unix file descriptor for writing to this stream.
Definition: wvfdstream.h:70
WvLogFileBase::_mid_line
virtual void _mid_line(const char *str, size_t len)
add text to the current log line.
Definition: wvlogfile.cc:52
WvLogFileBase::_end_line
virtual void _end_line()
End this (Guaranteed NonEmpty) log line.
Definition: wvlogfile.cc:58