52. 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 <agx/Logger.h>.

There are four different levels of messages as seen in Table 46.3: Values for Notify level

Table 52.1 Available macros for logging messages

Macro name

Description

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.

Examples of using the LOGGER macros:

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 LOGGER().setExceptionOnError( true ); is called prior to the call.

52.1. Redirecting Logger messages

Messages that are generated during runtime can contain important information. For example when reading mesh data the constructor of 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 Table 46.1 or through the API:

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.

52.1.1. Deriving from a callback

In many situations it is possible to derive from the 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 LoggerSubscriber.

To recieve messages through callbacks, one derives a class from agx::NotifyCallback and implements the message method.

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<MyNotifyCallback> callback = new MyNotifyCallback();
// Register the callback
LOGGER().addCallback(callback);

// Unregister the callback
LOGGER().removeCallback(callback);

52.1.2. Subscribing to messages

When you want to have full control over when messages are handled, the agx::LoggerSubscriber is a better option. It works like a mailbox. You setup the system and poll messages using the poll() method.

// 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<agx::LoggerSubscriber> 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);