Support Unicode reliably in logger, via explicit conversion to UTF-8.
Expand date buffer to avoid Y10K bug, Y100K bug, etc. This was SVN commit r7694.
This commit is contained in:
parent
018f3d71d9
commit
9b9e4fa1b4
@ -31,6 +31,8 @@
|
||||
#include <ctime>
|
||||
#include <ostream>
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
static const double RENDER_TIMEOUT = 10.0; // seconds before messages are deleted
|
||||
static const double RENDER_TIMEOUT_RATE = 10.0; // number of timed-out messages deleted per second
|
||||
static const size_t RENDER_LIMIT = 20; // maximum messages on screen at once
|
||||
@ -40,36 +42,35 @@ extern int g_xres, g_yres;
|
||||
// Set up a default logger that throws everything away, because that's
|
||||
// better than crashing. (This is particularly useful for unit tests which
|
||||
// don't care about any log output.)
|
||||
struct BlackHoleStreamBuf : public std::wstreambuf
|
||||
struct BlackHoleStreamBuf : public std::streambuf
|
||||
{
|
||||
} blackHoleStreamBuf;
|
||||
std::wostream blackHoleStream(&blackHoleStreamBuf);
|
||||
std::ostream blackHoleStream(&blackHoleStreamBuf);
|
||||
CLogger nullLogger(&blackHoleStream, &blackHoleStream, false, true);
|
||||
|
||||
CLogger* g_Logger = &nullLogger;
|
||||
|
||||
const wchar_t* html_header0 =
|
||||
L"<!DOCTYPE html>\n"
|
||||
L"<title>Pyrogenesis Log</title>\n"
|
||||
L"<style>\n"
|
||||
L"body { background: #eee; color: black; font-family: sans-serif; }\n"
|
||||
L"p { background: white; margin: 3px 0 3px 0; }\n"
|
||||
L".error { color: red; }\n"
|
||||
L".warning { color: blue; }\n"
|
||||
L"</style>\n"
|
||||
L"<h2>0 A.D. ";
|
||||
const char* html_header0 =
|
||||
"<!DOCTYPE html>\n"
|
||||
"<meta charset=\"utf-8\">\n"
|
||||
"<title>Pyrogenesis Log</title>\n"
|
||||
"<style>"
|
||||
"body { background: #eee; color: black; font-family: sans-serif; } "
|
||||
"p { background: white; margin: 3px 0 3px 0; } "
|
||||
".error { color: red; } "
|
||||
".warning { color: blue; }"
|
||||
"</style>\n"
|
||||
"<h2>0 A.D. ";
|
||||
|
||||
const wchar_t* html_header1 = L"</h2>\n";
|
||||
|
||||
const wchar_t* html_footer = L"";
|
||||
const char* html_header1 = "</h2>\n";
|
||||
|
||||
CLogger::CLogger()
|
||||
{
|
||||
fs::wpath mainlogPath(psLogDir()/L"mainlog.html");
|
||||
m_MainLog = new std::wofstream(utf8_from_wstring(mainlogPath.string()).c_str(), std::ofstream::out | std::ofstream::trunc);
|
||||
m_MainLog = new std::ofstream(utf8_from_wstring(mainlogPath.string()).c_str(), std::ofstream::out | std::ofstream::trunc);
|
||||
|
||||
fs::wpath interestinglogPath(psLogDir()/L"interestinglog.html");
|
||||
m_InterestingLog = new std::wofstream(utf8_from_wstring(interestinglogPath.string()).c_str(), std::ofstream::out | std::ofstream::trunc);
|
||||
m_InterestingLog = new std::ofstream(utf8_from_wstring(interestinglogPath.string()).c_str(), std::ofstream::out | std::ofstream::trunc);
|
||||
|
||||
m_OwnsStreams = true;
|
||||
m_UseDebugPrintf = true;
|
||||
@ -77,7 +78,7 @@ CLogger::CLogger()
|
||||
Init();
|
||||
}
|
||||
|
||||
CLogger::CLogger(std::wostream* mainLog, std::wostream* interestingLog, bool takeOwnership, bool useDebugPrintf)
|
||||
CLogger::CLogger(std::ostream* mainLog, std::ostream* interestingLog, bool takeOwnership, bool useDebugPrintf)
|
||||
{
|
||||
m_MainLog = mainLog;
|
||||
m_InterestingLog = interestingLog;
|
||||
@ -98,33 +99,31 @@ void CLogger::Init()
|
||||
m_NumberOfWarnings = 0;
|
||||
|
||||
//Write Headers for the HTML documents
|
||||
*m_MainLog << html_header0 << L"Main log" << html_header1;
|
||||
|
||||
*m_MainLog << html_header0 << "Main log" << html_header1;
|
||||
|
||||
//Write Headers for the HTML documents
|
||||
*m_InterestingLog << html_header0 << L"Main log (warnings and errors only)" << html_header1;
|
||||
*m_InterestingLog << html_header0 << "Main log (warnings and errors only)" << html_header1;
|
||||
}
|
||||
|
||||
CLogger::~CLogger ()
|
||||
{
|
||||
wchar_t buffer[128];
|
||||
swprintf_s(buffer, ARRAY_SIZE(buffer), L" with %d message(s), %d error(s) and %d warning(s).", m_NumberOfMessages,m_NumberOfErrors,m_NumberOfWarnings);
|
||||
char buffer[128];
|
||||
sprintf_s(buffer, ARRAY_SIZE(buffer), " with %d message(s), %d error(s) and %d warning(s).", m_NumberOfMessages,m_NumberOfErrors,m_NumberOfWarnings);
|
||||
|
||||
time_t t = time(NULL);
|
||||
struct tm* now = localtime(&t);
|
||||
wchar_t currentDate[11];
|
||||
swprintf_s(currentDate, ARRAY_SIZE(currentDate), L"%02d %02d %04d", now->tm_mon, now->tm_mday, (1900+now->tm_year));
|
||||
wchar_t currentTime[10];
|
||||
swprintf_s(currentTime, ARRAY_SIZE(currentTime), L"%02d:%02d:%02d", now->tm_hour, now->tm_min, now->tm_sec);
|
||||
char currentDate[17];
|
||||
sprintf_s(currentDate, ARRAY_SIZE(currentDate), "%02d %02d %04d", now->tm_mon, now->tm_mday, (1900+now->tm_year));
|
||||
char currentTime[10];
|
||||
sprintf_s(currentTime, ARRAY_SIZE(currentTime), "%02d:%02d:%02d", now->tm_hour, now->tm_min, now->tm_sec);
|
||||
|
||||
//Write closing text
|
||||
|
||||
*m_MainLog << L"<p>Engine exited successfully on " << currentDate;
|
||||
*m_MainLog << L" at " << currentTime << buffer << L"</p>\n";
|
||||
*m_MainLog << html_footer;
|
||||
*m_MainLog << "<p>Engine exited successfully on " << currentDate;
|
||||
*m_MainLog << " at " << currentTime << buffer << "</p>\n";
|
||||
|
||||
*m_InterestingLog << L"<p>Engine exited successfully on " << currentDate;
|
||||
*m_InterestingLog << L" at " << currentTime << buffer << L"</p>\n";
|
||||
*m_InterestingLog << html_footer;
|
||||
*m_InterestingLog << "<p>Engine exited successfully on " << currentDate;
|
||||
*m_InterestingLog << " at " << currentTime << buffer << "</p>\n";
|
||||
|
||||
if (m_OwnsStreams)
|
||||
{
|
||||
@ -133,13 +132,24 @@ CLogger::~CLogger ()
|
||||
}
|
||||
}
|
||||
|
||||
static std::string ToHTML(const wchar_t* message)
|
||||
{
|
||||
LibError err;
|
||||
std::string cmessage = utf8_from_wstring(message, &err);
|
||||
boost::algorithm::replace_all(cmessage, "&", "&");
|
||||
boost::algorithm::replace_all(cmessage, "<", "<");
|
||||
return cmessage;
|
||||
}
|
||||
|
||||
void CLogger::WriteMessage(const wchar_t* message)
|
||||
{
|
||||
std::string cmessage = ToHTML(message);
|
||||
|
||||
++m_NumberOfMessages;
|
||||
// if (m_UseDebugPrintf)
|
||||
// debug_printf(L"MESSAGE: %ls\n", message);
|
||||
|
||||
*m_MainLog << L"<p>" << message << L"</p>\n";
|
||||
*m_MainLog << "<p>" << cmessage << "</p>\n";
|
||||
m_MainLog->flush();
|
||||
|
||||
// Don't do this since it results in too much noise:
|
||||
@ -148,15 +158,17 @@ void CLogger::WriteMessage(const wchar_t* message)
|
||||
|
||||
void CLogger::WriteError(const wchar_t* message)
|
||||
{
|
||||
std::string cmessage = ToHTML(message);
|
||||
|
||||
++m_NumberOfErrors;
|
||||
if (m_UseDebugPrintf)
|
||||
debug_printf(L"ERROR: %ls\n", message);
|
||||
|
||||
if (g_Console) g_Console->InsertMessage(L"ERROR: %ls", message);
|
||||
*m_InterestingLog << L"<p class=\"error\">ERROR: "<< message << L"</p>\n";
|
||||
*m_InterestingLog << "<p class=\"error\">ERROR: " << cmessage << "</p>\n";
|
||||
m_InterestingLog->flush();
|
||||
|
||||
*m_MainLog << L"<p class=\"error\">ERROR: "<< message << L"</p>\n";
|
||||
*m_MainLog << "<p class=\"error\">ERROR: " << cmessage << "</p>\n";
|
||||
m_MainLog->flush();
|
||||
|
||||
PushRenderMessage(Error, message);
|
||||
@ -164,15 +176,17 @@ void CLogger::WriteError(const wchar_t* message)
|
||||
|
||||
void CLogger::WriteWarning(const wchar_t* message)
|
||||
{
|
||||
std::string cmessage = ToHTML(message);
|
||||
|
||||
++m_NumberOfWarnings;
|
||||
if (m_UseDebugPrintf)
|
||||
debug_printf(L"WARNING: %ls\n", message);
|
||||
|
||||
if (g_Console) g_Console->InsertMessage(L"WARNING: %ls", message);
|
||||
*m_InterestingLog << L"<p class=\"warning\">WARNING: "<< message << L"</p>\n";
|
||||
*m_InterestingLog << "<p class=\"warning\">WARNING: " << cmessage << "</p>\n";
|
||||
m_InterestingLog->flush();
|
||||
|
||||
*m_MainLog << L"<p class=\"warning\">WARNING: "<< message << L"</p>\n";
|
||||
*m_MainLog << "<p class=\"warning\">WARNING: " << cmessage << "</p>\n";
|
||||
m_MainLog->flush();
|
||||
|
||||
PushRenderMessage(Warning, message);
|
||||
@ -396,13 +410,14 @@ TestLogger::~TestLogger()
|
||||
|
||||
std::wstring TestLogger::GetOutput()
|
||||
{
|
||||
return m_Stream.str();
|
||||
LibError err;
|
||||
return wstring_from_utf8(m_Stream.str(), &err);
|
||||
}
|
||||
|
||||
TestStdoutLogger::TestStdoutLogger()
|
||||
{
|
||||
m_OldLogger = g_Logger;
|
||||
g_Logger = new CLogger(&std::wcout, &blackHoleStream, false, false);
|
||||
g_Logger = new CLogger(&std::cout, &blackHoleStream, false, false);
|
||||
}
|
||||
|
||||
TestStdoutLogger::~TestStdoutLogger()
|
||||
|
@ -52,7 +52,7 @@ public:
|
||||
|
||||
// Special constructor (mostly for testing) - outputs to provided streams.
|
||||
// Can take ownership of streams and delete them in the destructor.
|
||||
CLogger(std::wostream* mainLog, std::wostream* interestingLog, bool takeOwnership, bool useDebugPrintf);
|
||||
CLogger(std::ostream* mainLog, std::ostream* interestingLog, bool takeOwnership, bool useDebugPrintf);
|
||||
|
||||
~CLogger();
|
||||
|
||||
@ -89,8 +89,8 @@ private:
|
||||
void CleanupRenderQueue();
|
||||
|
||||
// the output streams
|
||||
std::wostream* m_MainLog;
|
||||
std::wostream* m_InterestingLog;
|
||||
std::ostream* m_MainLog;
|
||||
std::ostream* m_InterestingLog;
|
||||
bool m_OwnsStreams;
|
||||
|
||||
// whether errors should be reported via debug_printf (default)
|
||||
@ -129,7 +129,7 @@ public:
|
||||
std::wstring GetOutput();
|
||||
private:
|
||||
CLogger* m_OldLogger;
|
||||
std::wstringstream m_Stream;
|
||||
std::stringstream m_Stream;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -30,46 +30,46 @@ public:
|
||||
ParseOutput();
|
||||
|
||||
TS_ASSERT_EQUALS((int)lines.size(), 2);
|
||||
TS_ASSERT_EQUALS(lines[0], L"Test 1");
|
||||
TS_ASSERT_EQUALS(lines[1], L"Test 2");
|
||||
TS_ASSERT_EQUALS(lines[0], "Test 1");
|
||||
TS_ASSERT_EQUALS(lines[1], "Test 2");
|
||||
}
|
||||
|
||||
void test_overflow()
|
||||
{
|
||||
const int buflen = 512;
|
||||
|
||||
std::wstring msg0 (buflen-2, '*');
|
||||
std::wstring msg1 (buflen-1, '*');
|
||||
std::wstring msg2 (buflen, '*');
|
||||
std::wstring msg3 (buflen+1, '*');
|
||||
std::string msg0 (buflen-2, '*');
|
||||
std::string msg1 (buflen-1, '*');
|
||||
std::string msg2 (buflen, '*');
|
||||
std::string msg3 (buflen+1, '*');
|
||||
|
||||
std::wstring clipped (buflen-4, '*');
|
||||
clipped += L"...";
|
||||
std::string clipped (buflen-4, '*');
|
||||
clipped += "...";
|
||||
|
||||
logger->Log(CLogger::Normal, L"", L"%ls", msg0.c_str());
|
||||
logger->Log(CLogger::Normal, L"", L"%ls", msg1.c_str());
|
||||
logger->Log(CLogger::Normal, L"", L"%ls", msg2.c_str());
|
||||
logger->Log(CLogger::Normal, L"", L"%ls", msg3.c_str());
|
||||
logger->Log(CLogger::Normal, L"", L"%hs", msg0.c_str());
|
||||
logger->Log(CLogger::Normal, L"", L"%hs", msg1.c_str());
|
||||
logger->Log(CLogger::Normal, L"", L"%hs", msg2.c_str());
|
||||
logger->Log(CLogger::Normal, L"", L"%hs", msg3.c_str());
|
||||
|
||||
logger->LogOnce(CLogger::Normal, L"", L"%ls", msg0.c_str());
|
||||
logger->LogOnce(CLogger::Normal, L"", L"%ls", msg1.c_str());
|
||||
logger->LogOnce(CLogger::Normal, L"", L"%ls", msg2.c_str());
|
||||
logger->LogOnce(CLogger::Normal, L"", L"%ls", msg3.c_str());
|
||||
logger->LogOnce(CLogger::Normal, L"", L"%hs", msg0.c_str());
|
||||
logger->LogOnce(CLogger::Normal, L"", L"%hs", msg1.c_str());
|
||||
logger->LogOnce(CLogger::Normal, L"", L"%hs", msg2.c_str());
|
||||
logger->LogOnce(CLogger::Normal, L"", L"%hs", msg3.c_str());
|
||||
|
||||
logger->LogMessage(L"%ls", msg0.c_str());
|
||||
logger->LogMessage(L"%ls", msg1.c_str());
|
||||
logger->LogMessage(L"%ls", msg2.c_str());
|
||||
logger->LogMessage(L"%ls", msg3.c_str());
|
||||
logger->LogMessage(L"%hs", msg0.c_str());
|
||||
logger->LogMessage(L"%hs", msg1.c_str());
|
||||
logger->LogMessage(L"%hs", msg2.c_str());
|
||||
logger->LogMessage(L"%hs", msg3.c_str());
|
||||
|
||||
logger->LogWarning(L"%ls", msg0.c_str());
|
||||
logger->LogWarning(L"%ls", msg1.c_str());
|
||||
logger->LogWarning(L"%ls", msg2.c_str());
|
||||
logger->LogWarning(L"%ls", msg3.c_str());
|
||||
logger->LogWarning(L"%hs", msg0.c_str());
|
||||
logger->LogWarning(L"%hs", msg1.c_str());
|
||||
logger->LogWarning(L"%hs", msg2.c_str());
|
||||
logger->LogWarning(L"%hs", msg3.c_str());
|
||||
|
||||
logger->LogError(L"%ls", msg0.c_str());
|
||||
logger->LogError(L"%ls", msg1.c_str());
|
||||
logger->LogError(L"%ls", msg2.c_str());
|
||||
logger->LogError(L"%ls", msg3.c_str());
|
||||
logger->LogError(L"%hs", msg0.c_str());
|
||||
logger->LogError(L"%hs", msg1.c_str());
|
||||
logger->LogError(L"%hs", msg2.c_str());
|
||||
logger->LogError(L"%hs", msg3.c_str());
|
||||
|
||||
ParseOutput();
|
||||
|
||||
@ -88,28 +88,48 @@ public:
|
||||
TS_ASSERT_EQUALS(lines[9], clipped);
|
||||
TS_ASSERT_EQUALS(lines[10], clipped);
|
||||
|
||||
TS_ASSERT_EQUALS(lines[11], L"WARNING: "+msg0);
|
||||
TS_ASSERT_EQUALS(lines[12], L"WARNING: "+msg1);
|
||||
TS_ASSERT_EQUALS(lines[13], L"WARNING: "+clipped);
|
||||
TS_ASSERT_EQUALS(lines[14], L"WARNING: "+clipped);
|
||||
TS_ASSERT_EQUALS(lines[11], "WARNING: "+msg0);
|
||||
TS_ASSERT_EQUALS(lines[12], "WARNING: "+msg1);
|
||||
TS_ASSERT_EQUALS(lines[13], "WARNING: "+clipped);
|
||||
TS_ASSERT_EQUALS(lines[14], "WARNING: "+clipped);
|
||||
|
||||
TS_ASSERT_EQUALS(lines[15], L"ERROR: "+msg0);
|
||||
TS_ASSERT_EQUALS(lines[16], L"ERROR: "+msg1);
|
||||
TS_ASSERT_EQUALS(lines[17], L"ERROR: "+clipped);
|
||||
TS_ASSERT_EQUALS(lines[18], L"ERROR: "+clipped);
|
||||
TS_ASSERT_EQUALS(lines[15], "ERROR: "+msg0);
|
||||
TS_ASSERT_EQUALS(lines[16], "ERROR: "+msg1);
|
||||
TS_ASSERT_EQUALS(lines[17], "ERROR: "+clipped);
|
||||
TS_ASSERT_EQUALS(lines[18], "ERROR: "+clipped);
|
||||
}
|
||||
|
||||
void test_unicode()
|
||||
{
|
||||
logger->LogMessage(L"%lc %lc", 226, 295);
|
||||
|
||||
ParseOutput();
|
||||
|
||||
TS_ASSERT_EQUALS((int)lines.size(), 1);
|
||||
TS_ASSERT_EQUALS(lines[0], "\xC3\xA2 \xC4\xA7");
|
||||
}
|
||||
|
||||
void test_html()
|
||||
{
|
||||
logger->LogMessage(L"Test<a&b>c<d&e>");
|
||||
|
||||
ParseOutput();
|
||||
|
||||
TS_ASSERT_EQUALS((int)lines.size(), 1);
|
||||
TS_ASSERT_EQUALS(lines[0], "Test<a&b>c<d&e>");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CLogger* logger;
|
||||
std::wstringstream* mainlog;
|
||||
std::wstringstream* interestinglog;
|
||||
std::vector<std::wstring> lines;
|
||||
std::stringstream* mainlog;
|
||||
std::stringstream* interestinglog;
|
||||
std::vector<std::string> lines;
|
||||
|
||||
void setUp()
|
||||
{
|
||||
mainlog = new std::wstringstream();
|
||||
interestinglog = new std::wstringstream();
|
||||
mainlog = new std::stringstream();
|
||||
interestinglog = new std::stringstream();
|
||||
|
||||
logger = new CLogger(mainlog, interestinglog, true, false);
|
||||
|
||||
@ -124,9 +144,9 @@ public:
|
||||
|
||||
void ParseOutput()
|
||||
{
|
||||
const std::wstring header_end = L"</h2>\n";
|
||||
const std::string header_end = "</h2>\n";
|
||||
|
||||
std::wstring s = mainlog->str();
|
||||
std::string s = mainlog->str();
|
||||
size_t start = s.find(header_end);
|
||||
TS_ASSERT_DIFFERS(start, s.npos);
|
||||
s = s.substr(start + header_end.length());
|
||||
|
Loading…
Reference in New Issue
Block a user