diff --git a/docs/changes.txt b/docs/changes.txt index b1bd83d833..2f78b33680 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -129,9 +129,14 @@ Changes in behaviour not resulting in compilation errors, please read this! manual for more details. - wxTreeCtrl::GetSelection now asserts if the tree has the wxTR_MULTIPLE style. - Instead use GetSelections() for multiple items; or if you want only the + Instead use GetSelections() for multiple items; or if you want only the single item last touched, the new wxTreeCtrl::GetFocusedItem. +- Custom log targets should be updated to override wxLog::DoLogRecord() method + instead of DoLog() or DoLogString() and must be updated if they call the base + class versions of these functions as this won't work any more; please see the + documentation of wxLog for more information. + Changes in behaviour which may result in compilation errors ----------------------------------------------------------- diff --git a/include/wx/generic/logg.h b/include/wx/generic/logg.h index 627de94207..df1838f521 100644 --- a/include/wx/generic/logg.h +++ b/include/wx/generic/logg.h @@ -34,9 +34,7 @@ public: protected: // implement sink function - virtual void DoLogString(const wxString& szString, time_t t); - - wxSUPPRESS_DOLOGSTRING_HIDE_WARNING() + virtual void DoLogText(const wxString& msg); private: // the control we use @@ -63,9 +61,9 @@ public: virtual void Flush(); protected: - virtual void DoLog(wxLogLevel level, const wxString& szString, time_t t); - - wxSUPPRESS_DOLOG_HIDE_WARNING() + virtual void DoLogRecord(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info); // return the title to be used for the log dialog, depending on m_bErrors // and m_bWarnings values @@ -142,11 +140,7 @@ public: virtual void OnFrameDelete(wxFrame *frame); protected: - virtual void DoLog(wxLogLevel level, const wxString& szString, time_t t); - virtual void DoLogString(const wxString& szString, time_t t); - - wxSUPPRESS_DOLOG_HIDE_WARNING() - wxSUPPRESS_DOLOGSTRING_HIDE_WARNING() + virtual void DoLogTextAtLevel(wxLogLevel level, const wxString& msg); private: wxLogFrame *m_pLogFrame; // the log frame diff --git a/include/wx/log.h b/include/wx/log.h index 88b6475240..f3d824055b 100644 --- a/include/wx/log.h +++ b/include/wx/log.h @@ -55,6 +55,10 @@ typedef unsigned long wxLogLevel; #include "wx/dynarray.h" +#if wxUSE_THREADS + #include "wx/thread.h" +#endif // wxUSE_THREADS + // wxUSE_LOG_DEBUG enables the debug log messages #ifndef wxUSE_LOG_DEBUG #if wxDEBUG_LEVEL @@ -116,6 +120,33 @@ enum wxLogLevelValues #include "wx/iosfwrap.h" +// ---------------------------------------------------------------------------- +// information about a log record, i.e. unit of log output +// ---------------------------------------------------------------------------- + +struct wxLogRecordInfo +{ + wxLogRecordInfo() + { + timestamp = 0; + +#if wxUSE_THREADS + threadId = 0; +#endif // wxUSE_THREADS + } + + // default copy ctor, assignment operator and dtor are ok + + + // time of record generation + time_t timestamp; + +#if wxUSE_THREADS + // id of the thread which logged this record + wxThreadIdType threadId; +#endif // wxUSE_THREADS +}; + // ---------------------------------------------------------------------------- // 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 @@ -125,7 +156,11 @@ class WXDLLIMPEXP_BASE wxLog { public: // ctor - wxLog(){} + wxLog() { } + + // make dtor virtual for all derived classes + virtual ~wxLog(); + // these functions allow to completely disable all log messages @@ -136,10 +171,6 @@ public: static bool EnableLogging(bool doIt = true) { bool doLogOld = ms_doLog; ms_doLog = doIt; return doLogOld; } - // static sink function - see DoLog() for function to overload in the - // derived classes - static void OnLog(wxLogLevel level, const wxString& szString, time_t t); - // message buffering // flush shows all messages if they're not logged immediately (FILE @@ -175,7 +206,7 @@ public: static void Resume() { ms_suspendCount--; } // functions controlling the default wxLog behaviour - // verbose mode is activated by standard command-line '-verbose' + // verbose mode is activated by standard command-line '--verbose' // option static void SetVerbose(bool bVerbose = true) { ms_bVerbose = bVerbose; } @@ -234,22 +265,50 @@ public: static const wxString& GetTimestamp() { return ms_timestamp; } - // helpers + + // 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) static void TimeStamp(wxString *str); - // this method should only be called from derived classes DoLog() - // implementations and shouldn't be called directly, use logging functions - // instead - void Log(wxLogLevel level, const wxString& msg, time_t t) + // these methods should only be called from derived classes DoLogRecord(), + // DoLogTextAtLevel() and DoLogText() implementations respectively and + // shouldn't be called directly, use logging functions instead + void LogRecord(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info) { - DoLog(level, msg, t); + DoLogRecord(level, msg, info); } - // make dtor virtual for all derived classes - virtual ~wxLog(); + void LogTextAtLevel(wxLogLevel level, const wxString& msg) + { + DoLogTextAtLevel(level, msg); + } + + void LogText(const wxString& msg) + { + DoLogText(msg); + } + + // this is a helper used by wxLogXXX() functions, don't call it directly + // and see DoLog() for function to overload in the derived classes + static void OnLog(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info); + + // version called when no information about the location of the log record + // generation is available (but the time stamp is), it mainly exists for + // backwards compatibility, don't use it in new code + static void OnLog(wxLogLevel level, const wxString& msg, time_t t); + + // a helper calling the above overload with current time + static void OnLog(wxLogLevel level, const wxString& msg) + { + OnLog(level, msg, time(NULL)); + } // this method exists for backwards compatibility only, don't use @@ -257,8 +316,10 @@ public: #if WXWIN_COMPATIBILITY_2_6 // this function doesn't do anything any more, don't call it - wxDEPRECATED( static wxChar *SetLogBuffer(wxChar *buf, size_t size = 0) ); -#endif + wxDEPRECATED_INLINE( + static wxChar *SetLogBuffer(wxChar *, size_t = 0), return NULL; + ); +#endif // WXWIN_COMPATIBILITY_2_6 // don't use integer masks any more, use string trace masks instead #if WXWIN_COMPATIBILITY_2_8 @@ -269,13 +330,34 @@ public: #endif // WXWIN_COMPATIBILITY_2_8 protected: - // the logging functions that can be overridden + // the logging functions that can be overridden: DoLogRecord() is called + // for every "record", i.e. a unit of log output, to be logged and by + // default formats the message and passes it to DoLogTextAtLevel() which in + // turn passes it to DoLogText() by default + + // override this method if you want to change message formatting or do + // dynamic filtering + virtual void DoLogRecord(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info); - // default DoLog() prepends the time stamp and a prefix corresponding - // to the message to szString and then passes it to DoLogString() - virtual void DoLog(wxLogLevel level, const wxString& szString, time_t t); + // override this method to redirect output to different channels depending + // on its level only; if even the level doesn't matter, override + // DoLogText() instead + virtual void DoLogTextAtLevel(wxLogLevel level, const wxString& msg); + + // this function is not pure virtual as it might not be needed if you do + // the logging in overridden DoLogRecord() or DoLogTextAtLevel() directly + // but if you do not override them in your derived class you must override + // this one as the default implementation of it simply asserts + virtual void DoLogText(const wxString& msg); + + + // the rest of the functions are for backwards compatibility only, don't + // use them in new code; if you're updating your existing code you need to + // switch to overriding DoLogRecord/Text() above (although as long as these + // functions exist, log classes using them will continue to work) #if WXWIN_COMPATIBILITY_2_8 - // these shouldn't be used by new code wxDEPRECATED_BUT_USED_INTERNALLY( virtual void DoLog(wxLogLevel level, const char *szString, time_t t) ); @@ -283,47 +365,28 @@ protected: wxDEPRECATED_BUT_USED_INTERNALLY( virtual void DoLog(wxLogLevel level, const wchar_t *wzString, time_t t) ); -#endif // WXWIN_COMPATIBILITY_2_8 - void LogString(const wxString& szString, time_t t) - { DoLogString(szString, t); } - - // default DoLogString does nothing but is not pure virtual because if - // you override DoLog() you might not need it at all - virtual void DoLogString(const wxString& szString, time_t t); -#if WXWIN_COMPATIBILITY_2_8 // these shouldn't be used by new code - virtual void DoLogString(const char *WXUNUSED(szString), - time_t WXUNUSED(t)) {} - virtual void DoLogString(const wchar_t *WXUNUSED(szString), - time_t WXUNUSED(t)) {} + wxDEPRECATED_BUT_USED_INTERNALLY_INLINE( + virtual void DoLogString(const char *WXUNUSED(szString), + time_t WXUNUSED(t)), + ) + + wxDEPRECATED_BUT_USED_INTERNALLY_INLINE( + virtual void DoLogString(const wchar_t *WXUNUSED(wzString), + time_t WXUNUSED(t)), + ) #endif // WXWIN_COMPATIBILITY_2_8 - // this macro should be used in the derived classes to avoid warnings about - // hiding the other DoLog() overloads when overriding DoLog(wxString) -- - // but don't use it with MSVC which doesn't give this warning but does give - // warning when a deprecated function is overridden -#if WXWIN_COMPATIBILITY_2_8 && !defined(__VISUALC__) - #define wxSUPPRESS_DOLOG_HIDE_WARNING() \ - virtual void DoLog(wxLogLevel, const char *, time_t) { } \ - virtual void DoLog(wxLogLevel, const wchar_t *, time_t) { } - - #define wxSUPPRESS_DOLOGSTRING_HIDE_WARNING() \ - virtual void DoLogString(const char *, time_t) { } \ - virtual void DoLogString(const wchar_t *, time_t) { } -#else - #define wxSUPPRESS_DOLOG_HIDE_WARNING() - #define wxSUPPRESS_DOLOGSTRING_HIDE_WARNING() -#endif // log a message indicating the number of times the previous message was - // repeated if ms_prevCounter > 0, does nothing otherwise; return the old - // value of ms_prevCounter + // repeated if previous repetition counter is strictly positive, does + // nothing otherwise; return the old value of repetition counter unsigned LogLastRepeatIfNeeded(); private: // implement of LogLastRepeatIfNeeded(): it assumes that the - // caller had already locked ms_prevCS + // caller had already locked GetPreviousLogCS() unsigned LogLastRepeatIfNeededUnlocked(); // static variables @@ -333,11 +396,6 @@ private: // with the number of times it was repeated static bool ms_bRepetCounting; - static wxString ms_prevString; // previous message that was logged - static unsigned ms_prevCounter; // how many times it was repeated - static time_t ms_prevTimeStamp;// timestamp of the previous message - static wxLogLevel ms_prevLevel; // level of the previous message - static wxLog *ms_pLogger; // currently active log sink static bool ms_doLog; // false => all logging disabled static bool ms_bAutoCreate; // create new log targets on demand? @@ -378,15 +436,7 @@ public: virtual void Flush(); protected: -#if wxUSE_LOG_DEBUG || wxUSE_LOG_TRACE - virtual void DoLog(wxLogLevel level, const wxString& szString, time_t t); - - wxSUPPRESS_DOLOG_HIDE_WARNING() -#endif // wxUSE_LOG_DEBUG || wxUSE_LOG_TRACE - - virtual void DoLogString(const wxString& szString, time_t t); - - wxSUPPRESS_DOLOGSTRING_HIDE_WARNING() + virtual void DoLogTextAtLevel(wxLogLevel level, const wxString& msg); private: wxString m_str; @@ -404,9 +454,7 @@ public: protected: // implement sink function - virtual void DoLogString(const wxString& szString, time_t t); - - wxSUPPRESS_DOLOGSTRING_HIDE_WARNING() + virtual void DoLogText(const wxString& msg); FILE *m_fp; @@ -424,9 +472,7 @@ public: protected: // implement sink function - virtual void DoLogString(const wxString& szString, time_t t); - - wxSUPPRESS_DOLOGSTRING_HIDE_WARNING() + virtual void DoLogText(const wxString& msg); // using ptr here to avoid including from this file wxSTD ostream *m_ostr; @@ -499,10 +545,10 @@ public: void DetachOldLog() { m_logOld = NULL; } protected: - // pass the chain to the old logger if needed - virtual void DoLog(wxLogLevel level, const wxString& szString, time_t t); - - wxSUPPRESS_DOLOG_HIDE_WARNING() + // pass the record to the old logger if needed + virtual void DoLogRecord(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info); private: // the current log target diff --git a/interface/wx/log.h b/interface/wx/log.h index d404dafe9f..50042f4262 100644 --- a/interface/wx/log.h +++ b/interface/wx/log.h @@ -9,8 +9,7 @@ /** Different standard log levels (you may also define your own) used with - wxLog::OnLog() by standard wxLog functions wxLogError(), wxLogWarning(), - etc... + by standard wxLog functions wxLogError(), wxLogWarning(), etc... */ enum wxLogLevelValues { @@ -35,6 +34,24 @@ enum wxLogLevelValues */ typedef unsigned long wxLogLevel; +/** + Information about a log record (unit of the log output). + */ +struct wxLogRecordInfo +{ + /// Time when the log message was generated. + time_t timestamp; + + /** + Id of the thread in which the message was generated. + + This field is only available if wxWidgets was built with threads + support (wxUSE_THREADS == 1). + + @see wxThread::GetCurrentId() + */ + wxThreadIdType threadId; +}; /** @class wxLogWindow @@ -660,8 +677,7 @@ public: @section log_target Manipulating the log target The functions in this section work with and manipulate the active log - target. The OnLog() is called by the @e wxLogXXX() functions - and invokes the DoLog() of the active log target if any. + target. Get/Set methods are used to install/query the current active target and, finally, DontCreateOnDemand() disables the automatic creation of a standard @@ -670,7 +686,6 @@ public: easily lead to a loss of messages. See also: - @li OnLog() @li GetActiveTarget() @li SetActiveTarget() @li DontCreateOnDemand() @@ -767,12 +782,6 @@ public: */ static bool IsAllowedTraceMask(const wxString& mask); - /** - Forwards the message at specified level to the @e DoLog() function of the - active log target if there is any, does nothing otherwise. - */ - static void OnLog(wxLogLevel level, const wxString& msg, time_t t); - /** Remove the @a mask from the list of allowed masks for wxLogTrace(). @@ -854,54 +863,88 @@ public: static void Suspend(); /** - Log the given message. + Log the given record. This function should only be called from the DoLog() implementations in - the derived classes (which can't call wxLog::DoLog() directly as it is - protected), it should not be used for logging new messages which can be - only sent to the currently active logger using OnLog() which also - checks if the logging (for this level) is enabled while this method - just directly calls DoLog(). + the derived classes if they need to call DoLogRecord() on another log + object (they can, of course, just use wxLog::DoLogRecord() call syntax + to call it on the object itself). It should not be used for logging new + messages which can be only sent to the currently active logger using + OnLog() which also checks if the logging (for this level) is enabled + while this method just directly calls DoLog(). Example of use of this class from wxLogChain: @code - void wxLogChain::DoLog(wxLogLevel level, const wxString& msg, time_t t) + void wxLogChain::DoLogRecord(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info) { // let the previous logger show it if ( m_logOld && IsPassingMessages() ) - m_logOld->Log(level, msg, t); + m_logOld->LogRecord(level, msg, info); // and also send it to the new one if ( m_logNew && m_logNew != this ) - m_logNew->Log(level, msg, t); + m_logNew->LogRecord(level, msg, info); } @endcode - @since 2.9.0 + @since 2.9.1 */ - void Log(wxLogLevel level, const wxString& msg, time_t timestamp); + void LogRecord(wxLogLevel level, const wxString& msg, time_t timestamp); protected: + /** + @name Logging callbacks. + + The functions which should be overridden by custom log targets. + + When defining a new log target, you have a choice between overriding + DoLogRecord(), which provides maximal flexibility, DoLogTextAtLevel() + which can be used if you don't intend to change the default log + messages formatting but want to handle log messages of different levels + differently or, in the simplest case, DoLogText(). + */ + //@{ /** - Called to process the message of the specified severity. @a msg is the text - of the message as specified in the call of @e wxLogXXX() function which - generated it and @a timestamp is the moment when the message was generated. + Called to created log a new record. - The base class version prepends the timestamp to the message, adds a prefix - corresponding to the log level and then calls - DoLogString() with the resulting string. - */ - virtual void DoLog(wxLogLevel level, const wxString& msg, time_t timestamp); + Any log message created by wxLogXXX() functions is passed to this + method of the active log target. The default implementation prepends + the timestamp and, for some log levels (e.g. error and warning), the + corresponding prefix to @a msg and passes it to DoLogTextAtLevel(). + + You may override this method to implement custom formatting of the + log messages or to implement custom filtering of log messages (e.g. you + could discard all log messages coming from the given source file). + */ + virtual void DoLogRecord(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info); /** - Called to log the specified string. The timestamp is already included in the - string but still passed to this function. + Called to log the specified string at given level. - A simple implementation may just send the string to @c stdout or, better, - @c stderr. + The base class versions logs debug and trace messages on the system + default debug output channel and passes all the other messages to + DoLogText(). */ - virtual void DoLogString(const wxString& msg, time_t timestamp); + virtual void DoLogTextAtLevel(wxLogLevel level, const wxString& msg); + + /** + Called to log the specified string. + + A simple implementation might just send the string to @c stdout or + @c stderr or save it in a file (of course, the already existing + wxLogStderr can be used for this). + + The base class version of this function asserts so it must be + overridden if you don't override DoLogRecord() or DoLogTextAtLevel(). + */ + virtual void DoLogText(const wxString& msg); + + //@} }; diff --git a/src/common/log.cpp b/src/common/log.cpp index ce5bbd5c5e..4ee474bf00 100644 --- a/src/common/log.cpp +++ b/src/common/log.cpp @@ -98,6 +98,40 @@ static inline wxCriticalSection& GetPreviousLogCS() static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz); #endif +// ---------------------------------------------------------------------------- +// module globals +// ---------------------------------------------------------------------------- + +namespace +{ + +// this struct is used to store information about the previous log message used +// by OnLog() to (optionally) avoid logging multiple copies of the same message +struct PreviousLogInfo +{ + PreviousLogInfo() + { + numRepeated = 0; + } + + + // previous message itself + wxString msg; + + // its level + wxLogLevel level; + + // other information about it + wxLogRecordInfo info; + + // the number of times it was already repeated + unsigned numRepeated; +}; + +PreviousLogInfo gs_prevLog; + +} // anonymous namespace + // ============================================================================ // implementation // ============================================================================ @@ -112,8 +146,9 @@ static inline wxCriticalSection& GetPreviousLogCS() // generic log function void wxVLogGeneric(wxLogLevel level, const wxString& format, va_list argptr) { - if ( wxLog::IsEnabled() ) { - wxLog::OnLog(level, wxString::FormatV(format, argptr), time(NULL)); + if ( wxLog::IsEnabled() ) + { + wxLog::OnLog(level, wxString::FormatV(format, argptr)); } } @@ -166,10 +201,8 @@ void wxDoLogGenericUtf8(wxLogLevel level, const char *format, ...) #define IMPLEMENT_LOG_FUNCTION(level) \ void wxVLog##level(const wxString& format, va_list argptr) \ { \ - if ( wxLog::IsEnabled() ) { \ - wxLog::OnLog(wxLOG_##level, \ - wxString::FormatV(format, argptr), time(NULL)); \ - } \ + if ( wxLog::IsEnabled() ) \ + wxLog::OnLog(wxLOG_##level, wxString::FormatV(format, argptr)); \ } \ IMPLEMENT_LOG_FUNCTION_WCHAR(level) \ IMPLEMENT_LOG_FUNCTION_UTF8(level) @@ -233,10 +266,8 @@ void wxDoLogFatalErrorUtf8(const char *format, ...) void wxVLogVerbose(const wxString& format, va_list argptr) { if ( wxLog::IsEnabled() ) { - if ( wxLog::GetActiveTarget() != NULL && wxLog::GetVerbose() ) { - wxLog::OnLog(wxLOG_Info, - wxString::FormatV(format, argptr), time(NULL)); - } + if ( wxLog::GetActiveTarget() != NULL && wxLog::GetVerbose() ) + wxLog::OnLog(wxLOG_Info, wxString::FormatV(format, argptr)); } } @@ -269,8 +300,7 @@ void wxDoLogVerboseUtf8(const char *format, ...) { if ( wxLog::IsEnabled() ) { - wxLog::OnLog(wxLOG_Debug, - wxString::FormatV(format, argptr), time(NULL)); + wxLog::OnLog(wxLOG_Debug, wxString::FormatV(format, argptr)); } } @@ -302,7 +332,7 @@ void wxDoLogVerboseUtf8(const char *format, ...) wxString msg; msg << wxS("(") << mask << wxS(") ") << wxString::FormatV(format, argptr); - wxLog::OnLog(wxLOG_Trace, msg, time(NULL)); + wxLog::OnLog(wxLOG_Trace, msg); } } @@ -335,7 +365,7 @@ void wxDoLogVerboseUtf8(const char *format, ...) // that wxLogTrace(wxTraceRefCount | wxTraceOle) will only do something // if both bits are set. if ( wxLog::IsEnabled() && ((wxLog::GetTraceMask() & mask) == mask) ) { - wxLog::OnLog(wxLOG_Trace, wxString::FormatV(format, argptr), time(NULL)); + wxLog::OnLog(wxLOG_Trace, wxString::FormatV(format, argptr)); } } @@ -437,10 +467,10 @@ void WXDLLIMPEXP_BASE wxDoLogSysErrorUtf8(const char *format, ...) void WXDLLIMPEXP_BASE wxVLogSysError(long err, const wxString& format, va_list argptr) { - if ( wxLog::IsEnabled() ) { + if ( wxLog::IsEnabled() ) + { wxLog::OnLog(wxLOG_Error, - wxString::FormatV(format, argptr) + wxLogSysErrorHelper(err), - time(NULL)); + wxString::FormatV(format, argptr) + wxLogSysErrorHelper(err)); } } @@ -491,23 +521,23 @@ unsigned wxLog::LogLastRepeatIfNeeded() unsigned wxLog::LogLastRepeatIfNeededUnlocked() { - const unsigned count = ms_prevCounter; + const unsigned count = gs_prevLog.numRepeated; - if ( ms_prevCounter ) + if ( gs_prevLog.numRepeated ) { wxString msg; #if wxUSE_INTL msg.Printf(wxPLURAL("The previous message repeated once.", "The previous message repeated %lu times.", - ms_prevCounter), - ms_prevCounter); + gs_prevLog.numRepeated), + gs_prevLog.numRepeated); #else msg.Printf(wxS("The previous message was repeated %lu times."), - ms_prevCounter); + gs_prevLog.numRepeated); #endif - ms_prevCounter = 0; - ms_prevString.clear(); - DoLog(ms_prevLevel, msg, ms_prevTimeStamp); + gs_prevLog.numRepeated = 0; + gs_prevLog.msg.clear(); + DoLogRecord(gs_prevLog.level, msg, gs_prevLog.info); } return count; @@ -517,19 +547,39 @@ wxLog::~wxLog() { // Flush() must be called before destroying the object as otherwise some // messages could be lost - if ( ms_prevCounter ) + if ( gs_prevLog.numRepeated ) { wxMessageOutputDebug().Printf ( wxS("Last repeated message (\"%s\", %lu times) wasn't output"), - ms_prevString, - ms_prevCounter + gs_prevLog.msg, + gs_prevLog.numRepeated ); } } +// ---------------------------------------------------------------------------- +// wxLog logging functions +// ---------------------------------------------------------------------------- + /* static */ -void wxLog::OnLog(wxLogLevel level, const wxString& szString, time_t t) +void +wxLog::OnLog(wxLogLevel level, const wxString& msg, time_t t) +{ + wxLogRecordInfo info; + info.timestamp = t; +#if wxUSE_THREADS + info.threadId = wxThread::GetCurrentId(); +#endif // wxUSE_THREADS + + OnLog(level, msg, info); +} + +/* static */ +void +wxLog::OnLog(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info) { if ( IsEnabled() && ms_logLevel >= level ) { @@ -540,9 +590,9 @@ void wxLog::OnLog(wxLogLevel level, const wxString& szString, time_t t) { wxCRIT_SECT_LOCKER(lock, GetPreviousLogCS()); - if ( szString == ms_prevString ) + if ( msg == gs_prevLog.msg ) { - ms_prevCounter++; + gs_prevLog.numRepeated++; // nothing else to do, in particular, don't log the // repeated message @@ -552,42 +602,112 @@ void wxLog::OnLog(wxLogLevel level, const wxString& szString, time_t t) pLogger->LogLastRepeatIfNeededUnlocked(); // reset repetition counter for a new message - ms_prevString = szString; - ms_prevLevel = level; - ms_prevTimeStamp = t; + gs_prevLog.msg = msg; + gs_prevLog.level = level; + gs_prevLog.info = info; } - pLogger->DoLog(level, szString, t); + pLogger->DoLogRecord(level, msg, info); } } } -// deprecated function -#if WXWIN_COMPATIBILITY_2_6 - -wxChar *wxLog::SetLogBuffer(wxChar * WXUNUSED(buf), size_t WXUNUSED(size)) +void wxLog::DoLogRecord(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info) { - return NULL; -} - -#endif // WXWIN_COMPATIBILITY_2_6 - #if WXWIN_COMPATIBILITY_2_8 - -void wxLog::DoLog(wxLogLevel WXUNUSED(level), - const char *WXUNUSED(szString), - time_t WXUNUSED(t)) -{ -} - -void wxLog::DoLog(wxLogLevel WXUNUSED(level), - const wchar_t *WXUNUSED(wzString), - time_t WXUNUSED(t)) -{ -} - + // call the old DoLog() to ensure that existing custom log classes still + // work + // + // as the user code could have defined it as either taking "const char *" + // (in ANSI build) or "const wxChar *" (in ANSI/Unicode), we have no choice + // but to call both of them + DoLog(level, (const char*)msg.mb_str(), info.timestamp); + DoLog(level, (const wchar_t*)msg.wc_str(), info.timestamp); #endif // 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); +} + +void wxLog::DoLogTextAtLevel(wxLogLevel level, const wxString& msg) +{ + // we know about debug messages (because using wxMessageOutputDebug is the + // right thing to do in 99% of all cases and also for compatibility) but + // anything else needs to be handled in the derived class + if ( level == wxLOG_Debug || level == wxLOG_Trace ) + { + wxMessageOutputDebug().Output(msg + wxS('\n')); + } + else + { + DoLogText(msg); + } +} + +void wxLog::DoLogText(const wxString& WXUNUSED(msg)) +{ + // in 2.8-compatible build the derived class might override DoLog() or + // DoLogString() instead so we can't have this assert there +#if !WXWIN_COMPATIBILITY_2_8 + wxFAIL_MSG( "must be overridden if it is called" ); +#endif // WXWIN_COMPATIBILITY_2_8 +} + +void wxLog::DoLog(wxLogLevel WXUNUSED(level), const char *szString, time_t t) +{ + DoLogString(szString, t); +} + +void wxLog::DoLog(wxLogLevel WXUNUSED(level), const wchar_t *wzString, time_t t) +{ + DoLogString(wzString, t); +} + +// ---------------------------------------------------------------------------- +// wxLog active target management +// ---------------------------------------------------------------------------- + wxLog *wxLog::GetActiveTarget() { if ( ms_bAutoCreate && ms_pLogger == NULL ) { @@ -683,87 +803,6 @@ void wxLog::TimeStamp(wxString *str) #endif // wxUSE_DATETIME } -void wxLog::DoLog(wxLogLevel level, const wxString& szString, time_t t) -{ -#if WXWIN_COMPATIBILITY_2_8 - // DoLog() signature changed since 2.8, so we call the old versions here - // so that existing custom log classes still work: - DoLog(level, (const char*)szString.mb_str(), t); - DoLog(level, (const wchar_t*)szString.wc_str(), t); -#endif - - switch ( level ) { - case wxLOG_FatalError: - LogString(_("Fatal error: ") + szString, t); - LogString(_("Program aborted."), t); - Flush(); -#ifdef __WXWINCE__ - ExitThread(3); -#else - abort(); -#endif - break; - - case wxLOG_Error: - LogString(_("Error: ") + szString, t); - break; - - case wxLOG_Warning: - LogString(_("Warning: ") + szString, t); - break; - - case wxLOG_Info: - if ( GetVerbose() ) - case wxLOG_Message: - case wxLOG_Status: - default: // log unknown log levels too - LogString(szString, t); - break; - -#if wxUSE_LOG_DEBUG || wxUSE_LOG_TRACE -#if wxUSE_LOG_TRACE - case wxLOG_Trace: -#endif -#if wxUSE_LOG_DEBUG - case wxLOG_Debug: -#endif - { - wxString str; - - // don't prepend "debug/trace" prefix under MSW as it goes to - // the debug window anyhow and don't add time stamp neither as - // debug output viewers under Windows typically add it - // themselves anyhow - #ifndef __WXMSW__ - TimeStamp(&str); - - str += level == wxLOG_Trace ? wxT("Trace: ") - : wxT("Debug: "); - #endif // !__WXMSW__ - - str += szString; - wxMessageOutputDebug().Output(str); - } - break; -#endif // wxUSE_LOG_DEBUG || wxUSE_LOG_TRACE - } -} - -void wxLog::DoLogString(const wxString& szString, time_t t) -{ -#if WXWIN_COMPATIBILITY_2_8 - // DoLogString() signature changed since 2.8, so we call the old versions - // here so that existing custom log classes still work; unfortunately this - // also means that we can't have the wxFAIL_MSG below in compat mode - DoLogString((const char*)szString.mb_str(), t); - DoLogString((const wchar_t*)szString.wc_str(), t); -#else - wxFAIL_MSG(wxS("DoLogString must be overriden if it's called.")); - wxUnusedVar(szString); - wxUnusedVar(t); -#endif -} - void wxLog::Flush() { LogLastRepeatIfNeeded(); @@ -798,42 +837,20 @@ void wxLogBuffer::Flush() } } -#if wxUSE_LOG_DEBUG || wxUSE_LOG_TRACE - -void wxLogBuffer::DoLog(wxLogLevel level, const wxString& szString, time_t t) +void wxLogBuffer::DoLogTextAtLevel(wxLogLevel level, const wxString& msg) { // don't put debug messages in the buffer, we don't want to show // them to the user in a msg box, log them immediately - bool showImmediately = false; -#if wxUSE_LOG_TRACE - if ( level == wxLOG_Trace ) - showImmediately = true; -#endif -#if wxUSE_LOG_DEBUG - if ( level == wxLOG_Debug ) - showImmediately = true; -#endif - - if ( showImmediately ) + switch ( level ) { - wxString str; - TimeStamp(&str); - str += szString; + case wxLOG_Debug: + case wxLOG_Trace: + wxLog::DoLogTextAtLevel(level, msg); + break; - wxMessageOutputDebug dbgout; - dbgout.Printf(wxS("%s\n"), str.c_str()); + default: + m_str << msg << wxS("\n"); } - else - { - wxLog::DoLog(level, szString, t); - } -} - -#endif // wxUSE_LOG_DEBUG || wxUSE_LOG_TRACE - -void wxLogBuffer::DoLogString(const wxString& szString, time_t WXUNUSED(t)) -{ - m_str << szString << wxS("\n"); } // ---------------------------------------------------------------------------- @@ -848,14 +865,9 @@ wxLogStderr::wxLogStderr(FILE *fp) m_fp = fp; } -void wxLogStderr::DoLogString(const wxString& szString, time_t WXUNUSED(t)) +void wxLogStderr::DoLogText(const wxString& msg) { - wxString str; - TimeStamp(&str); - str << szString; - - wxFputs(str, m_fp); - wxFputc(wxS('\n'), m_fp); + wxFputs(msg + '\n', m_fp); fflush(m_fp); // under GUI systems such as Windows or Mac, programs usually don't have @@ -867,8 +879,7 @@ void wxLogStderr::DoLogString(const wxString& szString, time_t WXUNUSED(t)) wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL; if ( traits && !traits->HasStderr() ) { - wxMessageOutputDebug dbgout; - dbgout.Printf(wxS("%s\n"), str.c_str()); + wxMessageOutputDebug().Output(msg + wxS('\n')); } } } @@ -887,11 +898,9 @@ wxLogStream::wxLogStream(wxSTD ostream *ostr) m_ostr = ostr; } -void wxLogStream::DoLogString(const wxString& szString, time_t WXUNUSED(t)) +void wxLogStream::DoLogText(const wxString& msg) { - wxString stamp; - TimeStamp(&stamp); - (*m_ostr) << stamp << szString << wxSTD endl; + (*m_ostr) << msg << wxSTD endl; } #endif // wxUSE_STD_IOSTREAM @@ -933,15 +942,17 @@ void wxLogChain::Flush() m_logNew->Flush(); } -void wxLogChain::DoLog(wxLogLevel level, const wxString& szString, time_t t) +void wxLogChain::DoLogRecord(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info) { // let the previous logger show it if ( m_logOld && IsPassingMessages() ) - m_logOld->Log(level, szString, t); + m_logOld->LogRecord(level, msg, info); // and also send it to the new one if ( m_logNew && m_logNew != this ) - m_logNew->Log(level, szString, t); + m_logNew->LogRecord(level, msg, info); } #ifdef __VISUALC__ @@ -981,10 +992,6 @@ wxLogInterposerTemp::wxLogInterposerTemp() // ---------------------------------------------------------------------------- bool wxLog::ms_bRepetCounting = false; -wxString wxLog::ms_prevString; -unsigned int wxLog::ms_prevCounter = 0; -time_t wxLog::ms_prevTimeStamp= 0; -wxLogLevel wxLog::ms_prevLevel; wxLog *wxLog::ms_pLogger = NULL; bool wxLog::ms_doLog = true; diff --git a/src/generic/logg.cpp b/src/generic/logg.cpp index e6600b0ddb..08f2808d7b 100644 --- a/src/generic/logg.cpp +++ b/src/generic/logg.cpp @@ -224,16 +224,13 @@ void wxVLogStatus(wxFrame *pFrame, const wxString& format, va_list argptr) wxString msg; wxLog *pLog = wxLog::GetActiveTarget(); - if ( pLog != NULL ) { + if ( pLog != NULL ) + { msg.PrintfV(format, argptr); wxASSERT( gs_pFrame == NULL ); // should be reset! gs_pFrame = pFrame; -#ifdef __WXWINCE__ - wxLog::OnLog(wxLOG_Status, msg, 0); -#else - wxLog::OnLog(wxLOG_Status, msg, time(NULL)); -#endif + wxLog::OnLog(wxLOG_Status, msg); gs_pFrame = NULL; } } @@ -403,16 +400,19 @@ void wxLogGui::Flush() } // log all kinds of messages -void wxLogGui::DoLog(wxLogLevel level, const wxString& szString, time_t t) +void wxLogGui::DoLogRecord(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info) { - switch ( level ) { + switch ( level ) + { case wxLOG_Info: if ( GetVerbose() ) case wxLOG_Message: { - m_aMessages.Add(szString); + m_aMessages.Add(msg); m_aSeverity.Add(wxLOG_Message); - m_aTimes.Add((long)t); + m_aTimes.Add((long)info.timestamp); m_bHasMessages = true; } break; @@ -430,17 +430,11 @@ void wxLogGui::DoLog(wxLogLevel level, const wxString& szString, time_t t) } if ( pFrame && pFrame->GetStatusBar() ) - pFrame->SetStatusText(szString); + pFrame->SetStatusText(msg); } #endif // wxUSE_STATUSBAR break; - case wxLOG_FatalError: - // show this one immediately - wxMessageBox(szString, _("Fatal error"), wxICON_HAND); - wxExit(); - break; - case wxLOG_Error: if ( !m_bErrors ) { #if !wxUSE_LOG_DIALOG @@ -461,16 +455,16 @@ void wxLogGui::DoLog(wxLogLevel level, const wxString& szString, time_t t) m_bWarnings = true; } - m_aMessages.Add(szString); + m_aMessages.Add(msg); m_aSeverity.Add((int)level); - m_aTimes.Add((long)t); + m_aTimes.Add((long)info.timestamp); m_bHasMessages = true; break; default: // let the base class deal with debug/trace messages as well as any // custom levels - wxLog::DoLog(level, szString, t); + wxLog::DoLogRecord(level, msg, info); } } @@ -523,7 +517,7 @@ private: // do show the message in the text control void DoShowLogMessage(const wxString& message) { - m_pTextCtrl->AppendText(message); + m_pTextCtrl->AppendText(message + wxS('\n')); } wxTextCtrl *m_pTextCtrl; @@ -713,44 +707,21 @@ void wxLogWindow::Show(bool bShow) m_pLogFrame->Show(bShow); } -void wxLogWindow::DoLog(wxLogLevel level, const wxString& szString, time_t t) +void wxLogWindow::DoLogTextAtLevel(wxLogLevel level, const wxString& msg) { // first let the previous logger show it - wxLogPassThrough::DoLog(level, szString, t); + wxLogPassThrough::DoLogTextAtLevel(level, msg); - if ( m_pLogFrame ) { - switch ( level ) { - case wxLOG_Status: - // by default, these messages are ignored by wxLog, so process - // them ourselves - if ( !szString.empty() ) - { - wxString str; - str << _("Status: ") << szString; - LogString(str, t); - } - break; + if ( !m_pLogFrame ) + return; - // don't put trace messages in the text window for 2 reasons: - // 1) there are too many of them - // 2) they may provoke other trace messages thus sending a program - // into an infinite loop - case wxLOG_Trace: - break; - - default: - // and this will format it nicely and call our DoLogString() - wxLog::DoLog(level, szString, t); - } - } -} - -void wxLogWindow::DoLogString(const wxString& szString, time_t WXUNUSED(t)) -{ - wxString msg; - - TimeStamp(&msg); - msg << szString << wxT('\n'); + // don't put trace messages in the text window for 2 reasons: + // 1) there are too many of them + // 2) they may provoke other trace messages (e.g. wxMSW code uses + // wxLogTrace to log Windows messages and adding text to the control + // sends more of them) thus sending a program into an infinite loop + if ( level == wxLOG_Trace ) + return; m_pLogFrame->AddLogMessage(msg); } @@ -1197,13 +1168,9 @@ wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl) m_pTextCtrl = pTextCtrl; } -void wxLogTextCtrl::DoLogString(const wxString& szString, time_t WXUNUSED(t)) +void wxLogTextCtrl::DoLogText(const wxString& msg) { - wxString msg; - TimeStamp(&msg); - - msg << szString << wxT('\n'); - m_pTextCtrl->AppendText(msg); + m_pTextCtrl->AppendText(msg + wxS('\n')); } #endif // wxUSE_LOG && wxUSE_TEXTCTRL diff --git a/tests/log/logtest.cpp b/tests/log/logtest.cpp index c03862816d..089ef82c51 100644 --- a/tests/log/logtest.cpp +++ b/tests/log/logtest.cpp @@ -21,15 +21,18 @@ #include "wx/log.h" #endif // WX_PRECOMP +#include "wx/scopeguard.h" + // ---------------------------------------------------------------------------- -// test logger +// test loggers // ---------------------------------------------------------------------------- -// simple log sink which just stores the messages logged for each level -class TestLog : public wxLog +// base class for all test loggers which simply store all logged messages for +// future examination in the test code +class TestLogBase : public wxLog { public: - TestLog() { } + TestLogBase() { } wxString GetLog(wxLogLevel level) const { @@ -43,19 +46,83 @@ public: } protected: - virtual void DoLog(wxLogLevel level, const wxString& str, time_t WXUNUSED(t)) + wxString m_logs[wxLOG_Trace + 1]; + + wxDECLARE_NO_COPY_CLASS(TestLogBase); +}; + +// simple log sink which just stores the messages logged for each level +class TestLog : public TestLogBase +{ +public: + TestLog() { } + +protected: + virtual void DoLogRecord(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& WXUNUSED(info)) + { + m_logs[level] = msg; + } + +private: + wxDECLARE_NO_COPY_CLASS(TestLog); +}; + +#if WXWIN_COMPATIBILITY_2_8 + +// log sink overriding the old DoLogXXX() functions should still work too + +// this one overrides DoLog(char*) +class CompatTestLog : public TestLogBase +{ +public: + CompatTestLog() { } + +protected: + virtual void DoLog(wxLogLevel level, const char *str, time_t WXUNUSED(t)) { m_logs[level] = str; } - wxSUPPRESS_DOLOG_HIDE_WARNING() + // get rid of the warning about hiding the other overload + virtual void DoLog(wxLogLevel WXUNUSED(level), + const wchar_t *WXUNUSED(str), + time_t WXUNUSED(t)) + { + } private: - wxString m_logs[wxLOG_Trace + 1]; - - wxDECLARE_NO_COPY_CLASS(TestLog); + wxDECLARE_NO_COPY_CLASS(CompatTestLog); }; +// and this one overload DoLogString(wchar_t*) +class CompatTestLog2 : public wxLog +{ +public: + CompatTestLog2() { } + + const wxString& Get() const { return m_msg; } + +protected: + virtual void DoLogString(const wchar_t *msg, time_t WXUNUSED(t)) + { + m_msg = msg; + } + + // get rid of the warning + virtual void DoLogString(const char *WXUNUSED(msg), time_t WXUNUSED(t)) + { + } + +private: + wxString m_msg; + + wxDECLARE_NO_COPY_CLASS(CompatTestLog2); +}; + +#endif // WXWIN_COMPATIBILITY_2_8 + // ---------------------------------------------------------------------------- // test class // ---------------------------------------------------------------------------- @@ -75,6 +142,10 @@ private: #if wxDEBUG_LEVEL CPPUNIT_TEST( Trace ); #endif // wxDEBUG_LEVEL +#if WXWIN_COMPATIBILITY_2_8 + CPPUNIT_TEST( CompatLogger ); + CPPUNIT_TEST( CompatLogger2 ); +#endif // WXWIN_COMPATIBILITY_2_8 CPPUNIT_TEST_SUITE_END(); void Functions(); @@ -82,6 +153,10 @@ private: #if wxDEBUG_LEVEL void Trace(); #endif // wxDEBUG_LEVEL +#if WXWIN_COMPATIBILITY_2_8 + void CompatLogger(); + void CompatLogger2(); +#endif // WXWIN_COMPATIBILITY_2_8 TestLog *m_log; wxLog *m_logOld; @@ -159,3 +234,27 @@ void LogTestCase::Trace() } #endif // wxDEBUG_LEVEL + +#if WXWIN_COMPATIBILITY_2_8 + +void LogTestCase::CompatLogger() +{ + CompatTestLog log; + wxLog * const logOld = wxLog::SetActiveTarget(&log); + wxON_BLOCK_EXIT1( wxLog::SetActiveTarget, logOld ); + + wxLogError("Old error"); + CPPUNIT_ASSERT_EQUAL( "Old error", log.GetLog(wxLOG_Error) ); +} + +void LogTestCase::CompatLogger2() +{ + CompatTestLog2 log; + wxLog * const logOld = wxLog::SetActiveTarget(&log); + wxON_BLOCK_EXIT1( wxLog::SetActiveTarget, logOld ); + + wxLogWarning("Old warning"); + CPPUNIT_ASSERT_EQUAL( "Old warning", log.Get() ); +} + +#endif // WXWIN_COMPATIBILITY_2_8