]> git.saurik.com Git - wxWidgets.git/commitdiff
Add wxLogFormatter to allow easier wxLog output customization.
authorVadim Zeitlin <vadim@wxwidgets.org>
Thu, 22 Dec 2011 13:35:01 +0000 (13:35 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Thu, 22 Dec 2011 13:35:01 +0000 (13:35 +0000)
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

docs/changes.txt
docs/doxygen/overviews/log.h
include/wx/log.h
interface/wx/log.h
src/common/log.cpp

index 4dfbd21c213cf8cf82d8e880f23a482ce0125ffd..f231ffda8854c4c673dbb3c3fc86e2b6f5e261a1 100644 (file)
@@ -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().
index 9b611bf9334fcac8e637ae3b4d2de5f850e9b684..916343b8c27322dbc810524b5dd1d530271ddebe 100644 (file)
@@ -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
 <hr>
 
 
@@ -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.
-
 */
 
index 8f872af38e273d1f3be2553cd1bee01f93af4d04..5d5f547e7532c417113295fd89d477a627e9e9d6 100644 (file)
@@ -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
     // ----------------
 
index a1d249c699f93bf6f15b47c08697f32a01ad1b61..314f82e24f09a376cc4f8124b09f4f5423bf5e63 100644 (file)
@@ -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);
     
 
     /**
index 20b146d2de65864a99ca05d6510a05cf465b34a6..e5070691dd430645208edab7be966f222212917e 100644 (file)
@@ -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();