.. include:: definitions.rstinc .. _logger: ======================= Logging system ======================= AGX contains a mechanism for logging messages to file, callbacks and/or console which is implemented through two classes, **agx::Logger** and **agx::Notify**. Notify is a class derived from std::ostringstream so it can be treated as any std::ostream using the overloaded << operator. The logging functionality is accessed through a number of macros defined in . There are four different levels of messages as seen in :numref:`Table %s: Values for Notify level ` .. _table-35: .. csv-table:: :header: "Macro name", "Description" :widths: auto "**LOGGER_DEBUG()**", "Start new debug message." "**LOGGER_INFO()**", "Start a new information message." "**LOGGER_WARNING()**", "Start a new warning message." "**LOGGER_ERROR()**", "Start a new error message." "**LOGGER_END()**", "Commit the started message (if the message is an error and an exception should be thrown, the actual error is thrown after this call)." "**LOGGER_ENDL()**", "Same as LOGGER_END() but add a std::endl to the stream." "**LOGGER_STATE()**", "Change the output state for this message. Determines what is written as a message, line number, current function etc." :numref:`Table %s: Available macros for logging messages. ` Examples of using the LOGGER macros: .. code-block:: c++ LOGGER_WARNING() << "The file: " << filename << " cannot be read" << LOGGER_ENDL(); LOGGER_INFO() << LOGGER_STATE(agx::Notify::PRINT_NONE) << "This is a plain message with no extra information " << LOGGER_ENDL(); LOGGER_ERROR() << "A serious problem has occurred" << LOGGER_END(); The last example with LOGGER_ERROR() will throw an exception if :cpp:`LOGGER().setExceptionOnError( true );` is called prior to the call. ---------------------------- Redirecting Logger messages ---------------------------- Messages that are generated during runtime can contain important information. For example when reading mesh data the constructor of :cpp:`agxCollide::Trimesh` can generate warnings about the validity of the mesh. If there is no console available, these messages will not be shown to the user. Either one have to enable the logging to file functionality through :numref:`table-32` or through the API: .. code-block:: c++ bool overwrite = true; // overwrite if there is already a log file with this name bool closeAndOpen = true; // If a log file is already open, close it and open the new, otherwise keep the previous one opened LOGGER().openLogFile( "c://temp/logfile.txt", overWrite, closeAndOpen ); There are also a number of ways to capture the messages written to the LOGGER system. ^^^^^^^^^^^^^^^^^^^^^^^^^^ Deriving from a callback ^^^^^^^^^^^^^^^^^^^^^^^^^^ In many situations it is possible to derive from the :cpp:`agx::NotifyCallback` class and recieve messages. .. note:: Callbacks are triggered from within the std::ostream system in C++. This can cause some issues in certain cases. For example in multi-threaded environments or when used in a C# context such as Unity3D. In those cases we recommend to use the :ref:`LoggerSubscriber`. To recieve messages through callbacks, one derives a class from :cpp:`agx::NotifyCallback` and implements the :cpp:`message` method. .. code-block:: c++ class MyNotifyCallback : public agx::NotifyCallback { public: MyNotifyCallback() {} ~NotifyCallback() {} /// Virtual method called whenever there is a message ready void message( const agx::String& msg, int notifyLevel) override { // Handle the message } }; agx::ref_ptr callback = new MyNotifyCallback(); // Register the callback LOGGER().addCallback(callback); // Unregister the callback LOGGER().removeCallback(callback); .. _logger_subscriber: ^^^^^^^^^^^^^^^^^^^^^^^^ Subscribing to messages ^^^^^^^^^^^^^^^^^^^^^^^^ When you want to have full control over when messages are handled, the :cpp:`agx::LoggerSubscriber` is a better option. It works like a mailbox. You setup the system and poll messages using the :cpp:`poll()` method. .. code-block:: c++ // Specify which levels you want to listen to int levelMask = agx::Notify::NOTIFY_ERROR | agx::Notify::NOTIFY_WARNING | agx::Notify::NOTIFY_INFO; agx::ref_ptr subscriber = new LoggerSubscriber(levelMask); /* Run simulation.... */ // Now we want to check for messages: if (!subscriber->empty()) { std::string messages = subscriber->pop(); std::cerr << "The message: " << message << std::endl; } // There is a "mail queue" for each level, so we can check them individually. // We can store the messages in an array also: agx::StringVector messages; // Are there any warnings? size_t numMessages = subscriber->pop(messages, agx::Notify::NOTIFY_WARNING);