Jump to content

  • Log In with Google      Sign In   
  • Create Account

FREE SOFTWARE GIVEAWAY

We have 4 x Pro Licences (valued at $59 each) for 2d modular animation software Spriter to give away in this Thursday's GDNet Direct email newsletter.


Read more in this forum topic or make sure you're signed up (from the right-hand sidebar on the homepage) and read Thursday's newsletter to get in the running!


How to Log and stay Modular


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
56 replies to this topic

#21 Riztro   Members   -  Reputation: 240

Like
0Likes
Like

Posted 06 September 2011 - 08:05 PM


I agree with the general sentiment that you are over-engineering, or that you are forcibly applying the principle of EVERYTHING MUST BE AN OBJECT which you may have picked up at college / uni. OOP is a methodology of programming, not an ideology.

When it comes to logging, you have a finite number of options.

1) Create a new logger and then throw it away every time you want to log something
2) have a single global logger. (no, I didn't say singleton, I said single global instance, the terms are not synonymous)
3) use a free function, or depending on the language, a static function.
4) pass a logger to every single constructor of every single object your game uses.

It should be obvious from these choices what the correct course of action is. Let the academics continue to think inside their little boxes up there in that ivory tower while you like, get some work done.


Option 4 with IoC wiring app/domain services is best.



Okay, I will talk with my team and see how we can implement that.

BTW thank you to everyone for all your wonderful answers. Even if I don't quote them, it doesn't mean I didn't learn a lot from them.

-Brent

Sponsor:

#22 Leviathyo   Members   -  Reputation: 100

Like
0Likes
Like

Posted 08 September 2011 - 06:07 AM

Along the lines of what return0 posted, I use a combination of the "Service Locator" pattern and "Dependency Injection".

EDIT: I don't log in any time-critical situations unless debugging, so the overhead of any service location is negligible.

#23 Bregma   Crossbones+   -  Reputation: 5480

Like
0Likes
Like

Posted 08 September 2011 - 08:21 AM

Now, all over the code I have included a file that declares a global log that is used by the engine. This global log is used throughout the engine and really messes up my goal of keeping things modular. Now, what is the best way to log but still keep things modular? Please provide more than just, "Use cout or printf".

I really have to stop and say "why not use std::clog or printf?"

I frequently hear "oh yes, the wonderful solution that does exactly what I need already exists, but it's not invented here" but I also see a lot of reinvented wheels.
Stephen M. Webb
Professional Free Software Developer

#24 speciesUnknown   Members   -  Reputation: 527

Like
2Likes
Like

Posted 08 September 2011 - 10:17 AM



I agree with the general sentiment that you are over-engineering, or that you are forcibly applying the principle of EVERYTHING MUST BE AN OBJECT which you may have picked up at college / uni. OOP is a methodology of programming, not an ideology.

When it comes to logging, you have a finite number of options.

1) Create a new logger and then throw it away every time you want to log something
2) have a single global logger. (no, I didn't say singleton, I said single global instance, the terms are not synonymous)
3) use a free function, or depending on the language, a static function.
4) pass a logger to every single constructor of every single object your game uses.

It should be obvious from these choices what the correct course of action is. Let the academics continue to think inside their little boxes up there in that ivory tower while you like, get some work done.


Option 4 with IoC wiring app/domain services is best.



Okay, I will talk with my team and see how we can implement that.

BTW thank you to everyone for all your wonderful answers. Even if I don't quote them, it doesn't mean I didn't learn a lot from them.

-Brent


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?
Don't thank me, thank the moon's gravitation pull! Post in My Journal and help me to not procrastinate!

#25 Kian   Members   -  Reputation: 242

Like
0Likes
Like

Posted 08 September 2011 - 12:30 PM

I want to think he was joking too. So much that I've convinced myself. The world is a much nicer place that way.

#26 VReality   Members   -  Reputation: 436

Like
0Likes
Like

Posted 08 September 2011 - 01:32 PM

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.

