ftpiiu_plugin/source/log.cpp

254 lines
5.5 KiB
C++
Raw Normal View History

2020-04-05 21:16:16 +02:00
// ftpd is a server implementation based on the following:
// - RFC 959 (https://tools.ietf.org/html/rfc959)
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
//
// Copyright (C) 2020 Michael Theall
//
// 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, see <https://www.gnu.org/licenses/>.
#include "log.h"
2020-04-10 04:21:25 +02:00
#include "platform.h"
2020-04-05 21:16:16 +02:00
#include "imgui.h"
#include <mutex>
2020-04-10 04:21:25 +02:00
#include <vector>
2020-04-05 21:16:16 +02:00
namespace
{
#ifdef _3DS
2020-04-06 07:36:03 +02:00
/// \brief Maximum number of log messages to keep
2020-04-05 21:16:16 +02:00
constexpr auto MAX_LOGS = 250;
#else
2020-04-06 07:36:03 +02:00
/// \brief Maximum number of log messages to keep
2020-04-05 21:16:16 +02:00
constexpr auto MAX_LOGS = 10000;
#endif
2020-04-06 07:36:03 +02:00
2020-04-17 22:32:39 +02:00
#ifdef CLASSIC
bool s_logUpdated = true;
#endif
2020-04-06 07:36:03 +02:00
/// \brief Message prefix
2020-04-05 21:16:16 +02:00
static char const *const s_prefix[] = {
2020-04-10 04:21:25 +02:00
[DEBUG] = "[DEBUG]",
[INFO] = "[INFO]",
[ERROR] = "[ERROR]",
[COMMAND] = "[COMMAND]",
[RESPONSE] = "[RESPONSE]",
2020-04-05 21:16:16 +02:00
};
2020-04-10 04:21:25 +02:00
/// \brief Log message
struct Message
{
/// \brief Parameterized constructor
/// \param level_ Log level
/// \param message_ Log message
Message (LogLevel const level_, std::string message_)
: level (level_), message (std::move (message_))
{
}
/// \brief Log level
LogLevel level;
/// \brief Log message
std::string message;
};
2020-04-05 21:16:16 +02:00
2020-04-10 04:21:25 +02:00
/// \brief Log messages
std::vector<Message> s_messages;
2020-04-17 22:32:39 +02:00
#ifndef NDS
2020-04-10 04:21:25 +02:00
/// \brief Log lock
platform::Mutex s_lock;
2020-04-17 22:32:39 +02:00
#endif
2020-04-10 04:21:25 +02:00
}
2020-04-05 21:16:16 +02:00
2020-04-10 04:21:25 +02:00
void drawLog ()
2020-04-05 21:16:16 +02:00
{
2020-04-17 22:32:39 +02:00
#ifndef NDS
2020-04-10 04:21:25 +02:00
auto const lock = std::scoped_lock (s_lock);
2020-04-17 22:32:39 +02:00
#endif
#ifdef CLASSIC
if (!s_logUpdated)
return;
2020-04-05 21:16:16 +02:00
2020-04-17 22:32:39 +02:00
s_logUpdated = false;
#endif
auto const maxLogs =
#ifdef CLASSIC
g_logConsole.windowHeight;
#else
MAX_LOGS;
#endif
if (s_messages.size () > static_cast<unsigned> (maxLogs))
2020-04-05 21:16:16 +02:00
{
2020-04-10 04:21:25 +02:00
auto const begin = std::begin (s_messages);
2020-04-17 22:32:39 +02:00
auto const end = std::next (begin, s_messages.size () - maxLogs);
2020-04-10 04:21:25 +02:00
s_messages.erase (begin, end);
2020-04-05 21:16:16 +02:00
}
2020-04-17 22:32:39 +02:00
#ifdef CLASSIC
char const *const s_colors[] = {
[DEBUG] = "\x1b[33;1m", // yellow
[INFO] = "\x1b[37;1m", // white
[ERROR] = "\x1b[31;1m", // red
[COMMAND] = "\x1b[32;1m", // green
[RESPONSE] = "\x1b[36;1m", // cyan
};
auto it = std::begin (s_messages);
if (s_messages.size () > static_cast<unsigned> (g_logConsole.windowHeight))
it = std::next (it, s_messages.size () - g_logConsole.windowHeight);
consoleSelect (&g_logConsole);
while (it != std::end (s_messages))
{
std::fputs (s_colors[it->level], stdout);
std::fputs (it->message.c_str (), stdout);
++it;
}
std::fflush (stdout);
s_messages.clear ();
#else
2020-04-10 07:46:46 +02:00
ImVec4 const s_colors[] = {
[DEBUG] = ImVec4 (1.0f, 1.0f, 0.4f, 1.0f), // yellow
[INFO] = ImGui::GetStyleColorVec4 (ImGuiCol_Text), // normal
[ERROR] = ImVec4 (1.0f, 0.4f, 0.4f, 1.0f), // red
[COMMAND] = ImVec4 (0.4f, 1.0f, 0.4f, 1.0f), // green
[RESPONSE] = ImVec4 (0.4f, 1.0f, 1.0f, 1.0f), // cyan
2020-04-05 21:16:16 +02:00
};
2020-04-10 04:21:25 +02:00
for (auto const &message : s_messages)
2020-04-05 21:16:16 +02:00
{
ImGui::PushStyleColor (ImGuiCol_Text, s_colors[message.level]);
ImGui::TextUnformatted (s_prefix[message.level]);
ImGui::SameLine ();
ImGui::TextUnformatted (message.message.c_str ());
ImGui::PopStyleColor ();
}
2020-04-06 07:36:03 +02:00
// auto-scroll if scroll bar is at end
2020-04-05 21:16:16 +02:00
if (ImGui::GetScrollY () >= ImGui::GetScrollMaxY ())
ImGui::SetScrollHereY (1.0f);
2020-04-17 22:32:39 +02:00
#endif
2020-04-05 21:16:16 +02:00
}
2020-04-10 04:21:25 +02:00
void debug (char const *const fmt_, ...)
2020-04-05 21:16:16 +02:00
{
#ifndef NDEBUG
va_list ap;
va_start (ap, fmt_);
2020-04-10 04:21:25 +02:00
addLog (DEBUG, fmt_, ap);
2020-04-05 21:16:16 +02:00
va_end (ap);
#endif
}
2020-04-10 04:21:25 +02:00
void info (char const *const fmt_, ...)
2020-04-05 21:16:16 +02:00
{
va_list ap;
va_start (ap, fmt_);
2020-04-10 04:21:25 +02:00
addLog (INFO, fmt_, ap);
2020-04-05 21:16:16 +02:00
va_end (ap);
}
2020-04-10 04:21:25 +02:00
void error (char const *const fmt_, ...)
2020-04-05 21:16:16 +02:00
{
va_list ap;
va_start (ap, fmt_);
2020-04-10 04:21:25 +02:00
addLog (ERROR, fmt_, ap);
2020-04-05 21:16:16 +02:00
va_end (ap);
}
2020-04-10 04:21:25 +02:00
void command (char const *const fmt_, ...)
2020-04-05 21:16:16 +02:00
{
va_list ap;
va_start (ap, fmt_);
2020-04-10 04:21:25 +02:00
addLog (COMMAND, fmt_, ap);
2020-04-05 21:16:16 +02:00
va_end (ap);
}
2020-04-10 04:21:25 +02:00
void response (char const *const fmt_, ...)
2020-04-05 21:16:16 +02:00
{
va_list ap;
va_start (ap, fmt_);
2020-04-10 04:21:25 +02:00
addLog (RESPONSE, fmt_, ap);
2020-04-05 21:16:16 +02:00
va_end (ap);
}
2020-04-10 04:21:25 +02:00
void addLog (LogLevel const level_, char const *const fmt_, va_list ap_)
2020-04-05 21:16:16 +02:00
{
#ifdef NDEBUG
if (level_ == DEBUG)
return;
#endif
2020-04-17 22:32:39 +02:00
#ifndef NDS
thread_local
#endif
static char buffer[1024];
2020-04-05 21:16:16 +02:00
std::vsnprintf (buffer, sizeof (buffer), fmt_, ap_);
buffer[sizeof (buffer) - 1] = '\0';
2020-04-17 22:32:39 +02:00
#ifndef NDS
2020-04-10 04:21:25 +02:00
auto const lock = std::scoped_lock (s_lock);
2020-04-17 22:32:39 +02:00
#endif
2020-04-05 21:16:16 +02:00
#ifndef NDEBUG
2020-04-17 22:32:39 +02:00
// std::fprintf (stderr, "%s", s_prefix[level_]);
// std::fputs (buffer, stderr);
2020-04-05 21:16:16 +02:00
#endif
2020-04-10 04:21:25 +02:00
s_messages.emplace_back (level_, buffer);
2020-04-17 22:32:39 +02:00
#ifdef CLASSIC
s_logUpdated = true;
#endif
2020-04-05 21:16:16 +02:00
}
2020-04-10 04:21:25 +02:00
void addLog (LogLevel const level_, std::string_view const message_)
2020-04-05 21:16:16 +02:00
{
#ifdef NDEBUG
if (level_ == DEBUG)
return;
#endif
auto msg = std::string (message_);
for (auto &c : msg)
{
2020-04-06 07:36:03 +02:00
// replace nul-characters with ? to avoid truncation
2020-04-05 21:16:16 +02:00
if (c == '\0')
c = '?';
}
2020-04-17 22:32:39 +02:00
#ifndef NDS
2020-04-10 04:21:25 +02:00
auto const lock = std::scoped_lock (s_lock);
2020-04-17 22:32:39 +02:00
#endif
2020-04-05 21:16:16 +02:00
#ifndef NDEBUG
2020-04-17 22:32:39 +02:00
// std::fprintf (stderr, "%s", s_prefix[level_]);
// std::fwrite (msg.data (), 1, msg.size (), stderr);
2020-04-05 21:16:16 +02:00
#endif
2020-04-10 04:21:25 +02:00
s_messages.emplace_back (level_, msg);
2020-04-17 22:32:39 +02:00
#ifdef CLASSIC
s_logUpdated = true;
#endif
2020-04-05 21:16:16 +02:00
}