Engine exited successfully on " << currentDate; *m_MainLog << " at " << currentTime << buffer << "
\n"; *m_InterestingLog << "Engine exited successfully on " << currentDate; *m_InterestingLog << " at " << currentTime << buffer << "
\n"; if (m_OwnsStreams) { SAFE_DELETE(m_InterestingLog); SAFE_DELETE(m_MainLog); } } static std::string ToHTML(const char* message) { std::string cmessage = message; boost::algorithm::replace_all(cmessage, "&", "&"); boost::algorithm::replace_all(cmessage, "<", "<"); return cmessage; } void CLogger::WriteMessage(const char* message, bool doRender = false) { std::string cmessage = ToHTML(message); CScopeLock lock(m_Mutex); ++m_NumberOfMessages; // if (m_UseDebugPrintf) // debug_printf("MESSAGE: %s\n", message); *m_MainLog << "" << cmessage << "
\n"; m_MainLog->flush(); if (doRender) { if (g_Console) g_Console->InsertMessage(std::string("INFO: ") + message); PushRenderMessage(Normal, message); } } void CLogger::WriteError(const char* message) { std::string cmessage = ToHTML(message); CScopeLock lock(m_Mutex); ++m_NumberOfErrors; if (m_UseDebugPrintf) debug_printf("ERROR: %s\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); CScopeLock 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(); CShaderTechniquePtr textTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_text); textTech->BeginPass(); CTextRenderer textRenderer(textTech->GetShader()); textRenderer.Font(font_name); textRenderer.Color(1.0f, 1.0f, 1.0f); // Offset by an extra 35px vertically to avoid the top bar. textRenderer.Translate(4.0f, 35.0f + lineSpacing, 0.0f); // (Lock must come after loading the CFont, since that might log error messages // and attempt to lock the mutex recursively which is forbidden) CScopeLock lock(m_Mutex); for (const RenderedMessage& msg : m_RenderMessages) { const char* type; if (msg.method == Normal) { type = "info"; textRenderer.Color(0.0f, 0.8f, 0.0f); } else if (msg.method == Warning) { type = "warning"; textRenderer.Color(1.0f, 1.0f, 0.0f); } else { type = "error"; textRenderer.Color(1.0f, 0.0f, 0.0f); } CMatrix3D savedTransform = textRenderer.GetTransform(); textRenderer.PrintfAdvance(L"[%8.3f] %hs: ", msg.time, type); // Display the actual message in white so it's more readable textRenderer.Color(1.0f, 1.0f, 1.0f); textRenderer.Put(0.0f, 0.0f, msg.message.c_str()); textRenderer.SetTransform(savedTransform); textRenderer.Translate(0.0f, (float)lineSpacing, 0.0f); } textRenderer.Render(); textTech->EndPass(); } 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() { CScopeLock 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; }