Author Topic: New debug output system  (Read 1232 times)

Crimson Wizard

  • Local Moderator
  • AGS Project Tracker Admins
    • Best Innovation Award Winner 2013, for spearheading the AGS 3.3.0 project
    •  
    • Lifetime Achievement Award Winner
    •  
    • Crimson Wizard worked on a game that was nominated for an AGS Award!
      Crimson Wizard worked on a game that won an AGS Award!
New debug output system
« on: 24 Jun 2012, 17:44 »
Well, I made this as a small experiment. I wrote a separate output "system" as a possible substitute for existing functions.
It may be found in the "feature_dbgoutput" branch here:
https://github.com/adventuregamestudio/ags/tree/feature_dbgoutput
Check that out (in both meanings :)) if you are interested. Actually I would like to hear some comments.

Idea was to have a simple subsystem meant for outputting text messages for debug or user notification purposes.
Output is being controlled by verbosity settings (at the moment they are hard-coded, but I supposed they should be read from config or command line).
Verbosity is defined as flags:
Code: C++
  1.         VERBOSE_DO_LOG          = 0x0001, // do log (otherwise do not, C.O.)
  2.  
  3.         // Debug reason is for random information about current game state,
  4.         // like outputting object values, or telling that execution has
  5.         // passed certain point in the function
  6.         REASON_DEBUG            = 0x0002,
  7.         // Notifications are made when the program is passing important
  8.         // checkpoints, like initializing or shutting engine down, or
  9.         // when program decides to make a workaround to solve some problem
  10.         REASON_NOTIFICATION     = 0x0004,
  11.         // Warnings are made when unexpected or generally strange behavior
  12.         // is detected in program, which does not necessarily mean error,
  13.         // but still may be a symptom of a bigger problem
  14.         REASON_WARNING          = 0x0008,
  15.         // Handled errors are ones that were 'fixed' by the program on run;
  16.         // There's high chance that program will continue running as normal,
  17.         // but message should be maid to bring user or dev's attention
  18.         REASON_HANDLED_ERROR    = 0x0010,
  19.         // Unhandled errors are errors that program was not able to fix;
  20.         // Program *may* try to continue, but there's no guarantee it will
  21.         // work as expected
  22.         REASON_UNHANDLED_ERROR  = 0x0020,
  23.         // Fatal errors make program abort immediately
  24.         REASON_FATAL_ERROR      = 0x0040,
  25.  
  26.         // Convenience aliases
  27.         REASON_FUNCTION_ENTER   = REASON_DEBUG,
  28.         REASON_FUNCTION_EXIT    = REASON_DEBUG,
  29.  
  30.         REASON_NOT_DEBUG        = 0x007D,
  31.         REASON_WARN_ERRORS      = 0x0079,
  32.         // Just print out the damned message!
  33.         REASON_WHATEVER         = 0x00FF
  34.  

System registers a variable number of "output targets" - objects of classes, which implement IOutputTarget interface. These may be newly created classes or any other preexisting class that was extended to become IOutputTarget implementation. For example, in this branch I made AGSPlatformDriver its implementor.

There's a general verbosity setting that is used to tell what types of messages should be completely ignored during game run, and each output target has its own verbosity setting that is being checked after general setting.
Print is made by ALL output targets that pass verbosity check. That means you may have, for example, a file output for printing out only serious errors, and console or editor debugger output for printing both errors and some minor debug notifications. You may even have two file outputs - one for general logging (some 'debug.log'), and separate one (original 'warnings.log') for printing only serious errors, and so forth - any combination of these.

Basically you register them like this:
Code: C++
  1.     out::add_output_target(TARGET_FILE, new Engine::out::CRawFileOutputTarget("warning.log"),
  2.         out::REASON_NOT_DEBUG, false);
  3.     out::add_output_target(TARGET_SYSTEMDEBUGGER, AGSPlatformDriver::GetDriver(),
  4.         out::REASON_WARN_ERRORS, true);
  5.     out::add_output_target(TARGET_GAMECONSOLE, new Engine::out::CConsoleOutputTarget(),
  6.         out::REASON_WHATEVER, false);
  7.     out::add_output_target(TARGET_FILE_EXTRA_TEST, new Engine::out::CRawFileOutputTarget("ags_game_debug.log"),
  8.         out::REASON_WHATEVER, false);
  9.  

There's a common function for printing simple message with given "reason", as well as bunch of convenience helper functions for more user-friendliness.
The output is made simply like:
Code: C++
  1.     out::fprint("Debug system: testing!");
  2.  
  3.     out::debug          ("This is a debug message, p1 = %d, p2 = %f", 1, 2.0);
  4.     out::debug          ("Function name", "This is a debug message, p1 = %d, p2 = %f", 1, 2.0);
  5.     out::notify         ("This is a notification, p1 = %d, p2 = %f", 1, 2.0);
  6.     out::notify         ("Function name", "This is a notification, p1 = %d, p2 = %f", 1, 2.0);
  7.     out::warn           ("This is a warning, p1 = %d, p2 = %f", 1, 2.0);
  8.     out::warn           ("Function name", "This is a warning, p1 = %d, p2 = %f", 1, 2.0);
  9.     out::handled_err    ("This is a handled error, p1 = %d, p2 = %f", 1, 2.0);
  10.     out::handled_err    ("Function name", "This is a handled error, p1 = %d, p2 = %f", 1, 2.0);
  11.     out::unhandled_err  ("This is an unhandled error, p1 = %d, p2 = %f", 1, 2.0);
  12.     out::unhandled_err  ("Function name", "This is an unhandled error, p1 = %d, p2 = %f", 1, 2.0);
  13.     out::fatal_err      ("This is a fatal error, p1 = %d, p2 = %f", 1, 2.0);
  14.     out::fatal_err      ("Function name", "This is a fatal error, p1 = %d, p2 = %f", 1, 2.0);
  15.     out::fnin           ("Function name");
  16.     out::fnout          ("Function name");
  17.  
  18.     out::fprint("Debug system: end testing!");
  19.  

The code is not final, ofc. Main issue is unsafe usage of string buffers, and also limits made by using static array, but this would be fixed as soon as utility classes are implemented.
I also may have gone too far with namespaces ;). On other hand the whole thing could be converted to the class in a matter of an hour.
Currently functionality mimics original AGS logging (file and writing to game console; btw it's funny I don't remember ever seeing that.... :/ or maybe I forgot how it looks like).

What do you think?
« Last Edit: 24 Jun 2012, 18:02 by Crimson Wizard »