#27 Codarki   Members   -  Reputation: 462

Like
0Likes
Like

Posted 08 September 2011 - 03:43 PM

How many objects really even need to log? I mean logically. What do they write to the log, error cases? Just do assert, throw exception, return error codes or something, and then log at the error handling code. Lets say for example a renderer class, why should it even have to know that a log exists, or even know about std::string. Now this limits a lot of classes that needs to know about logging, when there are special logging requirements, passing the log to a (high level) class is not so bad. Then use the global log for all general cases.

Also decide if there even will be a log in finished product, and what kind of log?

#28 return0   Members   -  Reputation: 444

Like
0Likes
Like

Posted 08 September 2011 - 04:30 PM

ILogServiceLocator to every constructor, enterprise!

#29 VReality   Members   -  Reputation: 436

Like
0Likes
Like

Posted 08 September 2011 - 08:48 PM

How many objects really even need to log? I mean logically. What do they write to the log, error cases?
...


Absolutely any system under development might benefit from the ability to log. Some random examples might be:
  • Memory manager: Allocations and deallocations, internal infrastructure state at these points, etc.
  • AI: State changes, stimulus events, etc.
  • Path Finder: Path requests, long searches, etc.
The problem with logs in videogame development isn't that no one needs them, it's that they quickly become unusable as they get spammed by every system under development.

#30 Slavik81   Members   -  Reputation: 360

Like
0Likes
Like

Posted 09 September 2011 - 01:36 AM

ILogServiceLocator to every constructor, enterprise!

The name alone makes me shudder in horror.

#31 speciesUnknown   Members   -  Reputation: 527

Like
0Likes
Like

Posted 10 September 2011 - 08:21 AM


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.


I hope so
Don't thank me, thank the moon's gravitation pull! Post in My Journal and help me to not procrastinate!

#32 Riztro   Members   -  Reputation: 240

Like
0Likes
Like

Posted 10 September 2011 - 09:43 AM

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

#33 undead   Members   -  Reputation: 320

Like
0Likes
Like

Posted 10 September 2011 - 09:56 AM

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.

#34 Riztro   Members   -  Reputation: 240

Like
0Likes
Like

Posted 10 September 2011 - 10:02 AM

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

#35 alnite   Crossbones+   -  Reputation: 2133

Like
0Likes
Like

Posted 10 September 2011 - 10:06 AM

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.

#36 undead   Members   -  Reputation: 320

Like
0Likes
Like

Posted 10 September 2011 - 10:19 AM

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:

#define my_LOG(sStr,...) myLogWrapper->EngineLog(my_LOGT_UNKNOWN,__LINE__,__FUNCTION__,__FILE__,sStr,__VA_ARGS__);
#define my_WRNLOG(sStr,...) myLogWrapper->EnginelLog(my_LOGT_WARNING,__LINE__,__FUNCTION__,__FILE__,sStr,__VA_ARGS__);
#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.



#37 Riztro   Members   -  Reputation: 240

Like
0Likes
Like

Posted 10 September 2011 - 10:24 AM

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:

#define my_LOG(sStr,...) myLogWrapper->EngineLog(my_LOGT_UNKNOWN,__LINE__,__FUNCTION__,__FILE__,sStr,__VA_ARGS__);
#define my_WRNLOG(sStr,...) myLogWrapper->EnginelLog(my_LOGT_WARNING,__LINE__,__FUNCTION__,__FILE__,sStr,__VA_ARGS__);
#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!



#38 ApochPiQ   Moderators   -  Reputation: 16412

Like
1Likes
Like

Posted 10 September 2011 - 04:20 PM

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.

#39 Telastyn   Crossbones+   -  Reputation: 3730

Like
0Likes
Like

Posted 10 September 2011 - 06:55 PM

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.

#40 Cornstalks   Crossbones+   -  Reputation: 6991

Like
0Likes
Like

Posted 10 September 2011 - 07:22 PM


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.

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.
[ 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 ]




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS