Search code examples
c++linuxwindowsloggingwxwidgets

how to split log messages into file and screen in wxwidgets


When I read the WxWidgets documentation, I get the impression that the developers wrote it just for themselves, just to remember what they did 20 years ago.

Regardless, I figured out how to send log messages to a file:

wxLog::SetActiveTarget(new wxLogStderr(fopen(logPath + "/wxApp.log", "w + ")));

and also I figured out how to change the format of the log messages:

wxLog::GetActiveTarget()->SetFormatter(new MyLogger);

But I didn't understand anything else.
So I want to ask my question here.

I want to make a log for my application.
Moreover, I want:

  1. all log messages to be written to a file
  2. at the same time some of these messages are displayed on the screen using wxTextCtrl.

    So I want to filter the log messages that are displayed on the screen, depending on the logging level:
    for example, I want to display in wxTextCtrl only log messages with "wxLOG_Info" and "wxLOG_Error" levels.

How can this be done in Windows and Linux in C++? It's best to show a code example.


Solution

  • I may be missing something but this seems very simple?

    For example, this could be the simplest possible log target which logs some messages into a wxTextCtrl and all of them into a wxFFile.

    #include <wx/wx.h>
    #include <wx/ffile.h>
    
    class MyLogTarget : public wxLog
    {
    public:
        // textCtrl must have longer lifetime than MyLogTarget
        MyLogTarget(wxTextCtrl* textCtrl, const wxString& fileName)
            : m_textCtrl(textCtrl), m_file(fileName, "a")
        {}
    protected:
        void DoLogTextAtLevel(wxLogLevel level, const wxString& msg) override
        {
            // preserve debug logging
            if ( level == wxLOG_Debug || level == wxLOG_Trace )
                wxLog::DoLogTextAtLevel(level, msg);
    
            if ( level == wxLOG_Info || level == wxLOG_Error )
                m_textCtrl->AppendText(msg + "\n");
            
            if ( m_file.IsOpened() )
                m_file.Write(msg + "\n");
        }
    private:
        wxTextCtrl* m_textCtrl;
        wxFFile     m_file;
    };
    
    class MyFrame : public wxFrame
    {
    public:
        MyFrame(wxWindow* parent = nullptr) : wxFrame(parent, wxID_ANY, "Test")
        {
            wxTextCtrl* logCtrl = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);
            wxLog::SetActiveTarget(new MyLogTarget(logCtrl, "log.txt"));
    
            wxLogDebug("Debug test");
            wxLogMessage("Message test");
            wxLogInfo("Info test");
            wxLogError("Error test");
        }
        ~MyFrame()
        {
            delete wxLog::SetActiveTarget(nullptr);
        }
    };
    
    class MyApp : public wxApp
    {
    public:
        bool OnInit() override
        {
            (new MyFrame())->Show();
            return true;
        }
    }; wxIMPLEMENT_APP(MyApp);
    

    Please notice that this would have to be extended to be usable in a real application, for example, handle the file encoding / flushing / cleaning / error handling (without getting into the endless logging loop), use the full path for the file (e.g., obtained with wxStandardPaths), use a log chain to preserve (some of?) the default wxWidgets logging...

    If you want to use separate log targets for logging into a text control and a file, it is still very simple except that you have to chain the log targets, as explained in the docs.