lock(m_Mutex);
++m_NumberOfErrors;
if (m_UseDebugPrintf)
debug_printf("ERROR: %.16000s\n", message);
if (g_Console) g_Console->InsertMessage(std::string("ERROR: ") + message);
*m_InterestingLog << "ERROR: " << cmessage << "
\n";
m_InterestingLog->flush();
*m_MainLog << "ERROR: " << cmessage << "
\n";
m_MainLog->flush();
PushRenderMessage(Error, message);
}
void CLogger::WriteWarning(const char* message)
{
std::string cmessage = ToHTML(message);
std::lock_guard lock(m_Mutex);
++m_NumberOfWarnings;
if (m_UseDebugPrintf)
debug_printf("WARNING: %s\n", message);
if (g_Console) g_Console->InsertMessage(std::string("WARNING: ") + message);
*m_InterestingLog << "WARNING: " << cmessage << "
\n";
m_InterestingLog->flush();
*m_MainLog << "WARNING: " << cmessage << "
\n";
m_MainLog->flush();
PushRenderMessage(Warning, message);
}
void CLogger::Render()
{
PROFILE3_GPU("logger");
CleanupRenderQueue();
CStrIntern font_name("mono-stroke-10");
CFontMetrics font(font_name);
int lineSpacing = font.GetLineSpacing();
CTextRenderer textRenderer;
textRenderer.SetCurrentFont(font_name);
textRenderer.SetCurrentColor(CColor(1.0f, 1.0f, 1.0f, 1.0f));
// Offset by an extra 35px vertically to avoid the top bar.
textRenderer.Translate(4.0f, 35.0f + lineSpacing);
// (Lock must come after loading the CFont, since that might log error messages
// and attempt to lock the mutex recursively which is forbidden)
std::lock_guard lock(m_Mutex);
for (const RenderedMessage& msg : m_RenderMessages)
{
const char* type;
if (msg.method == Normal)
{
type = "info";
textRenderer.SetCurrentColor(CColor(0.0f, 0.8f, 0.0f, 1.0f));
}
else if (msg.method == Warning)
{
type = "warning";
textRenderer.SetCurrentColor(CColor(1.0f, 1.0f, 0.0f, 1.0f));
}
else
{
type = "error";
textRenderer.SetCurrentColor(CColor(1.0f, 0.0f, 0.0f, 1.0f));
}
- CMatrix3D savedTransform = textRenderer.GetTransform();
+ const CVector2D savedTranslate = textRenderer.GetTranslate();
textRenderer.PrintfAdvance(L"[%8.3f] %hs: ", msg.time, type);
// Display the actual message in white so it's more readable
textRenderer.SetCurrentColor(CColor(1.0f, 1.0f, 1.0f, 1.0f));
textRenderer.Put(0.0f, 0.0f, msg.message.c_str());
- textRenderer.SetTransform(savedTransform);
+ textRenderer.ResetTranslate(savedTranslate);
textRenderer.Translate(0.0f, (float)lineSpacing);
}
CCanvas2D canvas;
canvas.DrawText(textRenderer);
}
void CLogger::PushRenderMessage(ELogMethod method, const char* message)
{
double now = timer_Time();
// Add each message line separately
const char* pos = message;
const char* eol;
while ((eol = strchr(pos, '\n')) != NULL)
{
if (eol != pos)
{
RenderedMessage r = { method, now, std::string(pos, eol) };
m_RenderMessages.push_back(r);
}
pos = eol + 1;
}
// Add the last line, if we didn't end on a \n
if (*pos != '\0')
{
RenderedMessage r = { method, now, std::string(pos) };
m_RenderMessages.push_back(r);
}
}
void CLogger::CleanupRenderQueue()
{
std::lock_guard lock(m_Mutex);
if (m_RenderMessages.empty())
return;
double now = timer_Time();
// Initialise the timer on the first call (since we can't do it in the ctor)
if (m_RenderLastEraseTime == -1.0)
m_RenderLastEraseTime = now;
// Delete old messages, approximately at the given rate limit (and at most one per frame)
if (now - m_RenderLastEraseTime > 1.0/RENDER_TIMEOUT_RATE)
{
if (m_RenderMessages[0].time + RENDER_TIMEOUT < now)
{
m_RenderMessages.pop_front();
m_RenderLastEraseTime = now;
}
}
// If there's still too many then delete the oldest
if (m_RenderMessages.size() > RENDER_LIMIT)
m_RenderMessages.erase(m_RenderMessages.begin(), m_RenderMessages.end() - RENDER_LIMIT);
}
TestLogger::TestLogger()
{
m_OldLogger = g_Logger;
g_Logger = new CLogger(&m_Stream, &blackHoleStream, false, false);
}
TestLogger::~TestLogger()
{
delete g_Logger;
g_Logger = m_OldLogger;
}
std::string TestLogger::GetOutput()
{
return m_Stream.str();
}
TestStdoutLogger::TestStdoutLogger()
{
m_OldLogger = g_Logger;
g_Logger = new CLogger(&std::cout, &blackHoleStream, false, false);
}
TestStdoutLogger::~TestStdoutLogger()
{
delete g_Logger;
g_Logger = m_OldLogger;
}