使用软件,日志可以提供比较多的运行信息,研究日志也是一种学习。
Godot有两种运行模式:带控制台与不带控制台。带控制台的情况下,运行时会打开一个DOS窗口,具体的日志信息就显示在DOS窗口中。但不带控制台,就我目前的了解,这些日志就不知道在哪里显示了。
基于单步调试,发现各种日志输出将调用CompositeLogger的各个loggers的logv、log_error函数,而在windows下运行,会有WindowsTerminalLogger实例对象,最终会调用WindowsTerminalLogger::logv/log_error函数。为了看看是否有效果,我在WindowsTerminalLogger::logv中输出的地方,加上DrGraph前缀输出。
void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_err) {
if (!should_log(p_err)) {
return;
}
const unsigned int BUFFER_SIZE = 16384;
char buf[BUFFER_SIZE + 1]; // +1 for the terminating character
int len = vsnprintf(buf, BUFFER_SIZE, p_format, p_list);
if (len <= 0) {
return;
}
if ((unsigned int)len >= BUFFER_SIZE) {
len = BUFFER_SIZE; // Output is too big, will be truncated
}
buf[len] = 0;
int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, nullptr, 0);
if (wlen < 0) {
return;
}
wchar_t *wbuf = (wchar_t *)memalloc((len + 1) * sizeof(wchar_t));
ERR_FAIL_NULL_MSG(wbuf, "Out of memory.");
MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen);
wbuf[wlen] = 0;
if (p_err) {
fwprintf(stderr, L"DrGraph - %ls", wbuf);
} else {
wprintf(L"DrGraph - %ls", wbuf);
}
memfree(wbuf);
#ifdef DEBUG_ENABLED
fflush(stdout);
#endif
}
好象log_error最终也会调用logv,所以其余地方就不用改了。 编译后,带控制台方式运行起来,果然改到了地方:
对于非控制台方式启动运行,日志输出函数应该就没有输出显示的地方了。
if (p_err) {
fwprintf(stderr, L"DrGraph - %ls", wbuf);
} else {
wprintf(L"DrGraph - %ls", wbuf);
}
可以加上一个日志输出文件,以记录这些信息。简单地通过ofstream来实现一些功能函数:
std::string logFileName = "";
std::string getLogFileName() {
if (logFileName.length() == 0) {
TCHAR szFileName[256];
memset(szFileName, 0, sizeof(szFileName));
GetModuleFileName(NULL, szFileName, sizeof(szFileName));
logFileName = szFileName;
int nPos = logFileName.rfind("\\");
if (nPos != -1)
logFileName.erase(nPos, logFileName.size() - nPos);
logFileName += "\\DrGraph_debug.log";
}
return logFileName;
}
std::ofstream * getLogStream() {
std::ios_base::openmode mode = logFileName.length() > 0 ? std::ios_base::app : std::ios_base::trunc;
std::string fileName = getLogFileName();
std::ofstream * os = new std::ofstream(fileName, mode);
if (!os->is_open())
fwprintf(stderr, L"DrGraph - Can't open log file %hs to write", fileName.c_str()); // %Ts
return os;
}
std::string dateTimePrefix() {
struct tm *ptr;
time_t lt;
char str[80];
lt = time(NULL);
ptr = localtime(<);
strftime(str, 100, "%F %T", ptr);
auto now = std::chrono::system_clock::now();
std::chrono::milliseconds ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000;
std::ostringstream oss;
oss.fill('0');
oss << str << ":" << std::setw(3) << ms.count() << " > Log by DrGraph > ";
return oss.str();
}
在logv调用后,增加自定义日志输出
std::ofstream *os = getLogStream();
(*os) << dateTimePrefix() << buf;
delete os;
这样,在运行后,会自动生成日志文件DrGraph_debug.log
再加一个宏定义
#define DEBUG_SOURCE_POS print_line("[SourceCode Position Info] > Function", __FUNCTION__, "@File", __FILE__, "::Line", __LINE__)
在待输出日志的地方加上DEBUG_SOURCE_POS,就可以看到简单源码信息
感觉好多了。