1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Assorted wxLogXXX functions, and wxLog (sink for logs)
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
21 #pragma implementation "log.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
33 #include "wx/string.h"
38 #include "wx/window.h"
40 #include "wx/msw/private.h"
46 #include "wx/textfile.h"
48 #include "wx/wxchar.h"
50 #include "wx/thread.h"
54 // other standard headers
59 #if defined(__WXMSW__)
60 #include "wx/msw/private.h" // includes windows.h for OutputDebugString
63 #if defined(__WXMAC__)
64 #include "wx/mac/private.h" // includes mac headers
67 // ----------------------------------------------------------------------------
68 // non member functions
69 // ----------------------------------------------------------------------------
71 // define this to enable wrapping of log messages
72 //#define LOG_PRETTY_WRAP
74 #ifdef LOG_PRETTY_WRAP
75 static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
);
78 // ============================================================================
80 // ============================================================================
82 // ----------------------------------------------------------------------------
84 // ----------------------------------------------------------------------------
86 // log functions can't allocate memory (LogError("out of memory...") should
87 // work!), so we use a static buffer for all log messages
88 #define LOG_BUFFER_SIZE (4096)
90 // static buffer for error messages
91 static wxChar s_szBuf
[LOG_BUFFER_SIZE
];
95 // the critical section protecting the static buffer
96 static wxCriticalSection gs_csLogBuf
;
98 #endif // wxUSE_THREADS
100 // return true if we have a non NULL non disabled log target
101 static inline bool IsLoggingEnabled()
103 return wxLog::IsEnabled() && (wxLog::GetActiveTarget() != NULL
);
106 // ----------------------------------------------------------------------------
107 // implementation of Log functions
109 // NB: unfortunately we need all these distinct functions, we can't make them
110 // macros and not all compilers inline vararg functions.
111 // ----------------------------------------------------------------------------
113 // generic log function
114 void wxLogGeneric(wxLogLevel level
, const wxChar
*szFormat
, va_list argptr
)
116 if ( IsLoggingEnabled() ) {
117 wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
);
119 wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
);
121 wxLog::OnLog(level
, s_szBuf
, time(NULL
));
125 void wxLogGeneric(wxLogLevel level
, const wxChar
*szFormat
, ...)
128 va_start(argptr
, szFormat
);
129 wxLogGeneric(level
, szFormat
, argptr
);
133 #define IMPLEMENT_LOG_FUNCTION(level) \
134 void wxLog##level(const wxChar *szFormat, va_list argptr) \
136 if ( IsLoggingEnabled() ) { \
137 wxCRIT_SECT_LOCKER(locker, gs_csLogBuf); \
139 wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr); \
141 wxLog::OnLog(wxLOG_##level, s_szBuf, time(NULL)); \
144 void wxLog##level(const wxChar *szFormat, ...) \
147 va_start(argptr, szFormat); \
148 wxLog##level(szFormat, argptr); \
152 IMPLEMENT_LOG_FUNCTION(FatalError
)
153 IMPLEMENT_LOG_FUNCTION(Error
)
154 IMPLEMENT_LOG_FUNCTION(Warning
)
155 IMPLEMENT_LOG_FUNCTION(Message
)
156 IMPLEMENT_LOG_FUNCTION(Info
)
157 IMPLEMENT_LOG_FUNCTION(Status
)
159 // same as info, but only if 'verbose' mode is on
160 void wxLogVerbose(const wxChar
*szFormat
, va_list argptr
)
162 if ( IsLoggingEnabled() ) {
163 wxLog
*pLog
= wxLog::GetActiveTarget();
164 if ( pLog
!= NULL
&& pLog
->GetVerbose() ) {
165 wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
);
167 wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
);
169 wxLog::OnLog(wxLOG_Info
, s_szBuf
, time(NULL
));
174 void wxLogVerbose(const wxChar
*szFormat
, ...)
177 va_start(argptr
, szFormat
);
178 wxLogVerbose(szFormat
, argptr
);
184 #define IMPLEMENT_LOG_DEBUG_FUNCTION(level) \
185 void wxLog##level(const wxChar *szFormat, va_list argptr) \
187 if ( IsLoggingEnabled() ) { \
188 wxCRIT_SECT_LOCKER(locker, gs_csLogBuf); \
190 wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr); \
192 wxLog::OnLog(wxLOG_##level, s_szBuf, time(NULL)); \
195 void wxLog##level(const wxChar *szFormat, ...) \
198 va_start(argptr, szFormat); \
199 wxLog##level(szFormat, argptr); \
203 void wxLogTrace(const wxChar
*mask
, const wxChar
*szFormat
, va_list argptr
)
205 if ( IsLoggingEnabled() && wxLog::IsAllowedTraceMask(mask
) ) {
206 wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
);
209 size_t len
= WXSIZEOF(s_szBuf
);
210 wxStrncpy(s_szBuf
, _T("("), len
);
211 len
-= 1; // strlen("(")
213 wxStrncat(p
, mask
, len
);
214 size_t lenMask
= wxStrlen(mask
);
218 wxStrncat(p
, _T(") "), len
);
222 wxVsnprintf(p
, len
, szFormat
, argptr
);
224 wxLog::OnLog(wxLOG_Trace
, s_szBuf
, time(NULL
));
228 void wxLogTrace(const wxChar
*mask
, const wxChar
*szFormat
, ...)
231 va_start(argptr
, szFormat
);
232 wxLogTrace(mask
, szFormat
, argptr
);
236 void wxLogTrace(wxTraceMask mask
, const wxChar
*szFormat
, va_list argptr
)
238 // we check that all of mask bits are set in the current mask, so
239 // that wxLogTrace(wxTraceRefCount | wxTraceOle) will only do something
240 // if both bits are set.
241 if ( IsLoggingEnabled() && ((wxLog::GetTraceMask() & mask
) == mask
) ) {
242 wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
);
244 wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
);
246 wxLog::OnLog(wxLOG_Trace
, s_szBuf
, time(NULL
));
250 void wxLogTrace(wxTraceMask mask
, const wxChar
*szFormat
, ...)
253 va_start(argptr
, szFormat
);
254 wxLogTrace(mask
, szFormat
, argptr
);
259 #define IMPLEMENT_LOG_DEBUG_FUNCTION(level)
262 IMPLEMENT_LOG_DEBUG_FUNCTION(Debug
)
263 IMPLEMENT_LOG_DEBUG_FUNCTION(Trace
)
265 // wxLogSysError: one uses the last error code, for other you must give it
268 // common part of both wxLogSysError
269 void wxLogSysErrorHelper(long lErrCode
)
271 wxChar szErrMsg
[LOG_BUFFER_SIZE
/ 2];
272 wxSnprintf(szErrMsg
, WXSIZEOF(szErrMsg
),
273 _(" (error %ld: %s)"), lErrCode
, wxSysErrorMsg(lErrCode
));
274 wxStrncat(s_szBuf
, szErrMsg
, WXSIZEOF(s_szBuf
) - wxStrlen(s_szBuf
));
276 wxLog::OnLog(wxLOG_Error
, s_szBuf
, time(NULL
));
279 void WXDLLEXPORT
wxLogSysError(const wxChar
*szFormat
, va_list argptr
)
281 if ( IsLoggingEnabled() ) {
282 wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
);
284 wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
);
286 wxLogSysErrorHelper(wxSysErrorCode());
290 void WXDLLEXPORT
wxLogSysError(const wxChar
*szFormat
, ...)
293 va_start(argptr
, szFormat
);
294 wxLogSysError(szFormat
, argptr
);
298 void WXDLLEXPORT
wxLogSysError(long lErrCode
, const wxChar
*szFormat
, va_list argptr
)
300 if ( IsLoggingEnabled() ) {
301 wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
);
303 wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
);
305 wxLogSysErrorHelper(lErrCode
);
309 void WXDLLEXPORT
wxLogSysError(long lErrCode
, const wxChar
*szFormat
, ...)
312 va_start(argptr
, szFormat
);
313 wxLogSysError(lErrCode
, szFormat
, argptr
);
317 // ----------------------------------------------------------------------------
318 // wxLog class implementation
319 // ----------------------------------------------------------------------------
323 m_bHasMessages
= FALSE
;
326 wxLog
*wxLog::GetActiveTarget()
328 if ( ms_bAutoCreate
&& ms_pLogger
== NULL
) {
329 // prevent infinite recursion if someone calls wxLogXXX() from
330 // wxApp::CreateLogTarget()
331 static bool s_bInGetActiveTarget
= FALSE
;
332 if ( !s_bInGetActiveTarget
) {
333 s_bInGetActiveTarget
= TRUE
;
335 // ask the application to create a log target for us
336 if ( wxTheApp
!= NULL
)
337 ms_pLogger
= wxTheApp
->CreateLogTarget();
339 ms_pLogger
= new wxLogStderr
;
341 s_bInGetActiveTarget
= FALSE
;
343 // do nothing if it fails - what can we do?
350 wxLog
*wxLog::SetActiveTarget(wxLog
*pLogger
)
352 if ( ms_pLogger
!= NULL
) {
353 // flush the old messages before changing because otherwise they might
354 // get lost later if this target is not restored
358 wxLog
*pOldLogger
= ms_pLogger
;
359 ms_pLogger
= pLogger
;
364 void wxLog::DontCreateOnDemand()
366 ms_bAutoCreate
= FALSE
;
368 // this is usually called at the end of the program and we assume that it
369 // is *always* called at the end - so we free memory here to avoid false
370 // memory leak reports from wxWin memory tracking code
374 void wxLog::RemoveTraceMask(const wxString
& str
)
376 int index
= ms_aTraceMasks
.Index(str
);
377 if ( index
!= wxNOT_FOUND
)
378 ms_aTraceMasks
.Remove((size_t)index
);
381 void wxLog::ClearTraceMasks()
383 ms_aTraceMasks
.Clear();
386 void wxLog::TimeStamp(wxString
*str
)
392 (void)time(&timeNow
);
393 wxStrftime(buf
, WXSIZEOF(buf
), ms_timestamp
, localtime(&timeNow
));
396 *str
<< buf
<< wxT(": ");
400 void wxLog::DoLog(wxLogLevel level
, const wxChar
*szString
, time_t t
)
403 case wxLOG_FatalError
:
404 DoLogString(wxString(_("Fatal error: ")) + szString
, t
);
405 DoLogString(_("Program aborted."), t
);
411 DoLogString(wxString(_("Error: ")) + szString
, t
);
415 DoLogString(wxString(_("Warning: ")) + szString
, t
);
422 default: // log unknown log levels too
423 DoLogString(szString
, t
);
430 wxString msg
= level
== wxLOG_Trace
? wxT("Trace: ")
440 void wxLog::DoLogString(const wxChar
*WXUNUSED(szString
), time_t WXUNUSED(t
))
442 wxFAIL_MSG(wxT("DoLogString must be overriden if it's called."));
447 // remember that we don't have any more messages to show
448 m_bHasMessages
= FALSE
;
451 // ----------------------------------------------------------------------------
452 // wxLogStderr class implementation
453 // ----------------------------------------------------------------------------
455 wxLogStderr::wxLogStderr(FILE *fp
)
463 #if defined(__WXMAC__) && !defined(__DARWIN__) && (__MWERKS__ > 0x5300)
465 #if !TARGET_API_MAC_CARBON
466 // MetroNub stuff doesn't seem to work in CodeWarrior 5.3 Carbon builds...
468 #ifndef __MetroNubUtils__
469 #include "MetroNubUtils.h"
488 #if TARGET_API_MAC_CARBON
490 #include <CodeFragments.h>
493 CallUniversalProc(UniversalProcPtr theProcPtr
, ProcInfoType procInfo
, ...);
495 ProcPtr gCallUniversalProc_Proc
= NULL
;
499 static MetroNubUserEntryBlock
* gMetroNubEntry
= NULL
;
501 static long fRunOnce
= false;
503 Boolean
IsCompatibleVersion(short inVersion
);
505 /* ---------------------------------------------------------------------------
507 --------------------------------------------------------------------------- */
509 Boolean
IsCompatibleVersion(short inVersion
)
511 Boolean result
= false;
515 MetroNubUserEntryBlock
* block
= (MetroNubUserEntryBlock
*)result
;
517 result
= (inVersion
<= block
->apiHiVersion
);
523 /* ---------------------------------------------------------------------------
525 --------------------------------------------------------------------------- */
527 Boolean
IsMetroNubInstalled()
534 gMetroNubEntry
= NULL
;
536 if (Gestalt(gestaltSystemVersion
, &value
) == noErr
&& value
< 0x1000)
538 /* look for MetroNub's Gestalt selector */
539 if (Gestalt(kMetroNubUserSignature
, &result
) == noErr
)
542 #if TARGET_API_MAC_CARBON
543 if (gCallUniversalProc_Proc
== NULL
)
545 CFragConnectionID connectionID
;
548 ProcPtr symbolAddress
;
550 CFragSymbolClass symbolClass
;
552 symbolAddress
= NULL
;
553 err
= GetSharedLibrary("\pInterfaceLib", kPowerPCCFragArch
, kFindCFrag
,
554 &connectionID
, &mainAddress
, errorString
);
558 gCallUniversalProc_Proc
= NULL
;
562 err
= FindSymbol(connectionID
, "\pCallUniversalProc",
563 (Ptr
*) &gCallUniversalProc_Proc
, &symbolClass
);
567 gCallUniversalProc_Proc
= NULL
;
574 MetroNubUserEntryBlock
* block
= (MetroNubUserEntryBlock
*)result
;
576 /* make sure the version of the API is compatible */
577 if (block
->apiLowVersion
<= kMetroNubUserAPIVersion
&&
578 kMetroNubUserAPIVersion
<= block
->apiHiVersion
)
579 gMetroNubEntry
= block
; /* success! */
588 #if TARGET_API_MAC_CARBON
589 return (gMetroNubEntry
!= NULL
&& gCallUniversalProc_Proc
!= NULL
);
591 return (gMetroNubEntry
!= NULL
);
595 /* ---------------------------------------------------------------------------
596 IsMWDebuggerRunning [v1 API]
597 --------------------------------------------------------------------------- */
599 Boolean
IsMWDebuggerRunning()
601 if (IsMetroNubInstalled())
602 return CallIsDebuggerRunningProc(gMetroNubEntry
->isDebuggerRunning
);
607 /* ---------------------------------------------------------------------------
608 AmIBeingMWDebugged [v1 API]
609 --------------------------------------------------------------------------- */
611 Boolean
AmIBeingMWDebugged()
613 if (IsMetroNubInstalled())
614 return CallAmIBeingDebuggedProc(gMetroNubEntry
->amIBeingDebugged
);
619 /* ---------------------------------------------------------------------------
620 UserSetWatchPoint [v2 API]
621 --------------------------------------------------------------------------- */
623 OSErr
UserSetWatchPoint (Ptr address
, long length
, WatchPointIDT
* watchPointID
)
625 if (IsMetroNubInstalled() && IsCompatibleVersion(kMetroNubUserAPIVersion
))
626 return CallUserSetWatchPointProc(gMetroNubEntry
->userSetWatchPoint
,
627 address
, length
, watchPointID
);
629 return errProcessIsNotClient
;
632 /* ---------------------------------------------------------------------------
633 ClearWatchPoint [v2 API]
634 --------------------------------------------------------------------------- */
636 OSErr
ClearWatchPoint (WatchPointIDT watchPointID
)
638 if (IsMetroNubInstalled() && IsCompatibleVersion(kMetroNubUserAPIVersion
))
639 return CallClearWatchPointProc(gMetroNubEntry
->clearWatchPoint
,
642 return errProcessIsNotClient
;
649 #endif // !TARGET_API_MAC_CARBON
651 #endif // defined(__WXMAC__) && !defined(__DARWIN__) && (__MWERKS__ > 0x5300)
653 void wxLogStderr::DoLogString(const wxChar
*szString
, time_t WXUNUSED(t
))
659 fputs(str
.mb_str(), m_fp
);
660 fputc(_T('\n'), m_fp
);
663 // under Windows, programs usually don't have stderr at all, so show the
664 // messages also under debugger - unless it's a console program
665 #if defined(__WXMSW__) && wxUSE_GUI && !defined(__WXMICROWIN__)
667 OutputDebugString(str
.c_str());
669 #if defined(__WXMAC__) && !defined(__DARWIN__) && wxUSE_GUI
671 strcpy( (char*) pstr
, str
.c_str() ) ;
672 strcat( (char*) pstr
, ";g" ) ;
673 c2pstr( (char*) pstr
) ;
675 Boolean running
= false ;
677 #if !TARGET_API_MAC_CARBON && (__MWERKS__ > 0x5300)
679 if ( IsMWDebuggerRunning() && AmIBeingMWDebugged() )
697 // ----------------------------------------------------------------------------
698 // wxLogStream implementation
699 // ----------------------------------------------------------------------------
701 #if wxUSE_STD_IOSTREAM
702 wxLogStream::wxLogStream(wxSTD ostream
*ostr
)
705 m_ostr
= &wxSTD cerr
;
710 void wxLogStream::DoLogString(const wxChar
*szString
, time_t WXUNUSED(t
))
714 (*m_ostr
) << str
<< wxConvertWX2MB(szString
) << wxSTD endl
;
716 #endif // wxUSE_STD_IOSTREAM
718 // ----------------------------------------------------------------------------
720 // ----------------------------------------------------------------------------
722 wxLogChain::wxLogChain(wxLog
*logger
)
725 m_logOld
= wxLog::SetActiveTarget(this);
728 void wxLogChain::SetLog(wxLog
*logger
)
730 if ( m_logNew
!= this )
733 wxLog::SetActiveTarget(logger
);
738 void wxLogChain::Flush()
743 // be careful to avoid inifinite recursion
744 if ( m_logNew
&& m_logNew
!= this )
748 void wxLogChain::DoLog(wxLogLevel level
, const wxChar
*szString
, time_t t
)
750 // let the previous logger show it
751 if ( m_logOld
&& IsPassingMessages() )
753 // bogus cast just to access protected DoLog
754 ((wxLogChain
*)m_logOld
)->DoLog(level
, szString
, t
);
757 if ( m_logNew
&& m_logNew
!= this )
760 ((wxLogChain
*)m_logNew
)->DoLog(level
, szString
, t
);
764 // ----------------------------------------------------------------------------
766 // ----------------------------------------------------------------------------
769 // "'this' : used in base member initializer list" - so what?
770 #pragma warning(disable:4355)
773 wxLogPassThrough::wxLogPassThrough()
779 #pragma warning(default:4355)
782 // ============================================================================
783 // Global functions/variables
784 // ============================================================================
786 // ----------------------------------------------------------------------------
788 // ----------------------------------------------------------------------------
790 wxLog
*wxLog::ms_pLogger
= (wxLog
*)NULL
;
791 bool wxLog::ms_doLog
= TRUE
;
792 bool wxLog::ms_bAutoCreate
= TRUE
;
793 bool wxLog::ms_bVerbose
= FALSE
;
795 size_t wxLog::ms_suspendCount
= 0;
798 const wxChar
*wxLog::ms_timestamp
= wxT("%X"); // time only, no date
800 const wxChar
*wxLog::ms_timestamp
= NULL
; // save space
803 wxTraceMask
wxLog::ms_ulTraceMask
= (wxTraceMask
)0;
804 wxArrayString
wxLog::ms_aTraceMasks
;
806 // ----------------------------------------------------------------------------
807 // stdout error logging helper
808 // ----------------------------------------------------------------------------
810 // helper function: wraps the message and justifies it under given position
811 // (looks more pretty on the terminal). Also adds newline at the end.
813 // TODO this is now disabled until I find a portable way of determining the
814 // terminal window size (ok, I found it but does anybody really cares?)
815 #ifdef LOG_PRETTY_WRAP
816 static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
)
818 size_t nMax
= 80; // FIXME
819 size_t nStart
= strlen(pszPrefix
);
823 while ( *psz
!= '\0' ) {
824 for ( n
= nStart
; (n
< nMax
) && (*psz
!= '\0'); n
++ )
828 if ( *psz
!= '\0' ) {
830 for ( n
= 0; n
< nStart
; n
++ )
833 // as we wrapped, squeeze all white space
834 while ( isspace(*psz
) )
841 #endif //LOG_PRETTY_WRAP
843 // ----------------------------------------------------------------------------
844 // error code/error message retrieval functions
845 // ----------------------------------------------------------------------------
847 // get error code from syste
848 unsigned long wxSysErrorCode()
850 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
852 return ::GetLastError();
854 // TODO what to do on Windows 3.1?
862 // get error message from system
863 const wxChar
*wxSysErrorMsg(unsigned long nErrCode
)
866 nErrCode
= wxSysErrorCode();
868 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
870 static wxChar s_szBuf
[LOG_BUFFER_SIZE
/ 2];
872 // get error message from system
874 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
876 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
880 // copy it to our buffer and free memory
881 if( lpMsgBuf
!= 0 ) {
882 wxStrncpy(s_szBuf
, (const wxChar
*)lpMsgBuf
, WXSIZEOF(s_szBuf
) - 1);
883 s_szBuf
[WXSIZEOF(s_szBuf
) - 1] = wxT('\0');
887 // returned string is capitalized and ended with '\r\n' - bad
888 s_szBuf
[0] = (wxChar
)wxTolower(s_szBuf
[0]);
889 size_t len
= wxStrlen(s_szBuf
);
892 if ( s_szBuf
[len
- 2] == wxT('\r') )
893 s_szBuf
[len
- 2] = wxT('\0');
897 s_szBuf
[0] = wxT('\0');
907 static wxChar s_szBuf
[LOG_BUFFER_SIZE
/ 2];
908 wxConvCurrent
->MB2WC(s_szBuf
, strerror(nErrCode
), WXSIZEOF(s_szBuf
) -1);
911 return strerror((int)nErrCode
);