Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an error reporting and general logging API #732

Open
kblaschke opened this issue Sep 20, 2023 · 2 comments
Open

Add an error reporting and general logging API #732

kblaschke opened this issue Sep 20, 2023 · 2 comments

Comments

@kblaschke
Copy link
Member

Currently, libprojectM has no good way of reporting errors and other information like debugging output to the integrating application. As of now, only failures loading a preset contain a mostly generic error message. Other debug messages can only be enabled in debug builds, printing directly to STDOUT, which isn't an optimal solution for a library.

For application developers and also preset authors, getting more information from the library is a great thing to increase productivity. We need to add a few API functions to configure and retrieve log messages:

  • Add API functions to register and unregister a logging callback function.
  • Add an API function to set the global log level.
  • Add an API function to set log levels per component (audio, presets, renderer etc.).

Since logging will happen in most classes of libprojectM, providing per-instance logging is a bit tricky if we don't want to pass on the logging instance to all classes. I'll put two suggestions up for discussion, but if anyone has a different idea, please post it!

Suggestion 1: Static global logging

This is the easiest way of adding logging to the library. Logging would not be tied to a specific projectM instance, but is configured globally for all instances created by the application. In almost all cases this will be perfectly fine, as most implementations will only need a single instance at any time. If multiple instances are running in parallel, the application needs to determine which instance has produced the log output, e.g. by using the thread ID or by remembering which projectM instance was called most recently.

Advantages:

  • Works before even creating an instance, so initialization errors can already be logged.
  • The callback doesn't even need to know about the projectM instances.
  • Very easy to implement in libprojectM using global variables to store the callback pointer and log levels.

Disadvantages:

  • No easy way to distinguish log output from different projectM instances.
  • Log levels can only be set globally for all instances.
  • Integrations have a harder time making the logging thread-safe if multiple projectM instances run in parallel on separate threads.

Suggestion 2: Thread-local instance binding

This would provide a way to set separate logging callbacks and levels for each projectM instance. Since only one instance can run at a time in each thread, we can use a thread-local global variable to store a pointer to the logging information (callback pointer and log levels). Every time a projectM API function is called, it would set the thread-local variable to the current instance's logging structure.

Advantages:

  • Callbacks get the projectM instance handle, and different callbacks can be defined per projectM instance, which makes it easy to determine the source instance.
  • No multi-threading issues in integrations if callbacks are defined per thread.
  • Log levels can be set per projectM instance.

Disadvantages:

  • Slightly harder to implement in libprojectM, but can use the C++11 thread_local specifier.
  • Still need a global callback for non-instance errors, e.g. to log errors during projectm_create().
  • More changes needed to always set the thread-local logging variable each time.
@revmischa
Copy link
Collaborator

I think #1 sounds a lot safer and easier.

Integrations have a harder time making the logging thread-safe if multiple projectM instances run in parallel on separate threads.

It's probably just gonna be a printf() type call anyway right? so worst case is they get interposed?
do apps even run multiple instances of pM ever?

@kblaschke
Copy link
Member Author

It's probably just gonna be a printf() type call anyway right?

The logging API will not print something, but rather pass log messages via a registered logging callback to the embedding application. The application can then decide what to do with the log message, e.g. log it to a file, print it on screen/console or something else.

The issue here is if projectM only supports a single statically stored callback, then all projectM instances will call the same callback function. This may lead to the callback being called at the same time in different threads if the app runs more than one projectM instance, e.g. to display multiple previews etc., and this may cause issues if not synchronized properly.

I concur that option 1 is probably the better solution, especially as thread-local variables are not always reliable.

Applications running multiple projectM instances in separate threads could use the thread ID to distinguish instances, or if they render multiple frames within the same thread with different instances, the app can simply store the active projectM instance handle before calling the render frame method.

And you're right, most applications will only ever runs a single projectM instance, so this issue is really an edge case.

I'll implement the static logging callback method, and document the multi-instance/multi-threading caveats accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

No branches or pull requests

2 participants