Logging, how do I do that?

Started by
13 comments, last by MindWipe 23 years, 2 months ago
I''ve created 3 variations of a general purpose logging module ... 2 variations for windows and 1 for BeOS ... and I just wanted to say ... Houdini ... in windows you must open/close your log file (at least in debug mode - or at least periodically) - because when your program spins/crashes/or locks ... you loose your entire log if it''s open ...you dont even get the stuff that was in it last time you closed ...so your log file MUST be closed at all times except the split second it''s logging.

One way to make this efficient is to have a fixed sized (to avoid dynamic memory overhead) internal buffer ... say 2-10KB in size (remember 4 lines at 255 is 1KB) and simply keep an index .. when the index gets to the end ... open the file ... copy the whole buffer in one call ... reset the index ... close the file.

In BeOS this is totally unnessesary ... the journalled file system retains everything .. if i send a byte to an open file ... then kill my program or pull power out of the socket ... when i reboot .. that byte is in the file ... neat huh

The 2 versions for windows are as follows ... the simple one .. which has a constructor which takes a file name and a logging threashold (so unimportant messages dont get logged) ... and a log method which takes the following: LogType (exception, debug data, execution trace, message), LogSeverity(the number that determines if the log should get loged at current log level), Source Module(the module of code in which the log call resides .. duh),LogDetails(a string which contains the specific information for this particular log). It is worth mentioning that these 4 values comprise a class I call LogEvent ... which represents a simple entity to log to the file .. and this class is derived from my Exception base class ...so you can do this

  ; assume EXCEPTION is an enum value for the EventType; assume MAJOR_EXCEPTION is a #define for a numeric constantif(!ProcessInput())    throw LogEvent(EXCEPTION,MAJOR_ERROR,"InputServer","ProcessInput() failed");  


then ... if that exception gets handled ...good ...if not ... when you get out to the main catch block ...you can not only see what the error was ..but its fairly easy to go from the log file right to the subsection of you program that might have throw it and do a grep or FindInFiles for the message string "ProcessInput() failed"

When runtime efficiency becomes an issue ... you replace the 3rd and 4th parameters with 32 bit int .. which you define in a central location ... and then use #defines ...so it looks like this:

  LogEvent(EXCEPTION,FATAL_ERROR,MODULE_IO,EXP_PROCESS_INPUT_FAILED);  


so you can still see all the details right in the code ... but only 4 ints are being created and passed around the program ... helps avoid really large string data areas and/or unneccesary dynamic memory allocation.

The second windows logging module I have is identical, but adds named block nesting to the log ... this stuff is useless in multithreaded code ... but it works great for people not yet using threads.

I also have additional heavyweight stuff in a debug only version .. which can have different logging levels set based on source module ... or turn on and off logging of certain types of logs (such as ... turn DebugInfo logging off....) ... but this overhead is too great to leave in release code ... so i don''t use it much. I''ve found all you really need is that single discriminating threashold ... and in debug only senerios ... the ability to tell the logger to additionally log everything from just one specified module ... that''s it ...

Whew ..that was a long post ..I''ll clean the code up this week/weekend .. if someone can just tell me how to make it availible ...I will .. it is thouroughly tested ... But I''m only going to post the easy to use simple version .. don''t want any confusing support headaches - and it''s what I use in real code anyway.
Advertisement
Tip 1:

You will want to log a message using minimum (typing) effort because you will be logging a lot. So if you put the logfunctions in a (singleton/static) class its best to use a macro to access them.

  class Log {    public:        static Log *instance();        void log(const char *formattedMessage, ...);};#define log Log::instance()->log  


This code allows you to log messages like this:
  log("Checking var: %d", i);  


Tip 2:

Using polymorphism is a highly flexible way to route messages realtime. For example, you could have different classes that can route messages to a logfile on disk, the debug panel in VC, a console, stdout or whatever.

A simple implementation would be:

  class Log {    public:        class Output {            virtual void write(const char *message) = 0;        };        class FileOutput : public Output {            protected:                FILE *file;            public:                void write(const char *message){                     fprintf(message);                }                FileOutput(const char *filename){                    file = fopen(filename, "wt");                    if(!file) throw Exception("Can''t create logfile");                }        }    protected:        Output    *output; // Current output class    public:        void logTo(Output *output);        void log(const char *message){            output->write(message);        }};  


Tip 3:

Don''t take message logging too lightly and make something crappy quick.
Its one of the best debugging tools you will ever get so spend some time on it to make it good.
Apologies for the post above.
It has some errors and I forgot to login.
Administrators: please remove it.

Tip 1:

You will want to log a message using minimum (typing) effort because you will be logging a lot. So if you put the logfunctions in a (singleton/static) class its best to use a macro to access them.

  class Log {    public:        static Log *instance();        void log(const char *formattedMessage, ...);};#define log Log::instance()->log  


This code allows you to log messages like this:

log("Checking var: %d", i);


Tip 2:

Using polymorphism is a highly flexible way to route messages realtime. For example, you could have different classes that can route messages to a logfile on disk, the debug panel in VC, a console, stdout or whatever.

A simple implementation would be:

        class Log {    public:        class Output {            virtual void write(const char *message) = 0;        };        class FileOutput : public Output {            protected:                FILE *file;            public:                void write(const char *message){                     fprintf(message);                }                FileOutput(const char *filename){                    file = fopen(filename, "wt");                    if(!file) throw Exception("Can't create logfile");                }                ~FileOutput(){                    fclose(file);                }        };    protected:        Output    *output; // Current output class    public:        void logTo(Output *output){            if(this->output != null) delete this->output;            this->output = output;        }        void log(const char *message){            if(output != null) output->write(message);        }        Log() : output(null) {}        ~Log(){ logTo(null); }};  


The implementation of this system with the Singleton pattern is as follows:

  Log::instance()->logTo(new Log::FileOutput("log.txt"));log("Log opened");Log::destroyInstance();  


Obviously this produces a file with the contents 'Log opened'

Tip 3:

Don't take message logging too lightly and make something crappy quick.
Its one of the best debugging tools you will ever get so spend some time on it to make it good.

BTW: This sourcecode is something I thought up from the top of my head. Being the genius that I am its probably bugfree but I make no warranties. :-)

Edited by - Countach on January 30, 2001 4:44:18 AM
-------homepage - email
Thanks everyone for all the help!

BUT! Many of the replies was a bit to complicated for me.
I just need:

- one funktion that makes a new txt-file (or re-make one)
- one that adds text to it, and closes it(If the program breaks)

That''s all. So that I can ie.

StartLogging("Log.txt");
Log("DDInit okey...");

Adding ints, and stuff to the log file would ofcourse help alot

/MindWipe

"If it doesn''t fit, force it; if it breaks, it needed replacement anyway."
"To some its a six-pack, to me it's a support group."
For everyone who''s interested you can access my updated logfile class in the code snippets section of www.gamedeveloper.net ( it''s a really great site btw ).

WHO DO THEY
THINK THEY''RE
FOOLING : YOU ?



GARAL website
WHO DO THEYTHINK THEY'REFOOLING : YOU ?

This topic is closed to new replies.

Advertisement