X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/bc73d5aec8ad40a3bdab1bd7af9dadb0e559c49a..0a84f928e9a273248a4843f6282507a39212d2d1:/include/wx/log.h diff --git a/include/wx/log.h b/include/wx/log.h index f3d824055b..3048b3d018 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" @@ -54,6 +64,7 @@ typedef unsigned long wxLogLevel; #endif // ! __WXPALMOS5__ #include "wx/dynarray.h" +#include "wx/hashmap.h" #if wxUSE_THREADS #include "wx/thread.h" @@ -77,13 +88,18 @@ typedef unsigned long wxLogLevel; #endif #endif // wxUSE_LOG_TRACE -// ---------------------------------------------------------------------------- -// forward declarations -// ---------------------------------------------------------------------------- +// 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 @@ -124,19 +140,72 @@ enum wxLogLevelValues // information about a log record, i.e. unit of log output // ---------------------------------------------------------------------------- -struct wxLogRecordInfo +class wxLogRecordInfo { +public: + // default ctor creates an uninitialized object wxLogRecordInfo() { - timestamp = 0; + 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 *component_) + { + filename = filename_; + func = func_; + line = line_; + component = component_; + + timestamp = time(NULL); #if wxUSE_THREADS - threadId = 0; + threadId = wxThread::GetCurrentId(); #endif // wxUSE_THREADS + + m_data = NULL; + } + + // we need to define copy ctor and assignment operator because of m_data + wxLogRecordInfo(const wxLogRecordInfo& other) + { + Copy(other); } - // default copy ctor, assignment operator and dtor are ok + wxLogRecordInfo& operator=(const wxLogRecordInfo& other) + { + if ( &other != this ) + { + delete m_data; + Copy(other); + } + return *this; + } + + // dtor is non-virtual, this class is not meant to be derived from + ~wxLogRecordInfo() + { + delete m_data; + } + + + // the file name and line number of the file where the log record was + // generated, if available or NULL and 0 otherwise + const char *filename; + int line; + + // the name of the function where the log record was generated (may be NULL + // 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; @@ -145,6 +214,99 @@ struct wxLogRecordInfo // id of the thread which logged this record wxThreadIdType threadId; #endif // wxUSE_THREADS + + + // store an arbitrary value in this record context + // + // wxWidgets always uses keys starting with "wx.", e.g. "wx.sys_error" + void StoreValue(const wxString& key, wxUIntPtr val) + { + if ( !m_data ) + m_data = new ExtraData; + + m_data->numValues[key] = val; + } + + void StoreValue(const wxString& key, const wxString& val) + { + if ( !m_data ) + m_data = new ExtraData; + + m_data->strValues[key] = val; + } + + + // these functions retrieve the value of either numeric or string key, + // return false if not found + bool GetNumValue(const wxString& key, wxUIntPtr *val) const + { + if ( !m_data ) + return false; + + wxStringToNumHashMap::const_iterator it = m_data->numValues.find(key); + if ( it == m_data->numValues.end() ) + return false; + + *val = it->second; + + return true; + } + + bool GetStrValue(const wxString& key, wxString *val) const + { + if ( !m_data ) + return false; + + wxStringToStringHashMap::const_iterator it = m_data->strValues.find(key); + if ( it == m_data->strValues.end() ) + return false; + + *val = it->second; + + return true; + } + +private: + void Copy(const wxLogRecordInfo& other) + { + memcpy(this, &other, sizeof(*this)); + if ( other.m_data ) + m_data = new ExtraData(*other.m_data); + } + + // extra data associated with the log record: this is completely optional + // and can be used to pass information from the log function to the log + // sink (e.g. wxLogSysError() uses this to pass the error code) + struct ExtraData + { + wxStringToNumHashMap numValues; + wxStringToStringHashMap strValues; + }; + + // NULL if not used + ExtraData *m_data; +}; + +#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; }; // ---------------------------------------------------------------------------- @@ -162,40 +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 disabled now? - static bool IsEnabled() { return ms_doLog; } + // is logging enabled at all now? + static bool IsEnabled() + { +#if wxUSE_THREADS + if ( !wxThread::IsMain() ) + return IsThreadLoggingEnabled(); +#endif // wxUSE_THREADS + + 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() @@ -205,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(); @@ -250,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; } @@ -325,8 +531,10 @@ public: #if WXWIN_COMPATIBILITY_2_8 wxDEPRECATED_INLINE( static void SetTraceMask(wxTraceMask ulMask), ms_ulTraceMask = ulMask; ) - wxDEPRECATED_BUT_USED_INTERNALLY_INLINE( static wxTraceMask GetTraceMask(), - return ms_ulTraceMask; ) + + // this one can't be marked deprecated as it's used in our own wxLogger + // below but it still is deprecated and shouldn't be used + static wxTraceMask GetTraceMask() { return ms_ulTraceMask; } #endif // WXWIN_COMPATIBILITY_2_8 protected: @@ -334,7 +542,7 @@ protected: // 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, @@ -370,11 +578,13 @@ protected: wxDEPRECATED_BUT_USED_INTERNALLY_INLINE( virtual void DoLogString(const char *WXUNUSED(szString), time_t WXUNUSED(t)), + wxEMPTY_PARAMETER_VALUE ) wxDEPRECATED_BUT_USED_INTERNALLY_INLINE( virtual void DoLogString(const wchar_t *WXUNUSED(wzString), time_t WXUNUSED(t)), + wxEMPTY_PARAMETER_VALUE ) #endif // WXWIN_COMPATIBILITY_2_8 @@ -385,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 // ---------------- @@ -593,15 +825,404 @@ private: #include "wx/generic/logg.h" #endif // wxUSE_GUI +// ---------------------------------------------------------------------------- +// wxLogger +// ---------------------------------------------------------------------------- + +// wxLogger is a helper class used by wxLogXXX() functions implementation, +// don't use it directly as it's experimental and subject to change (OTOH it +// might become public in the future if it's deemed to be useful enough) + +// contains information about the context from which a log message originates +// and provides Log() vararg method which forwards to wxLog::OnLog() and passes +// this context to it +class wxLogger +{ +public: + // ctor takes the basic information about the log record + wxLogger(wxLogLevel level, + const char *filename, + int line, + const char *func, + const char *component) + : m_level(level), + m_info(filename, line, func, component) + { + } + + // store extra data in our log record and return this object itself (so + // that further calls to its functions could be chained) + template + wxLogger& Store(const wxString& key, T val) + { + m_info.StoreValue(key, val); + return *this; + } + + // hack for "overloaded" wxLogXXX() functions: calling this method + // 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, wxUIntPtr value = 0) + { + wxASSERT_MSG( m_optKey.empty(), "can only have one optional value" ); + m_optKey = key; + + m_info.StoreValue(key, value); + return *this; + } + + + // non-vararg function used by wxVLogXXX(): + + // log the message at the level specified in the ctor if this log message + // is enabled + 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, m_info.component) ) + DoCallOnLog(format, argptr); + } + + // overloads used by functions with optional leading arguments (whose + // values are stored in the key passed to MaybeStore()) + void LogV(long num, const wxString& format, va_list argptr) + { + Store(m_optKey, num); + + LogV(format, argptr); + } + + void LogV(void *ptr, const wxString& format, va_list argptr) + { + Store(m_optKey, wxPtrToUInt(ptr)); + + 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(): + + // will log the message at the level specified in the ctor + // + // notice that this function supposes that the caller already checked that + // the level was enabled and does no checks itself + WX_DEFINE_VARARG_FUNC_VOID + ( + Log, + 1, (const wxFormatString&), + DoLog, DoLogUtf8 + ) + + // same as Log() but with an extra numeric or pointer parameters: this is + // used to pass an optional value by storing it in m_info under the name + // passed to MaybeStore() and is required to support "overloaded" versions + // of wxLogStatus() and wxLogSysError() + WX_DEFINE_VARARG_FUNC_VOID + ( + Log, + 2, (long, const wxFormatString&), + DoLogWithNum, DoLogWithNumUtf8 + ) + + // unfortunately we can't use "void *" here as we get overload ambiguities + // with Log(wxFormatString, ...) when the first argument is a "char *" or + // "wchar_t *" then -- so we only allow passing wxObject here, which is + // ugly but fine in practice as this overload is only used by wxLogStatus() + // whose first argument is a wxFrame + WX_DEFINE_VARARG_FUNC_VOID + ( + Log, + 2, (wxObject *, const wxFormatString&), + DoLogWithPtr, DoLogWithPtrUtf8 + ) + + // log the message at the level specified as its first argument + // + // as the macros don't have access to the level argument in this case, this + // function does check that the level is enabled itself + WX_DEFINE_VARARG_FUNC_VOID + ( + LogAtLevel, + 2, (wxLogLevel, const wxFormatString&), + DoLogAtLevel, DoLogAtLevelUtf8 + ) + + // special versions for wxLogTrace() which is passed either string or + // integer mask as first argument determining whether the message should be + // logged or not + WX_DEFINE_VARARG_FUNC_VOID + ( + LogTrace, + 2, (const wxString&, const wxFormatString&), + DoLogTrace, DoLogTraceUtf8 + ) + +#if WXWIN_COMPATIBILITY_2_8 + WX_DEFINE_VARARG_FUNC_VOID + ( + LogTrace, + 2, (wxTraceMask, const wxFormatString&), + DoLogTraceMask, DoLogTraceMaskUtf8 + ) +#endif // WXWIN_COMPATIBILITY_2_8 + +#ifdef __WATCOMC__ + // workaround for http://bugzilla.openwatcom.org/show_bug.cgi?id=351 + WX_VARARG_WATCOM_WORKAROUND(void, Log, + 1, (const wxString&), + (wxFormatString(f1))) + WX_VARARG_WATCOM_WORKAROUND(void, Log, + 1, (const wxCStrData&), + (wxFormatString(f1))) + WX_VARARG_WATCOM_WORKAROUND(void, Log, + 1, (const char*), + (wxFormatString(f1))) + WX_VARARG_WATCOM_WORKAROUND(void, Log, + 1, (const wchar_t*), + (wxFormatString(f1))) + + WX_VARARG_WATCOM_WORKAROUND(void, Log, + 2, (long, const wxString&), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, Log, + 2, (long, const wxCStrData&), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, Log, + 2, (long, const char *), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, Log, + 2, (long, const wchar_t *), + (f1, wxFormatString(f2))) + + WX_VARARG_WATCOM_WORKAROUND(void, Log, + 2, (wxObject *, const wxString&), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, Log, + 2, (wxObject *, const wxCStrData&), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, Log, + 2, (wxObject *, const char *), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, Log, + 2, (wxObject *, const wchar_t *), + (f1, wxFormatString(f2))) + + WX_VARARG_WATCOM_WORKAROUND(void, LogAtLevel, + 2, (wxLogLevel, const wxString&), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, LogAtLevel, + 2, (wxLogLevel, const wxCStrData&), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, LogAtLevel, + 2, (wxLogLevel, const char *), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, LogAtLevel, + 2, (wxLogLevel, const wchar_t *), + (f1, wxFormatString(f2))) + + WX_VARARG_WATCOM_WORKAROUND(void, LogTrace, + 2, (const wxString&, const wxString&), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, LogTrace, + 2, (const wxString&, const wxCStrData&), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, LogTrace, + 2, (const wxString&, const char *), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, LogTrace, + 2, (const wxString&, const wchar_t *), + (f1, wxFormatString(f2))) + +#if WXWIN_COMPATIBILITY_2_8 + WX_VARARG_WATCOM_WORKAROUND(void, LogTrace, + 2, (wxTraceMask, wxTraceMask), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, LogTrace, + 2, (wxTraceMask, const wxCStrData&), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, LogTrace, + 2, (wxTraceMask, const char *), + (f1, wxFormatString(f2))) + WX_VARARG_WATCOM_WORKAROUND(void, LogTrace, + 2, (wxTraceMask, const wchar_t *), + (f1, wxFormatString(f2))) +#endif // WXWIN_COMPATIBILITY_2_8 +#endif // __WATCOMC__ + +private: +#if !wxUSE_UTF8_LOCALE_ONLY + void DoLog(const wxChar *format, ...) + { + va_list argptr; + va_start(argptr, format); + DoCallOnLog(format, argptr); + va_end(argptr); + } + + void DoLogWithNum(long num, const wxChar *format, ...) + { + Store(m_optKey, num); + + va_list argptr; + va_start(argptr, format); + DoCallOnLog(format, argptr); + va_end(argptr); + } + + void DoLogWithPtr(void *ptr, const wxChar *format, ...) + { + Store(m_optKey, wxPtrToUInt(ptr)); + + va_list argptr; + va_start(argptr, format); + DoCallOnLog(format, argptr); + va_end(argptr); + } + + void DoLogAtLevel(wxLogLevel level, const wxChar *format, ...) + { + if ( !wxLog::IsLevelEnabled(level, m_info.component) ) + return; + + va_list argptr; + va_start(argptr, format); + DoCallOnLog(level, format, argptr); + va_end(argptr); + } + + void DoLogTrace(const wxString& mask, const wxChar *format, ...) + { + if ( !wxLog::IsAllowedTraceMask(mask) ) + return; + + Store(wxLOG_KEY_TRACE_MASK, mask); + + va_list argptr; + va_start(argptr, format); + DoCallOnLog(format, argptr); + va_end(argptr); + } + +#if WXWIN_COMPATIBILITY_2_8 + void DoLogTraceMask(wxTraceMask mask, const wxChar *format, ...) + { + if ( (wxLog::GetTraceMask() & mask) != mask ) + return; + + Store(wxLOG_KEY_TRACE_MASK, mask); + + va_list argptr; + va_start(argptr, format); + DoCallOnLog(format, argptr); + va_end(argptr); + } +#endif // WXWIN_COMPATIBILITY_2_8 +#endif // !wxUSE_UTF8_LOCALE_ONLY + +#if wxUSE_UNICODE_UTF8 + void DoLogUtf8(const char *format, ...) + { + va_list argptr; + va_start(argptr, format); + DoCallOnLog(format, argptr); + va_end(argptr); + } + + void DoLogWithNumUtf8(long num, const char *format, ...) + { + Store(m_optKey, num); + + va_list argptr; + va_start(argptr, format); + DoCallOnLog(format, argptr); + va_end(argptr); + } + + void DoLogWithPtrUtf8(void *ptr, const char *format, ...) + { + Store(m_optKey, wxPtrToUInt(ptr)); + + va_list argptr; + va_start(argptr, format); + DoCallOnLog(format, argptr); + va_end(argptr); + } + + void DoLogAtLevelUtf8(wxLogLevel level, const char *format, ...) + { + if ( !wxLog::IsLevelEnabled(level, m_info.component) ) + return; + + va_list argptr; + va_start(argptr, format); + DoCallOnLog(level, format, argptr); + va_end(argptr); + } + + void DoLogTraceUtf8(const wxString& mask, const char *format, ...) + { + if ( !wxLog::IsAllowedTraceMask(mask) ) + return; + + Store(wxLOG_KEY_TRACE_MASK, mask); + + va_list argptr; + va_start(argptr, format); + DoCallOnLog(format, argptr); + va_end(argptr); + } + +#if WXWIN_COMPATIBILITY_2_8 + void DoLogTraceMaskUtf8(wxTraceMask mask, const char *format, ...) + { + if ( (wxLog::GetTraceMask() & mask) != mask ) + return; + + Store(wxLOG_KEY_TRACE_MASK, mask); + + va_list argptr; + va_start(argptr, format); + DoCallOnLog(format, argptr); + va_end(argptr); + } +#endif // WXWIN_COMPATIBILITY_2_8 +#endif // wxUSE_UNICODE_UTF8 + + void DoCallOnLog(wxLogLevel level, const wxString& format, va_list argptr) + { + wxLog::OnLog(level, wxString::FormatV(format, argptr), m_info); + } + + void DoCallOnLog(const wxString& format, va_list argptr) + { + DoCallOnLog(m_level, format, argptr); + } + + + const wxLogLevel m_level; + wxLogRecordInfo m_info; + + wxString m_optKey; + + wxDECLARE_NO_COPY_CLASS(wxLogger); +}; + // ============================================================================ // global functions // ============================================================================ -// ---------------------------------------------------------------------------- -// Log functions should be used by application instead of stdio, iostream &c -// for log messages for easy redirection -// ---------------------------------------------------------------------------- - // ---------------------------------------------------------------------------- // get error code/error message from system in a portable way // ---------------------------------------------------------------------------- @@ -613,76 +1234,165 @@ WXDLLIMPEXP_BASE unsigned long wxSysErrorCode(); WXDLLIMPEXP_BASE const wxChar* wxSysErrorMsg(unsigned long nErrCode = 0); // ---------------------------------------------------------------------------- -// define wxLog +// define wxLog() functions which can be used by application instead of +// stdio, iostream &c for log messages for easy redirection // ---------------------------------------------------------------------------- -#define DECLARE_LOG_FUNCTION(level) \ - extern void WXDLLIMPEXP_BASE \ - wxDoLog##level##Wchar(const wxChar *format, ...); \ - extern void WXDLLIMPEXP_BASE \ - wxDoLog##level##Utf8(const char *format, ...); \ - WX_DEFINE_VARARG_FUNC_VOID(wxLog##level, \ - 1, (const wxFormatString&), \ - wxDoLog##level##Wchar, wxDoLog##level##Utf8) \ - DECLARE_LOG_FUNCTION_WATCOM(level) \ - extern void WXDLLIMPEXP_BASE wxVLog##level(const wxString& format, \ - va_list argptr) +/* + The code below is unreadable because it (unfortunately unavoidably) + contains a lot of macro magic but all it does is to define wxLogXXX() such + that you can call them as vararg functions to log a message at the + corresponding level. + + More precisely, it defines: + + - wxLog{FatalError,Error,Warning,Message,Verbose,Debug}() functions + taking the format string and additional vararg arguments if needed. + - wxLogGeneric(wxLogLevel level, const wxString& format, ...) which + takes the log level explicitly. + - wxLogSysError(const wxString& format, ...) and wxLogSysError(long + err, const wxString& format, ...) which log a wxLOG_Error severity + message with the error message corresponding to the system error code + err or the last error. + - wxLogStatus(const wxString& format, ...) which logs the message into + the status bar of the main application window and its overload + wxLogStatus(wxFrame *frame, const wxString& format, ...) which logs it + into the status bar of the specified frame. + - wxLogTrace(Mask mask, const wxString& format, ...) which only logs + the message is the specified mask is enabled. This comes in two kinds: + Mask can be a wxString or a long. Both are deprecated. + + In addition, wxVLogXXX() versions of all the functions above are also + defined. They take a va_list argument instead of "...". + */ -#ifdef __WATCOMC__ - // workaround for http://bugzilla.openwatcom.org/show_bug.cgi?id=351; - // can't use WX_WATCOM_ONLY_CODE here because the macro would expand to - // something too big for Borland C++ to handle - #define DECLARE_LOG_FUNCTION_WATCOM(level) \ - WX_VARARG_WATCOM_WORKAROUND(void, wxLog##level, \ - 1, (const wxString&), \ - (wxFormatString(f1))) \ - WX_VARARG_WATCOM_WORKAROUND(void, wxLog##level, \ - 1, (const wxCStrData&), \ - (wxFormatString(f1))) \ - WX_VARARG_WATCOM_WORKAROUND(void, wxLog##level, \ - 1, (const char*), \ - (wxFormatString(f1))) \ - WX_VARARG_WATCOM_WORKAROUND(void, wxLog##level, \ - 1, (const wchar_t*), \ - (wxFormatString(f1))) -#else - #define DECLARE_LOG_FUNCTION_WATCOM(level) -#endif +// creates wxLogger object for the current location +#define wxMAKE_LOGGER(level) \ + 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 +#define wxDO_LOG(level) wxMAKE_LOGGER(level).Log -#define DECLARE_LOG_FUNCTION2_EXP(level, argclass, arg, expdecl) \ - extern void expdecl wxDoLog##level##Wchar(argclass arg, \ - const wxChar *format, ...); \ - extern void expdecl wxDoLog##level##Utf8(argclass arg, \ - const char *format, ...); \ - WX_DEFINE_VARARG_FUNC_VOID(wxLog##level, \ - 2, (argclass, const wxFormatString&), \ - wxDoLog##level##Wchar, wxDoLog##level##Utf8) \ - DECLARE_LOG_FUNCTION2_EXP_WATCOM(level, argclass, arg, expdecl) \ - extern void expdecl wxVLog##level(argclass arg, \ - const wxString& format, \ - va_list argptr) +// this is the non-vararg equivalent +#define wxDO_LOGV(level, format, argptr) \ + wxMAKE_LOGGER(level).LogV(format, argptr) -#ifdef __WATCOMC__ - // workaround for http://bugzilla.openwatcom.org/show_bug.cgi?id=351; - // can't use WX_WATCOM_ONLY_CODE here because the macro would expand to - // something too big for Borland C++ to handle - #define DECLARE_LOG_FUNCTION2_EXP_WATCOM(level, argclass, arg, expdecl) \ - WX_VARARG_WATCOM_WORKAROUND(void, wxLog##level, \ - 2, (argclass, const wxString&), \ - (f1, wxFormatString(f2))) \ - WX_VARARG_WATCOM_WORKAROUND(void, wxLog##level, \ - 2, (argclass, const wxCStrData&), \ - (f1, wxFormatString(f2))) \ - WX_VARARG_WATCOM_WORKAROUND(void, wxLog##level, \ - 2, (argclass, const char*), \ - (f1, wxFormatString(f2))) \ - WX_VARARG_WATCOM_WORKAROUND(void, wxLog##level, \ - 2, (argclass, const wchar_t*), \ - (f1, wxFormatString(f2))) -#else - #define DECLARE_LOG_FUNCTION2_EXP_WATCOM(level, argclass, arg, expdecl) -#endif +// this macro declares wxLog() macro which logs whatever follows it if +// logging at specified level is enabled (notice that if it is false, the +// following arguments are not even evaluated which is good as it avoids +// unnecessary overhead) +// +// Note: the strange if/else construct is needed to make the following code +// +// if ( cond ) +// wxLogError("!!!"); +// else +// ... +// +// work as expected, without it the second "else" would match the "if" +// inside wxLogError(). Unfortunately code like +// +// if ( cond ) +// wxLogError("!!!"); +// +// now provokes "suggest explicit braces to avoid ambiguous 'else'" +// warnings from g++ 4.3 and later with -Wparentheses on but they can be +// 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, wxLOG_COMPONENT) ) \ + {} \ + else \ + wxDO_LOG(level) + +// wxLogFatalError() is special as it can't be disabled +#define wxLogFatalError wxDO_LOG(FatalError) +#define wxVLogFatalError(format, argptr) wxDO_LOGV(FatalError, format, argptr) + +#define wxLogError wxDO_LOG_IF_ENABLED(Error) +#define wxVLogError(format, argptr) wxDO_LOGV(Error, format, argptr) + +#define wxLogWarning wxDO_LOG_IF_ENABLED(Warning) +#define wxVLogWarning(format, argptr) wxDO_LOGV(Warning, format, argptr) + +#define wxLogMessage wxDO_LOG_IF_ENABLED(Message) +#define wxVLogMessage(format, argptr) wxDO_LOGV(Message, format, argptr) + +// this one is special as it only logs if we're in verbose mode +#define wxLogVerbose \ + if ( !(wxLog::IsLevelEnabled(wxLOG_Info, wxLOG_COMPONENT) && \ + wxLog::GetVerbose()) ) \ + {} \ + else \ + wxDO_LOG(Info) +#define wxVLogVerbose(format, argptr) \ + if ( !(wxLog::IsLevelEnabled(wxLOG_Info, wxLOG_COMPONENT) && \ + wxLog::GetVerbose()) ) \ + {} \ + else \ + wxDO_LOGV(Info, format, argptr) + +// deprecated synonyms for wxLogVerbose() and wxVLogVerbose() +#define wxLogInfo wxLogVerbose +#define wxVLogInfo wxVLogVerbose + + +// another special case: the level is passed as first argument of the function +// and so is not available to the macro +// +// notice that because of this, arguments of wxLogGeneric() are currently +// always evaluated, unlike for the other log functions +#define wxLogGeneric wxMAKE_LOGGER(Max).LogAtLevel +#define wxVLogGeneric(level, format, argptr) \ + if ( !wxLog::IsLevelEnabled(wxLOG_##level, wxLOG_COMPONENT) ) \ + {} \ + else \ + wxDO_LOGV(level, format, argptr) + + +// 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, wxLOG_COMPONENT) ) \ + {} \ + else \ + 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, \ + wxSysErrorCode()).LogV + +#if wxUSE_GUI + // wxLogStatus() is similar to wxLogSysError() as it allows to optionally + // specify the frame to which the message should go + #define wxLOG_KEY_FRAME "wx.frame" + + #define wxLogStatus \ + if ( !wxLog::IsLevelEnabled(wxLOG_Status, wxLOG_COMPONENT) ) \ + {} \ + else \ + wxMAKE_LOGGER(Status).MaybeStore(wxLOG_KEY_FRAME).Log + + #define wxVLogStatus(format, argptr) \ + wxMAKE_LOGGER(Status).MaybeStore(wxLOG_KEY_FRAME).LogV +#endif // wxUSE_GUI #else // !wxUSE_LOG @@ -693,13 +1403,6 @@ WXDLLIMPEXP_BASE const wxChar* wxSysErrorMsg(unsigned long nErrCode = 0); #undef wxUSE_LOG_TRACE #define wxUSE_LOG_TRACE 0 -#ifdef __WATCOMC__ - // workaround for http://bugzilla.openwatcom.org/show_bug.cgi?id=351 - #define WX_WATCOM_ONLY_CODE( x ) x -#else - #define WX_WATCOM_ONLY_CODE( x ) -#endif - #if defined(__WATCOMC__) || defined(__MINGW32__) // Mingw has similar problem with wxLogSysError: #define WX_WATCOM_OR_MINGW_ONLY_CODE( x ) x @@ -707,28 +1410,47 @@ WXDLLIMPEXP_BASE const wxChar* wxSysErrorMsg(unsigned long nErrCode = 0); #define WX_WATCOM_OR_MINGW_ONLY_CODE( x ) #endif -// log functions do nothing at all -#define DECLARE_LOG_FUNCTION(level) \ - WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 1, (const wxString&)) \ +// define macros for defining log functions which do nothing at all +// +// 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 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 DECLARE_LOG_FUNCTION2_EXP(level, argclass, arg, expdecl) \ - WX_DEFINE_VARARG_FUNC_NOP(wxLog##level, 2, (argclass, const wxString&)) \ +#define wxDEFINE_EMPTY_LOG_FUNCTION2(level, argclass) \ + 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); +wxDEFINE_EMPTY_LOG_FUNCTION(Error); +wxDEFINE_EMPTY_LOG_FUNCTION(SysError); +wxDEFINE_EMPTY_LOG_FUNCTION2(SysError, long); +wxDEFINE_EMPTY_LOG_FUNCTION(Warning); +wxDEFINE_EMPTY_LOG_FUNCTION(Message); +wxDEFINE_EMPTY_LOG_FUNCTION(Info); +wxDEFINE_EMPTY_LOG_FUNCTION(Verbose); + +wxDEFINE_EMPTY_LOG_FUNCTION2(Generic, wxLogLevel); + +#if wxUSE_GUI + wxDEFINE_EMPTY_LOG_FUNCTION(Status); + wxDEFINE_EMPTY_LOG_FUNCTION2(Status, wxFrame *); +#endif // wxUSE_GUI + // Empty Class to fake wxLogNull class WXDLLIMPEXP_BASE wxLogNull { @@ -745,51 +1467,6 @@ public: #endif // wxUSE_LOG/!wxUSE_LOG -#define DECLARE_LOG_FUNCTION2(level, argclass, arg) \ - DECLARE_LOG_FUNCTION2_EXP(level, argclass, arg, WXDLLIMPEXP_BASE) - -// VC6 produces a warning if we a macro expanding to nothing to -// DECLARE_LOG_FUNCTION2: -#if defined(__VISUALC__) && __VISUALC__ < 1300 - #pragma warning(push) - // "not enough actual parameters for macro 'DECLARE_LOG_FUNCTION2_EXP'" - #pragma warning(disable:4003) -#endif - -// a generic function for all levels (level is passes as parameter) -DECLARE_LOG_FUNCTION2(Generic, wxLogLevel, level); - -// one function per each level -DECLARE_LOG_FUNCTION(FatalError); -DECLARE_LOG_FUNCTION(Error); -DECLARE_LOG_FUNCTION(Warning); -DECLARE_LOG_FUNCTION(Message); -DECLARE_LOG_FUNCTION(Info); -DECLARE_LOG_FUNCTION(Verbose); - -// this function sends the log message to the status line of the top level -// application frame, if any -DECLARE_LOG_FUNCTION(Status); - -#if wxUSE_GUI - // this one is the same as previous except that it allows to explicitly - class WXDLLIMPEXP_FWD_CORE wxFrame; - // specify the frame to which the output should go - DECLARE_LOG_FUNCTION2_EXP(Status, wxFrame *, pFrame, WXDLLIMPEXP_CORE); -#endif // wxUSE_GUI - -// additional one: as wxLogError, but also logs last system call error code -// and the corresponding error message if available -DECLARE_LOG_FUNCTION(SysError); - -// and another one which also takes the error code (for those broken APIs -// that don't set the errno (like registry APIs in Win32)) -DECLARE_LOG_FUNCTION2(SysError, long, lErrCode); -#ifdef __WATCOMC__ -// workaround for http://bugzilla.openwatcom.org/show_bug.cgi?id=351 -DECLARE_LOG_FUNCTION2(SysError, unsigned long, lErrCode); -#endif - // debug functions can be completely disabled in optimized builds @@ -814,38 +1491,29 @@ DECLARE_LOG_FUNCTION2(SysError, unsigned long, lErrCode); #endif #if wxUSE_LOG_DEBUG - DECLARE_LOG_FUNCTION(Debug); + #define wxLogDebug wxDO_LOG_IF_ENABLED(Debug) + #define wxVLogDebug(format, argptr) wxDO_LOGV(Debug, format, argptr) #else // !wxUSE_LOG_DEBUG #define wxVLogDebug(fmt, valist) wxLogNop() #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 - // this version only logs the message if the mask had been added to the - // list of masks with AddTraceMask() - DECLARE_LOG_FUNCTION2(Trace, const wxString&, mask); -#ifdef __WATCOMC__ - // workaround for http://bugzilla.openwatcom.org/show_bug.cgi?id=351 - DECLARE_LOG_FUNCTION2(Trace, const char*, mask); - DECLARE_LOG_FUNCTION2(Trace, const wchar_t*, mask); -#endif - - // and this one does nothing if all of level bits are not set in - // wxLog::GetActive()->GetTraceMask() -- it's deprecated in favour of - // string identifiers -#if WXWIN_COMPATIBILITY_2_8 - DECLARE_LOG_FUNCTION2(Trace, wxTraceMask, mask); -#ifdef __WATCOMC__ - // workaround for http://bugzilla.openwatcom.org/show_bug.cgi?id=351 - DECLARE_LOG_FUNCTION2(Trace, int, mask); -#endif -#endif // WXWIN_COMPATIBILITY_2_8 - + #define wxLogTrace \ + 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() @@ -853,9 +1521,9 @@ DECLARE_LOG_FUNCTION2(SysError, unsigned long, lErrCode); #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*)) @@ -864,10 +1532,6 @@ DECLARE_LOG_FUNCTION2(SysError, unsigned long, lErrCode); #endif // HAVE_VARIADIC_MACROS/!HAVE_VARIADIC_MACROS #endif // wxUSE_LOG_TRACE/!wxUSE_LOG_TRACE -#if defined(__VISUALC__) && __VISUALC__ < 1300 - #pragma warning(pop) -#endif - // wxLogFatalError helper: show the (fatal) error to the user in a safe way, // i.e. without using wxMessageBox() for example because it could crash void WXDLLIMPEXP_BASE @@ -909,5 +1573,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_