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

Logging Pt.2: Predefined categories and usage examples #2782

Closed
wants to merge 30 commits into from
Closed

Logging Pt.2: Predefined categories and usage examples #2782

wants to merge 30 commits into from

Conversation

uklotzde
Copy link
Contributor

@uklotzde uklotzde commented May 12, 2020

TODO

The documentation can be found in logging.h.

  • Add predefined root and top-level logging categories
  • Provide a macro for creating custom child categories
  • Provide 2 reference examples (now using Info instead of Debug logs):
    • SoundSourceFFmpeg
    • SoundDevicePortAudio
  • Add missing thread names

SoundDevicePortAudio logs:

Debug [Main]: SoundManager::setupDevices()
Info [Main] mixxx.device.PortAudio: Opening device "default"
Info [Main] mixxx.device.PortAudio: Number of output channels: 2
Info [Main] mixxx.device.PortAudio: Number of input channels: 0
Info [Main] mixxx.device.PortAudio: Requested sample rate: 44100 Hz
Info [Main] mixxx.device.PortAudio: Number of sample frames per buffer: 1024
Info [Main] mixxx.device.PortAudio: Latency per buffer: 23.22 ms
Info [Main] mixxx.device.PortAudio: Opening stream 14
Debug [Controller]: Found "" "" "r1" S/N "" "Interface 0"
Warning [Controller]: USB permissions problem (or device error.) Your account needs write access to USB HID controllers.
[New Thread 0x7ffeb38d1700 (LWP 14515)]
Debug [Controller]: Found "" "" "r1" S/N "" "Interface 1"
Warning [Controller]: USB permissions problem (or device error.) Your account needs write access to USB HID controllers.
Debug [Controller]: Found "" "" "r4624" S/N "" "Interface 0"
Warning [Controller]: USB permissions problem (or device error.) Your account needs write access to USB HID controllers.
Debug [Controller]: Found "" "" "r4624" S/N "" "Interface 1"
Warning [Controller]: USB permissions problem (or device error.) Your account needs write access to USB HID controllers.
Debug [Controller]: Found "" "" "r4624" S/N "" "Interface 2"
Warning [Controller]: USB permissions problem (or device error.) Your account needs write access to USB HID controllers.
Debug [Controller]: ControllerManager::getControllerList
Debug [Controller]: Controller polling stopped.
Debug [Main] mixxx.device.PortAudio: "SoundDeviceId(default, 14)" Opened stream
Info [Main] mixxx.device.PortAudio: Loading dynamic library "libportaudio.so.2"
Debug [Main] mixxx.device.PortAudio: Loaded dynamic library "libportaudio.so.2"
Info [Main] mixxx.device.PortAudio: Enabling real-time scheduling
[New Thread 0x7ffea70d0700 (LWP 14516)]
Debug [Main] mixxx.device.PortAudio: "SoundDeviceId(default, 14)" Stream started
Info [Main] mixxx.device.PortAudio: Actual sample rate: 44100 Hz
Info [Main] mixxx.device.PortAudio: Actual output latency: 46.4399 ms
Info [0x7fff00007720] mixxx.device.PortAudio: SSE: Enabling denormals to zero mode
Info [0x7fff00007720] mixxx.device.PortAudio: SSE: Enabling flush to zero mode
Info [0x7fff00007720] mixxx.device.PortAudio: Denormals to zero mode is working

SoundSourceFFmpeg logs:

