blob: 13b8bc814b234525e82aa99f4b67b2ffb2517ab4 [file] [log] [blame]
#include <MessageLog.h>
#include <cstdio>
const char* MessageLog::MESSAGES_HEADER = "\x1b[0;30;42m MESSAGES \x1b[0m\n";
const char* MessageLog::F_ALERT_MARK = "\x1b[1;33;41m ALERT \x1b[0m";
const char* MessageLog::F_WARN_MARK = "\x1b[1;33;45m WARN \x1b[0m";
const char* MessageLog::F_INFO_MARK = "\x1b[0;30;32m INFO \x1b[0m";
const char* MessageLog::F_ALERT = "\x1b[1;31m";
const char* MessageLog::F_WARN = "\x1b[1;33m";
const char* MessageLog::F_INFO = "\x1b[1;32m";
const char* MessageLog::F_RESET = "\x1b[0m";
const std::string MessageLog::CAPACITY_ERROR = "MessageLog(): Illegal capacity (entries < 1)";
const size_t MessageLog::DATE_BUFFER_SIZE = 24;
const char* MessageLog::DATE_FORMAT = "%FT%TZ ";
const size_t MessageLog::DATE_LENGTH = 21;
// @throws std::bad_alloc, std::out_of_range
MessageLog::MessageLog(size_t entries):
capacity(entries)
{
if (capacity < 1)
{
throw std::out_of_range(CAPACITY_ERROR);
}
log_entries = std::unique_ptr<entry*[]>(new entry*[capacity]);
date_buffer = std::unique_ptr<char[]>(new char[DATE_BUFFER_SIZE]);
}
MessageLog::~MessageLog() noexcept
{
clear_impl();
}
bool MessageLog::has_entries() const
{
return (filled || index > 0);
}
// @throws std::bad_alloc
void MessageLog::add_entry(log_level level, std::string& message)
{
std::unique_ptr<std::string> message_copy;
if (format_date())
{
message_copy = std::unique_ptr<std::string>(new std::string(date_buffer.get()));
*message_copy += message;
}
else
{
message_copy = std::unique_ptr<std::string>(new std::string(message));
}
if (filled)
{
delete log_entries[index]->message;
log_entries[index]->message = message_copy.release();
log_entries[index]->level = level;
}
else
{
// Keep unique_ptr ownership of message_copy while constructing the new
// entry, otherwise message_copy would not be deallocated if
// entry construction throws
log_entries[index] = new MessageLog::entry {level, message_copy.get()};
static_cast<void> (message_copy.release());
}
++index;
if (index >= capacity)
{
filled = true;
index = 0;
}
}
// @throws std::bad_alloc
void MessageLog::add_entry(log_level level, const char* message)
{
std::string message_str(message);
add_entry(level, message_str);
}
void MessageLog::clear()
{
clear_impl();
filled = false;
index = 0;
}
void MessageLog::clear_impl() noexcept
{
size_t limit = filled ? capacity : index;
for (size_t slot = 0; slot < limit; ++slot)
{
delete log_entries[slot]->message;
delete log_entries[slot];
}
}
void MessageLog::display_messages(std::FILE* out) const
{
if (filled || index > 0)
{
std::fputs(MESSAGES_HEADER, out);
MessageLog::EntriesIterator iter(*this);
size_t count = iter.get_size();
for (size_t slot = 0; slot < count; ++slot)
{
MessageLog::entry* log_entry = iter.next();
const char* mark = F_ALERT_MARK;
const char* format = F_ALERT;
switch (log_entry->level)
{
case MessageLog::log_level::INFO:
mark = F_INFO_MARK;
format = F_INFO;
break;
case MessageLog::log_level::WARN:
mark = F_WARN_MARK;
format = F_WARN;
break;
case MessageLog::log_level::ALERT:
// fall-through
default:
// defaults to the alert format
break;
}
std::fprintf(out, " %s %s%s%s\n", mark, format, log_entry->message->c_str(), F_RESET);
}
std::fflush(out);
}
}
bool MessageLog::format_date() noexcept
{
bool rc = false;
if (gettimeofday(&utc_time, NULL) == 0)
{
if (gmtime_r(&(utc_time.tv_sec), &time_fields) != nullptr)
{
if (strftime(date_buffer.get(), DATE_BUFFER_SIZE, DATE_FORMAT, &time_fields) == DATE_LENGTH)
{
rc = true;
}
}
}
return rc;
}
MessageLog::EntriesIterator::EntriesIterator(const MessageLog& log_ref):
log_obj(log_ref),
circular_mode(log_ref.filled)
{
if (circular_mode)
{
iter_index = log_obj.index;
}
}
MessageLog::EntriesIterator::~EntriesIterator() noexcept
{
}
MessageLog::entry* MessageLog::EntriesIterator::next()
{
MessageLog::entry* item {nullptr};
if (circular_mode || iter_index < log_obj.index)
{
item = log_obj.log_entries[iter_index];
++iter_index;
if (iter_index >= log_obj.capacity)
{
circular_mode = false;
iter_index = 0;
}
}
return item;
}
size_t MessageLog::EntriesIterator::get_size() const
{
return log_obj.filled ? log_obj.capacity : log_obj.index;
}
bool MessageLog::EntriesIterator::has_next() const
{
return circular_mode || iter_index < log_obj.index;
}