Darwin-Streaming-Server/Server.tproj/QTSSErrorLogModule.cpp

401 lines
13 KiB
C++
Raw Normal View History

/*
*
* @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: QTSSErrorLogModule.cpp
Contains: Implementation of object defined in .h file.
*/
#include <string.h>
#include "QTSSErrorLogModule.h"
#include "QTSSMessages.h"
#include "QTSSRollingLog.h"
#include "QTSServerInterface.h"
#include "QTSSExpirationDate.h"
#include "OSMemory.h"
#include "OS.h"
#include "Task.h"
// STATIC FUNCTIONS
// The dispatch function for this module
static QTSS_Error QTSSErrorLogModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParamBlock);
// A service routine allowing other modules to roll the log
static QTSS_Error RollErrorLog(QTSS_ServiceFunctionArgsPtr inArgs);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Shutdown();
static QTSS_Error LogError(QTSS_RoleParamPtr inParamBlock);
static void CheckErrorLogState();
static QTSS_Error StateChange(QTSS_StateChange_Params* stateChangeParams);
static void WriteStartupMessage();
static void WriteShutdownMessage();
typedef char* LevelMsg;
static LevelMsg sErrorLevel[] = {
"FATAL:",
"WARNING:",
"INFO:",
"ASSERT:",
"DEBUG:"
};
// QTSSERRORLOG CLASS DEFINITION
class QTSSErrorLog : public QTSSRollingLog
{
public:
QTSSErrorLog() : QTSSRollingLog() {this->SetTaskName("QTSSErrorLog");}
virtual ~QTSSErrorLog() {}
virtual char* GetLogName() { return QTSServerInterface::GetServer()->GetPrefs()->GetErrorLogName();}
virtual char* GetLogDir() { return QTSServerInterface::GetServer()->GetPrefs()->GetErrorLogDir();}
virtual UInt32 GetRollIntervalInDays() { return QTSServerInterface::GetServer()->GetPrefs()->GetErrorRollIntervalInDays();}
virtual UInt32 GetMaxLogBytes() { return QTSServerInterface::GetServer()->GetPrefs()->GetMaxErrorLogBytes();}
};
//ERRORLOGCHECKTASK CLASS DEFINITION
class ErrorLogCheckTask : public Task
{
public:
ErrorLogCheckTask() : Task() {this->SetTaskName("ErrorLogCheckTask"); this->Signal(Task::kStartEvent); }
virtual ~ErrorLogCheckTask() {}
private:
virtual SInt64 Run();
};
const UInt32 kMaxLogStringLen = 2172;
// STATIC DATA
static OSMutex* sLogMutex = NULL;//Log module isn't reentrant
static QTSSErrorLog* sErrorLog = NULL;
static char sLastErrorString[kMaxLogStringLen] = "";
static int sDupErrorStringCount = 0;
static Bool16 sStartedUp = false;
static ErrorLogCheckTask* sErrorLogCheckTask = NULL;
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSErrorLogModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSErrorLogModuleDispatch);
}
QTSS_Error QTSSErrorLogModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParamBlock)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParamBlock->regParams);
case QTSS_StateChange_Role:
return StateChange(&inParamBlock->stateChangeParams);
case QTSS_ErrorLog_Role:
return LogError(inParamBlock);
case QTSS_Shutdown_Role:
return Shutdown();
}
return QTSS_NoErr;
}
// ROLE METHODS
QTSS_Error Register(QTSS_Register_Params* inParams)
{
sLogMutex = NEW OSMutex();
// Do role & service setup
(void)QTSS_AddRole(QTSS_ErrorLog_Role);
(void)QTSS_AddRole(QTSS_Shutdown_Role);
(void)QTSS_AddRole(QTSS_StateChange_Role);
(void)QTSS_AddService("RollErrorLog", &RollErrorLog);
// Unlike most modules, all initialization for this module happens in
// the register role. This is so that this error log can begin logging
// errors ASAP.
CheckErrorLogState();
WriteStartupMessage();
// Tell the server our name!
static char* sModuleName = "QTSSErrorLogModule";
::strcpy(inParams->outModuleName, sModuleName);
sErrorLogCheckTask = NEW ErrorLogCheckTask();
return QTSS_NoErr;
}
QTSS_Error Shutdown()
{
WriteShutdownMessage();
if (sErrorLogCheckTask != NULL)
{
// sErrorLogCheckTask is a task object, so don't delete it directly
// instead we signal it to kill itself.
sErrorLogCheckTask->Signal(Task::kKillEvent);
sErrorLogCheckTask = NULL;
}
return QTSS_NoErr;
}
QTSS_Error StateChange(QTSS_StateChange_Params* stateChangeParams)
{
if (stateChangeParams->inNewState == qtssIdleState)
{
WriteShutdownMessage();
}
else if (stateChangeParams->inNewState == qtssRunningState)
{
// Always force our preferences to be reread when we change
// the server's state back to the start -- [sfu]
QTSS_ServiceID id;
(void) QTSS_IDForService(QTSS_REREAD_PREFS_SERVICE, &id);
(void) QTSS_DoService(id, NULL);
WriteStartupMessage();
}
return QTSS_NoErr;
}
QTSS_Error LogError(QTSS_RoleParamPtr inParamBlock)
{
Assert(NULL != inParamBlock->errorParams.inBuffer);
if (inParamBlock->errorParams.inBuffer == NULL)
return QTSS_NoErr;
UInt16 verbLvl = (UInt16) inParamBlock->errorParams.inVerbosity;
if (verbLvl >= qtssIllegalVerbosity)
verbLvl = qtssFatalVerbosity;
QTSServerPrefs* thePrefs = QTSServerInterface::GetServer()->GetPrefs();
OSMutexLocker locker(sLogMutex);
if (thePrefs->GetErrorLogVerbosity() >= inParamBlock->errorParams.inVerbosity)
{
size_t inStringLen = ::strlen(inParamBlock->errorParams.inBuffer);
size_t lastStringLen = ::strlen(sLastErrorString);
Bool16 isDuplicate = true;
if (inStringLen > sizeof(sLastErrorString) -1) //truncate to max char buffer subtract \0 terminator
inStringLen = sizeof(sLastErrorString) -1;
if (lastStringLen != inStringLen) //same size?
isDuplicate = false; // different sizes
else if (::strncmp(inParamBlock->errorParams.inBuffer, sLastErrorString, lastStringLen ) != 0 ) //same chars?
isDuplicate = false; //different chars
//is this error message the same as the last one we received?
if ( isDuplicate )
{ //yes? increment count and bail if it's not the first time we've seen this message (otherwise fall thourhg and write it to the log)
sDupErrorStringCount++;
return QTSS_NoErr;
}
else
{
//we have a new error message, write a "previous line" message before writing the new log entry
if ( sDupErrorStringCount >= 1 )
{
/*** clean this up - lots of duplicate code ***/
//The error logger is the bottleneck for any and all messages printed by the server.
//For debugging purposes, these messages can be printed to stdout as well.
if (thePrefs->IsScreenLoggingEnabled())
qtss_printf("--last message repeated %d times\n", sDupErrorStringCount);
CheckErrorLogState();
if (sErrorLog == NULL)
return QTSS_NoErr;
//timestamp the error
char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes];
Bool16 result = QTSSRollingLog::FormatDate(theDateBuffer, false);
//for now, just ignore the error.
if (!result)
theDateBuffer[0] = '\0';
char tempBuffer[kMaxLogStringLen];
qtss_snprintf(tempBuffer,sizeof(tempBuffer), "%s: --last message repeated %d times\n", theDateBuffer, sDupErrorStringCount);
sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll);
sDupErrorStringCount = 0;
}
::strlcpy(sLastErrorString, inParamBlock->errorParams.inBuffer, sizeof(sLastErrorString));
}
//The error logger is the bottleneck for any and all messages printed by the server.
//For debugging purposes, these messages can be printed to stdout as well.
if (thePrefs->IsScreenLoggingEnabled())
qtss_printf("%s %s\n", sErrorLevel[verbLvl], inParamBlock->errorParams.inBuffer);
CheckErrorLogState();
if (sErrorLog == NULL)
return QTSS_NoErr;
//timestamp the error
char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes];
Bool16 result = QTSSRollingLog::FormatDate(theDateBuffer, false);
//for now, just ignore the error.
if (!result)
theDateBuffer[0] = '\0';
char tempBuffer[kMaxLogStringLen];
qtss_snprintf(tempBuffer,sizeof(tempBuffer), "%s: %s %s\n", theDateBuffer, sErrorLevel[verbLvl], inParamBlock->errorParams.inBuffer);
tempBuffer[sizeof(tempBuffer)-2] = '\n'; //make sure the entry has a line feed before the \0 terminator
tempBuffer[sizeof(tempBuffer)-1] = '\0'; //make sure it is 0 terminated.
sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll);
}
return QTSS_NoErr;
}
void CheckErrorLogState()
{
//this function makes sure the logging state is in synch with the preferences.
//extern variable declared in QTSSPreferences.h
QTSServerPrefs* thePrefs = QTSServerInterface::GetServer()->GetPrefs();
//check error log.
if ((NULL == sErrorLog) && (thePrefs->IsErrorLogEnabled()))
{
sErrorLog = NEW QTSSErrorLog();
sErrorLog->EnableLog();
}
if ((NULL != sErrorLog) && (!thePrefs->IsErrorLogEnabled()))
{
sErrorLog->Delete(); //sErrorLog is a task object, so don't delete it directly
sErrorLog = NULL;
}
}
// SERVICE ROUTINES
QTSS_Error RollErrorLog(QTSS_ServiceFunctionArgsPtr /*inArgs*/)
{
OSMutexLocker locker(sLogMutex);
if (sErrorLog != NULL)
sErrorLog->RollLog();
return QTSS_NoErr;
}
void WriteStartupMessage()
{
if (sStartedUp)
return;
sStartedUp = true;
//format a date for the startup time
char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes];
Bool16 result = QTSSRollingLog::FormatDate(theDateBuffer, false);
char tempBuffer[kMaxLogStringLen];
if (result)
qtss_snprintf(tempBuffer,sizeof(tempBuffer), "# Streaming STARTUP %s\n", theDateBuffer);
// log startup message to error log as well.
if ((result) && (sErrorLog != NULL))
sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll);
//write the expire date to the log
if ( QTSSExpirationDate::WillSoftwareExpire() && sErrorLog != NULL )
{
QTSSExpirationDate::sPrintExpirationDate(tempBuffer);
sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll);
}
}
void WriteShutdownMessage()
{
if (!sStartedUp)
return;
sStartedUp = false;
//log shutdown message
//format a date for the shutdown time
char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes];
Bool16 result = QTSSRollingLog::FormatDate(theDateBuffer, false);
char tempBuffer[kMaxLogStringLen];
if (result)
qtss_snprintf(tempBuffer, sizeof(tempBuffer), "# Streaming SHUTDOWN %s\n", theDateBuffer);
if ( result && sErrorLog != NULL )
sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll);
}
// This task runs once an hour to check and see if the log needs to roll.
SInt64 ErrorLogCheckTask::Run()
{
static Bool16 firstTime = true;
// don't check the log for rolling the first time we run.
if (firstTime)
{
firstTime = false;
}
else
{
Bool16 success = false;
if (sErrorLog != NULL && sErrorLog->IsLogEnabled())
success = sErrorLog->CheckRollLog();
Assert(success);
}
// execute this task again in one hour.
return (60*60*1000);
}