X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/d993e05b2dca1092b98936c7f3b4917c41922d01..96dc06fdcece9307ffb0c23bb6944ff03c526808:/include/wx/log.h diff --git a/include/wx/log.h b/include/wx/log.h index a28d31300c..6cbf62540d 100644 --- a/include/wx/log.h +++ b/include/wx/log.h @@ -15,30 +15,26 @@ #include "wx/defs.h" // ---------------------------------------------------------------------------- -// common constants for use in wxUSE_LOG/!wxUSE_LOG +// types // ---------------------------------------------------------------------------- -// the trace masks have been superceded by symbolic trace constants, they're -// for compatibility only andwill be removed soon - do NOT use them +// NB: this is needed even if wxUSE_LOG == 0 +typedef unsigned long wxLogLevel; -// meaning of different bits of the trace mask (which allows selectively -// enable/disable some trace messages) -#define wxTraceMemAlloc 0x0001 // trace memory allocation (new/delete) -#define wxTraceMessages 0x0002 // trace window messages/X callbacks -#define wxTraceResAlloc 0x0004 // trace GDI resource allocation -#define wxTraceRefCount 0x0008 // trace various ref counting operations +// the trace masks have been superseded by symbolic trace constants, they're +// for compatibility only and will be removed soon - do NOT use them +#if WXWIN_COMPATIBILITY_2_8 + #define wxTraceMemAlloc 0x0001 // trace memory allocation (new/delete) + #define wxTraceMessages 0x0002 // trace window messages/X callbacks + #define wxTraceResAlloc 0x0004 // trace GDI resource allocation + #define wxTraceRefCount 0x0008 // trace various ref counting operations -#ifdef __WXMSW__ - #define wxTraceOleCalls 0x0100 // OLE interface calls -#endif + #ifdef __WXMSW__ + #define wxTraceOleCalls 0x0100 // OLE interface calls + #endif -// ---------------------------------------------------------------------------- -// types -// ---------------------------------------------------------------------------- - -// NB: these types are needed even if wxUSE_LOG == 0 -typedef unsigned long wxTraceMask; -typedef unsigned long wxLogLevel; + typedef unsigned long wxTraceMask; +#endif // WXWIN_COMPATIBILITY_2_8 // ---------------------------------------------------------------------------- // headers @@ -47,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" @@ -56,25 +62,42 @@ typedef unsigned long wxLogLevel; #endif #include "wx/dynarray.h" +#include "wx/hashmap.h" +#if wxUSE_THREADS + #include "wx/thread.h" +#endif // wxUSE_THREADS + +// wxUSE_LOG_DEBUG enables the debug log messages #ifndef wxUSE_LOG_DEBUG -# ifdef __WXDEBUG__ -# define wxUSE_LOG_DEBUG 1 -# else // !__WXDEBUG__ -# define wxUSE_LOG_DEBUG 0 -# endif + #if wxDEBUG_LEVEL + #define wxUSE_LOG_DEBUG 1 + #else // !wxDEBUG_LEVEL + #define wxUSE_LOG_DEBUG 0 + #endif #endif -// ---------------------------------------------------------------------------- -// forward declarations -// ---------------------------------------------------------------------------- - -#if wxUSE_GUI - class WXDLLIMPEXP_CORE wxTextCtrl; - class WXDLLIMPEXP_CORE wxLogFrame; - class WXDLLIMPEXP_CORE wxFrame; - class WXDLLIMPEXP_CORE wxWindow; -#endif // wxUSE_GUI +// wxUSE_LOG_TRACE enables the trace messages, they are disabled by default +#ifndef wxUSE_LOG_TRACE + #if wxDEBUG_LEVEL + #define wxUSE_LOG_TRACE 1 + #else // !wxDEBUG_LEVEL + #define wxUSE_LOG_TRACE 0 + #endif +#endif // wxUSE_LOG_TRACE + +// 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; + + #ifdef WXBUILDING + #define wxLOG_COMPONENT "wx" + #endif +#endif // ---------------------------------------------------------------------------- // constants @@ -111,6 +134,207 @@ enum wxLogLevelValues #include "wx/iosfwrap.h" +// ---------------------------------------------------------------------------- +// information about a log record, i.e. unit of log output +// ---------------------------------------------------------------------------- + +class wxLogRecordInfo +{ +public: + // default ctor creates an uninitialized object + wxLogRecordInfo() + { + 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 = 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); + } + + 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; + +#if wxUSE_THREADS + // 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; +}; + +// ---------------------------------------------------------------------------- +// Derive from this class to customize format of log messages. +// ---------------------------------------------------------------------------- + +class WXDLLIMPEXP_BASE wxLogFormatter +{ +public: + // Default constructor. + wxLogFormatter() { } + + // Trivial but virtual destructor for the base class. + virtual ~wxLogFormatter() { } + + + // Override this method to implement custom formatting of the given log + // record. The default implementation simply prepends a level-dependent + // prefix to the message and optionally adds a time stamp. + virtual wxString Format(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info) const; + +protected: + // Override this method to change just the time stamp formatting. It is + // called by default Format() implementation. + virtual wxString FormatTime(time_t t) const; +}; + + // ---------------------------------------------------------------------------- // derive from this class to redirect (or suppress, or ...) log messages // normally, only a single instance of this class exists but it's not enforced @@ -120,46 +344,106 @@ class WXDLLIMPEXP_BASE wxLog { public: // ctor - wxLog(){} + wxLog() : m_formatter(new wxLogFormatter) { } + + // make dtor virtual for all derived classes + virtual ~wxLog(); + + + // log messages selection + // ---------------------- - // these functions allow to completely disable all log messages + // 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; } - // 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 - // 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() @@ -169,14 +453,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(); @@ -192,12 +468,8 @@ public: // gets duplicate counting status static bool GetRepetitionCounting() { return ms_bRepetCounting; } - // trace mask (see wxTraceXXX constants for details) - static void SetTraceMask(wxTraceMask ulMask) { ms_ulTraceMask = ulMask; } - // add string trace mask - static void AddTraceMask(const wxString& str) - { ms_aTraceMasks.push_back(str); } + static void AddTraceMask(const wxString& str); // add string trace mask static void RemoveTraceMask(const wxString& str); @@ -205,41 +477,88 @@ public: // remove all string trace masks static void ClearTraceMasks(); - // get string trace masks - static const wxArrayString &GetTraceMasks() { return ms_aTraceMasks; } + // get string trace masks: note that this is MT-unsafe if other threads can + // call AddTraceMask() concurrently + static const wxArrayString& GetTraceMasks(); - // sets the timestamp string: this is used as strftime() format string - // for the log targets which add time stamps to the messages - set it - // to NULL to disable time stamping completely. - static void SetTimestamp(const wxString& ts) { ms_timestamp = ts; } + // is this trace mask in the list? + static bool IsAllowedTraceMask(const wxString& mask); - // accessors + // log formatting + // ----------------- - // gets the verbose status - static bool GetVerbose() { return ms_bVerbose; } + // Change wxLogFormatter object used by wxLog to format the log messages. + // + // wxLog takes ownership of the pointer passed in but the caller is + // responsible for deleting the returned pointer. + wxLogFormatter* SetFormatter(wxLogFormatter* formatter); - // get trace mask - static wxTraceMask GetTraceMask() { return ms_ulTraceMask; } - // is this trace mask in the list? - static bool IsAllowedTraceMask(const wxString& mask); + // All the time stamp related functions below only work when the default + // wxLogFormatter is being used. Defining a custom formatter overrides them + // as it could use its own time stamp format or format messages without + // using time stamp at all. - // return the current loglevel limit - static wxLogLevel GetLogLevel() { return ms_logLevel; } - // get the current timestamp format string (may be NULL) + // sets the time stamp string format: this is used as strftime() format + // string for the log targets which add time stamps to the messages; set + // it to empty string to disable time stamping completely. + static void SetTimestamp(const wxString& ts) { ms_timestamp = ts; } + + // disable time stamping of log messages + static void DisableTimestamp() { SetTimestamp(wxEmptyString); } + + + // get the current timestamp format string (maybe empty) static const wxString& GetTimestamp() { return ms_timestamp; } - // helpers - // put the time stamp into the string if ms_timestamp != NULL (don't - // change it otherwise) + // 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 is not empty (don't + // change it otherwise); the first overload uses the current time. static void TimeStamp(wxString *str); + static void TimeStamp(wxString *str, time_t t); + + // these methods should only be called from derived classes DoLogRecord(), + // DoLogTextAtLevel() and DoLogText() implementations respectively and + // shouldn't be called directly, use logging functions instead + void LogRecord(wxLogLevel level, + const wxString& msg, + const wxLogRecordInfo& info) + { + 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 @@ -247,17 +566,50 @@ 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 + static wxDEPRECATED_INLINE( + wxChar *SetLogBuffer(wxChar *, size_t = 0), return NULL; + ); +#endif // WXWIN_COMPATIBILITY_2_6 -protected: - // the logging functions that can be overriden + // don't use integer masks any more, use string trace masks instead +#if WXWIN_COMPATIBILITY_2_8 + static wxDEPRECATED_INLINE( void SetTraceMask(wxTraceMask ulMask), + ms_ulTraceMask = ulMask; ) + + // 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 - // 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); +protected: + // 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); + + // 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) ); @@ -265,38 +617,66 @@ 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)) {} -#endif + 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 + - // log a line containing the number of times the previous message was - // repeated - // returns: the number - static unsigned DoLogNumberOfRepeats(); + // log a message indicating the number of times the previous message was + // repeated if previous repetition counter is strictly positive, does + // nothing otherwise; return the old value of repetition counter + unsigned LogLastRepeatIfNeeded(); private: +#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); + + + // variables + // ---------------- + + wxLogFormatter *m_formatter; // We own this pointer. + + // static variables // ---------------- - // traditional behaviour or counting repetitions + // if true, don't log the same message multiple times, only log it once + // with the number of times it was repeated static bool ms_bRepetCounting; - static wxString ms_prevString; // previous message that was logged - // how many times the previous message was logged - static unsigned ms_prevCounter; - 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 @@ -307,19 +687,21 @@ private: static size_t ms_suspendCount; // if positive, logs are not flushed - // format string for strftime(), if NULL, time stamping log messages is + // format string for strftime(), if empty, time stamping log messages is // disabled static wxString ms_timestamp; +#if WXWIN_COMPATIBILITY_2_8 static wxTraceMask ms_ulTraceMask; // controls wxLogTrace behaviour - static wxArrayString ms_aTraceMasks; // more powerful filter for wxLogTrace +#endif // WXWIN_COMPATIBILITY_2_8 }; // ---------------------------------------------------------------------------- // "trivial" derivations of wxLog // ---------------------------------------------------------------------------- -// log everything to a buffer +// log everything except for the debug/trace messages (which are passed to +// wxMessageOutputDebug) to a buffer class WXDLLIMPEXP_BASE wxLogBuffer : public wxLog { public: @@ -333,13 +715,12 @@ public: virtual void Flush(); protected: - virtual void DoLog(wxLogLevel level, const wxString& szString, time_t t); - virtual void DoLogString(const wxString& szString, time_t t); + virtual void DoLogTextAtLevel(wxLogLevel level, const wxString& msg); private: wxString m_str; - DECLARE_NO_COPY_CLASS(wxLogBuffer) + wxDECLARE_NO_COPY_CLASS(wxLogBuffer); }; @@ -348,15 +729,15 @@ class WXDLLIMPEXP_BASE wxLogStderr : public wxLog { public: // redirect log output to a FILE - wxLogStderr(FILE *fp = (FILE *) NULL); + wxLogStderr(FILE *fp = NULL); protected: // implement sink function - virtual void DoLogString(const wxString& szString, time_t t); + virtual void DoLogText(const wxString& msg); FILE *m_fp; - DECLARE_NO_COPY_CLASS(wxLogStderr) + wxDECLARE_NO_COPY_CLASS(wxLogStderr); }; #if wxUSE_STD_IOSTREAM @@ -370,7 +751,7 @@ public: protected: // implement sink function - virtual void DoLogString(const wxString& szString, time_t t); + virtual void DoLogText(const wxString& msg); // using ptr here to avoid including from this file wxSTD ostream *m_ostr; @@ -439,9 +820,14 @@ public: // override base class version to flush the old logger as well virtual void Flush(); + // call to avoid destroying the old log target + 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); + // 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 @@ -453,17 +839,32 @@ private: // do we pass the messages to the old logger? bool m_bPassMessages; - DECLARE_NO_COPY_CLASS(wxLogChain) + wxDECLARE_NO_COPY_CLASS(wxLogChain); }; // a chain log target which uses itself as the new logger -class WXDLLIMPEXP_BASE wxLogPassThrough : public wxLogChain + +#define wxLogPassThrough wxLogInterposer + +class WXDLLIMPEXP_BASE wxLogInterposer : public wxLogChain { public: - wxLogPassThrough(); + wxLogInterposer(); private: - DECLARE_NO_COPY_CLASS(wxLogPassThrough) + wxDECLARE_NO_COPY_CLASS(wxLogInterposer); +}; + +// a temporary interposer which doesn't destroy the old log target +// (calls DetachOldLog) + +class WXDLLIMPEXP_BASE wxLogInterposerTemp : public wxLogChain +{ +public: + wxLogInterposerTemp(); + +private: + wxDECLARE_NO_COPY_CLASS(wxLogInterposerTemp); }; #if wxUSE_GUI @@ -471,15 +872,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 // ---------------------------------------------------------------------------- @@ -491,109 +1281,223 @@ 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 -#ifdef __WATCOMC__ - // workaround for http://bugzilla.openwatcom.org/show_bug.cgi?id=351 - #define WX_WATCOM_ONLY_CODE( x ) x +#undef wxUSE_LOG_DEBUG +#define wxUSE_LOG_DEBUG 0 + +#undef wxUSE_LOG_TRACE +#define wxUSE_LOG_TRACE 0 + +#if defined(__WATCOMC__) || defined(__MINGW32__) + // Mingw has similar problem with wxLogSysError: + #define WX_WATCOM_OR_MINGW_ONLY_CODE( x ) x #else - #define WX_WATCOM_ONLY_CODE( x ) + #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&)) \ - WX_WATCOM_ONLY_CODE( \ +#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 { @@ -610,114 +1514,70 @@ 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 - // "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); +// debug functions can be completely disabled in optimized builds -// 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 WXDLLEXPORT 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 do nothing in release mode -#if wxUSE_LOG && wxUSE_LOG_DEBUG - DECLARE_LOG_FUNCTION(Debug); - - // there is no more unconditional LogTrace: it is not different from - // LogDebug and it creates overload ambiguities - //DECLARE_LOG_FUNCTION(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 - 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 -#else //!debug || !wxUSE_LOG - // these functions do nothing in release builds, but don't define them as - // nothing as it could result in different code structure in debug and - // release and this could result in trouble when these macros are used - // inside if/else - // - // note that making wxVLogDebug/Trace() themselves (empty inline) functions - // is a bad idea as some compilers are stupid enough to not inline even - // empty functions if their parameters are complicated enough, but by - // defining them as an empty inline function we ensure that even dumbest - // compilers optimise them away +// if these log functions are disabled, we prefer to define them as (empty) +// variadic macros as this completely removes them and their argument +// evaluation from the object code but if this is not supported by compiler we +// use empty inline functions instead (defining them as nothing would result in +// compiler warnings) +// +// note that making wxVLogDebug/Trace() themselves (empty inline) functions is +// a bad idea as some compilers are stupid enough to not inline even empty +// functions if their parameters are complicated enough, but by defining them +// as an empty inline function we ensure that even dumbest compilers optimise +// them away +#ifdef __BORLANDC__ + // but Borland gives "W8019: Code has no effect" for wxLogNop() so we need + // to define it differently for it to avoid these warnings (same problem as + // with wxUnusedVar()) + #define wxLogNop() { } +#else inline void wxLogNop() { } +#endif +#if wxUSE_LOG_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() - #define wxVLogTrace(mask, fmt, valist) wxLogNop() #ifdef HAVE_VARIADIC_MACROS - // unlike the inline functions below, this completely removes the - // wxLogXXX calls from the object file: #define wxLogDebug(fmt, ...) wxLogNop() + #else // !HAVE_VARIADIC_MACROS + 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, 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() + + #ifdef HAVE_VARIADIC_MACROS #define wxLogTrace(mask, fmt, ...) wxLogNop() #else // !HAVE_VARIADIC_MACROS - //inline void wxLogDebug(const wxString& fmt, ...) {} - WX_DEFINE_VARARG_FUNC_NOP(wxLogDebug, 1, (const wxString&)) - //inline void wxLogTrace(wxTraceMask, const wxString& fmt, ...) {} - //inline void wxLogTrace(const wxString&, const wxString& fmt, ...) {} - WX_DEFINE_VARARG_FUNC_NOP(wxLogTrace, 2, (wxTraceMask, const wxString&)) - WX_DEFINE_VARARG_FUNC_NOP(wxLogTrace, 2, (const wxString&, const wxString&)) + #if WXWIN_COMPATIBILITY_2_8 + WX_DEFINE_VARARG_FUNC_NOP(wxLogTrace, 2, (wxTraceMask, const wxFormatString&)) + #endif + 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*)) WX_DEFINE_VARARG_FUNC_NOP(wxLogTrace, 2, (const wchar_t*, const wchar_t*)) #endif #endif // HAVE_VARIADIC_MACROS/!HAVE_VARIADIC_MACROS -#endif // debug/!debug - -#if defined(__VISUALC__) && __VISUALC__ < 1300 - #pragma warning(default:4003) -#endif +#endif // wxUSE_LOG_TRACE/!wxUSE_LOG_TRACE // wxLogFatalError helper: show the (fatal) error to the user in a safe way, // i.e. without using wxMessageBox() for example because it could crash @@ -728,7 +1588,7 @@ wxSafeShowMessage(const wxString& title, const wxString& text); // debug only logging functions: use them with API name and error code // ---------------------------------------------------------------------------- -#ifdef __WXDEBUG__ +#if wxUSE_LOG_DEBUG // make life easier for people using VC++ IDE: clicking on the message // will take us immediately to the place of the failed API #ifdef __VISUALC__ @@ -746,10 +1606,10 @@ wxSafeShowMessage(const wxString& title, const wxString& text); #define wxLogLastError(api) wxLogApiError(api, wxSysErrorCode()) -#else //!debug +#else // !wxUSE_LOG_DEBUG #define wxLogApiError(api, err) wxLogNop() #define wxLogLastError(api) wxLogNop() -#endif //debug/!debug +#endif // wxUSE_LOG_DEBUG/!wxUSE_LOG_DEBUG // wxCocoa has additiional trace masks #if defined(__WXCOCOA__) @@ -760,5 +1620,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 wxIMPLEMENT_APP() so usually it doesn't need to be used explicitly +#if defined(NDEBUG) && wxUSE_LOG_DEBUG + #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_