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.
lunarium_OLD/src/utils/Logger.cpp

419 lines
10 KiB
C++

/******************************************************************************
* 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 <ctime>
#include <stdarg.h>
#include <cstring>
#include <stdexcept>
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;
now = localtime(&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, BUFFER_SIZE);
if (strlen(message) >= BUFFER_SIZE)
throw std::runtime_error("Log message size exceeds buffer size");
// Fill the buffer with the formatted message
va_list args;
va_start(args, message);
vsprintf(mBuffer, 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;
}
}