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

Allow suppressing GTK log messages #22424

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
135 changes: 135 additions & 0 deletions include/wx/gtk/private/log.h
@@ -0,0 +1,135 @@
///////////////////////////////////////////////////////////////////////////////
// Name: wx/gtk/private/log.h
// Purpose: Support for filtering GTK log messages.
// Author: Vadim Zeitlin
// Created: 2022-05-11
// Copyright: (c) 2022 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////

#ifndef _WX_GTK_PRIVATE_LOG_H_
#define _WX_GTK_PRIVATE_LOG_H_

#include <glib.h>

// Support for custom log writers is only available in glib 2.50 or later.
#if GLIB_CHECK_VERSION(2, 50, 0)
#define wxHAS_GLIB_LOG_WRITER
#endif

namespace wxGTKImpl
{

#ifdef wxHAS_GLIB_LOG_WRITER

// LogFilter is the base class for filtering GTK log messages
//
// Note that all members of this class are defined in src/gtk/app.cpp.
class LogFilter
{
public:
LogFilter()
{
m_next = NULL;
}

// Function to call to install this filter as the active one.
// Does nothing and just returns false if run-time glib version is too old.
bool Install();

protected:
// Function to override in the derived class to actually filter: return
// true if the message should be suppressed or false if it should be passed
// through to the default writer (which may, or not, show it).
virtual bool Filter(GLogLevelFlags log_level,
const GLogField* fields,
gsize n_fields) const = 0;

// Typically called from the derived class dtor to stop using this filter.
void Uninstall();

private:
// The function used as glib log writer.
static GLogWriterOutput
wx_log_writer(GLogLevelFlags log_level,
const GLogField *fields,
gsize n_fields,
gpointer user_data);

// False initially, set to true when we install wx_log_writer() as the log
// writer. Once we do it, we never change it any more.
static bool ms_installed;

// We maintain a simple linked list of log filters and this is the head of
// this list.
static LogFilter* ms_first;

// Next entry in the linked list, may be null.
LogFilter* m_next;

wxDECLARE_NO_COPY_CLASS(LogFilter);
};

// LogFilterByLevel filters out all the messages at the specified level.
class LogFilterByLevel : public LogFilter
{
public:
LogFilterByLevel() { }

void SetLevelToIgnore(int flags)
{
m_logLevelToIgnore = flags;
}

protected:
bool Filter(GLogLevelFlags log_level,
const GLogField* WXUNUSED(fields),
gsize WXUNUSED(n_fields)) const wxOVERRIDE
{
return log_level & m_logLevelToIgnore;
}

private:
int m_logLevelToIgnore;

wxDECLARE_NO_COPY_CLASS(LogFilterByLevel);
};

// LogFilterByMessage filters out all the messages with the specified content.
class LogFilterByMessage : public LogFilter
{
public:
// Objects of this class are supposed to be created with literal strings as
// argument, so don't bother copying the string but just use the pointer.
explicit LogFilterByMessage(const char* message)
: m_message(message)
{
// We shouldn't warn about anything if Install() failed.
m_warnNotFiltered = Install();
}

// Remove this filter when the object goes out of scope.
//
// The dtor also checks if we actually filtered the message and logs a
// trace message with the "gtklog" mask if we didn't: this allows checking
// if the filter is actually being used.
~LogFilterByMessage();

protected:
bool Filter(GLogLevelFlags WXUNUSED(log_level),
const GLogField* fields,
gsize n_fields) const wxOVERRIDE;

private:
const char* const m_message;

mutable bool m_warnNotFiltered;

wxDECLARE_NO_COPY_CLASS(LogFilterByMessage);
};

#endif // wxHAS_GLIB_LOG_WRITER

} // namespace wxGTKImpl

#endif // _WX_GTK_PRIVATE_LOG_H_
113 changes: 89 additions & 24 deletions src/gtk/app.cpp
Expand Up @@ -30,6 +30,7 @@
#include "wx/msgout.h"

#include "wx/gtk/private.h"
#include "wx/gtk/private/log.h"

#include "wx/gtk/mimetype.h"
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -178,48 +179,112 @@ bool wxApp::DoIdle()
return keepSource;
}

