Very rough first start, not even everything added
This commit is contained in:
commit
af3619d4fa
88 changed files with 24251 additions and 0 deletions
550
APICommonCode/QTSSRollingLog.cpp
Normal file
550
APICommonCode/QTSSRollingLog.cpp
Normal file
|
@ -0,0 +1,550 @@
|
|||
/*
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*
|
||||
*/
|
||||
/*
|
||||
File: QTSSRollingLog.cpp
|
||||
|
||||
Contains: Implements object defined in .h file
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include "SafeStdLib.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#ifndef __Win32__
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include "QTSSRollingLog.h"
|
||||
#include "OS.h"
|
||||
#include "OSMemory.h"
|
||||
#include "OSArrayObjectDeleter.h"
|
||||
#include "ResizeableStringFormatter.h"
|
||||
|
||||
static Bool16 sCloseOnWrite = true;
|
||||
|
||||
QTSSRollingLog::QTSSRollingLog() :
|
||||
fLog(NULL),
|
||||
fLogCreateTime(-1),
|
||||
fLogFullPath(NULL),
|
||||
fAppendDotLog(true),
|
||||
fLogging(true)
|
||||
{
|
||||
this->SetTaskName("QTSSRollingLog");
|
||||
}
|
||||
|
||||
QTSSRollingLog::~QTSSRollingLog()
|
||||
{
|
||||
//
|
||||
// Log should already be closed, but just in case...
|
||||
this->CloseLog();
|
||||
delete [] fLogFullPath;
|
||||
}
|
||||
|
||||
// Set this to true to get the log to close the file between writes.
|
||||
void QTSSRollingLog::SetCloseOnWrite(Bool16 closeOnWrite)
|
||||
{
|
||||
sCloseOnWrite = closeOnWrite;
|
||||
}
|
||||
|
||||
Bool16 QTSSRollingLog::IsLogEnabled()
|
||||
{
|
||||
return sCloseOnWrite || (fLog != NULL);
|
||||
}
|
||||
|
||||
void QTSSRollingLog::WriteToLog(char* inLogData, Bool16 allowLogToRoll)
|
||||
{
|
||||
OSMutexLocker locker(&fMutex);
|
||||
|
||||
if (fLogging == false)
|
||||
return;
|
||||
|
||||
if (sCloseOnWrite && fLog == NULL)
|
||||
this->EnableLog(fAppendDotLog ); //re-open log file before we write
|
||||
|
||||
if (allowLogToRoll)
|
||||
(void)this->CheckRollLog();
|
||||
|
||||
if (fLog != NULL)
|
||||
{
|
||||
qtss_fprintf(fLog, "%s", inLogData);
|
||||
::fflush(fLog);
|
||||
}
|
||||
|
||||
if (sCloseOnWrite)
|
||||
this->CloseLog( false );
|
||||
}
|
||||
|
||||
Bool16 QTSSRollingLog::RollLog()
|
||||
{
|
||||
OSMutexLocker locker(&fMutex);
|
||||
|
||||
//returns false if an error occurred, true otherwise
|
||||
|
||||
//close the old file.
|
||||
if (fLog != NULL)
|
||||
this->CloseLog();
|
||||
|
||||
if (fLogging == false)
|
||||
return false;
|
||||
|
||||
//rename the old file
|
||||
Bool16 result = this->RenameLogFile(fLogFullPath);
|
||||
if (result)
|
||||
this->EnableLog(fAppendDotLog);//re-opens log file
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
char* QTSSRollingLog::GetLogPath(char *extension)
|
||||
{
|
||||
char *thePath = NULL;
|
||||
|
||||
OSCharArrayDeleter logDir(this->GetLogDir()); //The string passed into this function is a copy
|
||||
OSCharArrayDeleter logName(this->GetLogName()); //The string passed into this function is a copy
|
||||
|
||||
ResizeableStringFormatter formatPath(NULL,0); //allocate the buffer
|
||||
formatPath.PutFilePath(logDir, logName);
|
||||
|
||||
if ( extension != NULL)
|
||||
formatPath.Put(extension);
|
||||
|
||||
formatPath.PutTerminator();
|
||||
thePath = formatPath.GetBufPtr();
|
||||
|
||||
formatPath.Set(NULL,0); //don't delete buffer we are returning the path as a result
|
||||
|
||||
return thePath;
|
||||
}
|
||||
|
||||
void QTSSRollingLog::EnableLog( Bool16 appendDotLog )
|
||||
{
|
||||
//
|
||||
// Start this object running!
|
||||
this->Signal(Task::kStartEvent);
|
||||
|
||||
OSMutexLocker locker(&fMutex);
|
||||
|
||||
fAppendDotLog = appendDotLog;
|
||||
|
||||
if (fLogging == false)
|
||||
return;
|
||||
|
||||
char *extension = ".log";
|
||||
if (!appendDotLog)
|
||||
extension = NULL;
|
||||
|
||||
delete[] fLogFullPath;
|
||||
fLogFullPath = this->GetLogPath(extension);
|
||||
|
||||
//we need to make sure that when we create a new log file, we write the
|
||||
//log header at the top
|
||||
Bool16 logExists = this->DoesFileExist(fLogFullPath);
|
||||
|
||||
//create the log directory if it doesn't already exist
|
||||
if (!logExists)
|
||||
{
|
||||
OSCharArrayDeleter tempDir(this->GetLogDir());
|
||||
OS::RecursiveMakeDir(tempDir.GetObject());
|
||||
}
|
||||
|
||||
fLog = ::fopen(fLogFullPath, "a+");//open for "append"
|
||||
if (NULL != fLog)
|
||||
{
|
||||
if (!logExists) //the file is new, write a log header with the create time of the file.
|
||||
{ fLogCreateTime = this->WriteLogHeader(fLog);
|
||||
#if __MacOSX__
|
||||
(void) ::chown(fLogFullPath, 76, (gid_t)-1);//set owner to user qtss.
|
||||
#endif
|
||||
}
|
||||
else //the file is old, read the log header to find the create time of the file.
|
||||
fLogCreateTime = this->ReadLogHeader(fLog);
|
||||
}
|
||||
}
|
||||
|
||||
void QTSSRollingLog::CloseLog( Bool16 leaveEnabled )
|
||||
{
|
||||
OSMutexLocker locker(&fMutex);
|
||||
|
||||
if (leaveEnabled)
|
||||
sCloseOnWrite = true;
|
||||
|
||||
if (fLog != NULL)
|
||||
{
|
||||
::fclose(fLog);
|
||||
fLog = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//returns false if some error has occurred
|
||||
Bool16 QTSSRollingLog::FormatDate(char *ioDateBuffer, Bool16 logTimeInGMT)
|
||||
{
|
||||
Assert(NULL != ioDateBuffer);
|
||||
|
||||
//use ansi routines for getting the date.
|
||||
time_t calendarTime = ::time(NULL);
|
||||
Assert(-1 != calendarTime);
|
||||
if (-1 == calendarTime)
|
||||
return false;
|
||||
|
||||
struct tm* theTime = NULL;
|
||||
struct tm timeResult;
|
||||
|
||||
if (logTimeInGMT)
|
||||
theTime = ::qtss_gmtime(&calendarTime, &timeResult);
|
||||
else
|
||||
theTime = qtss_localtime(&calendarTime, &timeResult);
|
||||
|
||||
Assert(NULL != theTime);
|
||||
|
||||
if (NULL == theTime)
|
||||
return false;
|
||||
|
||||
// date time needs to look like this for extended log file format: 2001-03-16 23:34:54
|
||||
// this wonderful ANSI routine just does it for you.
|
||||
// the format is YYYY-MM-DD HH:MM:SS
|
||||
// the date time is in GMT, unless logTimeInGMT is false, in which case
|
||||
// the time logged is local time
|
||||
//qtss_strftime(ioDateBuffer, kMaxDateBufferSize, "%d/%b/%Y:%H:%M:%S", theLocalTime);
|
||||
qtss_strftime(ioDateBuffer, kMaxDateBufferSizeInBytes, "%Y-%m-%d %H:%M:%S", theTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool16 QTSSRollingLog::CheckRollLog()
|
||||
{
|
||||
//returns false if an error occurred, true otherwise
|
||||
if (fLog == NULL)
|
||||
return true;
|
||||
|
||||
//first check to see if log rolling should happen because of a date interval.
|
||||
//This takes precedence over size based log rolling
|
||||
// this is only if a client connects just between 00:00:00 and 00:01:00
|
||||
// since the task object runs every minute
|
||||
|
||||
// when an entry is written to the log file, only the file size must be checked
|
||||
// to see if it exceeded the limits
|
||||
|
||||
// the roll interval should be monitored in a task object
|
||||
// and rolled at midnight if the creation time has exceeded.
|
||||
if ((-1 != fLogCreateTime) && (0 != this->GetRollIntervalInDays()))
|
||||
{
|
||||
time_t logCreateTimeMidnight = -1;
|
||||
QTSSRollingLog::ResetToMidnight(&fLogCreateTime, &logCreateTimeMidnight);
|
||||
Assert(logCreateTimeMidnight != -1);
|
||||
|
||||
time_t calendarTime = ::time(NULL);
|
||||
|
||||
Assert(-1 != calendarTime);
|
||||
if (-1 != calendarTime)
|
||||
{
|
||||
double theExactInterval = ::difftime(calendarTime, logCreateTimeMidnight);
|
||||
SInt32 theCurInterval = (SInt32)::floor(theExactInterval);
|
||||
|
||||
//transfer_roll_interval is in days, theCurInterval is in seconds
|
||||
SInt32 theRollInterval = this->GetRollIntervalInDays() * 60 * 60 * 24;
|
||||
if (theCurInterval > theRollInterval)
|
||||
return this->RollLog();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//now check size based log rolling
|
||||
UInt32 theCurrentPos = ::ftell(fLog);
|
||||
//max_transfer_log_size being 0 is a signal to ignore the setting.
|
||||
if ((this->GetMaxLogBytes() != 0) &&
|
||||
(theCurrentPos > this->GetMaxLogBytes()))
|
||||
return this->RollLog();
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool16 QTSSRollingLog::RenameLogFile(const char* inFileName)
|
||||
{
|
||||
//returns false if an error occurred, true otherwise
|
||||
|
||||
//this function takes care of renaming a log file from "myLogFile.log" to
|
||||
//"myLogFile.981217000.log" or if that is already taken, myLogFile.981217001.log", etc
|
||||
|
||||
//fix 2287086. Rolled log name can be different than original log name
|
||||
//GetLogDir returns a copy of the log dir
|
||||
OSCharArrayDeleter logDirectory(this->GetLogDir());
|
||||
|
||||
//create the log directory if it doesn't already exist
|
||||
OS::RecursiveMakeDir(logDirectory.GetObject());
|
||||
|
||||
//GetLogName returns a copy of the log name
|
||||
OSCharArrayDeleter logBaseName(this->GetLogName());
|
||||
|
||||
//QTStreamingServer.981217003.log
|
||||
//format the new file name
|
||||
OSCharArrayDeleter theNewNameBuffer(NEW char[::strlen(logDirectory) + kMaxFilenameLengthInBytes + 3]);
|
||||
|
||||
//copy over the directory - append a '/' if it's missing
|
||||
::strcpy(theNewNameBuffer, logDirectory);
|
||||
if (theNewNameBuffer[::strlen(theNewNameBuffer)-1] != kPathDelimiterChar)
|
||||
{
|
||||
::strcat(theNewNameBuffer, kPathDelimiterString);
|
||||
}
|
||||
|
||||
//copy over the base filename
|
||||
::strcat(theNewNameBuffer, logBaseName.GetObject());
|
||||
|
||||
//append the date the file was created
|
||||
struct tm timeResult;
|
||||
struct tm* theLocalTime = qtss_localtime(&fLogCreateTime, &timeResult);
|
||||
char timeString[10];
|
||||
qtss_strftime(timeString, 10, ".%y%m%d", theLocalTime);
|
||||
::strcat(theNewNameBuffer, timeString);
|
||||
|
||||
SInt32 theBaseNameLength = ::strlen(theNewNameBuffer);
|
||||
|
||||
|
||||
//loop until we find a unique name to rename this file
|
||||
//and append the log number and suffix
|
||||
SInt32 theErr = 0;
|
||||
for (SInt32 x = 0; (theErr == 0) && (x<=1000); x++)
|
||||
{
|
||||
if (x == 1000) //we don't have any digits left, so just reuse the "---" until tomorrow...
|
||||
{
|
||||
//add a bogus log number and exit the loop
|
||||
qtss_sprintf(theNewNameBuffer + theBaseNameLength, "---.log");
|
||||
break;
|
||||
}
|
||||
|
||||
//add the log number & suffix
|
||||
qtss_sprintf(theNewNameBuffer + theBaseNameLength, "%03"_S32BITARG_".log", x);
|
||||
|
||||
//assume that when ::stat returns an error, it is becase
|
||||
//the file doesnt exist. Once that happens, we have a unique name
|
||||
// csl - shouldn't you watch for a ENOENT result?
|
||||
struct stat theIdontCare;
|
||||
theErr = ::stat(theNewNameBuffer, &theIdontCare);
|
||||
WarnV((theErr == 0 || OSThread::GetErrno() == ENOENT), "unexpected stat error in RenameLogFile");
|
||||
|
||||
}
|
||||
|
||||
//rename the file. Use posix rename function
|
||||
int result = ::rename(inFileName, theNewNameBuffer);
|
||||
if (result == -1)
|
||||
theErr = (SInt32)OSThread::GetErrno();
|
||||
else
|
||||
theErr = 0;
|
||||
|
||||
WarnV(theErr == 0 , "unexpected rename error in RenameLogFile");
|
||||
|
||||
|
||||
if (theErr != 0)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool16 QTSSRollingLog::DoesFileExist(const char *inPath)
|
||||
{
|
||||
struct stat theStat;
|
||||
int theErr = ::stat(inPath, &theStat);
|
||||
if (theErr != 0)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
time_t QTSSRollingLog::WriteLogHeader(FILE* inFile)
|
||||
{
|
||||
OSMutexLocker locker(&fMutex);
|
||||
|
||||
//The point of this header is to record the exact time the log file was created,
|
||||
//in a format that is easy to parse through whenever we open the file again.
|
||||
//This is necessary to support log rolling based on a time interval, and POSIX doesn't
|
||||
//support a create date in files.
|
||||
time_t calendarTime = ::time(NULL);
|
||||
Assert(-1 != calendarTime);
|
||||
if (-1 == calendarTime)
|
||||
return -1;
|
||||
|
||||
struct tm timeResult;
|
||||
struct tm* theLocalTime = qtss_localtime(&calendarTime, &timeResult);
|
||||
Assert(NULL != theLocalTime);
|
||||
if (NULL == theLocalTime)
|
||||
return -1;
|
||||
|
||||
//
|
||||
// Files are always created at hour 0 (we don't care about the time, we always
|
||||
// want them to roll at midnight.
|
||||
//theLocalTime->tm_hour = 0;
|
||||
//theLocalTime->tm_min = 0;
|
||||
//theLocalTime->tm_sec = 0;
|
||||
|
||||
char tempbuf[1024];
|
||||
qtss_strftime(tempbuf, sizeof(tempbuf), "#Log File Created On: %m/%d/%Y %H:%M:%S\n", theLocalTime);
|
||||
//qtss_sprintf(tempbuf, "#Log File Created On: %d/%d/%d %d:%d:%d %d:%d:%d GMT\n",
|
||||
// theLocalTime->tm_mon, theLocalTime->tm_mday, theLocalTime->tm_year,
|
||||
// theLocalTime->tm_hour, theLocalTime->tm_min, theLocalTime->tm_sec,
|
||||
// theLocalTime->tm_yday, theLocalTime->tm_wday, theLocalTime->tm_isdst);
|
||||
this->WriteToLog(tempbuf, !kAllowLogToRoll);
|
||||
|
||||
return this->ReadLogHeader(inFile);
|
||||
}
|
||||
|
||||
time_t QTSSRollingLog::ReadLogHeader(FILE* inFile)
|
||||
{
|
||||
OSMutexLocker locker(&fMutex);
|
||||
|
||||
//This function reads the header in a log file, returning the time stored
|
||||
//at the beginning of this file. This value is used to determine when to
|
||||
//roll the log.
|
||||
//Returns -1 if the header is bogus. In that case, just ignore time based log rolling
|
||||
|
||||
//first seek to the beginning of the file
|
||||
SInt32 theCurrentPos = ::ftell(inFile);
|
||||
if (theCurrentPos == -1)
|
||||
return -1;
|
||||
(void)::rewind(inFile);
|
||||
|
||||
const UInt32 kMaxHeaderLength = 500;
|
||||
char theFirstLine[kMaxHeaderLength];
|
||||
|
||||
if (NULL == ::fgets(theFirstLine, kMaxHeaderLength, inFile))
|
||||
{
|
||||
::fseek(inFile, 0, SEEK_END);
|
||||
return -1;
|
||||
}
|
||||
::fseek(inFile, 0, SEEK_END);
|
||||
|
||||
struct tm theFileCreateTime;
|
||||
|
||||
// Zero out fields we will not be using
|
||||
theFileCreateTime.tm_isdst = -1;
|
||||
theFileCreateTime.tm_wday = 0;
|
||||
theFileCreateTime.tm_yday = 0;
|
||||
|
||||
//if (EOF == ::sscanf(theFirstLine, "#Log File Created On: %d/%d/%d %d:%d:%d\n",
|
||||
// &theFileCreateTime.tm_mon, &theFileCreateTime.tm_mday, &theFileCreateTime.tm_year,
|
||||
// &theFileCreateTime.tm_hour, &theFileCreateTime.tm_min, &theFileCreateTime.tm_sec))
|
||||
// return -1;
|
||||
|
||||
//
|
||||
// We always want to roll at hour 0, so ignore the time of creation
|
||||
|
||||
if (EOF == ::sscanf(theFirstLine, "#Log File Created On: %d/%d/%d %d:%d:%d\n",
|
||||
&theFileCreateTime.tm_mon, &theFileCreateTime.tm_mday, &theFileCreateTime.tm_year,
|
||||
&theFileCreateTime.tm_hour, &theFileCreateTime.tm_min, &theFileCreateTime.tm_sec))
|
||||
return -1;
|
||||
|
||||
//
|
||||
// It should be like this anyway, but if the log file is legacy, then...
|
||||
// No! The log file will have the actual time in it but we shall return the exact time
|
||||
//theFileCreateTime.tm_hour = 0;
|
||||
//theFileCreateTime.tm_min = 0;
|
||||
//theFileCreateTime.tm_sec = 0;
|
||||
|
||||
// Actually, it seems like all platforms need this.
|
||||
//#ifdef __Win32__
|
||||
// Win32 has slightly different atime basis than UNIX.
|
||||
theFileCreateTime.tm_yday--;
|
||||
theFileCreateTime.tm_mon--;
|
||||
theFileCreateTime.tm_year -= 1900;
|
||||
//#endif
|
||||
|
||||
#if 0
|
||||
//use ansi routines for getting the date.
|
||||
time_t calendarTime = ::time(NULL);
|
||||
Assert(-1 != calendarTime);
|
||||
if (-1 == calendarTime)
|
||||
return false;
|
||||
|
||||
struct tm timeResult;
|
||||
struct tm* theLocalTime = qtss_localtime(&calendarTime, &timeResult);
|
||||
Assert(NULL != theLocalTime);
|
||||
if (NULL == theLocalTime)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
//ok, we should have a filled in tm struct. Convert it to a time_t.
|
||||
//time_t thePoopTime = ::mktime(theLocalTime);
|
||||
time_t theTime = ::mktime(&theFileCreateTime);
|
||||
return theTime;
|
||||
}
|
||||
|
||||
SInt64 QTSSRollingLog::Run()
|
||||
{
|
||||
//
|
||||
// If we are going away, just return
|
||||
EventFlags events = this->GetEvents();
|
||||
if (events & Task::kKillEvent)
|
||||
return -1;
|
||||
|
||||
OSMutexLocker locker(&fMutex);
|
||||
|
||||
UInt32 theRollInterval = (this->GetRollIntervalInDays()) * 60 * 60 * 24;
|
||||
|
||||
if((fLogCreateTime != -1) && (fLog != NULL))
|
||||
{
|
||||
time_t logRollTimeMidnight = -1;
|
||||
this->ResetToMidnight(&fLogCreateTime, &logRollTimeMidnight);
|
||||
Assert(logRollTimeMidnight != -1);
|
||||
|
||||
if(theRollInterval != 0)
|
||||
{
|
||||
time_t calendarTime = ::time(NULL);
|
||||
Assert(-1 != calendarTime);
|
||||
double theExactInterval = ::difftime(calendarTime, logRollTimeMidnight);
|
||||
if(theExactInterval > 0) {
|
||||
UInt32 theCurInterval = (UInt32)::floor(theExactInterval);
|
||||
if (theCurInterval >= theRollInterval)
|
||||
this->RollLog();
|
||||
}
|
||||
}
|
||||
}
|
||||
return 60 * 1000;
|
||||
}
|
||||
|
||||
void QTSSRollingLog::ResetToMidnight(time_t* inTimePtr, time_t* outTimePtr)
|
||||
{
|
||||
if(*inTimePtr == -1)
|
||||
{
|
||||
*outTimePtr = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
struct tm timeResult;
|
||||
struct tm* theLocalTime = qtss_localtime(inTimePtr, &timeResult);
|
||||
Assert(theLocalTime != NULL);
|
||||
|
||||
theLocalTime->tm_hour = 0;
|
||||
theLocalTime->tm_min = 0;
|
||||
theLocalTime->tm_sec = 0;
|
||||
|
||||
// some weird stuff
|
||||
//theLocalTime->tm_yday--;
|
||||
//theLocalTime->tm_mon--;
|
||||
//theLocalTime->tm_year -= 1900;
|
||||
|
||||
*outTimePtr = ::mktime(theLocalTime);
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue