mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-19 06:45:39 +00:00
Common/Log: Add file output
This commit is contained in:
parent
828513409f
commit
4702110474
|
@ -1,7 +1,9 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
|
#include "file_system.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
|
#include <cstdio>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -15,6 +17,8 @@
|
||||||
|
|
||||||
namespace Log {
|
namespace Log {
|
||||||
|
|
||||||
|
static const char s_log_level_characters[LOGLEVEL_COUNT] = { 'X', 'E', 'W', 'P', 'S', 'I', 'D', 'R', 'B', 'T' };
|
||||||
|
|
||||||
struct RegisteredCallback
|
struct RegisteredCallback
|
||||||
{
|
{
|
||||||
CallbackFunctionType Function;
|
CallbackFunctionType Function;
|
||||||
|
@ -36,6 +40,12 @@ static bool s_debugOutputEnabled = false;
|
||||||
static String s_debugOutputChannelFilter;
|
static String s_debugOutputChannelFilter;
|
||||||
static LOGLEVEL s_debugOutputLevelFilter = LOGLEVEL_TRACE;
|
static LOGLEVEL s_debugOutputLevelFilter = LOGLEVEL_TRACE;
|
||||||
|
|
||||||
|
static bool s_fileOutputEnabled = false;
|
||||||
|
static bool s_fileOutputTimestamp = false;
|
||||||
|
static String s_fileOutputChannelFilter;
|
||||||
|
static LOGLEVEL s_fileOutputLevelFilter = LOGLEVEL_TRACE;
|
||||||
|
std::unique_ptr<std::FILE, void (*)(std::FILE*)> s_fileOutputHandle(nullptr, [](std::FILE* fp) { if (fp) { std::fclose(fp); } });
|
||||||
|
|
||||||
void RegisterCallback(CallbackFunctionType callbackFunction, void* pUserParam)
|
void RegisterCallback(CallbackFunctionType callbackFunction, void* pUserParam)
|
||||||
{
|
{
|
||||||
RegisteredCallback Callback;
|
RegisteredCallback Callback;
|
||||||
|
@ -69,28 +79,36 @@ static void ExecuteCallbacks(const char* channelName, const char* functionName,
|
||||||
|
|
||||||
static void FormatLogMessageForDisplay(const char* channelName, const char* functionName, LOGLEVEL level,
|
static void FormatLogMessageForDisplay(const char* channelName, const char* functionName, LOGLEVEL level,
|
||||||
const char* message, void (*printCallback)(const char*, void*),
|
const char* message, void (*printCallback)(const char*, void*),
|
||||||
void* pCallbackUserData)
|
void* pCallbackUserData, bool timestamp = true)
|
||||||
{
|
{
|
||||||
static const char levelCharacters[LOGLEVEL_COUNT] = {'X', 'E', 'W', 'P', 'S', 'I', 'D', 'R', 'B', 'T'};
|
if (timestamp)
|
||||||
|
{
|
||||||
|
// find time since start of process
|
||||||
|
float messageTime =
|
||||||
|
static_cast<float>(Common::Timer::ConvertValueToSeconds(Common::Timer::GetValue() - s_startTimeStamp));
|
||||||
|
|
||||||
// find time since start of process
|
// write prefix
|
||||||
float messageTime =
|
char prefix[256];
|
||||||
static_cast<float>(Common::Timer::ConvertValueToSeconds(Common::Timer::GetValue() - s_startTimeStamp));
|
if (level <= LOGLEVEL_PERF)
|
||||||
|
std::snprintf(prefix, countof(prefix), "[%10.4f] %c(%s): ", messageTime, s_log_level_characters[level],
|
||||||
|
functionName);
|
||||||
|
else
|
||||||
|
std::snprintf(prefix, countof(prefix), "[%10.4f] %c/%s: ", messageTime, s_log_level_characters[level],
|
||||||
|
channelName);
|
||||||
|
|
||||||
// write prefix
|
printCallback(prefix, pCallbackUserData);
|
||||||
#ifndef Y_BUILD_CONFIG_SHIPPING
|
}
|
||||||
char prefix[256];
|
|
||||||
if (level <= LOGLEVEL_PERF)
|
|
||||||
std::snprintf(prefix, countof(prefix), "[%10.4f] %c(%s): ", messageTime, levelCharacters[level], functionName);
|
|
||||||
else
|
else
|
||||||
std::snprintf(prefix, countof(prefix), "[%10.4f] %c/%s: ", messageTime, levelCharacters[level], channelName);
|
{
|
||||||
|
// write prefix
|
||||||
|
char prefix[256];
|
||||||
|
if (level <= LOGLEVEL_PERF)
|
||||||
|
std::snprintf(prefix, countof(prefix), "%c(%s): ", s_log_level_characters[level], functionName);
|
||||||
|
else
|
||||||
|
std::snprintf(prefix, countof(prefix), "%c/%s: ", s_log_level_characters[level], channelName);
|
||||||
|
|
||||||
printCallback(prefix, pCallbackUserData);
|
printCallback(prefix, pCallbackUserData);
|
||||||
#else
|
}
|
||||||
char prefix[256];
|
|
||||||
std::snprintf(prefix, countof(prefix), "[%10.4f] %c/%s: ", messageTime, levelCharacters[level], channelName);
|
|
||||||
printCallback(prefix, pCallbackUserData);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// write message
|
// write message
|
||||||
printCallback(message, pCallbackUserData);
|
printCallback(message, pCallbackUserData);
|
||||||
|
@ -152,8 +170,8 @@ static void DebugOutputLogCallback(void* pUserParam, const char* channelName, co
|
||||||
if (!s_debugOutputEnabled || level > s_debugOutputLevelFilter || s_debugOutputChannelFilter.Find(channelName) >= 0)
|
if (!s_debugOutputEnabled || level > s_debugOutputLevelFilter || s_debugOutputChannelFilter.Find(channelName) >= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FormatLogMessageForDisplay(channelName, functionName, level, message,
|
FormatLogMessageForDisplay(
|
||||||
[](const char* text, void*) { OutputDebugStringA(text); }, nullptr);
|
channelName, functionName, level, message, [](const char* text, void*) { OutputDebugStringA(text); }, nullptr);
|
||||||
|
|
||||||
OutputDebugStringA("\n");
|
OutputDebugStringA("\n");
|
||||||
}
|
}
|
||||||
|
@ -287,6 +305,73 @@ void SetDebugOutputParams(bool enabled, const char* channelFilter /* = nullptr *
|
||||||
s_debugOutputLevelFilter = levelFilter;
|
s_debugOutputLevelFilter = levelFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void FileOutputLogCallback(void* pUserParam, const char* channelName, const char* functionName, LOGLEVEL level,
|
||||||
|
const char* message)
|
||||||
|
{
|
||||||
|
if (level > s_fileOutputLevelFilter || s_fileOutputChannelFilter.Find(channelName) >= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (s_fileOutputTimestamp)
|
||||||
|
{
|
||||||
|
// find time since start of process
|
||||||
|
float messageTime =
|
||||||
|
static_cast<float>(Common::Timer::ConvertValueToSeconds(Common::Timer::GetValue() - s_startTimeStamp));
|
||||||
|
|
||||||
|
// write prefix
|
||||||
|
if (level <= LOGLEVEL_PERF)
|
||||||
|
{
|
||||||
|
std::fprintf(s_fileOutputHandle.get(), "[%10.4f] %c(%s): %s\n", messageTime, s_log_level_characters[level],
|
||||||
|
functionName, message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::fprintf(s_fileOutputHandle.get(), "[%10.4f] %c/%s: %s\n", messageTime, s_log_level_characters[level],
|
||||||
|
channelName, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (level <= LOGLEVEL_PERF)
|
||||||
|
{
|
||||||
|
std::fprintf(s_fileOutputHandle.get(), "%c(%s): %s\n", s_log_level_characters[level], functionName, message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::fprintf(s_fileOutputHandle.get(), "%c/%s: %s\n", s_log_level_characters[level], channelName, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetFileOutputParams(bool enabled, const char* filename, bool timestamps /* = true */,
|
||||||
|
const char* channelFilter /* = nullptr */, LOGLEVEL levelFilter /* = LOGLEVEL_TRACE */)
|
||||||
|
{
|
||||||
|
if (s_fileOutputEnabled != enabled)
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
s_fileOutputHandle.reset(FileSystem::OpenCFile(filename, "wb"));
|
||||||
|
if (!s_fileOutputHandle)
|
||||||
|
{
|
||||||
|
Log::Writef("Log", __FUNCTION__, LOGLEVEL_ERROR, "Failed to open log file '%s'", filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterCallback(FileOutputLogCallback, nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UnregisterCallback(FileOutputLogCallback, nullptr);
|
||||||
|
s_fileOutputHandle.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
s_fileOutputEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(s_callback_mutex);
|
||||||
|
s_fileOutputChannelFilter = (channelFilter != nullptr) ? channelFilter : "";;
|
||||||
|
s_fileOutputLevelFilter = levelFilter;
|
||||||
|
}
|
||||||
|
|
||||||
void SetFilterLevel(LOGLEVEL level)
|
void SetFilterLevel(LOGLEVEL level)
|
||||||
{
|
{
|
||||||
DebugAssert(level < LOGLEVEL_COUNT);
|
DebugAssert(level < LOGLEVEL_COUNT);
|
||||||
|
|
|
@ -35,6 +35,10 @@ void SetConsoleOutputParams(bool enabled, const char* channelFilter = nullptr, L
|
||||||
// adds a debug console output [win32/android only]
|
// adds a debug console output [win32/android only]
|
||||||
void SetDebugOutputParams(bool enabled, const char* channelFilter = nullptr, LOGLEVEL levelFilter = LOGLEVEL_TRACE);
|
void SetDebugOutputParams(bool enabled, const char* channelFilter = nullptr, LOGLEVEL levelFilter = LOGLEVEL_TRACE);
|
||||||
|
|
||||||
|
// adds a file output
|
||||||
|
void SetFileOutputParams(bool enabled, const char* filename, bool timestamps = true,
|
||||||
|
const char* channelFilter = nullptr, LOGLEVEL levelFilter = LOGLEVEL_TRACE);
|
||||||
|
|
||||||
// Sets global filtering level, messages below this level won't be sent to any of the logging sinks.
|
// Sets global filtering level, messages below this level won't be sent to any of the logging sinks.
|
||||||
void SetFilterLevel(LOGLEVEL level);
|
void SetFilterLevel(LOGLEVEL level);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue