Output levels:
In my opinion we should make a difference between messages which are important for the user (player) and messages which are important for the developers. User related messages are shown in the console while developer related messages are only written to the logfile. Additionally we may need some extra verbose output if we're about to debug a specific part of the application (e.g. level loading, network, notifications, etc).
This leaves us which 3 major output levels: normal (user), internal, and verbose.
- normal output is visible in the console and the logfile
- internal output is visible in the logfile
- verbose output is visible in the logfile only when requested (through a config value)
- status: a notification about the current state of the application
- error: there was an unrecoverable error
- warning: there was a recoverable error
- info: some progress information of little importance
- user status
- user error
- user warning
- user info
- internal status
- internal error
- internal warning
- internal info
- verbose
- (verbose++)
Guidelines:
Here are my guidelines how to use the different output levels:
Amount of output:
- output with user level should be very limited and actually important for the user. Target: A few dozen lines in the console after playing some levels (~10 lines for startup, ~5 per level)
- output with internal level should be helpful for debugging. Target: a few hundred lines in the logfile after playing some levels (~100 lines for startup, ~50 per level)
- output with user level is used to directly communicate with the user. It should not be repeated. For example, if the initialization of openAL fails, we print one error. But we don't print an error each time we try to load a sound source.
- There's no need to print 20 lines to the console within 1 second. The target is to not exceed 1 line per second (unless the user manages to change the application's state more frequently by clicking lots of buttons).
- if an action takes more than 1 second to process (e.g. level loading), some intermediate progress information can be printed with user info level.
- Both major output levels (user, internal) have 4 minor sub-levels (status, error, warning, info). The difference is that internal output is more fine grained than user output.
- If the user clicks a button there should be at most 1 state change at user level. However this state change may require 10 internal state changes
- User notifications should only contain stuff the user knows. He should know what a level file is, but he doesn't have to know that it contains XML. As a result, print a user error if the level file doesn't exist, but only an internal error if the XML code is malformed
- output with user status level should inform the user about the state of the application (e.g. application started, loosely related to gamestates).
- output with internal status level should give us some information in which state the application was before it crashed.
- output with info level can be used to print some intermediate information between two states.
- error level is for unrecoverable errors: the application has to go back to the old state (or to another known state, e.g. mainmenu). It's not possible to have 2 consecutive errors (theoretically).
- warnings are for recoverable errors. There can be multiple warnings at the same time, but to meet the "Frequency of output" guidelines, several warnings of the same type should be summarized (and yes, this means some work)
Also the border between errors and warnings is blurred: In general, error messages are for unrecoverable errors when the requested operation can not be completed. Warnings are for recoverable errors. However it's possible that something which triggers an internal error is at the same time also a user warning, for example during level loading when some object could not be created (internal error in the factory), but the loader recovers from this error and prints only a warning to the user.
Some more points:
- No output in tick(), it simply becomes too much and is probably not helpful
- Chat and kill-messages should not be treated as output, more like notifications
Examples:
Here are some examples in the context of level loading. Note that, as stated above, the difference between user info and internal status, as well as internal info and verbose is not clearly defined. It depends on how useful the information actually is and if we can adhere to the "Amount of output" guidelines.
- user status:
status: Loading level tutorial.oxw...
status: Finished loading - user error:
error: File "tutorial2.oxw" does not exist - user warning:
warning: Could not find mesh "debris.mesh"
warning: 3 object(s) could not be loaded - user info:
info: Loading templates...
info: Loading objects...
info: Preloading meshes...
info: Preloading materials...
info: Player entered the game - internal status:
status (internal): Opened level file "tutorial.oxw"
status (internal): Parsing XML...
status (internal): Processing Lua... - internal error:
error (internal): Preloading: File "debris.mesh" does not exist (in object of type "Model" loaded from tutorial.oxw line 208)
error (internal): Class "SpaceShiiip" does not exist (tutorial.oxw line 54) - internal warning:
warning (internal): Could not convert string "hello world" to float
warning (internal): Found unknown XML attribute "orientationnnn" in object of type "Model" (tutorial.oxw line 208) - internal info:
info (internal): Created instance of class "Model"
info (internal): Preloading "debris.mesh"... - verbose:
verbose (loader): Loading XML attribute "position": string "1,2,3" converted to Vector3(1, 2, 3)
verbose (loader): Loading XML attribute "scale": string "hello world" converted to float 0.0000