projectM-visualizer / projectm

projectM - Cross-platform Music Visualization Library. Open-source and Milkdrop-compatible.
https://discord.gg/mMrxAqaa3W
GNU Lesser General Public License v2.1
3.22k stars 364 forks source link

Add an error reporting and general logging API #732

Open kblaschke opened 9 months ago

kblaschke commented 9 months ago

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:

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:

Disadvantages:

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:

Disadvantages:

revmischa commented 3 weeks ago

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 commented 3 weeks ago

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.