X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/b356aa554a82f4d82f0c12abb83eb4656204fe7a..7282b0678a879077a109495cb3cd20b28ea9cf45:/include/wx/log.h diff --git a/include/wx/log.h b/include/wx/log.h index 34168befc1..c632435b62 100644 --- a/include/wx/log.h +++ b/include/wx/log.h @@ -43,6 +43,16 @@ typedef unsigned long wxLogLevel; #include "wx/string.h" #include "wx/strvararg.h" +// ---------------------------------------------------------------------------- +// forward declarations +// ---------------------------------------------------------------------------- + +class WXDLLIMPEXP_FWD_BASE wxObject; + +#if wxUSE_GUI + class WXDLLIMPEXP_FWD_CORE wxFrame; +#endif // wxUSE_GUI + #if wxUSE_LOG #include "wx/arrstr.h" @@ -78,15 +88,18 @@ typedef unsigned long wxLogLevel; #endif #endif // wxUSE_LOG_TRACE -// ---------------------------------------------------------------------------- -// forward declarations -// ---------------------------------------------------------------------------- - -class WXDLLIMPEXP_FWD_BASE wxObject; +// wxLOG_COMPONENT identifies the component which generated the log record and +// can be #define'd to a user-defined value when compiling the user code to use +// component-based filtering (see wxLog::SetComponentLevel()) +#ifndef wxLOG_COMPONENT + // this is a variable and not a macro in order to allow the user code to + // just #define wxLOG_COMPONENT without #undef'ining it first + extern WXDLLIMPEXP_DATA_BASE(const char *) wxLOG_COMPONENT; -#if wxUSE_GUI - class WXDLLIMPEXP_FWD_CORE wxFrame; -#endif // wxUSE_GUI + #ifdef WXBUILDING + #define wxLOG_COMPONENT "wx" + #endif +#endif // ---------------------------------------------------------------------------- // constants @@ -133,18 +146,20 @@ public: // default ctor creates an uninitialized object wxLogRecordInfo() { - memset(this, 0, sizeof(this)); + memset(this, 0, sizeof(*this)); } // normal ctor, used by wxLogger specifies the location of the log // statement; its time stamp and thread id are set up here wxLogRecordInfo(const char *filename_, int line_, - const char *func_) + const char *func_, + const char *component_) { filename = filename_; func = func_; line = line_; + component = component_; timestamp = time(NULL); @@ -188,6 +203,10 @@ public: // if the compiler doesn't support __FUNCTION__) const char *func; + // the name of the component which generated this message, may be NULL if + // not set (i.e. wxLOG_COMPONENT not defined) + const char *component; + // time of record generation time_t timestamp; @@ -250,7 +269,7 @@ public: private: void Copy(const wxLogRecordInfo& other) { - memcpy(this, &other, sizeof(wxLogRecordInfo)); + memcpy(this, &other, sizeof(*this)); if ( other.m_data ) m_data = new ExtraData(*other.m_data); } @@ -270,6 +289,26 @@ private: #define wxLOG_KEY_TRACE_MASK "wx.trace_mask" +// ---------------------------------------------------------------------------- +// log record: a unit of log output +// ---------------------------------------------------------------------------- + +struct wxLogRecord +{ + wxLogRecord(wxLogLevel level_, + const wxString& msg_, + const wxLogRecordInfo& info_) + : level(level_), + msg(msg_), + info(info_) + { + } + + wxLogLevel level; + wxString msg; + wxLogRecordInfo info; +}; + // ---------------------------------------------------------------------------- // 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 @@ -285,44 +324,100 @@ public: virtual ~wxLog(); - // these functions allow to completely disable all log messages + // log messages selection + // ---------------------- + + // these functions allow to completely disable all log messages or disable + // log messages at level less important than specified for the current + // thread // is logging enabled at all now? - static bool IsEnabled() { return ms_doLog; } + static bool IsEnabled() + { +#if wxUSE_THREADS + if ( !wxThread::IsMain() ) + return IsThreadLoggingEnabled(); +#endif // wxUSE_THREADS - // is logging at this level enabled? - static bool IsLevelEnabled(wxLogLevel level) - { return IsEnabled() && level <= ms_logLevel; } + return ms_doLog; + } // change the flag state, return the previous one - static bool EnableLogging(bool doIt = true) - { bool doLogOld = ms_doLog; ms_doLog = doIt; return doLogOld; } + static bool EnableLogging(bool enable = true) + { +#if wxUSE_THREADS + if ( !wxThread::IsMain() ) + return EnableThreadLogging(enable); +#endif // wxUSE_THREADS + + bool doLogOld = ms_doLog; + ms_doLog = enable; + return doLogOld; + } + + // return the current global log level + static wxLogLevel GetLogLevel() { return ms_logLevel; } + + // set global log level: messages with level > logLevel will not be logged + static void SetLogLevel(wxLogLevel logLevel) { ms_logLevel = logLevel; } + + // set the log level for the given component + static void SetComponentLevel(const wxString& component, wxLogLevel level); + + // return the effective log level for this component, falling back to + // parent component and to the default global log level if necessary + // + // NB: component argument is passed by value and not const reference in an + // attempt to encourage compiler to avoid an extra copy: as we modify + // the component internally, we'd create one anyhow and like this it + // can be avoided if the string is a temporary anyhow + static wxLogLevel GetComponentLevel(wxString component); + + + // is logging of messages from this component enabled at this level? + // + // usually always called with wxLOG_COMPONENT as second argument + static bool IsLevelEnabled(wxLogLevel level, wxString component) + { + return IsEnabled() && level <= GetComponentLevel(component); + } + + + // enable/disable messages at wxLOG_Verbose level (only relevant if the + // current log level is greater or equal to it) + // + // notice that verbose mode can be activated by the standard command-line + // '--verbose' option + static void SetVerbose(bool bVerbose = true) { ms_bVerbose = bVerbose; } + + // check if verbose messages are enabled + static bool GetVerbose() { return ms_bVerbose; } + // message buffering + // ----------------- // flush shows all messages if they're not logged immediately (FILE - // and iostream logs don't need it, but wxGuiLog does to avoid showing + // and iostream logs don't need it, but wxLogGui does to avoid showing // 17 modal dialogs one after another) virtual void Flush(); - // flush the active target if any - static void FlushActive() - { - if ( !ms_suspendCount ) - { - wxLog *log = GetActiveTarget(); - if ( log ) - log->Flush(); - } - } + // flush the active target if any and also output any pending messages from + // background threads + static void FlushActive(); - // only one sink is active at each moment - // get current log target, will call wxApp::CreateLogTarget() to - // create one if none exists + // only one sink is active at each moment get current log target, will call + // wxAppTraits::CreateLogTarget() to create one if none exists static wxLog *GetActiveTarget(); - // change log target, pLogger may be NULL - static wxLog *SetActiveTarget(wxLog *pLogger); + // change log target, logger may be NULL + static wxLog *SetActiveTarget(wxLog *logger); + +#if wxUSE_THREADS + // change log target for the current thread only, shouldn't be called from + // the main thread as it doesn't use thread-specific log target + static wxLog *SetThreadActiveTarget(wxLog *logger); +#endif // wxUSE_THREADS // suspend the message flushing of the main target until the next call // to Resume() - this is mainly for internal use (to prevent wxYield() @@ -332,14 +427,6 @@ public: // must be called for each Suspend()! static void Resume() { ms_suspendCount--; } - // functions controlling the default wxLog behaviour - // verbose mode is activated by standard command-line '--verbose' - // option - static void SetVerbose(bool bVerbose = true) { ms_bVerbose = bVerbose; } - - // Set log level. Log messages with level > logLevel will not be logged. - static void SetLogLevel(wxLogLevel logLevel) { ms_logLevel = logLevel; } - // should GetActiveTarget() try to create a new log object if the // current is NULL? static void DontCreateOnDemand(); @@ -366,7 +453,7 @@ public: // get string trace masks: note that this is MT-unsafe if other threads can // call AddTraceMask() concurrently - static const wxArrayString& GetTraceMasks() { return ms_aTraceMasks; } + static const wxArrayString& GetTraceMasks(); // 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 @@ -377,17 +464,9 @@ public: static void DisableTimestamp() { SetTimestamp(wxEmptyString); } - // accessors - - // gets the verbose status - static bool GetVerbose() { return ms_bVerbose; } - // is this trace mask in the list? static bool IsAllowedTraceMask(const wxString& mask); - // return the current loglevel limit - static wxLogLevel GetLogLevel() { return ms_logLevel; } - // get the current timestamp format string (maybe empty) static const wxString& GetTimestamp() { return ms_timestamp; } @@ -516,9 +595,31 @@ protected: unsigned LogLastRepeatIfNeeded(); private: - // implement of LogLastRepeatIfNeeded(): it assumes that the - // caller had already locked GetPreviousLogCS() - unsigned LogLastRepeatIfNeededUnlocked(); +#if wxUSE_THREADS + // called from FlushActive() to really log any buffered messages logged + // from the other threads + void FlushThreadMessages(); + + // these functions are called for non-main thread only by IsEnabled() and + // EnableLogging() respectively + static bool IsThreadLoggingEnabled(); + static bool EnableThreadLogging(bool enable = true); +#endif // wxUSE_THREADS + + // get the active log target for the main thread, auto-creating it if + // necessary + // + // this is called from GetActiveTarget() and OnLog() when they're called + // from the main thread + static wxLog *GetMainThreadActiveTarget(); + + // called from OnLog() if it's called from the main thread or if we have a + // (presumably MT-safe) thread-specific logger and by FlushThreadMessages() + // when it plays back the buffered messages logged from the other threads + void CallDoLogNow(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info); + // static variables // ---------------- @@ -543,9 +644,6 @@ private: #if WXWIN_COMPATIBILITY_2_8 static wxTraceMask ms_ulTraceMask; // controls wxLogTrace behaviour #endif // WXWIN_COMPATIBILITY_2_8 - - // currently enabled trace masks - static wxArrayString ms_aTraceMasks; }; // ---------------------------------------------------------------------------- @@ -742,9 +840,10 @@ public: wxLogger(wxLogLevel level, const char *filename, int line, - const char *func) + const char *func, + const char *component) : m_level(level), - m_info(filename, line, func) + m_info(filename, line, func, component) { } @@ -761,12 +860,12 @@ public: // indicates that we may have an extra first argument preceding the format // string and that if we do have it, we should store it in m_info using the // given key (while by default 0 value will be used) - wxLogger& MaybeStore(const wxString& key) + wxLogger& MaybeStore(const wxString& key, wxUIntPtr value = 0) { wxASSERT_MSG( m_optKey.empty(), "can only have one optional value" ); m_optKey = key; - m_info.StoreValue(key, 0); + m_info.StoreValue(key, value); return *this; } @@ -778,7 +877,8 @@ public: void LogV(const wxString& format, va_list argptr) { // remember that fatal errors can't be disabled - if ( m_level == wxLOG_FatalError || wxLog::IsLevelEnabled(m_level) ) + if ( m_level == wxLOG_FatalError || + wxLog::IsLevelEnabled(m_level, m_info.component) ) DoCallOnLog(format, argptr); } @@ -798,6 +898,16 @@ public: LogV(format, argptr); } + void LogVTrace(const wxString& mask, const wxString& format, va_list argptr) + { + if ( !wxLog::IsAllowedTraceMask(mask) ) + return; + + Store(wxLOG_KEY_TRACE_MASK, mask); + + LogV(format, argptr); + } + // vararg functions used by wxLogXXX(): @@ -980,7 +1090,7 @@ private: void DoLogAtLevel(wxLogLevel level, const wxChar *format, ...) { - if ( !wxLog::IsLevelEnabled(level) ) + if ( !wxLog::IsLevelEnabled(level, m_info.component) ) return; va_list argptr; @@ -1049,7 +1159,7 @@ private: void DoLogAtLevelUtf8(wxLogLevel level, const char *format, ...) { - if ( !wxLog::IsLevelEnabled(level) ) + if ( !wxLog::IsLevelEnabled(level, m_info.component) ) return; va_list argptr; @@ -1094,7 +1204,7 @@ private: void DoCallOnLog(const wxString& format, va_list argptr) { - wxLog::OnLog(m_level, wxString::FormatV(format, argptr), m_info); + DoCallOnLog(m_level, format, argptr); } @@ -1155,7 +1265,7 @@ WXDLLIMPEXP_BASE const wxChar* wxSysErrorMsg(unsigned long nErrCode = 0); // creates wxLogger object for the current location #define wxMAKE_LOGGER(level) \ - wxLogger(wxLOG_##level, __FILE__, __LINE__, __WXFUNCTION__) + wxLogger(wxLOG_##level, __FILE__, __LINE__, __WXFUNCTION__, wxLOG_COMPONENT) // this macro generates the expression which logs whatever follows it in // parentheses at the level specified as argument @@ -1188,13 +1298,13 @@ WXDLLIMPEXP_BASE const wxChar* wxSysErrorMsg(unsigned long nErrCode = 0); // easily fixed by adding curly braces around wxLogError() and at least // the code still does do the right thing. #define wxDO_LOG_IF_ENABLED(level) \ - if ( !wxLog::IsLevelEnabled(wxLOG_##level) ) \ + if ( !wxLog::IsLevelEnabled(wxLOG_##level, wxLOG_COMPONENT) ) \ {} \ else \ wxDO_LOG(level) // wxLogFatalError() is special as it can't be disabled -#define wxLogFatalError wxDO_LOG(FatalError) +#define wxLogFatalError wxDO_LOG(FatalError) #define wxVLogFatalError(format, argptr) wxDO_LOGV(FatalError, format, argptr) #define wxLogError wxDO_LOG_IF_ENABLED(Error) @@ -1208,12 +1318,14 @@ WXDLLIMPEXP_BASE const wxChar* wxSysErrorMsg(unsigned long nErrCode = 0); // this one is special as it only logs if we're in verbose mode #define wxLogVerbose \ - if ( !(wxLog::IsLevelEnabled(wxLOG_Info) && wxLog::GetVerbose()) ) \ + if ( !(wxLog::IsLevelEnabled(wxLOG_Info, wxLOG_COMPONENT) && \ + wxLog::GetVerbose()) ) \ {} \ else \ wxDO_LOG(Info) #define wxVLogVerbose(format, argptr) \ - if ( !(wxLog::IsLevelEnabled(wxLOG_Info) && wxLog::GetVerbose()) ) \ + if ( !(wxLog::IsLevelEnabled(wxLOG_Info, wxLOG_COMPONENT) && \ + wxLog::GetVerbose()) ) \ {} \ else \ wxDO_LOGV(Info, format, argptr) @@ -1230,7 +1342,7 @@ WXDLLIMPEXP_BASE const wxChar* wxSysErrorMsg(unsigned long nErrCode = 0); // always evaluated, unlike for the other log functions #define wxLogGeneric wxMAKE_LOGGER(Max).LogAtLevel #define wxVLogGeneric(level, format, argptr) \ - if ( !wxLog::IsLevelEnabled(wxLOG_##level) ) \ + if ( !wxLog::IsLevelEnabled(wxLOG_##level, wxLOG_COMPONENT) ) \ {} \ else \ wxDO_LOGV(level, format, argptr) @@ -1239,19 +1351,30 @@ WXDLLIMPEXP_BASE const wxChar* wxSysErrorMsg(unsigned long nErrCode = 0); // wxLogSysError() needs to stash the error code value in the log record info // so it needs special handling too; additional complications arise because the // error code may or not be present as the first argument +// +// notice that we unfortunately can't avoid the call to wxSysErrorCode() even +// though it may be unneeded if an explicit error code is passed to us because +// the message might not be logged immediately (e.g. it could be queued for +// logging from the main thread later) and so we can't to wait until it is +// logged to determine whether we have last error or not as it will be too late +// and it will have changed already by then (in fact it even changes when +// wxString::Format() is called because of vsnprintf() inside it so it can +// change even much sooner) #define wxLOG_KEY_SYS_ERROR_CODE "wx.sys_error" #define wxLogSysError \ - if ( !wxLog::IsLevelEnabled(wxLOG_Error) ) \ + if ( !wxLog::IsLevelEnabled(wxLOG_Error, wxLOG_COMPONENT) ) \ {} \ else \ - wxMAKE_LOGGER(Error).MaybeStore(wxLOG_KEY_SYS_ERROR_CODE).Log + wxMAKE_LOGGER(Error).MaybeStore(wxLOG_KEY_SYS_ERROR_CODE, \ + wxSysErrorCode()).Log // unfortunately we can't have overloaded macros so we can't define versions // both with and without error code argument and have to rely on LogV() // overloads in wxLogger to select between them #define wxVLogSysError \ - wxMAKE_LOGGER(Error).MaybeStore(wxLOG_KEY_SYS_ERROR_CODE).LogV + wxMAKE_LOGGER(Error).MaybeStore(wxLOG_KEY_SYS_ERROR_CODE, \ + wxSysErrorCode()).LogV #if wxUSE_GUI // wxLogStatus() is similar to wxLogSysError() as it allows to optionally @@ -1259,7 +1382,7 @@ WXDLLIMPEXP_BASE const wxChar* wxSysErrorMsg(unsigned long nErrCode = 0); #define wxLOG_KEY_FRAME "wx.frame" #define wxLogStatus \ - if ( !wxLog::IsLevelEnabled(wxLOG_Status) ) \ + if ( !wxLog::IsLevelEnabled(wxLOG_Status, wxLOG_COMPONENT) ) \ {} \ else \ wxMAKE_LOGGER(Status).MaybeStore(wxLOG_KEY_FRAME).Log @@ -1289,24 +1412,24 @@ WXDLLIMPEXP_BASE const wxChar* wxSysErrorMsg(unsigned long nErrCode = 0); // WX_WATCOM_ONLY_CODE is needed to work around // http://bugzilla.openwatcom.org/show_bug.cgi?id=351 #define wxDEFINE_EMPTY_LOG_FUNCTION(level) \ - WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 1, (const wxString&)) \ + WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 1, (const wxFormatString&)) \ WX_WATCOM_ONLY_CODE( \ WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 1, (const char*)) \ WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 1, (const wchar_t*)) \ WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 1, (const wxCStrData&)) \ ) \ - inline void wxVLog##level(const wxString& WXUNUSED(format), \ + inline void wxVLog##level(const wxFormatString& WXUNUSED(format), \ va_list WXUNUSED(argptr)) { } \ #define wxDEFINE_EMPTY_LOG_FUNCTION2(level, argclass) \ - WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 2, (argclass, const wxString&)) \ + WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 2, (argclass, const wxFormatString&)) \ WX_WATCOM_OR_MINGW_ONLY_CODE( \ WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 2, (argclass, const char*)) \ WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 2, (argclass, const wchar_t*)) \ WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 2, (argclass, const wxCStrData&)) \ ) \ inline void wxVLog##level(argclass WXUNUSED(arg), \ - const wxString& WXUNUSED(format), \ + const wxFormatString& WXUNUSED(format), \ va_list WXUNUSED(argptr)) {} wxDEFINE_EMPTY_LOG_FUNCTION(FatalError); @@ -1373,16 +1496,21 @@ public: #ifdef HAVE_VARIADIC_MACROS #define wxLogDebug(fmt, ...) wxLogNop() #else // !HAVE_VARIADIC_MACROS - WX_DEFINE_VARARG_FUNC_NOP(wxLogDebug, 1, (const wxString&)) + WX_DEFINE_VARARG_FUNC_NOP(wxLogDebug, 1, (const wxFormatString&)) #endif #endif // wxUSE_LOG_DEBUG/!wxUSE_LOG_DEBUG #if wxUSE_LOG_TRACE #define wxLogTrace \ - if ( !wxLog::IsLevelEnabled(wxLOG_Trace) ) \ + if ( !wxLog::IsLevelEnabled(wxLOG_Trace, wxLOG_COMPONENT) ) \ {} \ else \ wxMAKE_LOGGER(Trace).LogTrace + #define wxVLogTrace \ + if ( !wxLog::IsLevelEnabled(wxLOG_Trace, wxLOG_COMPONENT) ) \ + {} \ + else \ + wxMAKE_LOGGER(Trace).LogVTrace #else // !wxUSE_LOG_TRACE #define wxVLogTrace(mask, fmt, valist) wxLogNop() @@ -1390,9 +1518,9 @@ public: #define wxLogTrace(mask, fmt, ...) wxLogNop() #else // !HAVE_VARIADIC_MACROS #if WXWIN_COMPATIBILITY_2_8 - WX_DEFINE_VARARG_FUNC_NOP(wxLogTrace, 2, (wxTraceMask, const wxString&)) + WX_DEFINE_VARARG_FUNC_NOP(wxLogTrace, 2, (wxTraceMask, const wxFormatString&)) #endif - WX_DEFINE_VARARG_FUNC_NOP(wxLogTrace, 2, (const wxString&, const wxString&)) + WX_DEFINE_VARARG_FUNC_NOP(wxLogTrace, 2, (const wxString&, const wxFormatString&)) #ifdef __WATCOMC__ // workaround for http://bugzilla.openwatcom.org/show_bug.cgi?id=351 WX_DEFINE_VARARG_FUNC_NOP(wxLogTrace, 2, (const char*, const char*)) @@ -1442,5 +1570,14 @@ wxSafeShowMessage(const wxString& title, const wxString& text); #undef WX_WATCOM_ONLY_CODE #endif +// macro which disables debug logging in release builds: this is done by +// default by IMPLEMENT_APP() so usually it doesn't need to be used explicitly +#ifdef NDEBUG + #define wxDISABLE_DEBUG_LOGGING_IN_RELEASE_BUILD() \ + wxLog::SetLogLevel(wxLOG_Info) +#else // !NDEBUG + #define wxDISABLE_DEBUG_LOGGING_IN_RELEASE_BUILD() +#endif // NDEBUG/!NDEBUG + #endif // _WX_LOG_H_