Re: Output levels
Posted: Mon Aug 01, 2011 9:18 pm
I spent my weekend replacing COUT() with orxout() and because this means I edited pretty much everyone's code, I think I owe you a summary of what I did and what you should expect from it (and what not)
First of all, the number of output levels almost doubled. This means for every existing line of output I had to use one of the new levels:
COUT(0) -> orxout(message) or orxout(debug_output)
COUT(1) -> orxout(user_error) or orxout(internal_error)
COUT(2) -> orxout(user_warning) or orxout(internal_warning)
COUT(3) -> orxout(user_status), orxout(user_info), orxout(internal_status), or orxout(internal_info)
COUT(4) -> orxout(verbose)
COUT(5) -> orxout(verbose_more)
COUT(6) -> orxout(verbose_ultra)
I tried to use the best level for each message, but obviously it's not perfect. Naturally I used the internal levels more often than the user levels. There are no definite rules how to use the levels and I can not really estimate the frequency of each output, hence it's hard to get it right without testing.
As you can imagine, while replacing roughly 1000 lines I surely made some mistakes. Unfortunately that's hard to find because the vast majority of output is either error, warning, or verbose. Errors and warnings are usually not visible while executing orxonox (because there are no errors) and verbose output is not visible unless its context is activated. This means we have to see in the next time if output still works as it should and everyone is asked to clean up the output in his own code if necessary. But no hurry.
And please don't think there's only one correct way to use the output levels and if you do it wrong you get punched in the face. In fact your output can have almost every possible level as long as it makes some sense and is useful.
While looking through the code I also noticed that many people used some workarounds with the old COUT levels. For example, if they had important errors and less important errors, the less important errors were often printed with warning level. And less important warnings with info level. In the new output system this doesn't work anymore because every output with error levels has "Error: " in front of it (the same also with warning, status, etc). But since there are user and internal levels, we're probably fine. I tried to transform output levels in these casees in a meaningful way, i.e. if a message described an error but used warning level, I used internal_error.
---
Because every line of output now has a prefix depending on the level ("Error: ", "Warning: ", etc) I removed these tokens from the output messages because it would be redundant.
Additionally I changed std::endl to endl - even though I don't care about these additional 5 chars I think many pps students do, so since we already allow the use of "endl" we may as well make it the default.
---
About the output contexts: I used about 20 contexts for the framework and about 10 for the game logic. Basically every verbose output needs a context because otherwise you will not see it (unless you activate ALL verbose output, but you probably don't want that). So there are cases were I used a context for one single line. In other cases output was used for dozens of output lines, not only verbose but also warnings, errors, etc, for example everything related to quests.
Note that you don't have to use a context for every message. It's mostly useful for verbose output, but if we already have a context for some part of the code, we may also use it for non-verbose output.
---
In the next days I will add some more output with status and info level, and also tweak the existing output a bit. The output levels status and info are important because they guide the user through the different states of the game and provide valuable information in the log file. The goal is to get some information in which state the game was if it crashes.
---
Something else I noticed is that obviously everyone thinks his part of the game is the most important part and consequently uses "strong" output levels and messages. There is some code in orxonox where a slightly wrong parameter results in "FATAL ERROR! something went terribly wrong" assert(0); or something similar
I mean I totally understand this from a programmer's point of view and I don't want to blame anyone (it concerns also my code), but we should try to print our internal errors only with internal output level and don't annoy the user with a load of (for him) unimportant messages. Also aborting the game will be bad practice if we finally get an editor because then the game should keep running and just print an error message. I didn't change anything of this though, that's up for future changes.
First of all, the number of output levels almost doubled. This means for every existing line of output I had to use one of the new levels:
COUT(0) -> orxout(message) or orxout(debug_output)
COUT(1) -> orxout(user_error) or orxout(internal_error)
COUT(2) -> orxout(user_warning) or orxout(internal_warning)
COUT(3) -> orxout(user_status), orxout(user_info), orxout(internal_status), or orxout(internal_info)
COUT(4) -> orxout(verbose)
COUT(5) -> orxout(verbose_more)
COUT(6) -> orxout(verbose_ultra)
I tried to use the best level for each message, but obviously it's not perfect. Naturally I used the internal levels more often than the user levels. There are no definite rules how to use the levels and I can not really estimate the frequency of each output, hence it's hard to get it right without testing.
As you can imagine, while replacing roughly 1000 lines I surely made some mistakes. Unfortunately that's hard to find because the vast majority of output is either error, warning, or verbose. Errors and warnings are usually not visible while executing orxonox (because there are no errors) and verbose output is not visible unless its context is activated. This means we have to see in the next time if output still works as it should and everyone is asked to clean up the output in his own code if necessary. But no hurry.
And please don't think there's only one correct way to use the output levels and if you do it wrong you get punched in the face. In fact your output can have almost every possible level as long as it makes some sense and is useful.
While looking through the code I also noticed that many people used some workarounds with the old COUT levels. For example, if they had important errors and less important errors, the less important errors were often printed with warning level. And less important warnings with info level. In the new output system this doesn't work anymore because every output with error levels has "Error: " in front of it (the same also with warning, status, etc). But since there are user and internal levels, we're probably fine. I tried to transform output levels in these casees in a meaningful way, i.e. if a message described an error but used warning level, I used internal_error.
---
Because every line of output now has a prefix depending on the level ("Error: ", "Warning: ", etc) I removed these tokens from the output messages because it would be redundant.
Additionally I changed std::endl to endl - even though I don't care about these additional 5 chars I think many pps students do, so since we already allow the use of "endl" we may as well make it the default.
---
About the output contexts: I used about 20 contexts for the framework and about 10 for the game logic. Basically every verbose output needs a context because otherwise you will not see it (unless you activate ALL verbose output, but you probably don't want that). So there are cases were I used a context for one single line. In other cases output was used for dozens of output lines, not only verbose but also warnings, errors, etc, for example everything related to quests.
Note that you don't have to use a context for every message. It's mostly useful for verbose output, but if we already have a context for some part of the code, we may also use it for non-verbose output.
---
In the next days I will add some more output with status and info level, and also tweak the existing output a bit. The output levels status and info are important because they guide the user through the different states of the game and provide valuable information in the log file. The goal is to get some information in which state the game was if it crashes.
---
Something else I noticed is that obviously everyone thinks his part of the game is the most important part and consequently uses "strong" output levels and messages. There is some code in orxonox where a slightly wrong parameter results in "FATAL ERROR! something went terribly wrong" assert(0); or something similar
I mean I totally understand this from a programmer's point of view and I don't want to blame anyone (it concerns also my code), but we should try to print our internal errors only with internal output level and don't annoy the user with a load of (for him) unimportant messages. Also aborting the game will be bad practice if we finally get an editor because then the game should keep running and just print an error message. I didn't change anything of this though, that's up for future changes.