Debug [AnalyzerThread 0 #1] mixxx.source.FFmpeg: Opened stream for decoding { index 0 | id 0 | codec_type 1 | codec_id 86017 | channels 2 | channel_layout 3 | channel_layout (fixed) 3 | format 8 | sample_rate 44100 | bit_rate 320000 | frame_size 1152 | initial_padding 0 | trailing_padding 0 | seek_preroll 0 | start_time 353600 | duration 2794291200 | nb_frames 0 | time_base 1 / 14112000 }
Debug [AnalyzerThread 0 #1] mixxx.source.FFmpeg: Lead-in frames [0 -> 1105)
[mp3float @ 0x7ffee0066f40] Could not update timestamps for skipped samples.
Debug [AnalyzerThread 0 #1]: AnalysisDAO fetched 0 analyses, 0 bytes for track 306621 in 0 ms
Debug [AnalyzerThread 0 #1]: Skipping AnalyzerEbur128
Debug [AnalyzerThread 0 #1]: AnalyzerBeats preference settings: 

@uklotzde uklotzde added this to the 2.4.0 milestone May 12, 2020
@@ -17,6 +16,18 @@
#include "controllers/controllerdebug.h"
#include "util/assert.h"

Q_LOGGING_CATEGORY(mixxxLog, MIXXX_LOGGING_CATEGORY_ROOT)

Q_LOGGING_CATEGORY(mixxxLogConfig, MIXXX_LOGGING_CATEGORY_CONFIG)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any advantage from defining them all in one place?
Personally, I think all these macros look ugly.

I'd prefer this:

bool Highscore::load(const QString &filepath)
{
    QLoggingCategory fcIo("fc.io");
    qCDebug(fcIo) << "Loading highscore ...";
    QFile file(filepath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qCWarning(fcIo) << "Could not open file " << filepath << "!";
        return;
    }
    // ...
}

[...]

QLoggingCategory represents a category in code, in this case it's "fc.io". It's not a one to one mapping though ... you can have multiple instances representing the same category.

Source: https://www.qt.io/blog/2014/03/11/qt-weekly-1-categorized-logging

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea was to group the logs under some predefined top-level categories to allow basic filtering without needing to know all child categories. The custom macro MIXXX_LOGGING_CATEGORY_PARENT_CHILD establishes a naming convention.

The macros Q_DECLARE_LOGGING_CATEGORY/Q_LOGGING_CATEGORY help to find all categories that are available, even if they are only defined locally inside a .cpp file and not intended to be reused.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea was to group the logs under some predefined top-level categories to allow basic filtering without needing to know all child categories. The custom macro MIXXX_LOGGING_CATEGORY_PARENT_CHILD establishes a naming convention.

I thought this is done implicitly by separating the category name with dots.
And filtering can be done like this:

QT_LOGGING_RULES="mixxx.engine.controls.*=true"

The macros Q_DECLARE_LOGGING_CATEGORY/Q_LOGGING_CATEGORY help to find all categories that are available, even if they are only defined locally inside a .cpp file and not intended to be reused.

So you can list all categories via command line? Or do you mean by grepping through the source?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@uklotzde can you elaborate?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like to introduce a custom macro. But so far I didn't have any idea how to achieve this in a better way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could put all category strings in a single header file and then include them from there. That way you only need to seach-replace in a single file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this restriction the macros could be avoided.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After 2nd thought: Not viable. A huge global file that might contain a separate logging category for each module (comprising 1 or a few .cpp files) is unmaintainable.

How is a user supposed to chose the correct one? Even the list of predefined top-level logging categories in logging.h is long already.

Copy link
Member

@Holzhaus Holzhaus Jun 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I also thought about this and I'm not even sure we need this global list or the macros. When we define the logging category inside the source file, like this it becomes much easier to find the correct logging category to enable debug messages for:

QLoggingCategory logcat("device.soundsource.portaudio");

You're right that it becomes a bit tedious to restructure logging categories later on if the fully qualified names are spread across the code base like this, but it's still possible to mass-replace them in a bash one-liner using find/grep/sed. And I don' t think we want to restructure them that often, and the instant visibility of the full logging category name when reading the source file is very convenient and outweighs this potential issue IMHO.

@@ -79,15 +79,15 @@ inline int64_t getStreamStartTime(const AVStream& avStream) {
auto start_time = avStream.start_time;
if (start_time == AV_NOPTS_VALUE) {
// This case is not unlikely, e.g. happens when decoding WAV files.
#if ENABLE_TRACING
kLogger.trace()
#if ENABLE_TRACE_LOG
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we get rid of this?

I don't think this makes a notable performance improvement:

Note: Arguments aren't processed if the debug output for that category is not enabled, so don't rely on any side effects.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Qt does not provided a detailed trace level, only debug. Until the default log level has been switched from debug to info we cannot avoid this conditional compilation to avoid log spam.

And even then log messages in real-time threads need to be disabled at compile time by using conditional compilation. That's the reason why I also picked an example from the sound device code.

Copy link
Member

@Holzhaus Holzhaus Jun 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need something more detailed than debug? IMHO it makes more sense to switch on debug messages for the logging category you're interested in and use INFO for everything else by default.

Why do they need to be disabled in RT code? If arguments aren't processed if debug logging is disabled (as the documentation states), shouldn't this just cause one additional conditional branch per log statement? That should be OK even in performance-critical code, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String operations are too slow for realtime code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logs we are talking about in the real-time code are only used for tracing execution paths in the callback loop during development. These are invoked frequently and should not be compiled into any executable version, even if they would be real-time safe.

The Qt logging facilities will have some performance impact at runtime. And they have been designed for general purpose desktop UI applications, not for performance critical code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add a global define to enable verbose debug logs like in SoundSourceFFmpeg. These could be enabled permanently once we have switched the default log level from Debug to Info.

The local defines for trace logs in SoundDevicePortAudio are still needed as explained. I will try to add a reference to this example as blueprint.

@@ -17,6 +16,18 @@
#include "controllers/controllerdebug.h"
#include "util/assert.h"

Q_LOGGING_CATEGORY(mixxxLog, MIXXX_LOGGING_CATEGORY_ROOT)

Q_LOGGING_CATEGORY(mixxxLogConfig, MIXXX_LOGGING_CATEGORY_CONFIG)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could put all category strings in a single header file and then include them from there. That way you only need to seach-replace in a single file.

Q_DECLARE_LOGGING_CATEGORY(mixxxLogEngine)
#define MIXXX_LOGGING_CATEGORY_ENGINE \
MIXXX_LOGGING_CATEGORY_PARENT_CHILD(MIXXX_LOGGING_CATEGORY_ROOT, "engine")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Base logging category for effects
Q_DECLARE_LOGGING_CATEGORY(mixxxLogEffects)
#define MIXXX_LOGGING_CATEGORY_ENGINE \
MIXXX_LOGGING_CATEGORY_PARENT_CHILD(MIXXX_LOGGING_CATEGORY_ROOT, "effects")

I can then use this in #2618 after this is merged.

Comment on lines +69 to +72
/// Base logging category for scripting
Q_DECLARE_LOGGING_CATEGORY(mixxxLogScript)
#define MIXXX_LOGGING_CATEGORY_SCRIPT \
MIXXX_LOGGING_CATEGORY_PARENT_CHILD(MIXXX_LOGGING_CATEGORY_ROOT, "script")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Base logging category for scripting
Q_DECLARE_LOGGING_CATEGORY(mixxxLogScript)
#define MIXXX_LOGGING_CATEGORY_SCRIPT \
MIXXX_LOGGING_CATEGORY_PARENT_CHILD(MIXXX_LOGGING_CATEGORY_ROOT, "script")
/// Base logging category for controllers
Q_DECLARE_LOGGING_CATEGORY(mixxxLogController)
#define MIXXX_LOGGING_CATEGORY_SCRIPT \
MIXXX_LOGGING_CATEGORY_PARENT_CHILD(MIXXX_LOGGING_CATEGORY_ROOT, "controller")

There is info logged by the controller system apart from scripts. ControllerEngine could create a mixxx.controller.CONTROLLERNAME.script subcategory for messages from the script.

Comment on lines +123 to +124
/// in your local repo while debugging. Disable it before submitting a pull
/// request. Use qCDebug() for these trace logs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// in your local repo while debugging. Disable it before submitting a pull
/// request. Use qCDebug() for these trace logs.
/// in your local repo while debugging. Disable it before committing.
/// Use qCDebug() for these trace logs.

Comment on lines +49 to +52
/// Base logging category for hardware I/O sound devices
Q_DECLARE_LOGGING_CATEGORY(mixxxLogDevice)
#define MIXXX_LOGGING_CATEGORY_DEVICE \
MIXXX_LOGGING_CATEGORY_PARENT_CHILD(MIXXX_LOGGING_CATEGORY_ROOT, "device")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Base logging category for hardware I/O sound devices
Q_DECLARE_LOGGING_CATEGORY(mixxxLogDevice)
#define MIXXX_LOGGING_CATEGORY_DEVICE \
MIXXX_LOGGING_CATEGORY_PARENT_CHILD(MIXXX_LOGGING_CATEGORY_ROOT, "device")
/// Base logging category for hardware I/O sound devices
Q_DECLARE_LOGGING_CATEGORY(mixxxLogAudioDevice)
#define MIXXX_LOGGING_CATEGORY_DEVICE \
MIXXX_LOGGING_CATEGORY_PARENT_CHILD(MIXXX_LOGGING_CATEGORY_ROOT, "audiodevice")

"Device" by itself could mean lots of things.

# Conflicts:
#	src/sources/soundsourceffmpeg.cpp
@uklotzde uklotzde marked this pull request as draft September 4, 2020 07:04
@ywwg
Copy link
Member

ywwg commented Oct 16, 2020

Can we have an update on this PR? Do people like where it is going? What do we need to do to make it not a draft?

@uklotzde
Copy link
Contributor Author

I am not able to continue working on this until other PRs have been finished and merged.

@Be-ing Be-ing changed the base branch from master to main October 23, 2020 23:23
# Conflicts:
#	src/soundio/sounddeviceportaudio.cpp
#	src/soundio/sounddeviceportaudio.h
#	src/sources/soundsourceffmpeg.cpp
#	src/sources/soundsourceffmpeg.h
@github-actions
Copy link

This PR is marked as stale because it has been open 90 days with no activity.

@github-actions github-actions bot added the stale Stale issues that haven't been updated for a long time. label Jan 25, 2021
@uklotzde
Copy link
Contributor Author

I am tired to work on C++ code and don't plan to revive this PR.

@uklotzde uklotzde closed this Feb 23, 2021
@uklotzde
Copy link
Contributor Author

I will delete the corresponding branches in my fork eventually to get rid of the cognitive load.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
code quality stale Stale issues that haven't been updated for a long time.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants