2016-02-01 21:55:43 +00:00
|
|
|
|
#include "Log.h"
|
2016-04-26 22:27:24 +00:00
|
|
|
|
#include "File.h"
|
|
|
|
|
#include "StrFmt.h"
|
|
|
|
|
|
2016-04-25 10:49:12 +00:00
|
|
|
|
#include <cstdarg>
|
2016-04-26 22:27:24 +00:00
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
|
|
// Thread-specific log prefix provider
|
|
|
|
|
thread_local std::string(*g_tls_log_prefix)() = nullptr;
|
2014-06-17 15:44:03 +00:00
|
|
|
|
|
2016-01-12 21:57:16 +00:00
|
|
|
|
namespace _log
|
2014-06-17 15:44:03 +00:00
|
|
|
|
{
|
2016-04-26 22:27:24 +00:00
|
|
|
|
struct listener
|
|
|
|
|
{
|
|
|
|
|
listener() = default;
|
|
|
|
|
|
|
|
|
|
virtual ~listener() = default;
|
|
|
|
|
|
|
|
|
|
virtual void log(const channel& ch, level sev, const std::string& text) = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
void log(const std::string& text);
|
|
|
|
|
|
|
|
|
|
// Get current file size (may be used by secondary readers)
|
|
|
|
|
std::size_t size() const;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct file_listener : public file_writer, public listener
|
|
|
|
|
{
|
|
|
|
|
file_listener(const std::string& name)
|
|
|
|
|
: file_writer(name)
|
|
|
|
|
, listener()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Encode level, current thread name, channel name and write log message
|
|
|
|
|
virtual void log(const channel& ch, level sev, const std::string& text) override;
|
|
|
|
|
};
|
|
|
|
|
|
2016-02-01 21:55:43 +00:00
|
|
|
|
static file_listener& get_logger()
|
2014-06-17 15:44:03 +00:00
|
|
|
|
{
|
2016-02-01 21:55:43 +00:00
|
|
|
|
// Use magic static
|
|
|
|
|
static file_listener logger("RPCS3.log");
|
|
|
|
|
return logger;
|
2014-06-17 15:44:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-01 21:55:43 +00:00
|
|
|
|
channel GENERAL(nullptr, level::notice);
|
2016-01-12 21:57:16 +00: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 15:44:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-25 10:49:12 +00:00
|
|
|
|
void _log::channel::broadcast(const _log::channel& ch, _log::level sev, const char* fmt...)
|
2014-06-17 15:44:03 +00:00
|
|
|
|
{
|
2016-04-25 10:49:12 +00:00
|
|
|
|
va_list args;
|
|
|
|
|
va_start(args, fmt);
|
2016-04-26 22:27:24 +00:00
|
|
|
|
get_logger().log(ch, sev, fmt::unsafe_vformat(fmt, args));
|
2016-04-25 10:49:12 +00:00
|
|
|
|
va_end(args);
|
2014-06-17 15:44:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-01 21:55:43 +00:00
|
|
|
|
[[noreturn]] extern void catch_all_exceptions();
|
|
|
|
|
|
2016-01-12 21:57:16 +00:00
|
|
|
|
_log::file_writer::file_writer(const std::string& name)
|
2014-06-17 15:44:03 +00:00
|
|
|
|
{
|
2016-01-12 21:57:16 +00:00
|
|
|
|
try
|
2014-06-17 15:44:03 +00:00
|
|
|
|
{
|
2016-02-01 21:55:43 +00:00
|
|
|
|
if (!m_file.open(fs::get_config_dir() + name, fs::rewrite + fs::append))
|
2014-06-17 15:44:03 +00:00
|
|
|
|
{
|
2016-04-26 22:27:24 +00:00
|
|
|
|
throw fmt::exception("Can't create log file %s (error %d)", name, fs::error);
|
2014-06-17 15:44:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-01 21:55:43 +00:00
|
|
|
|
catch (...)
|
2014-06-17 15:44:03 +00:00
|
|
|
|
{
|
2016-02-01 21:55:43 +00:00
|
|
|
|
catch_all_exceptions();
|
2016-01-12 21:57:16 +00:00
|
|
|
|
}
|
2014-06-17 15:44:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-12 21:57:16 +00:00
|
|
|
|
void _log::file_writer::log(const std::string& text)
|
2014-06-27 07:22:00 +00:00
|
|
|
|
{
|
2016-01-12 21:57:16 +00:00
|
|
|
|
m_file.write(text);
|
2014-06-27 07:22:00 +00:00
|
|
|
|
}
|
2015-04-25 13:29:05 +00:00
|
|
|
|
|
2016-01-12 21:57:16 +00:00
|
|
|
|
std::size_t _log::file_writer::size() const
|
2014-06-27 07:22:00 +00:00
|
|
|
|
{
|
2016-02-01 21:55:43 +00:00
|
|
|
|
return m_file.pos();
|
2014-06-27 07:22:00 +00:00
|
|
|
|
}
|
2014-06-17 15:44:03 +00:00
|
|
|
|
|
2016-01-12 21:57:16 +00:00
|
|
|
|
void _log::file_listener::log(const _log::channel& ch, _log::level sev, const std::string& text)
|
2014-06-17 15:44:03 +00:00
|
|
|
|
{
|
2016-01-12 21:57:16 +00:00
|
|
|
|
std::string msg; msg.reserve(text.size() + 200);
|
|
|
|
|
|
|
|
|
|
// Used character: U+00B7 (Middle Dot)
|
|
|
|
|
switch (sev)
|
2014-06-17 15:44:03 +00:00
|
|
|
|
{
|
2016-01-12 21:57:16 +00:00
|
|
|
|
case level::always: msg = u8"·A "; break;
|
|
|
|
|
case level::fatal: msg = u8"·F "; break;
|
|
|
|
|
case level::error: msg = u8"·E "; break;
|
|
|
|
|
case level::todo: msg = u8"·U "; break;
|
|
|
|
|
case level::success: msg = u8"·S "; break;
|
|
|
|
|
case level::warning: msg = u8"·W "; break;
|
|
|
|
|
case level::notice: msg = u8"·! "; break;
|
|
|
|
|
case level::trace: msg = u8"·T "; break;
|
2014-06-17 15:44:03 +00:00
|
|
|
|
}
|
2015-04-25 13:29:05 +00:00
|
|
|
|
|
2016-01-12 21:57:16 +00:00
|
|
|
|
// TODO: print time?
|
2015-04-25 13:29:05 +00:00
|
|
|
|
|
2016-04-26 22:27:24 +00:00
|
|
|
|
if (auto prefix = g_tls_log_prefix)
|
2016-01-12 21:57:16 +00:00
|
|
|
|
{
|
|
|
|
|
msg += '{';
|
2016-04-26 22:27:24 +00:00
|
|
|
|
msg += prefix();
|
2016-01-12 21:57:16 +00:00
|
|
|
|
msg += "} ";
|
|
|
|
|
}
|
2016-02-01 21:55:43 +00:00
|
|
|
|
|
|
|
|
|
if (ch.name)
|
2016-01-12 21:57:16 +00:00
|
|
|
|
{
|
|
|
|
|
msg += ch.name;
|
|
|
|
|
msg += sev == level::todo ? " TODO: " : ": ";
|
|
|
|
|
}
|
|
|
|
|
else if (sev == level::todo)
|
|
|
|
|
{
|
|
|
|
|
msg += "TODO: ";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
msg += text;
|
|
|
|
|
msg += '\n';
|
2015-01-18 22:54:56 +00:00
|
|
|
|
|
2016-01-12 21:57:16 +00:00
|
|
|
|
file_writer::log(msg);
|
2015-01-18 22:54:56 +00:00
|
|
|
|
}
|