概述

wxWidgets中提供了基本的日志记录功能wxLog类,该类定义了日志目标的标准接口以及该接口的几种标准实现以及与之配合使用的一系列功能。

wxLogXXX() 函数。它们都具有与printf() 或vprintf() 相同的语法,即它们将格式字符串作为第一个参数,并分别使用可变数量的参数或可变参数列表指针。这些都是:

  • wxLogMessage:  适用于所有普通的参考消息。默认情况下,它们还会显示在消息框中。
  • wxLogInfo
  • wxLogWarning: 发出警告,  它们通常也会显示给用户,但不要中断程序的工作。
  • wxLogError: 是用于错误消息(即必须显示给用户的消息)的函数。默认处理是弹出一个消息框,以通知用户有关此消息的信息。
  • wxLogDebug:  是调试输出函数。 它仅在调试模式下(定义了预处理器符号WXDEBUG时)才执行任何操作,而在正式模式下则为无。
  • wxLogVerbose: 用于详细输出。通常,它是隐藏的,但如果用户希望了解有关程序进度的更多详细信息,则可以激活它。
  • wxLogStatus:  用于状态消息。它们将进入活动的或指定的(作为第一个参数)wxFrame(如果有)的状态栏中。

这几个Log函数分别处在不同的等级(level),通过调用wxLog::SetLogLevel(level)可以设置当前允许输出的最高level.

默认情况下,大多数日志消息都是启用的。这意味着将处理wxWidgets代码本身日志的错误(例如wxFile::Open()在无法打开文件时记录错误),并显示给用户。要完全禁用日志记录,可以使用wxLog::EnableLogging()方法,或更常见的是使用wxLogNull类,该类会暂时禁用日志记录,并在销毁日志时将其恢复为原始设置。

如果仅记录重要消息中,可以通过wxLog::SetLogLevel()设置日志级别 ,  例如wxLOG_Warning值–这将完全禁用严重性小于警告的所有日志记录消息,因此将不再向用户显示wxLogMessage()输出。 此外,可以为不同的日志组件分别设置日志级别。



日志目标

wxWidgets具有日志目标的概念:它只是从wxLog派生的类。这样,它实现了基类的虚函数,在记录消息时会调用这些虚函数。任何时候只有一个日志目标处于活动状态,这是wxLogXXX()函数使用的目标。日志对象(即从wxLog派生的类的对象)的正常用法是通过调用SetActiveTarget()将其安装为活动目标,并且随后对wxLogXXX()函数的所有调用都会自动使用它。

要创建一个新的日志目标类,您只需要从wxLog派生它,并覆盖其中的wxLog::DoLogRecord(),wxLog::DoLogTextAtLevel()和wxLog::DoLogText()中的一个或几个即可。第一个是最灵活的,它允许您更改消息的格式,动态过滤和重定向它们等等, 所有日志消息(由wxLogFatalError()生成的消息除外)均通过此函数传递。如果您只是想将日志消息重定向到其他地方而不更改其格式,则应重写wxLog::DoLogTextAtLevel()。最后,如果您只想重定向日志消息,而目标不取决于消息日志级别,则只需重写wxLog::DoLogText()就足够了。

wxLog派生了一些预定义的类,这些类可能有助于了解如何创建新的日志目标类,当然,无需进行任何更改就可以使用它们

wxLogStderr:此类将消息记录到FILE *,默认情况下使用stderr作为名称。

wxLogStream:此类具有与wxLogStderr相同的功能,但是使用ostream和cerr而不是FILE *和stderr。

wxLogGui:这是wxWidgets应用程序的标准日志目标(如果您不执行任何操作,默认情况下会使用它),并且为给定平台提供对所有类型消息的最合理处理。

wxLogWindow:此日志目标提供一个“日志控制台”,该控制台收集由应用程序生成的所有消息,并将它们传递给先前的活动日志目标。日志窗口框架有一个菜单,允许用户清除日志,完全关闭日志或将所有消息保存到文件中。

wxLogBuffer:此目标将所有记录的消息收集在一个内部缓冲区中,以便稍后将其全部显示给用户。

wxLogNull:最后一个日志类非常特殊:它什么也不做。可以实例化此类的对象以(临时)禁止wxLogXXX() 函数的输出。例如,尝试打开不存在的文件通常会引发错误消息,但是如果由于某些原因它是不需要的,则使用以下结构

wxFile file;
// wxFile.Open() normally complains if file can't be opened, we don't want it
{
    wxLogNull logNo;
    if ( !file.Open("bar") )
    {
        // ... process error ourselves ...
    }
} // ~wxLogNull called, old log sink restored
wxLogMessage("..."); // ok



日志定制

要更改日志记录行为,可以自定义一个日志类。

