12/18/09

I'm a lumberjack and I'm ok!

I had another busy night of game development last night. The new feature of the night was to add a logger to my engine.  I had 3 requirements for my logger:
  1. I need to be able to create multiple instances of the logger so I can create log files for each area of my engine.
  2. I need to be able to differentiate between normal debug messages and error messages when I'm reviewing the log.
  3. I need to be able to disable logging across the system at compile time.
With these requirements in mind, I created a pretty simple logging class that is easy to use and gets the job done.
class Logger
{
    //Fields
    FILE* file;  //File the log is saved to

    //Methods
    void CloseFile(); //Closes the logging file
public:
    //Constructor/Destructors
    Logger(){file = NULL;}
    ~Logger();

    //Methods
    bool OpenLog(char* filename);     //Opens the log with the given filename
    void Write(char* msg, ...);       //Writes a normal message to the log
    void WriteError(char* msg, ...);  //Writes the message to the log and flags it as error
};
Nothing too earth shattering here.  To use the logger, create an instance then call OpenLog with the filename of the log file you want created.  One note you need to make here is that if you are creating files in another directory make sure that directory exists.  C++ won't create the directory for you if it doesn't.

I'm rather proud of the write methods.  As you can see they use variable arguments which is something I've never done before.  This will allow me to use formatting in the messages I send to the log, instead of either having to create the formatted message before I send it to the logger or adding a write method for every combo of parameters I need.  Here are the guts of  Write:

void Logger::Write(char *msg, ...)
{
#ifdef LOGGING_ON
    //Get the variable arguments
    va_list listPointer;
    va_start( listPointer, msg );

    //Get the current time
    boost::posix_time::ptime t(boost::posix_time::second_clock::local_time());
    std::string time = boost::posix_time::to_simple_string(t);

    //Write time

    fprintf(file,"[%s] ",time.c_str());

    //Write the msg
    vfprintf(file,msg,listPointer);

    //End the line
    fprintf(file,"\n");

    //flush the file
    fflush(file);
#endif
}
WriteError is almost exactly the same except it adds "Error!!!!!!" to the message so you can differentiate error messages in the log from normal messages.  The key to the whole operation is vfprintf.  This method allows me to just pass the va_list right in without caring what's actually in it.  Also you can see that I'm checking for the definintion of LOGGING_ON.  Commenting out that defininition which lives in Logger.h will turn off logging system wide at compile time.

The one area where this logger fails is that it is not thread safe.  If I ever did plan on adding multiple threads that wrote to the same log file, this wouldn't work very well because there exists the possibility that the two threads would be writing to the log at the same time causing some jumbled log messages.  Right now I don't plan on adding multi-threading to my engine, but if I do, I'll need to revisit this.

Now that I have logging in place, my next step is to come up with a consitent and elegent way of handling exceptions.  This probably isn't going to be a new class that I write, but instead a standard for how I write all code.  I've got a little research to do, but as always, I'll let you know how it goes.

References
Here are some thinks to sites that helped me develop the logger:

No comments:

Post a Comment