| #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; |
| } |