Rev 14 | Blame | Last modification | View Log | RSS feed
/*
* Copyright (C) 2020, 2021 by Andreas Theofilu <andreas@theosys.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <iostream>
#include <fstream>
#include <sstream>
#include <ios>
#include <time.h>
#include <mutex>
#include "terror.h"
#include "tconfig.h"
using std::string;
std::mutex message_mutex;
bool TError::mHaveError = false;
terrtype_t TError::mErrType = TERRNONE;
TStreamError *TError::mCurrent = nullptr;
std::string TError::msError;
int TStreamError::mIndent = 1;
std::ostream *TStreamError::mStream = nullptr;
std::string TStreamError::mLogfile;
bool TStreamError::mInitialized = false;
unsigned int TStreamError::mLogLevel = LOG_PROTOCOL;
TStreamError::TStreamError(const string& logFile, const std::string& logLevel)
{
if (!logFile.empty())
setLogFile(logFile);
else if (!TConfig::getLogFile().empty())
setLogFile(TConfig::getLogFile());
if (!logLevel.empty())
setLogLevel(logLevel);
else if (!TConfig::getLogLevel().empty())
setLogLevel(TConfig::getLogFile());
_init();
}
TStreamError::~TStreamError()
{
if (mStream && mStream != &std::cout)
{
delete mStream;
mStream = nullptr;
mInitialized = false;
}
}
void TStreamError::setLogLevel(const std::string& slv)
{
size_t pos = slv.find("|");
size_t start = 0;
string lv;
mLogLevel = 0;
while (pos != string::npos)
{
lv = slv.substr(start, pos - start);
start = pos + 1;
mLogLevel |= _getLevel(lv);
pos = slv.find("|", start);
}
mLogLevel |= _getLevel(slv.substr(start));
}
bool TStreamError::checkFilter(terrtype_t err)
{
if (err == TERRINFO && (mLogLevel & LOG_INFO) != 0)
return true;
else if (err == TERRWARNING && (mLogLevel & LOG_WARNING) != 0)
return true;
else if (err == TERRERROR && (mLogLevel & LOG_ERROR) != 0)
return true;
else if (err == TERRTRACE && (mLogLevel & LOG_TRACE) != 0)
return true;
else if (err == TERRDEBUG && (mLogLevel & LOG_DEBUG) != 0)
return true;
return false;
}
bool TStreamError::checkFilter(int lv)
{
if ((mLogLevel & lv) != 0)
return true;
return false;
}
unsigned int TStreamError::_getLevel(const std::string& slv)
{
if (slv.compare(SLOG_NONE) == 0)
return LOG_NONE;
if (slv.compare(SLOG_INFO) == 0)
return LOG_INFO;
if (slv.compare(SLOG_WARNING) == 0)
return LOG_WARNING;
if (slv.compare(SLOG_ERROR) == 0)
return LOG_ERROR;
if (slv.compare(SLOG_TRACE) == 0)
return LOG_TRACE;
if (slv.compare(SLOG_DEBUG) == 0)
return LOG_DEBUG;
if (slv.compare(SLOG_PROTOCOL) == 0)
return LOG_PROTOCOL;
if (slv.compare(SLOG_ALL) == 0)
return LOG_ALL;
return LOG_NONE;
}
void TStreamError::_init()
{
if (mInitialized)
return;
mInitialized = true;
if (!mLogfile.empty())
{
if (mStream)
delete mStream;
mStream = new std::ofstream(mLogfile.c_str(), std::ios::out | std::ios::ate);
if (!mStream || mStream->fail())
mStream = &std::cout;
}
else
mStream = &std::cout;
if (!TConfig::isLongFormat())
*mStream << "Logfile started at " << getTime() << std::endl;
*mStream << TConfig::getProgName() << " version " << V_MAJOR << "." << V_MINOR << "." << V_PATCH << std::endl;
*mStream << "(C) Copyright by Andreas Theofilu <andreas@theosys.at>" << std::endl << std::endl << std::flush;
if (TConfig::isLongFormat())
*mStream << "Timestamp Type LNr., File name , Message" << std::endl;
else
*mStream << "Type LNr., Message" << std::endl;
*mStream << "-----------------------------------------------------------------" << std::endl;
}
void TStreamError::logMsg(std::ostream& str)
{
_init();
if (!mStream || str.fail())
return;
// Save the old format settings in case they were manipulated
std::ios oldstate(nullptr);
oldstate.copyfmt(std::cout);
// Print out the message
std::stringstream s;
s << str.rdbuf() << std::ends;
*mStream << s.str() << std::flush;
mStream->copyfmt(oldstate); // Restore old format (reset)
}
void TStreamError::decIndent()
{
if (mIndent > 0)
mIndent--;
}
string TStreamError::getTime()
{
time_t rawtime;
struct tm * timeinfo;
char buffer[80];
time(&rawtime);
timeinfo = localtime(&rawtime);
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);
string str(buffer);
return str;
}
std::ostream& indent(std::ostream& os)
{
if (os.fail())
return os;
if (TStreamError::getIndent() > 0)
os << std::setw(TStreamError::getIndent()) << " ";
return os;
}
/********************************************************************/
std::mutex tracer_mutex;
TTracer::TTracer(const std::string& msg, int line, char *file)
{
if (!TStreamError::checkFilter(LOG_TRACE))
return;
tracer_mutex.lock();
mFile = file;
size_t pos = mFile.find_last_of("/");
if (pos != string::npos)
mFile = mFile.substr(pos + 1);
if (!TConfig::isLongFormat())
TError::Current()->logMsg(*TStreamError::getStream() << "TRC " << std::setw(5) << std::right << line << ", " << indent << "{entry " << msg << std::endl);
else
TError::Current()->logMsg(*TStreamError::getStream() << TStreamError::getTime() << " TRC " << std::setw(5) << std::right << line << ", " << std::setw(20) << std::left << mFile << ", " << indent << "{entry " << msg << std::endl);
TError::Current()->incIndent();
mHeadMsg = msg;
mLine = line;
tracer_mutex.unlock();
}
TTracer::~TTracer()
{
if (!TStreamError::checkFilter(LOG_TRACE))
return;
tracer_mutex.lock();
TError::Current()->decIndent();
if (!TConfig::isLongFormat())
TError::Current()->logMsg(*TStreamError::getStream() << "TRC , " << indent << "}exit " << mHeadMsg << std::endl);
else
TError::Current()->logMsg(*TStreamError::getStream() << TStreamError::getTime() << " TRC , " << std::setw(20) << std::left << mFile << ", " << indent << "}exit " << mHeadMsg << std::endl);
mHeadMsg.clear();
tracer_mutex.unlock();
}
/********************************************************************/
TError::~TError()
{
if (mCurrent)
{
delete mCurrent;
mCurrent = nullptr;
}
}
void TError::lock()
{
message_mutex.lock();
}
void TError::unlock()
{
message_mutex.unlock();
}
TStreamError* TError::Current()
{
if (!mCurrent)
mCurrent = new TStreamError(TConfig::getLogFile(), TConfig::getLogLevel());
return mCurrent;
}
void TError::logHex(char* str, size_t size)
{
if (!str || !size)
return;
message_mutex.lock();
Current();
// Save the old format settings in case they were manipulated
std::ios oldstate(nullptr);
oldstate.copyfmt(std::cout);
// Print out the message
std::ostream *stream = mCurrent->getStream();
*stream << strToHex(str, size, 16, true, 12) << std::endl;
stream->copyfmt(oldstate); // Restore old format (reset)
message_mutex.unlock();
}
string TError::toHex(int num, int width)
{
string ret;
std::stringstream stream;
stream << std::setfill ('0') << std::setw(width) << std::hex << num;
ret = stream.str();
return ret;
}
string TError::strToHex(const char *str, size_t size, int width, bool format, int indent)
{
DECL_TRACER("TError::strToHex(const char *str, size_t size, int width, bool format, int indent)");
int len = 0, pos = 0, old = 0;
int w = (format) ? 1 : width;
string out, left, right;
string ind;
if (indent > 0)
{
for (int j = 0; j < indent; j++)
ind.append(" ");
}
for (size_t i = 0; i < size; i++)
{
if (len >= w)
{
left.append(" ");
len = 0;
}
if (format && i > 0 && (pos % width) == 0)
{
out += ind + toHex(old, 4) + ": " + left + " | " + right + "\n";
left.clear();
right.clear();
old = pos;
}
int c = *(str+i) & 0x000000ff;
left.append(toHex(c, 2));
if (format)
{
if (std::isprint(c))
right.push_back(c);
else
right.push_back('.');
}
len++;
pos++;
}
if (!format)
return left;
else if (pos > 0)
{
if ((pos % width) != 0)
{
for (int i = 0; i < (width - (pos % width)); i++)
left.append(" ");
}
out += ind + toHex(old, 4)+": "+left + " | " + right;
}
return out;
}
void TError::setErrorMsg(const std::string& msg)
{
if (msg.empty())
return;
msError = msg;
mHaveError = true;
mErrType = TERRERROR;
}
void TError::setErrorMsg(terrtype_t t, const std::string& msg)
{
if (msg.empty())
return;
msError = msg;
mHaveError = true;
mErrType = t;
}
std::ostream & TError::append(int lv, std::ostream& os)
{
std::string prefix;
Current();
switch (lv)
{
case LOG_INFO: prefix = "INF >>, "; break;
case LOG_WARNING: prefix = "WRN !!, "; break;
case LOG_ERROR: prefix = "ERR *****, "; break;
case LOG_TRACE: prefix = "TRC , "; break;
case LOG_DEBUG: prefix = "DBG --, "; break;
default:
prefix = " ";
}
if (!TConfig::isLongFormat())
return os << prefix;
else
return os << TStreamError::getTime() << " " << prefix << std::setw(20) << " " << ", ";
}