log file for game
William Labbett

Need some advice on how to deal with the log file in my game.

The way I do things at the moment is :

#ifdef USE_LOG_FILE
   logfile << "some message about something.\n";
#endif

So the code only runs if there's a define for USE_LOG_FILE in a header somewhere.

What I'm thinking of doing instead is :-

if(use_log)
{
   logfile << "........\n";
}

Where the use_log is a variable set in the config file for the game.

This way means that the code is always run, even if it's just checking the if condition.

Is this way an acceptable way to do things ?

I prefer it because it means the log file doesn't have to get created.

LennyLen

Is this way an acceptable way to do things ?

Certainly. The checks aren't going to affect the game performance, and it means the logs can easily be enabled after the application has already been deployed, which is not possible when using the preprocessor method.

The only downside is that if the logs are never needed, the executable will be bigger than necessary. But the additional size is most likely insignificant, so this is a mostly moot point.

Quote:

I prefer it because it means the log file doesn't have to get created.

That applies to both methods.

William Labbett

Thanks.

Slartibartfast

#ifdef USE_LOG_FILE
logfile << "some message about something.\n";
#endif

This is usually done like:

#ifdef USE_LOG_FILE
#define LOG(blah) logfile<<(blah)
#else
#define LOG(blah)
#endif
...
LOG(".....\n");

Quote:

if(use_log)
{
logfile << "........\n";
}

Also nice. You could similarly stick that into an inlined function and have an easier time typing it; You can even use varargs to make it behave more like you'd expect a logging function to behave.

Though maybe I'm saying obvious things.

beoran

And an example in plain C:

#SelectExpand
1/* 2Log file example with allegro config file use. 3Compile with gcc on Linux: 4 5gcc forum_2.c -I /usr/local/include -L /usr/local/lib -lallegro_color -lallegro_font -lallegro_ttf -lallegro -o forum2 6 7Make an app.ini file in the executable file's directory that contains 8log=app.log and the log will be written to app.log. If the config value is 9empty, no logging takes place. 10*/ 11 12#include <allegro5/allegro.h> 13#include <stdio.h> 14#include <stdarg.h> 15 16 17#define APP_CONFIG_NAME "app.ini" 18 19FILE * log_open() { 20 FILE * result; 21 ALLEGRO_CONFIG * config; 22 const char * logname; 23 config = al_load_config_file(APP_CONFIG_NAME); 24 if (!config) return NULL; 25 logname = al_get_config_value(config, NULL, "log"); 26 if (logname) { 27 result = fopen(logname, "at"); 28 } 29 al_destroy_config(config); 30 return result; 31} 32 33void log_close(FILE * logfile) { 34 if(logfile) fclose(logfile); 35} 36 37void log_log(FILE * logfile, char * format, ...) { 38 va_list ap; 39 if (!logfile) return; 40 va_start(ap, format); 41 vfprintf(logfile, format, ap); 42 va_end(ap); 43 fflush(logfile); 44} 45 46 47int main (void) { 48 FILE * logfile = log_open(); 49 log_log(logfile, "This will be logged, but only if app.ini specifies it.\n"); log_log(logfile, "And with arguments: %d, %f\n", 11, 20.13); 50 log_close(logfile); 51 return 0; 52}

LennyLen
beoran said:

And an example in plain C:

That looks almost identical to mine, except that I add a timestamp to each line as it is written to the log.

beoran

Yeah, LennyLen, and with some changes and a macro or two you can get _LINE_ and _FILE_ in there as well. Just flex it to your own needs. :)

Luiji99

Also very useful, but not vital since you can gleam it from __FILE__ and __LINE__, is __func__. However, __func__ is C99 and MSVC calls it __FUNCTION__. Luckily, Allegro 5 is awesome and will automatically define __func__ as __FUNCTION__ for MSVC and will simply define it to "???" where not supported. I'd recommend log lines like:

__FILE__:__LINE__:__func__: Super important message

I.e.

kernel.c:1924:farbiktubennagen: Holy shit look a dog!!!!!

GullRaDriel

I like these threads. Pinned !

Thomas Fjellstrom
Luiji99 said:

Also very useful, but not vital since you can gleam it from _FILE_ and _LINE_, is _func_. However, _func_ is C99 and MSVC calls it __FUNCTION__.

GCC also has __FUNCTION__ it also has __PRETTY_FUNCTION__ which helps out with C++ somewhat.

William Labbett

Thanks for the help guys.

I like

if(use_log)
{
   logfile << "Initialising tertiary subsystem 3.\n";
}

It's super simple.

If I have to use conditional includes then there's two versions of the program, one which can do logs and another that can't. I'd rather have one program that can do both depending on config file.

I think I'll do

LOG(x) (if(use_log) logfile << x << "\n";)

Luiji99

That type of #define is good, too. In fact, I'd definitely recommend that over two executables considering you want it to be enabled/disabled by the user. You could still use #define LOG(x) (use_log ? logfile << __FILE__ << ":" __LINE__ ":" << __PRETTY_FUNCTION__ << ": " << (x) : (void) 0)

I'm pretty sure that putting an if statement in parenthesis is invalid. You will have to use the tertiary operator like boolean_value ? true_execution : (void) 0, as most implementations of assert do.

Audric

The general issue of "; in macro?" is solved by a construct, seemingly ugly but fully working:
#define MYMACRO do { your stuff } while (0)

Luiji99

I'd imagine that the assert method would be more efficient, only because I haven't yet found an implementation of assert that uses the do {} while(0) kludge.

bamccaig
#SelectExpand
1class Logger 2{ 3 bool enabled_; 4 std::ofstream log_; 5public: 6 Logger(): 7 enabled_(false) 8 { 9 } 10 11 Logger(const std::string & file, bool enabled = true): 12 enabled_(enabled) 13 log_(file, ios::base::app) 14 { 15 } 16 17 // ... etc ... 18 19 const Logger & operator<<(const std::string & arg) const 20 { 21 if(this->enabled_) 22 { 23 this->log_ /* etc ... */ << arg << std::endl; 24 } 25 26 return *this; 27 } 28};

(Untested, but you get the idea...)

Logger null;
Logger disabled("my.log", false);
Logger log("my.log");

null << "This isn't logged anywhere.";
disabled << "This either.";
log << "This is.";

Edgar Reynaldo

I second bamccaig, although I abstract my log even further to point to any ostream and then OutputLog() retrieves a reference.

   OutputLog() << "Write this to the log ...." << endl;

It doesn't allow for disabling the log but I usually only use the log for diagnostic purposes.

Thread #611548. Printed from Allegro.cc