You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
414 lines
9.9 KiB
C++
414 lines
9.9 KiB
C++
|
4 years ago
|
/******************************************************************************
|
||
|
|
* File - Logger.h
|
||
|
|
* Author - Joey Pollack
|
||
|
|
* Date - 2019/06/24 (y/m/d)
|
||
|
|
* Mod Date - 2019/06/26 (y/m/d)
|
||
|
|
* Description - Main interface to the logging system. Manages a linked
|
||
|
|
* list of built-in and user-defined listerers.
|
||
|
|
******************************************************************************/
|
||
|
|
|
||
|
|
#include "Logger.h"
|
||
|
|
#include <iostream>
|
||
|
|
#include <sstream>
|
||
|
|
#include <iomanip>
|
||
|
|
#include <time.h>
|
||
|
|
#include <stdarg.h>
|
||
|
|
|
||
|
|
namespace Lunarium
|
||
|
|
{
|
||
|
|
//namespace LogLevel
|
||
|
|
//{
|
||
|
|
// bool LevelIsSet(uint16_t buf, uint16_t mask)
|
||
|
|
// {
|
||
|
|
// return buf & mask;
|
||
|
|
// }
|
||
|
|
|
||
|
|
// std::string FormatLevel(uint16_t level)
|
||
|
|
// {
|
||
|
|
// if (LevelIsSet(level, FATAL_ERROR))
|
||
|
|
// {
|
||
|
|
// return "[FATAL ERROR] ";
|
||
|
|
// }
|
||
|
|
// else if (LevelIsSet(level, ERROR))
|
||
|
|
// {
|
||
|
|
// return "[ERROR] ";
|
||
|
|
// }
|
||
|
|
// else if (LevelIsSet(level, WARNING))
|
||
|
|
// {
|
||
|
|
// return "[WARNING] ";
|
||
|
|
// }
|
||
|
|
// else if (LevelIsSet(level, INFO))
|
||
|
|
// {
|
||
|
|
// return "[INFO] ";
|
||
|
|
// }
|
||
|
|
// else if (LevelIsSet(level, INFO_VERBOSE))
|
||
|
|
// {
|
||
|
|
// return "[INFO_VERBOSE] ";
|
||
|
|
// }
|
||
|
|
// else if (LevelIsSet(level, OGL_DEBUG))
|
||
|
|
// {
|
||
|
|
// return "[OGL_DEBUG] ";
|
||
|
|
// }
|
||
|
|
// else
|
||
|
|
// {
|
||
|
|
// return "[UNKNOWN] ";
|
||
|
|
// }
|
||
|
|
// }
|
||
|
|
//}
|
||
|
|
|
||
|
|
//namespace LogCategory
|
||
|
|
//{
|
||
|
|
// bool CategoryIsSet(uint16_t buf, uint16_t mask)
|
||
|
|
// {
|
||
|
|
// return buf & mask;
|
||
|
|
// }
|
||
|
|
// std::string FormatCategory(uint16_t level)
|
||
|
|
// {
|
||
|
|
// if (CategoryIsSet(level, CORE))
|
||
|
|
// {
|
||
|
|
// return "[CORE] ";
|
||
|
|
// }
|
||
|
|
// else if (CategoryIsSet(level, WINDOW_CONTROLLER))
|
||
|
|
// {
|
||
|
|
// return "[WINDOW CONTROLLER] ";
|
||
|
|
// }
|
||
|
|
// else if (CategoryIsSet(level, GRAPHICS))
|
||
|
|
// {
|
||
|
|
// return "[GRAPHICS] ";
|
||
|
|
// }
|
||
|
|
// else if (CategoryIsSet(level, UTILITIES))
|
||
|
|
// {
|
||
|
|
// return "[UTILITIES] ";
|
||
|
|
// }
|
||
|
|
// else if (CategoryIsSet(level, USER_PROJECT))
|
||
|
|
// {
|
||
|
|
// return "[USER PROJECT] ";
|
||
|
|
// }
|
||
|
|
// else
|
||
|
|
// {
|
||
|
|
// return "[UNKNOWN] ";
|
||
|
|
// }
|
||
|
|
// }
|
||
|
|
//}
|
||
|
|
|
||
|
|
LogListener::LogListener(uint32_t acceptedLogLevels, uint32_t acceptedLogCategories, const char* myName)
|
||
|
|
: mAcceptedLogLevels(acceptedLogLevels), mAcceptedLogCategories(acceptedLogCategories), mMyName(myName)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
const std::string & LogListener::GetName() const
|
||
|
|
{
|
||
|
|
return mMyName;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool LogListener::CategoryIsSet(uint32_t id) const
|
||
|
|
{
|
||
|
|
return mAcceptedLogCategories & id;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool LogListener::LevelIsSet(uint32_t id) const
|
||
|
|
{
|
||
|
|
return mAcceptedLogLevels & id;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ////////////////////////////////////////////////////////////////////////
|
||
|
|
// ========================================================================
|
||
|
|
// Standard Listener
|
||
|
|
// ========================================================================
|
||
|
|
// ////////////////////////////////////////////////////////////////////////
|
||
|
|
StandardListener::StandardListener(uint32_t acceptedLogLevels, uint32_t acceptedLogCategories, const char* myName)
|
||
|
|
: LogListener(acceptedLogLevels, acceptedLogCategories, myName)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
bool StandardListener::Log(LogMessage& message)
|
||
|
|
{
|
||
|
|
if (!LevelIsSet(message.LogLevel) ||
|
||
|
|
!CategoryIsSet(message.LogCategory))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
std::cout << std::endl << Logger::TimeStamp() << Logger::GetCategoryName(message.LogCategory)
|
||
|
|
<< Logger::GetLevelName(message.LogLevel) << message.Message;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ////////////////////////////////////////////////////////////////////////
|
||
|
|
// ========================================================================
|
||
|
|
// Error Listener
|
||
|
|
// ========================================================================
|
||
|
|
// ////////////////////////////////////////////////////////////////////////
|
||
|
|
ErrorListener::ErrorListener(uint32_t acceptedLogLevels, uint32_t acceptedLogCategories, const char* myName)
|
||
|
|
: LogListener(acceptedLogLevels, acceptedLogCategories, myName)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ErrorListener::Log(LogMessage& message)
|
||
|
|
{
|
||
|
|
if (!LevelIsSet(message.LogLevel) ||
|
||
|
|
!CategoryIsSet(message.LogCategory))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
std::cerr << std::endl << Logger::TimeStamp() << Logger::GetCategoryName(message.LogCategory)
|
||
|
|
<< Logger::GetLevelName(message.LogLevel) << message.Message;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ////////////////////////////////////////////////////////////////////////
|
||
|
|
// ========================================================================
|
||
|
|
// File Listener
|
||
|
|
// ========================================================================
|
||
|
|
// ////////////////////////////////////////////////////////////////////////
|
||
|
|
FileListener::FileListener(std::ofstream & of, uint32_t acceptedLogLevels, uint32_t acceptedLogCategories, const char* myName)
|
||
|
|
: mOF(of), LogListener(acceptedLogLevels, acceptedLogCategories, myName)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
bool FileListener::Log(LogMessage& message)
|
||
|
|
{
|
||
|
|
if (!LevelIsSet(message.LogLevel) ||
|
||
|
|
!CategoryIsSet(message.LogCategory))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
mOF << std::endl << Logger::TimeStamp() << Logger::GetCategoryName(message.LogCategory)
|
||
|
|
<< Logger::GetLevelName(message.LogLevel) << message.Message;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
// ////////////////////////////////////////////////////////////////////////
|
||
|
|
// ========================================================================
|
||
|
|
// Main Logger
|
||
|
|
// ========================================================================
|
||
|
|
// ////////////////////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
Logger* Logger::mInstance = nullptr;
|
||
|
|
uint32_t Logger::mNextCategoryID = 0x01;
|
||
|
|
uint32_t Logger::mNextLevelID = 0x01;
|
||
|
|
std::map<uint32_t, std::string> Logger::mCategoryNames;
|
||
|
|
std::map<uint32_t, std::string> Logger::mLevelNames;
|
||
|
|
|
||
|
|
std::string Logger::TimeStamp()
|
||
|
|
{
|
||
|
|
time_t t = time(0);
|
||
|
|
|
||
|
|
struct tm now;
|
||
|
|
localtime_s(&now, &t);
|
||
|
|
|
||
|
|
std::ostringstream oss("");
|
||
|
|
oss << "[" << (now.tm_year + 1900) << "/";
|
||
|
|
oss << std::setfill('0') << std::setw(2);
|
||
|
|
oss << (now.tm_mon + 1) << "/"
|
||
|
|
<< std::setw(2) << now.tm_mday << " -- "
|
||
|
|
<< std::setw(2) << now.tm_hour << ":"
|
||
|
|
<< std::setw(2) << now.tm_min << ":"
|
||
|
|
<< std::setw(2) << now.tm_sec << "] ";
|
||
|
|
return oss.str();
|
||
|
|
}
|
||
|
|
|
||
|
|
Logger::Logger()
|
||
|
|
{
|
||
|
|
|
||
|
|
// Register built-in levels
|
||
|
|
RegisterLevel("INFO");
|
||
|
|
RegisterLevel("WARNING");
|
||
|
|
RegisterLevel("ERROR");
|
||
|
|
RegisterLevel("FATAL_ERROR");
|
||
|
|
RegisterLevel("INFO_VERBOSE");
|
||
|
|
RegisterLevel("OGL_DEBUG");
|
||
|
|
|
||
|
|
RegisterCategory("CORE");
|
||
|
|
RegisterCategory("WINDOW_CONTROLLER");
|
||
|
|
RegisterCategory("GRAPHICS");
|
||
|
|
RegisterCategory("UTILITIES");
|
||
|
|
}
|
||
|
|
|
||
|
|
Logger * Logger::GetInstance()
|
||
|
|
{
|
||
|
|
if (!Logger::mInstance)
|
||
|
|
{
|
||
|
|
Logger::mInstance = new Logger;
|
||
|
|
Logger::mInstance->mTimer.Reset();
|
||
|
|
}
|
||
|
|
|
||
|
|
return Logger::mInstance;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Logger::FreeInstance()
|
||
|
|
{
|
||
|
|
delete mInstance;
|
||
|
|
mInstance = nullptr;
|
||
|
|
|
||
|
|
Logger::mNextCategoryID = 0x01;
|
||
|
|
Logger::mNextLevelID = 0x01;
|
||
|
|
|
||
|
|
Logger::mCategoryNames.clear();
|
||
|
|
Logger::mLevelNames.clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
uint32_t Logger::RegisterCategory(const char * name)
|
||
|
|
{
|
||
|
|
Logger::mCategoryNames[mNextCategoryID] = name;
|
||
|
|
uint32_t id = mNextCategoryID;
|
||
|
|
mNextCategoryID = mNextCategoryID << 1;
|
||
|
|
return id;
|
||
|
|
}
|
||
|
|
|
||
|
|
uint32_t Logger::RegisterLevel(const char * name)
|
||
|
|
{
|
||
|
|
Logger::mLevelNames[mNextLevelID] = name;
|
||
|
|
uint32_t id = mNextLevelID;
|
||
|
|
mNextLevelID = mNextLevelID << 1;
|
||
|
|
return id;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string Logger::GetCategoryName(uint32_t id)
|
||
|
|
{
|
||
|
|
if (Logger::mCategoryNames.find(id) == Logger::mCategoryNames.end())
|
||
|
|
return "[]";
|
||
|
|
|
||
|
|
std::string name = "[";
|
||
|
|
name += Logger::mCategoryNames[id];
|
||
|
|
name += "] ";
|
||
|
|
|
||
|
|
return name;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string Logger::GetLevelName(uint32_t id)
|
||
|
|
{
|
||
|
|
if (Logger::mLevelNames.find(id) == Logger::mLevelNames.end())
|
||
|
|
return "[]";
|
||
|
|
|
||
|
|
std::string name = "[";
|
||
|
|
name += Logger::mLevelNames[id];
|
||
|
|
name += "] ";
|
||
|
|
|
||
|
|
return name;
|
||
|
|
}
|
||
|
|
|
||
|
|
LogListener* Logger::AddListener(LogListener * pl)
|
||
|
|
{
|
||
|
|
mListeners.push_back(pl);
|
||
|
|
return pl;
|
||
|
|
}
|
||
|
|
|
||
|
|
LogListener* Logger::RemoveListener(LogListener * pl)
|
||
|
|
{
|
||
|
|
mListeners.remove(pl);
|
||
|
|
return pl;
|
||
|
|
}
|
||
|
|
|
||
|
|
LogListener* Logger::RemoveListener(const char * withName)
|
||
|
|
{
|
||
|
|
LogListener* pl = nullptr;
|
||
|
|
for (auto iter = mListeners.begin(); iter != mListeners.end(); iter++)
|
||
|
|
{
|
||
|
|
if ((*iter)->GetName() == withName)
|
||
|
|
{
|
||
|
|
pl = (*iter);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
mListeners.remove(pl);
|
||
|
|
return pl;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Logger::FreeAllListeners()
|
||
|
|
{
|
||
|
|
for (auto iter = mListeners.begin(); iter != mListeners.end(); iter++)
|
||
|
|
{
|
||
|
|
delete (*iter);
|
||
|
|
}
|
||
|
|
|
||
|
|
mListeners.clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
void Logger::ClearBacklog()
|
||
|
|
{
|
||
|
|
mBacklog.clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
void Logger::Log(uint32_t logCategory, uint32_t logLevel, const char * message, ...)
|
||
|
|
{
|
||
|
|
// clear the buffer
|
||
|
|
memset(mBuffer, 0, 1024);
|
||
|
|
|
||
|
|
// Fill the buffer with the formatted message
|
||
|
|
va_list args;
|
||
|
|
va_start(args, message);
|
||
|
|
vsprintf_s(mBuffer, BUFFER_SIZE, message, args);
|
||
|
|
va_end(args);
|
||
|
|
|
||
|
|
double timeStamp = mTimer.GetElapsedSeconds();
|
||
|
|
|
||
|
|
LogMessage lm;
|
||
|
|
lm.LogCategory = logCategory;
|
||
|
|
lm.LogLevel = logLevel;
|
||
|
|
lm.Message = mBuffer;
|
||
|
|
lm.Repeats = 0;
|
||
|
|
lm.RepeatAlertSent = false;
|
||
|
|
lm.SentTimeStamp = timeStamp;
|
||
|
|
|
||
|
|
bool send = true;
|
||
|
|
bool track = true;
|
||
|
|
|
||
|
|
// Check for repeats
|
||
|
|
for (int i = 0; i < mBacklog.size(); i++)
|
||
|
|
{
|
||
|
|
LogMessage& prevMsg = mBacklog[i];
|
||
|
|
if (prevMsg.Message == lm.Message)
|
||
|
|
{
|
||
|
|
|
||
|
|
if (prevMsg.RepeatAlertSent)
|
||
|
|
{
|
||
|
|
send = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// only suppress the messsage if it has been less than
|
||
|
|
// 1 second since it was last sent
|
||
|
|
double timeSinceLastSend = timeStamp - prevMsg.SentTimeStamp;
|
||
|
|
if (timeSinceLastSend <= 1.0)
|
||
|
|
{
|
||
|
|
prevMsg.Repeats += 1;
|
||
|
|
|
||
|
|
if (prevMsg.Repeats > 100 && !prevMsg.RepeatAlertSent)
|
||
|
|
{
|
||
|
|
lm.Message = lm.Message + " [This message has rapidly repeated over 100 times! No more repeats will be sent out!]";
|
||
|
|
prevMsg.RepeatAlertSent = true;
|
||
|
|
track = false;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
send = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
prevMsg.SentTimeStamp = timeStamp;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
if (!send)
|
||
|
|
return;
|
||
|
|
|
||
|
|
// Send to all listeners
|
||
|
|
for (auto iter = mListeners.begin(); iter != mListeners.end(); iter++)
|
||
|
|
{
|
||
|
|
if ((*iter)->Log(lm))
|
||
|
|
{
|
||
|
|
// Track which listeners handle the message
|
||
|
|
lm.HandledBy.push_back((*iter)->GetName());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (track)
|
||
|
|
mBacklog.push_back(lm);
|
||
|
|
}
|
||
|
|
|
||
|
|
const std::vector<LogMessage>& Logger::GetBacklog() const
|
||
|
|
{
|
||
|
|
return mBacklog;
|
||
|
|
}
|
||
|
|
}
|