// Custom Glib log writer: setting it is only possible with glib 2.50 or later.
#if GLIB_CHECK_VERSION(2, 50, 0)
extern "C" {
static GLogWriterOutput
wx_log_writer(GLogLevelFlags log_level,
const GLogField *fields,
gsize n_fields,
gpointer user_data)
#ifdef wxHAS_GLIB_LOG_WRITER

namespace wxGTKImpl
{
const wxUIntPtr log_mask = reinterpret_cast<wxUIntPtr>(user_data);

GLogWriterOutput result;
if (log_level & log_mask)
bool LogFilter::ms_installed = false;
LogFilter* LogFilter::ms_first = NULL;

/* static */
GLogWriterOutput
LogFilter::wx_log_writer(GLogLevelFlags log_level,
const GLogField *fields,
gsize n_fields,
gpointer WXUNUSED(user_data))
{
for ( const LogFilter* lf = LogFilter::ms_first; lf; lf = lf->m_next )
{
result = G_LOG_WRITER_HANDLED;
if ( lf->Filter(log_level, fields, n_fields) )
return G_LOG_WRITER_HANDLED;
}
else

return g_log_writer_default(log_level, fields, n_fields, NULL);
}

bool LogFilter::Install()
{
if ( !ms_installed )
{
result = g_log_writer_default(log_level, fields, n_fields, NULL);
if ( glib_check_version(2, 50, 0) != 0 )
{
// No runtime support for log callback, we can't do anything.
return false;
}

g_log_set_writer_func(LogFilter::wx_log_writer, NULL, NULL);
ms_installed = true;
}
return result;

// Put this object in front of the linked list.
m_next = ms_first;
ms_first = this;

return true;
}

void LogFilter::Uninstall()
{
if ( !ms_installed )
{
// We don't do anything at all in this case.
return;
}

// We should be uninstalling only the currently installed filter.
wxASSERT( ms_first == this );

ms_first = m_next;
}

/* static */
void wxApp::GTKSuppressDiagnostics(int flags)
bool LogFilterByMessage::Filter(GLogLevelFlags WXUNUSED(log_level),
const GLogField* fields,
gsize n_fields) const
{
if (glib_check_version(2, 50, 0) == 0)
for ( gsize n = 0; n < n_fields; ++n )
{
g_log_set_writer_func(
wx_log_writer,
(wxUIntToPtr)(flags == -1 ? G_LOG_LEVEL_MASK : flags),
NULL);
const GLogField& f = fields[n];
if ( strcmp(f.key, "MESSAGE") == 0 )
{
if ( strcmp(static_cast<const char*>(f.value), m_message) == 0 )
{
// This is the message we want to filter.
m_warnNotFiltered = false;
return true;
}
}
}

return false;
}

LogFilterByMessage::~LogFilterByMessage()
{
Uninstall();

if ( m_warnNotFiltered )
{
wxLogTrace("gtklog", "Message \"%s\" wasn't logged.", m_message);
}
}

} // namespace wxGTKImpl

/* static */
void wxApp::GTKSuppressDiagnostics(int flags)
{
static wxGTKImpl::LogFilterByLevel s_logFilter;
s_logFilter.SetLevelToIgnore(flags);
s_logFilter.Install();
}
#else // glib < 2.50
#else // !wxHAS_GLIB_LOG_WRITER
/* static */
void wxApp::GTKSuppressDiagnostics(int WXUNUSED(flags))
{
// We can't do anything here.
}
#endif // glib >=/< 2.50
#endif // wxHAS_GLIB_LOG_WRITER/!wxHAS_GLIB_LOG_WRITER

//-----------------------------------------------------------------------------
// wxApp
Expand Down
7 changes: 7 additions & 0 deletions src/gtk/notebook.cpp
Expand Up @@ -26,6 +26,7 @@

#include "wx/gtk/private.h"
#include "wx/gtk/private/image.h"
#include "wx/gtk/private/log.h"
#include "wx/gtk/private/stylecontext.h"

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -426,6 +427,12 @@ wxNotebookPage *wxNotebook::DoRemovePage( size_t page )
if ( !client )
return NULL;

// Suppress bogus assertion failures happening deel inside ATK that can't
vadz marked this conversation as resolved.
Show resolved Hide resolved
// be avoided in any other way, see #22176.
wxGTKImpl::LogFilterByMessage filterLog(
"gtk_notebook_get_tab_label: assertion 'list != NULL' failed"
);

// we don't need to unparent the client->m_widget; GTK+ will do
// that for us (and will throw a warning if we do it!)
gtk_notebook_remove_page( GTK_NOTEBOOK(m_widget), page );
Expand Down