From: Vadim Zeitlin Date: Thu, 22 Dec 2011 13:35:01 +0000 (+0000) Subject: Add wxLogFormatter to allow easier wxLog output customization. X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/4ffdb64018ac4e4d76c57914d02f8547bf445683?hp=2cc6b51b90824893ed26ff9ff08b41fe289fd26f Add wxLogFormatter to allow easier wxLog output customization. Delegate the log string creation to wxLogFormatter. This allows defining a custom object of a class derived from it to customize the log output instead of having to override DoLogRecord() in wxLog itself. Closes #13792. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@70086 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/docs/changes.txt b/docs/changes.txt index 4dfbd21c21..f231ffda88 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -448,6 +448,10 @@ Major new features in this release 2.9.4: ------ +All: + +- Added wxLogFormatter to allow customizing wxLog output (Sébastien Gallou). + All (GUI): - Added wxFilePickerCtrl::SetInitialDirectory(). diff --git a/docs/doxygen/overviews/log.h b/docs/doxygen/overviews/log.h index 9b611bf933..916343b8c2 100644 --- a/docs/doxygen/overviews/log.h +++ b/docs/doxygen/overviews/log.h @@ -11,7 +11,7 @@ @page overview_log wxLog Classes Overview Classes: wxLog, wxLogStderr, wxLogStream, wxLogTextCtrl, wxLogWindow, wxLogGui, wxLogNull, wxLogBuffer, - wxLogChain, wxLogInterposer, wxLogInterposerTemp, wxStreamToTextRedirector + wxLogChain, wxLogInterposer, wxLogInterposerTemp, wxStreamToTextRedirector, wxLogFormatter Table of contents: @li @ref overview_log_introduction @@ -20,7 +20,6 @@ Table of contents: @li @ref overview_log_mt @li @ref overview_log_customize @li @ref overview_log_tracemasks -@li @ref overview_log_timestamps
@@ -275,8 +274,15 @@ want to redirect the log output elsewhere, without taking into account the level of the message. If you do want to handle messages of different levels differently, then you should override wxLog::DoLogTextAtLevel(). -Finally, if more control over the output format is needed, then the first -function must be overridden as it allows to construct custom messages +Additionally, you can customize the way full log messages are constructed from +the components (such as time stamp, source file information, logging thread ID +and so on). This task is performed by wxLogFormatter class so you need to +derive a custom class from it and override its Format() method to build the log +messages in desired way. Notice that if you just need to modify (or suppress) +the time stamp display, overriding FormatTime() is enough. + +Finally, if even more control over the output format is needed, then +DoLogRecord() can be overridden as it allows to construct custom messages depending on the log level or even do completely different things depending on the message severity (for example, throw away all messages except warnings and errors, show warnings on the screen and forward the error @@ -321,25 +327,5 @@ wxLog::AddTraceMask( wxTRACE_OleCalls ); The standard trace masks are given in wxLogTrace() documentation. - -@section overview_log_timestamps Timestamps - -The wxLog::LogRecord() function automatically prepends a time stamp -to all the messages. The format of the time stamp may be changed: it can be -any string with % specifications fully described in the documentation of the -standard @e strftime() function. For example, the default format is -@c "[%d/%b/%y %H:%M:%S] " which gives something like @c "[17/Sep/98 22:10:16] " -(without quotes) for the current date. - -Setting an empty string as the time format or calling the shortcut wxLog::DisableTimestamp(), -disables timestamping of the messages completely. - -@note -Timestamping is disabled for Visual C++ users in debug builds by -default because otherwise it would be impossible to directly go to the line -from which the log message was generated by simply clicking in the debugger -window on the corresponding error message. If you wish to enable it, please -use SetTimestamp() explicitly. - */ diff --git a/include/wx/log.h b/include/wx/log.h index 8f872af38e..5d5f547e75 100644 --- a/include/wx/log.h +++ b/include/wx/log.h @@ -309,6 +309,34 @@ struct wxLogRecord wxLogRecordInfo info; }; +// ---------------------------------------------------------------------------- +// Derive from this class to customize format of log messages. +// ---------------------------------------------------------------------------- + +class WXDLLIMPEXP_BASE wxLogFormatter +{ +public: + // Default constructor. + wxLogFormatter() { } + + // Trivial but virtual destructor for the base class. + virtual ~wxLogFormatter() { } + + + // Override this method to implement custom formatting of the given log + // record. The default implementation simply prepends a level-dependent + // prefix to the message and optionally adds a time stamp. + virtual wxString Format(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info) const; + +protected: + // Override this method to change just the time stamp formatting. It is + // called by default Format() implementation. + virtual wxString FormatTime(time_t t) const; +}; + + // ---------------------------------------------------------------------------- // derive from this class to redirect (or suppress, or ...) log messages // normally, only a single instance of this class exists but it's not enforced @@ -318,7 +346,7 @@ class WXDLLIMPEXP_BASE wxLog { public: // ctor - wxLog() { } + wxLog() : m_formatter(new wxLogFormatter) { } // make dtor virtual for all derived classes virtual ~wxLog(); @@ -455,6 +483,26 @@ public: // call AddTraceMask() concurrently static const wxArrayString& GetTraceMasks(); + // is this trace mask in the list? + static bool IsAllowedTraceMask(const wxString& mask); + + + // log formatting + // ----------------- + + // Change wxLogFormatter object used by wxLog to format the log messages. + // + // wxLog takes ownership of the pointer passed in but the caller is + // responsible for deleting the returned pointer. + wxLogFormatter* SetFormatter(wxLogFormatter* formatter); + + + // All the time stamp related functions below only work when the default + // wxLogFormatter is being used. Defining a custom formatter overrides them + // as it could use its own time stamp format or format messages without + // using time stamp at all. + + // sets the time stamp string format: this is used as strftime() format // string for the log targets which add time stamps to the messages; set // it to empty string to disable time stamping completely. @@ -464,9 +512,6 @@ public: static void DisableTimestamp() { SetTimestamp(wxEmptyString); } - // is this trace mask in the list? - static bool IsAllowedTraceMask(const wxString& mask); - // get the current timestamp format string (maybe empty) static const wxString& GetTimestamp() { return ms_timestamp; } @@ -475,9 +520,10 @@ public: // helpers: all functions in this section are mostly for internal use only, // don't call them from your code even if they are not formally deprecated - // put the time stamp into the string if ms_timestamp != NULL (don't - // change it otherwise) + // put the time stamp into the string if ms_timestamp is not empty (don't + // change it otherwise); the first overload uses the current time. static void TimeStamp(wxString *str); + static void TimeStamp(wxString *str, time_t t); // these methods should only be called from derived classes DoLogRecord(), // DoLogTextAtLevel() and DoLogText() implementations respectively and @@ -621,6 +667,12 @@ private: const wxLogRecordInfo& info); + // variables + // ---------------- + + wxLogFormatter *m_formatter; // We own this pointer. + + // static variables // ---------------- diff --git a/interface/wx/log.h b/interface/wx/log.h index a1d249c699..314f82e24f 100644 --- a/interface/wx/log.h +++ b/interface/wx/log.h @@ -583,6 +583,102 @@ public: + +/** + @class wxLogFormatter + + wxLogFormatter class is used to format the log messages. It implements the + default formatting and can be derived from to create custom formatters. + + The default implementation formats the message into a string containing + the time stamp, level-dependent prefix and the message itself. + + To change it, you can derive from it and override its Format() method. For + example, to include the thread id in the log messages you can use + @code + class LogFormatterWithThread : public wxLogFormatter + { + virtual wxString Format(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info) const + { + return wxString::Format("[%d] %s(%d) : %s", + info.threadId, info.filename, info.line, msg); + } + }; + @endcode + And then associate it with wxLog instance using its SetFormatter(). Then, + if you call: + + @code + wxLogMessage(_("*** Application started ***")); + @endcode + + the log output could be something like: + + @verbatim + [7872] d:\testApp\src\testApp.cpp(85) : *** Application started *** + @endverbatim + + @library{wxbase} + @category{logging} + + @see @ref overview_log + + @since 2.9.4 +*/ +class wxLogFormatter +{ +public: + /** + The default ctor does nothing. + */ + wxLogFormatter(); + + + /** + This function creates the full log message string. + + Override it to customize the output string format. + + @param level + The level of this log record, e.g. ::wxLOG_Error. + @param msg + The log message itself. + @param info + All the other information (such as time, component, location...) + associated with this log record. + + @return + The formated message. + + @note + Time stamping is disabled for Visual C++ users in debug builds by + default because otherwise it would be impossible to directly go to the line + from which the log message was generated by simply clicking in the debugger + window on the corresponding error message. If you wish to enable it, override + FormatTime(). + */ + virtual wxString Format(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info) const; + +protected: + /** + This function formats the time stamp part of the log message. + + Override this function if you need to customize just the time stamp. + + @param time + Time to format. + + @return + The formated time string, may be empty. + */ + virtual wxString FormatTime(time_t time) const; +}; + + /** @class wxLog @@ -599,7 +695,7 @@ public: @note For console-mode applications, the default target is wxLogStderr, so that all @e wxLogXXX() functions print on @c stderr when @c wxUSE_GUI = 0. - @library{wxcore} + @library{wxbase} @category{logging} @see @ref overview_log, @ref group_funcmacro_log "wxLogXXX() functions" @@ -863,6 +959,9 @@ public: /** Returns the current timestamp format string. + + Notice that the current time stamp is only used by the default log + formatter and custom formatters may ignore this format. */ static const wxString& GetTimestamp(); @@ -871,12 +970,20 @@ public: messages. The string may contain any normal characters as well as % prefixed format specifiers, see @e strftime() manual for details. Passing an empty string to this function disables message time stamping. + + Notice that the current time stamp is only used by the default log + formatter and custom formatters may ignore this format. You can also + define a custom wxLogFormatter to customize the time stamp handling + beyond changing its format. */ static void SetTimestamp(const wxString& format); /** Disables time stamping of the log messages. + Notice that the current time stamp is only used by the default log + formatter and custom formatters may ignore calls to this function. + @since 2.9.0 */ static void DisableTimestamp(); @@ -899,6 +1006,19 @@ public: //@} + + /** + Sets the specified formatter as the active one. + + @param formatter + The new formatter. If @NULL, reset to the default formatter. + + Returns the pointer to the previous formatter. You must delete it + if you don't plan to attach it again to a wxLog object later. + + @since 2.9.4 + */ + wxLogFormatter *SetFormatter(wxLogFormatter* formatter); /** diff --git a/src/common/log.cpp b/src/common/log.cpp index 20b146d2de..e5070691dd 100644 --- a/src/common/log.cpp +++ b/src/common/log.cpp @@ -204,6 +204,61 @@ void wxSafeShowMessage(const wxString& title, const wxString& text) #endif } +// ---------------------------------------------------------------------------- +// wxLogFormatter class implementation +// ---------------------------------------------------------------------------- + +wxString +wxLogFormatter::Format(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info) const +{ + wxString prefix = FormatTime(info.timestamp); + + switch ( level ) + { + case wxLOG_Error: + prefix += _("Error: "); + break; + + case wxLOG_Warning: + prefix += _("Warning: "); + break; + + // don't prepend "debug/trace" prefix under MSW as it goes to the debug + // window anyhow and so can't be confused with something else +#ifndef __WXMSW__ + case wxLOG_Debug: + // this prefix (as well as the one below) is intentionally not + // translated as nobody translates debug messages anyhow + prefix += "Debug: "; + break; + + case wxLOG_Trace: + prefix += "Trace: "; + break; +#endif // !__WXMSW__ + } + + return prefix + msg; +} + +wxString +wxLogFormatter::FormatTime(time_t t) const +{ + wxString str; + + // don't time stamp debug messages under MSW as debug viewers usually + // already have an option to do it +#ifdef __WXMSW__ + if ( level != wxLOG_Debug && level != wxLOG_Trace ) +#endif // __WXMSW__ + wxLog::TimeStamp(&str, t); + + return str; +} + + // ---------------------------------------------------------------------------- // wxLog class implementation // ---------------------------------------------------------------------------- @@ -266,6 +321,8 @@ wxLog::~wxLog() gs_prevLog.numRepeated ); } + + delete m_formatter; } // ---------------------------------------------------------------------------- @@ -404,47 +461,8 @@ void wxLog::DoLogRecord(wxLogLevel level, wxUnusedVar(info); #endif // WXWIN_COMPATIBILITY_2_8/!WXWIN_COMPATIBILITY_2_8 - - // TODO: it would be better to extract message formatting in a separate - // wxLogFormatter class but for now we hard code formatting here - - wxString prefix; - - // don't time stamp debug messages under MSW as debug viewers usually - // already have an option to do it -#ifdef __WXMSW__ - if ( level != wxLOG_Debug && level != wxLOG_Trace ) -#endif // __WXMSW__ - TimeStamp(&prefix); - - // TODO: use the other wxLogRecordInfo fields - - switch ( level ) - { - case wxLOG_Error: - prefix += _("Error: "); - break; - - case wxLOG_Warning: - prefix += _("Warning: "); - break; - - // don't prepend "debug/trace" prefix under MSW as it goes to the debug - // window anyhow and so can't be confused with something else -#ifndef __WXMSW__ - case wxLOG_Debug: - // this prefix (as well as the one below) is intentionally not - // translated as nobody translates debug messages anyhow - prefix += "Debug: "; - break; - - case wxLOG_Trace: - prefix += "Trace: "; - break; -#endif // !__WXMSW__ - } - - DoLogTextAtLevel(level, prefix + msg); + // Use wxLogFormatter to format the message + DoLogTextAtLevel(level, m_formatter->Format (level, msg, info)); } void wxLog::DoLogTextAtLevel(wxLogLevel level, const wxString& msg) @@ -692,17 +710,38 @@ void wxLog::ClearTraceMasks() // wxLog miscellaneous other methods // ---------------------------------------------------------------------------- +#if wxUSE_DATETIME + void wxLog::TimeStamp(wxString *str) { -#if wxUSE_DATETIME if ( !ms_timestamp.empty() ) { *str = wxDateTime::UNow().Format(ms_timestamp); *str += wxS(": "); } -#endif // wxUSE_DATETIME } +void wxLog::TimeStamp(wxString *str, time_t t) +{ + if ( !ms_timestamp.empty() ) + { + *str = wxDateTime(t).Format(ms_timestamp); + *str += wxS(": "); + } +} + +#else // !wxUSE_DATETIME + +void wxLog::TimeStamp(wxString*) +{ +} + +void wxLog::TimeStamp(wxString*, time_t) +{ +} + +#endif // wxUSE_DATETIME/!wxUSE_DATETIME + #if wxUSE_THREADS void wxLog::FlushThreadMessages() @@ -745,6 +784,14 @@ bool wxLog::EnableThreadLogging(bool enable) #endif // wxUSE_THREADS +wxLogFormatter *wxLog::SetFormatter(wxLogFormatter* formatter) +{ + wxLogFormatter* formatterOld = m_formatter; + m_formatter = formatter ? formatter : new wxLogFormatter; + + return formatterOld; +} + void wxLog::Flush() { LogLastRepeatIfNeeded();