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
This commit is contained in:
Vadim Zeitlin
2011-12-22 13:35:01 +00:00
parent 2cc6b51b90
commit 4ffdb64018
5 changed files with 283 additions and 74 deletions

View 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().

View 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.
*/

View 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
// ----------------

View 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);
/**

View 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();