2016-02-01 22:55:43 +01:00
|
|
|
|
#include "Log.h"
|
2016-04-27 00:27:24 +02:00
|
|
|
|
#include "File.h"
|
|
|
|
|
#include "StrFmt.h"
|
|
|
|
|
|
2016-07-21 00:00:31 +02:00
|
|
|
|
#include "rpcs3_version.h"
|
2016-04-27 00:27:24 +02:00
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
|
|
// Thread-specific log prefix provider
|
|
|
|
|
thread_local std::string(*g_tls_log_prefix)() = nullptr;
|
2014-06-17 17:44:03 +02:00
|
|
|
|
|
2016-08-03 22:51:05 +02:00
|
|
|
|
template<>
|
|
|
|
|
void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
|
|
|
|
|
{
|
|
|
|
|
format_enum(out, arg, [](auto lev)
|
|
|
|
|
{
|
|
|
|
|
switch (lev)
|
|
|
|
|
{
|
|
|
|
|
case logs::level::always: return "Nothing";
|
|
|
|
|
case logs::level::fatal: return "Fatal";
|
|
|
|
|
case logs::level::error: return "Error";
|
|
|
|
|
case logs::level::todo: return "TODO";
|
|
|
|
|
case logs::level::success: return "Success";
|
|
|
|
|
case logs::level::warning: return "Warning";
|
|
|
|
|
case logs::level::notice: return "Notice";
|
|
|
|
|
case logs::level::trace: return "Trace";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return unknown;
|
|
|
|
|
});
|
|
|
|
|
}
|
2016-05-13 16:01:48 +02:00
|
|
|
|
|
|
|
|
|
namespace logs
|
2014-06-17 17:44:03 +02:00
|
|
|
|
{
|
2016-04-27 00:27:24 +02:00
|
|
|
|
class file_writer
|
|
|
|
|
{
|
|
|
|
|
// Could be memory-mapped file
|
|
|
|
|
fs::file m_file;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
file_writer(const std::string& name);
|
|
|
|
|
|
|
|
|
|
virtual ~file_writer() = default;
|
|
|
|
|
|
|
|
|
|
// Append raw data
|
2016-07-21 00:00:31 +02:00
|
|
|
|
void log(const char* text, std::size_t size);
|
2016-04-27 00:27:24 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct file_listener : public file_writer, public listener
|
|
|
|
|
{
|
|
|
|
|
file_listener(const std::string& name)
|
|
|
|
|
: file_writer(name)
|
|
|
|
|
, listener()
|
|
|
|
|
{
|
2016-07-21 00:00:31 +02:00
|
|
|
|
const std::string& start = fmt::format("\xEF\xBB\xBF" "RPCS3 v%s\n", rpcs3::version.to_string());
|
|
|
|
|
file_writer::log(start.data(), start.size());
|
2016-04-27 00:27:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Encode level, current thread name, channel name and write log message
|
2016-07-21 00:00:31 +02:00
|
|
|
|
virtual void log(const message& msg) override;
|
2016-04-27 00:27:24 +02:00
|
|
|
|
};
|
|
|
|
|
|
2016-07-21 00:00:31 +02:00
|
|
|
|
static file_listener* get_logger()
|
2014-06-17 17:44:03 +02:00
|
|
|
|
{
|
2016-02-01 22:55:43 +01:00
|
|
|
|
// Use magic static
|
|
|
|
|
static file_listener logger("RPCS3.log");
|
2016-07-21 00:00:31 +02:00
|
|
|
|
return &logger;
|
2014-06-17 17:44:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-01 22:55:43 +01:00
|
|
|
|
channel GENERAL(nullptr, level::notice);
|
2016-01-12 22:57:16 +01:00
|
|
|
|
channel LOADER("LDR", level::notice);
|
|
|
|
|
channel MEMORY("MEM", level::notice);
|
|
|
|
|
channel RSX("RSX", level::notice);
|
|
|
|
|
channel HLE("HLE", level::notice);
|
|
|
|
|
channel PPU("PPU", level::notice);
|
|
|
|
|
channel SPU("SPU", level::notice);
|
|
|
|
|
channel ARMv7("ARMv7");
|
2014-06-17 17:44:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-21 00:00:31 +02:00
|
|
|
|
void logs::listener::add(logs::listener* _new)
|
|
|
|
|
{
|
|
|
|
|
// Get first (main) listener
|
|
|
|
|
listener* lis = get_logger();
|
|
|
|
|
|
|
|
|
|
// Install new listener at the end of linked list
|
|
|
|
|
while (lis->m_next || !lis->m_next.compare_and_swap_test(nullptr, _new))
|
|
|
|
|
{
|
|
|
|
|
lis = lis->m_next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-03 22:51:05 +02:00
|
|
|
|
void logs::channel::broadcast(const logs::channel& ch, logs::level sev, const char* fmt, const fmt::supplementary_info* sup, const u64* args)
|
2014-06-17 17:44:03 +02:00
|
|
|
|
{
|
2016-08-03 22:51:05 +02:00
|
|
|
|
std::string text; fmt::raw_append(text, fmt, sup, args);
|
|
|
|
|
std::string prefix(g_tls_log_prefix ? g_tls_log_prefix() : "");
|
2016-07-21 00:00:31 +02:00
|
|
|
|
|
|
|
|
|
// Prepare message information
|
|
|
|
|
message msg;
|
|
|
|
|
msg.ch = &ch;
|
|
|
|
|
msg.sev = sev;
|
|
|
|
|
msg.text = text.data();
|
|
|
|
|
msg.text_size = text.size();
|
|
|
|
|
msg.prefix = prefix.data();
|
|
|
|
|
msg.prefix_size = prefix.size();
|
|
|
|
|
|
|
|
|
|
// Get first (main) listener
|
|
|
|
|
listener* lis = get_logger();
|
|
|
|
|
|
|
|
|
|
// Send message to all listeners
|
|
|
|
|
while (lis)
|
|
|
|
|
{
|
|
|
|
|
lis->log(msg);
|
|
|
|
|
lis = lis->m_next;
|
|
|
|
|
}
|
2014-06-17 17:44:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-01 22:55:43 +01:00
|
|
|
|
[[noreturn]] extern void catch_all_exceptions();
|
|
|
|
|
|
2016-05-13 16:01:48 +02:00
|
|
|
|
logs::file_writer::file_writer(const std::string& name)
|
2014-06-17 17:44:03 +02:00
|
|
|
|
{
|
2016-01-12 22:57:16 +01:00
|
|
|
|
try
|
2014-06-17 17:44:03 +02:00
|
|
|
|
{
|
2016-02-01 22:55:43 +01:00
|
|
|
|
if (!m_file.open(fs::get_config_dir() + name, fs::rewrite + fs::append))
|
2014-06-17 17:44:03 +02:00
|
|
|
|
{
|
2016-07-21 00:00:31 +02:00
|
|
|
|
throw fmt::exception("Can't create log file %s (error %s)", name, fs::g_tls_error);
|
2014-06-17 17:44:03 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-01 22:55:43 +01:00
|
|
|
|
catch (...)
|
2014-06-17 17:44:03 +02:00
|
|
|
|
{
|
2016-02-01 22:55:43 +01:00
|
|
|
|
catch_all_exceptions();
|
2016-01-12 22:57:16 +01:00
|
|
|
|
}
|
2014-06-17 17:44:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-21 00:00:31 +02:00
|
|
|
|
void logs::file_writer::log(const char* text, std::size_t size)
|
2014-06-27 09:22:00 +02:00
|
|
|
|
{
|
2016-07-21 00:00:31 +02:00
|
|
|
|
m_file.write(text, size);
|
2014-06-27 09:22:00 +02:00
|
|
|
|
}
|
2014-06-17 17:44:03 +02:00
|
|
|
|
|
2016-07-21 00:00:31 +02:00
|
|
|
|
void logs::file_listener::log(const logs::message& msg)
|
2014-06-17 17:44:03 +02:00
|
|
|
|
{
|
2016-07-21 00:00:31 +02:00
|
|
|
|
std::string text; text.reserve(msg.text_size + msg.prefix_size + 200);
|
2016-01-12 22:57:16 +01:00
|
|
|
|
|
|
|
|
|
// Used character: U+00B7 (Middle Dot)
|
2016-07-21 00:00:31 +02:00
|
|
|
|
switch (msg.sev)
|
2014-06-17 17:44:03 +02:00
|
|
|
|
{
|
2016-07-21 00:00:31 +02:00
|
|
|
|
case level::always: text = u8"·A "; break;
|
|
|
|
|
case level::fatal: text = u8"·F "; break;
|
|
|
|
|
case level::error: text = u8"·E "; break;
|
|
|
|
|
case level::todo: text = u8"·U "; break;
|
|
|
|
|
case level::success: text = u8"·S "; break;
|
|
|
|
|
case level::warning: text = u8"·W "; break;
|
|
|
|
|
case level::notice: text = u8"·! "; break;
|
|
|
|
|
case level::trace: text = u8"·T "; break;
|
2014-06-17 17:44:03 +02:00
|
|
|
|
}
|
2015-04-25 15:29:05 +02:00
|
|
|
|
|
2016-01-12 22:57:16 +01:00
|
|
|
|
// TODO: print time?
|
2015-04-25 15:29:05 +02:00
|
|
|
|
|
2016-07-21 00:00:31 +02:00
|
|
|
|
if (msg.prefix_size > 0)
|
2016-01-12 22:57:16 +01:00
|
|
|
|
{
|
2016-07-21 00:00:31 +02:00
|
|
|
|
text += fmt::format("{%s} ", msg.prefix);
|
2016-01-12 22:57:16 +01:00
|
|
|
|
}
|
2016-02-01 22:55:43 +01:00
|
|
|
|
|
2016-07-21 00:00:31 +02:00
|
|
|
|
if (msg.ch->name)
|
2016-01-12 22:57:16 +01:00
|
|
|
|
{
|
2016-07-21 00:00:31 +02:00
|
|
|
|
text += msg.ch->name;
|
|
|
|
|
text += msg.sev == level::todo ? " TODO: " : ": ";
|
2016-01-12 22:57:16 +01:00
|
|
|
|
}
|
2016-07-21 00:00:31 +02:00
|
|
|
|
else if (msg.sev == level::todo)
|
2016-01-12 22:57:16 +01:00
|
|
|
|
{
|
2016-07-21 00:00:31 +02:00
|
|
|
|
text += "TODO: ";
|
2016-01-12 22:57:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-21 00:00:31 +02:00
|
|
|
|
text += msg.text;
|
|
|
|
|
text += '\n';
|
2015-01-18 23:54:56 +01:00
|
|
|
|
|
2016-07-21 00:00:31 +02:00
|
|
|
|
file_writer::log(text.data(), text.size());
|
2015-01-18 23:54:56 +01:00
|
|
|
|
}
|