Files
asarium 7029e4a10f Add compile time option to enable logging in release builds
This mostly meant for special docker standalone server builds where it
might be useful to have full release performance but also have the logs
available if necessary. This now always compiles the log statements but
enables or disables them with a constexpr so they should not appear in
actual release binaries.
2020-02-24 18:54:55 +01:00

319 lines
7.7 KiB
C++

/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <algorithm>
#ifdef WIN32
#include <direct.h>
#endif
#include "osapi/DebugWindow.h"
#include "osapi/osapi.h"
#include "osapi/outwnd.h"
#include "graphics/2d.h"
#include "freespaceresource.h"
#include "globalincs/systemvars.h"
#include "cfile/cfilesystem.h"
#include "parse/parselo.h"
struct outwnd_filter_struct {
char name[NAME_LENGTH];
bool enabled;
};
static SCP_vector<outwnd_filter_struct>& filter_vector()
{
static SCP_vector<outwnd_filter_struct> vec;
return vec;
}
// used for file logging
bool Log_debug_output_to_file = true;
void outwnd_print(const char* id = nullptr, const char* temp = nullptr);
// 0 = .cfg file found, 1 = not found and warning not printed yet, 2 = not found and warning printed
static ubyte Outwnd_no_filter_file = 0;
static ubyte outwnd_filter_loaded = 0;
static bool outwnd_inited = false;
static FILE* Log_fp = nullptr;
static bool Log_close_fp = false;
static const char* FreeSpace_logfilename = nullptr;
static std::unique_ptr<osapi::DebugWindow> debugWindow;
void load_filter_info()
{
FILE* fp;
char pathname[MAX_PATH_LEN];
char inbuf[NAME_LENGTH + 4];
outwnd_filter_struct new_filter;
outwnd_filter_loaded = 1;
memset(pathname, 0, sizeof(pathname));
snprintf(pathname, MAX_PATH_LEN, "%s/%s", Pathtypes[CF_TYPE_DATA].path, NOX("debug_filter.cfg"));
fp = fopen(os_get_config_path(pathname).c_str(), "rt");
if (!fp) {
Outwnd_no_filter_file = 1;
strcpy_s( new_filter.name, "error" );
new_filter.enabled = true;
filter_vector().push_back( new_filter );
strcpy_s( new_filter.name, "general" );
new_filter.enabled = true;
filter_vector().push_back( new_filter );
strcpy_s( new_filter.name, "warning" );
new_filter.enabled = true;
filter_vector().push_back( new_filter );
return;
}
Outwnd_no_filter_file = 0;
while ( fgets(inbuf, NAME_LENGTH+3, fp) ) {
if (*inbuf == '+')
new_filter.enabled = true;
else if (*inbuf == '-')
new_filter.enabled = false;
else
continue; // skip everything else
auto z = strlen(inbuf) - 1;
if (inbuf[z] == '\n')
inbuf[z] = 0;
Assert( strlen(inbuf+1) < NAME_LENGTH );
strcpy_s(new_filter.name, inbuf + 1);
if ( !stricmp(new_filter.name, "error") ) {
new_filter.enabled = true;
} else if ( !stricmp(new_filter.name, "general") ) {
new_filter.enabled = true;
} else if ( !stricmp(new_filter.name, "warning") ) {
new_filter.enabled = true;
}
filter_vector().push_back( new_filter );
}
if ( ferror(fp) && !feof(fp) )
nprintf(("Error", "Error reading \"%s\"\n", pathname));
fclose(fp);
}
void save_filter_info()
{
FILE* fp;
char pathname[MAX_PATH_LEN];
if (!outwnd_filter_loaded)
return;
if (Outwnd_no_filter_file)
return; // No file, don't save
memset(pathname, 0, sizeof(pathname));
snprintf(pathname, MAX_PATH_LEN, "%s/%s", Pathtypes[CF_TYPE_DATA].path, NOX("debug_filter.cfg"));
fp = fopen(os_get_config_path(pathname).c_str(), "wt");
if (fp) {
for (auto& i : filter_vector()) {
fprintf(fp, "%c%s\n", i.enabled ? '+' : '-', i.name);
}
fclose(fp);
}
}
void outwnd_printf2(const char *format, ...)
{
SCP_string temp;
va_list args;
if (format == nullptr)
return;
va_start(args, format);
vsprintf(temp, format, args);
va_end(args);
outwnd_print("General", temp.c_str());
}
void outwnd_printf(const char *id, const char *format, ...)
{
SCP_string temp;
va_list args;
if ((id == nullptr) || (format == nullptr))
return;
va_start(args, format);
vsprintf(temp, format, args);
va_end(args);
outwnd_print(id, temp.c_str());
}
void outwnd_print(const char *id, const char *tmp)
{
if (running_unittests) {
// Ignore all messages when running unit tests
return;
}
if ((id == nullptr) || (tmp == nullptr))
return;
if (!outwnd_inited)
return;
if (Outwnd_no_filter_file == 1) {
Outwnd_no_filter_file = 2;
outwnd_print("general", "==========================================================================\n");
outwnd_print("general", "DEBUG SPEW: No debug_filter.cfg found, so only general, error, and warning\n");
outwnd_print("general", "categories can be shown and no debug_filter.cfg info will be saved.\n");
outwnd_print("general", "==========================================================================\n");
}
auto filter = std::find_if(filter_vector().begin(), filter_vector().end(), [&id] (const outwnd_filter_struct& f) { return stricmp(f.name, id) == 0; });
// id found that isn't in the filter list yet
if ( filter == filter_vector().end() ) {
// Only create new filters if there was a filter file
if (Outwnd_no_filter_file)
return;
Assert( strlen(id)+1 < NAME_LENGTH );
outwnd_filter_struct new_filter;
strcpy_s(new_filter.name, id);
new_filter.enabled = stricmp(new_filter.name, "general") == 0 || stricmp(new_filter.name, "error") == 0 || stricmp(new_filter.name, "warning") == 0;
filter_vector().push_back( new_filter );
save_filter_info();
}
else if (!filter->enabled)
return;
if (Log_debug_output_to_file) {
if (Log_fp != nullptr) {
fputs(tmp, Log_fp);
fflush(Log_fp);
}
}
if (debugWindow) {
debugWindow->addDebugMessage(id, tmp);
}
}
extern const char* Osapi_legacy_mode_reason; // This was not exported in a header since it's not intended for general use
void outwnd_init()
{
if (outwnd_inited)
return;
if (!running_unittests && Log_fp == nullptr) {
SCP_string logpath;
if (!Cmdline_log_to_stdout) {
char pathname[MAX_PATH_LEN];
/* Set where the log file is going to go */
// Zacam: Set various conditions based on what type of log to generate.
if (Fred_running) {
FreeSpace_logfilename = "fred2_open.log";
} else if (Is_standalone) {
FreeSpace_logfilename = "fs2_standalone.log";
} else {
FreeSpace_logfilename = "fs2_open.log";
}
// create data file path if it does not exist
_mkdir(os_get_config_path(Pathtypes[CF_TYPE_DATA].path).c_str());
memset(pathname, 0, sizeof(pathname));
snprintf(pathname, MAX_PATH_LEN, "%s/%s", Pathtypes[CF_TYPE_DATA].path, FreeSpace_logfilename);
logpath = os_get_config_path(pathname);
Log_fp = fopen(logpath.c_str(), "wb");
Log_close_fp = true;
} else {
Log_fp = stdout;
Log_close_fp = false;
logpath = "<stdout>";
}
outwnd_inited = Log_fp != nullptr;
if (Log_fp == nullptr) {
fprintf(stderr, "Error opening %s\n", logpath.c_str());
} else {
time_t timedate = time(nullptr);
char datestr[50];
memset(datestr, 0, sizeof(datestr));
strftime(datestr, sizeof(datestr) - 1, "%a %b %d %H:%M:%S %Y", localtime(&timedate));
outwnd_printf("General", "Opened log '%s', %s ...\n", logpath.c_str(), datestr);
mprintf(("Legacy config mode is %s.\nReason: %s\n", os_is_legacy_mode() ? "ENABLED" : "DISABLED",
Osapi_legacy_mode_reason));
}
}
}
void outwnd_close()
{
if (!running_unittests && Log_fp != nullptr) {
time_t timedate = time(nullptr);
char datestr[50];
memset(datestr, 0, sizeof(datestr));
strftime(datestr, sizeof(datestr) - 1, "%a %b %d %H:%M:%S %Y", localtime(&timedate));
outwnd_printf("General", "... Log closed, %s\n", datestr);
if (Log_close_fp) {
fclose(Log_fp);
}
Log_fp = nullptr;
Log_close_fp = false;
}
outwnd_inited = false;
}
void outwnd_debug_window_init() {
debugWindow.reset(new osapi::DebugWindow());
}
void outwnd_debug_window_do_frame(float frametime) {
debugWindow->doFrame(frametime);
}
void outwnd_debug_window_deinit() {
debugWindow.reset();
}