How to Log and stay Modular

Started by
55 comments, last by ApochPiQ 12 years, 7 months ago

[quote name='speciesUnknown' timestamp='1315498641' post='4859092']
Holy shit man, option 4 was supposed to be an obviously wrong answer, and return0's addition was a joke O_o please tell me that from all this you didn't learn that the best solution was the most complicated and intrusive one?


You've been re-bad-joked.
[/quote]

I hope so
Don't thank me, thank the moon's gravitation pull! Post in My Journal and help me to not procrastinate!
Advertisement
Okay wait so what is the best way then? I shouldn't have the log passed as a param?
My solution is a mix of those proposed in some replies.

Base module providing logging capabilities.
Logger is a class but log messages are added via macros, different error levels.
Many logger classes each supporting different log formats (right now just txt, rtf and xml. xml logs have some extra features like error level filter search, etc.)

That way I still have global access but at the same time I can change the log type when I need it. Could easily write a network logger class and use it modifying 2 lines of code.
Also I can easily select verbosity based on error levels.

My solution is a mix of those proposed in some replies.

Base module providing logging capabilities.
Logger is a class but log messages are added via macros, different error levels.
Many logger classes each supporting different log formats (right now just txt, rtf and xml. xml logs have some extra features like error level filter search, etc.)

That way I still have global access but at the same time I can change the log type when I need it. Could easily write a network logger class and use it modifying 2 lines of code.
Also I can easily select verbosity based on error levels.


What do you mean by "log messages are added via macros" and "different error levels"? Could you please give some sample code too?

Thanks,
Brent

Okay wait so what is the best way then? I shouldn't have the log passed as a param?

You should create a web service that can receive your log. Everywhere in your code, you make an HTTP Post containing your log. The web service then translates the raw log to an excel spreadsheet with really nice formatting and color coded. This excel spreadsheet is then translated to HTML5/Javascript/CSS complete with charts and graphs.
It's simple.

You have a globally accessible class, let's say an application class even if it might not be the case. Depends on your design.

Question: who is responsible of enabling/disabling/configuring log?
Answer: your application, because different applications might require different logs and also you might want to change one line to enable/disable log, compile and run without rebuilding everything.

So your application class at startup should configure the logger.

Question nr.2: who can access log?
Answer: potentially every class.

So what you do is to build globally accessible macros for emitting logs pointing at a class WRAPPING the logger.

Example:

[color="#0000ff"][color="#0000ff"]#define my_LOG(sStr,...) myLogWrapper->EngineLog(my_LOGT_UNKNOWN,__LINE__,__FUNCTION__,__FILE__,sStr,__VA_ARGS__);
[color="#0000ff"][color="#0000ff"]#define my_WRNLOG(sStr,...) myLogWrapper->EnginelLog(my_LOGT_WARNING,__LINE__,__FUNCTION__,__FILE__,sStr,__VA_ARGS__);
[color="#0000ff"]#define my_ERRLOG(sStr,...) myLogWrapper->EnginelLog(my_LOGT_ERROR,__LINE__,__FUNCTION__,__FILE__,sStr,__VA_ARGS__);

Which is nothing special, just a call to a global log wrapper class passing also a log type.
The log type identifies the error level. For example the base module always logs CPU/OS/System infos and my rendering module logs the device capabilities. Both are INFO logs.

The point in having a wrapper is simple. The base module always initializes the wrapper but not the log. This way when you want a logger actually you ADD a logger to the wrapper.
So the function EngineLog, in its simplest form, parses all available logs from an array of logs. If none is availble no message is emitted, if many you have multiple output logs.

And what I like is it's easy to change how log works, for example if you wanted to bind specific log types to different log files all you would need to do is to write a new enginelog method, change the macros and just recompile. That's it.


It's simple.

You have a globally accessible class, let's say an application class even if it might not be the case. Depends on your design.

Question: who is responsible of enabling/disabling/configuring log?
Answer: your application, because different applications might require different logs and also you might want to change one line to enable/disable log, compile and run without rebuilding everything.

So your application class at startup should configure the logger.

Question nr.2: who can access log?
Answer: potentially every class.

So what you do is to build globally accessible macros for emitting logs pointing at a class WRAPPING the logger.

Example:

[color="#0000ff"][color="#0000ff"]#define my_LOG(sStr,...) myLogWrapper->EngineLog(my_LOGT_UNKNOWN,__LINE__,__FUNCTION__,__FILE__,sStr,__VA_ARGS__);
[color="#0000ff"][color="#0000ff"]#define my_WRNLOG(sStr,...) myLogWrapper->EnginelLog(my_LOGT_WARNING,__LINE__,__FUNCTION__,__FILE__,sStr,__VA_ARGS__);
[color="#0000ff"]#define my_ERRLOG(sStr,...) myLogWrapper->EnginelLog(my_LOGT_ERROR,__LINE__,__FUNCTION__,__FILE__,sStr,__VA_ARGS__);

Which is nothing special, just a call to a global log wrapper class passing also a log type.
The log type identifies the error level. For example the base module always logs CPU/OS/System infos and my rendering module logs the device capabilities. Both are INFO logs.

The point in having a wrapper is simple. The base module always initializes the wrapper but not the log. This way when you want a logger actually you ADD a logger to the wrapper.
So the function EngineLog, in its simplest form, parses all available logs from an array of logs. If none is availble no message is emitted, if many you have multiple output logs.

And what I like is it's easy to change how log works, for example if you wanted to bind specific log types to different log files all you would need to do is to write a new enginelog method, change the macros and just recompile. That's it.





Okay thankyou very much!

OK, seriously.


Someone please explain to me how the freaking hell it makes sense to use an object for logging when a free/static function can do exactly the same things at less overhead.


Please.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


OK, seriously.


Someone please explain to me how the freaking hell it makes sense to use an object for logging when a free/static function can do exactly the same things at less overhead.


Please.


How exactly are you going to provide a logging hook to a library by simply a free/static function?

The OP specifically asked how to do logging in a modular fashion. Discounting the utility of that requirement, at the very least you'll need some static variable that the user of the library can re-assign/populate.

[quote name='speciesUnknown' timestamp='1315498641' post='4859092']
Holy shit man, option 4 was supposed to be an obviously wrong answer, and return0's addition was a joke O_o please tell me that from all this you didn't learn that the best solution was the most complicated and intrusive one?


You've been re-bad-joked.
[/quote]
No, no he wasn't:

Okay wait so what is the best way then? I shouldn't have the log passed as a param?

And actually, that just makes it even funnier.



@ApochPiQ The difference is generally minimal, as is the overhead. I can understand someone having a personal preference (and depending on one's circumstances/needs, those preferences can be legit), but worrying about the overhead of calling a member function vs a free function for logging is a lame argument, IMHO. Compared to the file access/write times that logging takes, really, that kind of overhead is nothing to worry about.

But if you're talking about programming time/LOC overhead, well, both can be done relatively simply and in similar amounts of LOC/programming time. I agree that this logging systems often done in some kind of overkill way, but there's no reason one method can't be as simple as the other.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

This topic is closed to new replies.

Advertisement