定义一个继承自wxLog的类,该类在主应用程序窗口的某些部分显示所有日志消息,这些消息保留用于消息输出,而且模式消息框不会中断用户工作流程。

要自定义日志类,可以调用wxLog::SetActiveTarget() 或 创建wxAppTraits派生的类并覆盖其中wxAppTraits::CreateLogTarget() 虚拟方法,也可以将wxApp::CreateTraits() 覆盖为返回自定义日志对象的实例。

请注意,在后一种情况下,应该在程序开始期间以及程序关闭期间启动记录日志消息,因此不应该依赖主应用程序窗口的存在, 当用作wxWidgets的日志对象自动切换为使用wxLogStderr时,可以假设GUI(已经/仍然)可用。

在派生类中通过重写三个方法,以自定义日志消息处理:wxLog::DoLogRecord(),wxLog::DoLogTextAtLevel()和wxLog::DoLogText()。

如果需要对输出格式进行更多控制,则可以重写wxLog::DoLogRecord(),因为它允许根据日志级别构造自定义消息,甚至可以根据消息严重性来做完全不同的事情;

如果需要以不同的方式处理不同级别的消息,则应重写wxLog::DoLogTextAtLevel();

如果只是想将日志输出重定向到其他位置,而不考虑消息的级别,则重写wxLog::DoLogText()。

如果需要构造完整日志消息的方式(例如时间戳,源文件信息,日志记录线程ID等),需要从该类派生一个自定义类,并重写其Format()方法以按期望的方式构建日志消息, 如果只需要修改(或取消显示)时间戳显示,则覆盖FormatTime()就足够了。

日志类事例:

/*  mylog.h   */
#ifndef MYLOG_H
#define MYLOG_H

class MyLog : public wxLog
{
public:
    /************************************************************************/
    /*
    MyLog 构造函数构造一个日志信息对象
    参数信息:
    tofile	: bool类型,为true信息将被保存到文件,为0将以window的形式显示信息,默认为true
    filename    : 打印到文件名字,只有tofile为true方见效,如果tofile为false传递wxEmptyString,默认为mylog.log
    */
    /************************************************************************/
    MyLog(bool tofile = true, wxString filename = wxT("mylog.log"));
    ~MyLog();

private:
    FILE* m_logFile;
    void DoLogText(const wxString& msg) {};
};

#endif // MYLOG_H


/* mylog.cpp */
#include "MyLog.h"

#include "wx/log.h"
#include "wx/stdpaths.h"
#include "wx/file.h"
#include "wx/filename.h"

MyLog::MyLog(bool tofile, wxString filename)
    :m_logFile(NULL)
{
    wxLog::SetTimestamp(wxT("%Y-%m-%d %H:%M:%S"));
    wxLog::SetLogLevel(wxLOG_Debug);

    if(tofile)
    {
	wxString apppath;
        wxStandardPathsBase& stpd = wxStandardPaths::Get();
        wxFileName exeFile(stpd.GetExecutablePath());
        apppath = exeFile.GetPath(wxPATH_GET_VOLUME|wxPATH_GET_SEPARATOR);

        wxString file_path = apppath + filename;
        wxCharBuffer bf = file_path.ToUTF8();
        const char* c_filepath = bf.data();
        char* file_mode = "a+";
        if((m_logFile = fopen(c_filepath, file_mode)) == NULL)
        {
            wxLogError(wxT("Open file failed!"));
        }
        delete wxLog::SetActiveTarget(new wxLogStderr(m_logFile));
    } else {
	wxString LogTitle(wxT("Log Window"));
	wxLogWindow *log_window_m = new wxLogWindow(NULL, LogTitle, false, false);
	log_window_m->SetVerbose(true);
	log_window_m->Show(true);
    }
}

MyLog::~MyLog()
{
    if(m_logFile != NULL)
        fclose(m_logFile);
}

使用

/*   MyApp.h   */
#include <wx/app.h>
#include "MyLog.h"

class MyApp : public wxApp
{
    public:
        virtual bool OnInit();
        virtual int OnExit();        

        MyLog *mylog;
        ...
};


/*   MyApp.cpp   */
IMPLEMENT_APP(MyApp);   // 创建MyApp类的实例

bool MyApp::OnInit()
{
    mylog = new MyLog(true, wxT("my.log"));
    wxLogInfo("start myapp");
    ...
}

int MyApp::OnExit()
{
    if ( mylog != NULL )
    {
        delete mylog;
    }
}


/*  MyFrame.cpp */
void MyFrame::OnAbout(wxCommandEvent &event)
{
    wxString msg = wxString::Format(wxT("%s(%d: %s):%s"),__TFILE__, __LINE__, __FUNCTION__, wxT("about app"));
    wxLogInfo(msg);
    wxMessageBox(wxT("about"), wxT("Welcome to..."));
}