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" 
  42         #include "wx/msgdlg.h" 
  47 #include  "wx/textfile.h" 
  49 #include  "wx/wxchar.h" 
  51 #include  "wx/thread.h" 
  55 // other standard headers 
  60 #if defined(__WXMSW__) 
  61   #include  "wx/msw/private.h"      // includes windows.h for OutputDebugString 
  64 #if !defined(__WXMSW__) || defined(__WXMICROWIN__) 
  68 // ---------------------------------------------------------------------------- 
  69 // non member functions 
  70 // ---------------------------------------------------------------------------- 
  72 // define this to enable wrapping of log messages 
  73 //#define LOG_PRETTY_WRAP 
  75 #ifdef  LOG_PRETTY_WRAP 
  76   static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
); 
  79 // ============================================================================ 
  81 // ============================================================================ 
  83 // ---------------------------------------------------------------------------- 
  85 // ---------------------------------------------------------------------------- 
  87 // log functions can't allocate memory (LogError("out of memory...") should 
  88 // work!), so we use a static buffer for all log messages 
  89 #define LOG_BUFFER_SIZE   (4096) 
  91 // static buffer for error messages 
  92 static wxChar s_szBuf
[LOG_BUFFER_SIZE
]; 
  96 // the critical section protecting the static buffer 
  97 static wxCriticalSection gs_csLogBuf
; 
  99 #endif // wxUSE_THREADS 
 101 // return true if we have a non NULL non disabled log target 
 102 static inline bool IsLoggingEnabled() 
 104     return wxLog::IsEnabled() && (wxLog::GetActiveTarget() != NULL
); 
 107 // ---------------------------------------------------------------------------- 
 108 // implementation of Log functions 
 110 // NB: unfortunately we need all these distinct functions, we can't make them 
 111 //     macros and not all compilers inline vararg functions. 
 112 // ---------------------------------------------------------------------------- 
 114 // generic log function 
 115 void wxLogGeneric(wxLogLevel level
, const wxChar 
*szFormat
, ...) 
 117     if ( IsLoggingEnabled() ) { 
 118         wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
); 
 121         va_start(argptr
, szFormat
); 
 122         wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
); 
 125         wxLog::OnLog(level
, s_szBuf
, time(NULL
)); 
 129 #define IMPLEMENT_LOG_FUNCTION(level)                               \ 
 130   void wxLog##level(const wxChar *szFormat, ...)                    \ 
 132     if ( IsLoggingEnabled() ) {                                     \ 
 133       wxCRIT_SECT_LOCKER(locker, gs_csLogBuf);                      \ 
 136       va_start(argptr, szFormat);                                   \ 
 137       wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr);    \ 
 140       wxLog::OnLog(wxLOG_##level, s_szBuf, time(NULL));             \ 
 144 IMPLEMENT_LOG_FUNCTION(FatalError
) 
 145 IMPLEMENT_LOG_FUNCTION(Error
) 
 146 IMPLEMENT_LOG_FUNCTION(Warning
) 
 147 IMPLEMENT_LOG_FUNCTION(Message
) 
 148 IMPLEMENT_LOG_FUNCTION(Info
) 
 149 IMPLEMENT_LOG_FUNCTION(Status
) 
 151 // same as info, but only if 'verbose' mode is on 
 152 void wxLogVerbose(const wxChar 
*szFormat
, ...) 
 154     if ( IsLoggingEnabled() ) { 
 155         wxLog 
*pLog 
= wxLog::GetActiveTarget(); 
 156         if ( pLog 
!= NULL 
&& pLog
->GetVerbose() ) { 
 157             wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
); 
 160             va_start(argptr
, szFormat
); 
 161             wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
); 
 164             wxLog::OnLog(wxLOG_Info
, s_szBuf
, time(NULL
)); 
 171 #define IMPLEMENT_LOG_DEBUG_FUNCTION(level)                         \ 
 172   void wxLog##level(const wxChar *szFormat, ...)                    \ 
 174     if ( IsLoggingEnabled() ) {                                     \ 
 175       wxCRIT_SECT_LOCKER(locker, gs_csLogBuf);                      \ 
 178       va_start(argptr, szFormat);                                   \ 
 179       wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr);    \ 
 182       wxLog::OnLog(wxLOG_##level, s_szBuf, time(NULL));             \ 
 186   void wxLogTrace(const wxChar 
*mask
, const wxChar 
*szFormat
, ...) 
 188     if ( IsLoggingEnabled() && wxLog::IsAllowedTraceMask(mask
) ) { 
 189       wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
); 
 192       size_t len 
= WXSIZEOF(s_szBuf
); 
 193       wxStrncpy(s_szBuf
, _T("("), len
); 
 194       len 
-= 1; // strlen("(") 
 196       wxStrncat(p
, mask
, len
); 
 197       size_t lenMask 
= wxStrlen(mask
); 
 201       wxStrncat(p
, _T(") "), len
); 
 206       va_start(argptr
, szFormat
); 
 207       wxVsnprintf(p
, len
, szFormat
, argptr
); 
 210       wxLog::OnLog(wxLOG_Trace
, s_szBuf
, time(NULL
)); 
 214   void wxLogTrace(wxTraceMask mask
, const wxChar 
*szFormat
, ...) 
 216     // we check that all of mask bits are set in the current mask, so 
 217     // that wxLogTrace(wxTraceRefCount | wxTraceOle) will only do something 
 218     // if both bits are set. 
 219     if ( IsLoggingEnabled() && ((wxLog::GetTraceMask() & mask
) == mask
) ) { 
 220       wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
); 
 223       va_start(argptr
, szFormat
); 
 224       wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
); 
 227       wxLog::OnLog(wxLOG_Trace
, s_szBuf
, time(NULL
)); 
 232   #define IMPLEMENT_LOG_DEBUG_FUNCTION(level) 
 235 IMPLEMENT_LOG_DEBUG_FUNCTION(Debug
) 
 236 IMPLEMENT_LOG_DEBUG_FUNCTION(Trace
) 
 238 // wxLogSysError: one uses the last error code, for other  you must give it 
 241 // common part of both wxLogSysError 
 242 void wxLogSysErrorHelper(long lErrCode
) 
 244     wxChar szErrMsg
[LOG_BUFFER_SIZE 
/ 2]; 
 245     wxSnprintf(szErrMsg
, WXSIZEOF(szErrMsg
), 
 246                _(" (error %ld: %s)"), lErrCode
, wxSysErrorMsg(lErrCode
)); 
 247     wxStrncat(s_szBuf
, szErrMsg
, WXSIZEOF(s_szBuf
) - wxStrlen(s_szBuf
)); 
 249     wxLog::OnLog(wxLOG_Error
, s_szBuf
, time(NULL
)); 
 252 void WXDLLEXPORT 
wxLogSysError(const wxChar 
*szFormat
, ...) 
 254     if ( IsLoggingEnabled() ) { 
 255         wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
); 
 258         va_start(argptr
, szFormat
); 
 259         wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
); 
 262         wxLogSysErrorHelper(wxSysErrorCode()); 
 266 void WXDLLEXPORT 
wxLogSysError(long lErrCode
, const wxChar 
*szFormat
, ...) 
 268     if ( IsLoggingEnabled() ) { 
 269         wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
); 
 272         va_start(argptr
, szFormat
); 
 273         wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
); 
 276         wxLogSysErrorHelper(lErrCode
); 
 280 // ---------------------------------------------------------------------------- 
 281 // wxLog class implementation 
 282 // ---------------------------------------------------------------------------- 
 286     m_bHasMessages 
= FALSE
; 
 289 wxLog 
*wxLog::GetActiveTarget() 
 291     if ( ms_bAutoCreate 
&& ms_pLogger 
== NULL 
) { 
 292         // prevent infinite recursion if someone calls wxLogXXX() from 
 293         // wxApp::CreateLogTarget() 
 294         static bool s_bInGetActiveTarget 
= FALSE
; 
 295         if ( !s_bInGetActiveTarget 
) { 
 296             s_bInGetActiveTarget 
= TRUE
; 
 298             // ask the application to create a log target for us 
 299             if ( wxTheApp 
!= NULL 
) 
 300                 ms_pLogger 
= wxTheApp
->CreateLogTarget(); 
 302                 ms_pLogger 
= new wxLogStderr
; 
 304             s_bInGetActiveTarget 
= FALSE
; 
 306             // do nothing if it fails - what can we do? 
 313 wxLog 
*wxLog::SetActiveTarget(wxLog 
*pLogger
) 
 315     if ( ms_pLogger 
!= NULL 
) { 
 316         // flush the old messages before changing because otherwise they might 
 317         // get lost later if this target is not restored 
 321     wxLog 
*pOldLogger 
= ms_pLogger
; 
 322     ms_pLogger 
= pLogger
; 
 327 void wxLog::DontCreateOnDemand() 
 329     ms_bAutoCreate 
= FALSE
; 
 331     // this is usually called at the end of the program and we assume that it 
 332     // is *always* called at the end - so we free memory here to avoid false 
 333     // memory leak reports from wxWin  memory tracking code 
 337 void wxLog::RemoveTraceMask(const wxString
& str
) 
 339     int index 
= ms_aTraceMasks
.Index(str
); 
 340     if ( index 
!= wxNOT_FOUND 
) 
 341         ms_aTraceMasks
.Remove((size_t)index
); 
 344 void wxLog::ClearTraceMasks() 
 346     ms_aTraceMasks
.Clear(); 
 349 void wxLog::TimeStamp(wxString 
*str
) 
 355         (void)time(&timeNow
); 
 356         wxStrftime(buf
, WXSIZEOF(buf
), ms_timestamp
, localtime(&timeNow
)); 
 359         *str 
<< buf 
<< wxT(": "); 
 363 void wxLog::DoLog(wxLogLevel level
, const wxChar 
*szString
, time_t t
) 
 366         case wxLOG_FatalError
: 
 367             DoLogString(wxString(_("Fatal error: ")) + szString
, t
); 
 368             DoLogString(_("Program aborted."), t
); 
 374             DoLogString(wxString(_("Error: ")) + szString
, t
); 
 378             DoLogString(wxString(_("Warning: ")) + szString
, t
); 
 385         default:    // log unknown log levels too 
 386                 DoLogString(szString
, t
); 
 393                 wxString msg 
= level 
== wxLOG_Trace 
? wxT("Trace: ") 
 403 void wxLog::DoLogString(const wxChar 
*WXUNUSED(szString
), time_t WXUNUSED(t
)) 
 405     wxFAIL_MSG(wxT("DoLogString must be overriden if it's called.")); 
 410     // remember that we don't have any more messages to show 
 411     m_bHasMessages 
= FALSE
; 
 414 // ---------------------------------------------------------------------------- 
 415 // wxLogStderr class implementation 
 416 // ---------------------------------------------------------------------------- 
 418 wxLogStderr::wxLogStderr(FILE *fp
) 
 426 #if defined(__WXMAC__) && !defined(__UNIX__) 
 427 #define kDebuggerSignature        'MWDB' 
 429 static Boolean 
FindProcessBySignature(OSType signature
, ProcessInfoRec
* info
) 
 432     ProcessSerialNumber psn
; 
 433     Boolean found 
= false; 
 434     psn
.highLongOfPSN 
= 0; 
 435     psn
.lowLongOfPSN 
= kNoProcess
; 
 437     if (!info
) return false; 
 439     info
->processInfoLength 
= sizeof(ProcessInfoRec
); 
 440     info
->processName 
= NULL
; 
 441     info
->processAppSpec 
= NULL
; 
 444     while (!found 
&& err 
== noErr
) 
 446         err 
= GetNextProcess(&psn
); 
 449             err 
= GetProcessInformation(&psn
, info
); 
 450             found 
= err 
== noErr 
&& info
->processSignature 
== signature
; 
 456 pascal Boolean 
MWDebuggerIsRunning(void) 
 459     return FindProcessBySignature(kDebuggerSignature
, &info
); 
 462 pascal OSErr 
AmIBeingMWDebugged(Boolean
* result
) 
 465     ProcessSerialNumber psn
; 
 466     OSType sig 
= kDebuggerSignature
; 
 467     AppleEvent    theAE 
= {typeNull
, NULL
}; 
 468     AppleEvent    theReply 
= {typeNull
, NULL
}; 
 469     AEAddressDesc addr  
= {typeNull
, NULL
}; 
 473     if (!result
) return paramErr
; 
 475     err 
= AECreateDesc(typeApplSignature
, &sig
, sizeof(sig
), &addr
); 
 476     if (err 
!= noErr
) goto exit
; 
 478     err 
= AECreateAppleEvent(kDebuggerSignature
, 'Dbg?', &addr
, 
 479                 kAutoGenerateReturnID
, kAnyTransactionID
, &theAE
); 
 480     if (err 
!= noErr
) goto exit
; 
 482     GetCurrentProcess(&psn
); 
 483     err 
= AEPutParamPtr(&theAE
, keyDirectObject
, typeProcessSerialNumber
, 
 485     if (err 
!= noErr
) goto exit
; 
 487     err 
= AESend(&theAE
, &theReply
, kAEWaitReply
, kAENormalPriority
, 
 488                     kAEDefaultTimeout
, NULL
, NULL
); 
 489     if (err 
!= noErr
) goto exit
; 
 491     err 
= AEGetParamPtr(&theReply
, keyAEResult
, typeBoolean
, &actualType
, result
, 
 492                 sizeof(Boolean
), &actualSize
); 
 496         AEDisposeDesc(&addr
); 
 497     if (theAE
.dataHandle
) 
 498         AEDisposeDesc(&theAE
); 
 499     if (theReply
.dataHandle
) 
 500         AEDisposeDesc(&theReply
); 
 506 void wxLogStderr::DoLogString(const wxChar 
*szString
, time_t WXUNUSED(t
)) 
 512     fputs(str
.mb_str(), m_fp
); 
 513     fputc(_T('\n'), m_fp
); 
 516     // under Windows, programs usually don't have stderr at all, so show the 
 517     // messages also under debugger - unless it's a console program 
 518 #if defined(__WXMSW__) && wxUSE_GUI && !defined(__WXMICROWIN__) 
 520     OutputDebugString(str
.c_str()); 
 522 #if defined(__WXMAC__) && !defined(__WXMAC_X__) && wxUSE_GUI 
 524     strcpy( (char*) pstr 
, str
.c_str() ) ; 
 525     strcat( (char*) pstr 
, ";g" ) ; 
 526     c2pstr( (char*) pstr 
) ; 
 528     Boolean running 
= false ; 
 531     if ( MWDebuggerIsRunning() ) 
 533         AmIBeingMWDebugged( &running ) ; 
 556 // ---------------------------------------------------------------------------- 
 557 // wxLogStream implementation 
 558 // ---------------------------------------------------------------------------- 
 560 #if wxUSE_STD_IOSTREAM 
 561 wxLogStream::wxLogStream(wxSTD ostream 
*ostr
) 
 564         m_ostr 
= &wxSTD cerr
; 
 569 void wxLogStream::DoLogString(const wxChar 
*szString
, time_t WXUNUSED(t
)) 
 573     (*m_ostr
) << str 
<< wxConvertWX2MB(szString
) << wxSTD endl
; 
 575 #endif // wxUSE_STD_IOSTREAM 
 577 // ---------------------------------------------------------------------------- 
 579 // ---------------------------------------------------------------------------- 
 581 wxLogChain::wxLogChain(wxLog 
*logger
) 
 584     m_logOld 
= wxLog::SetActiveTarget(this); 
 587 void wxLogChain::SetLog(wxLog 
*logger
) 
 589     if ( m_logNew 
!= this ) 
 592     wxLog::SetActiveTarget(logger
); 
 597 void wxLogChain::Flush() 
 602     // be careful to avoid inifinite recursion 
 603     if ( m_logNew 
&& m_logNew 
!= this ) 
 607 void wxLogChain::DoLog(wxLogLevel level
, const wxChar 
*szString
, time_t t
) 
 609     // let the previous logger show it 
 610     if ( m_logOld 
&& IsPassingMessages() ) 
 612         // bogus cast just to access protected DoLog 
 613         ((wxLogChain 
*)m_logOld
)->DoLog(level
, szString
, t
); 
 616     if ( m_logNew 
&& m_logNew 
!= this ) 
 619         ((wxLogChain 
*)m_logNew
)->DoLog(level
, szString
, t
); 
 623 // ============================================================================ 
 624 // Global functions/variables 
 625 // ============================================================================ 
 627 // ---------------------------------------------------------------------------- 
 629 // ---------------------------------------------------------------------------- 
 631 wxLog          
*wxLog::ms_pLogger      
= (wxLog 
*)NULL
; 
 632 bool            wxLog::ms_doLog        
= TRUE
; 
 633 bool            wxLog::ms_bAutoCreate  
= TRUE
; 
 634 bool            wxLog::ms_bVerbose     
= FALSE
; 
 636 size_t          wxLog::ms_suspendCount 
= 0; 
 639     const wxChar 
*wxLog::ms_timestamp  
= wxT("%X");  // time only, no date 
 641     const wxChar 
*wxLog::ms_timestamp  
= NULL
;       // save space 
 644 wxTraceMask     
wxLog::ms_ulTraceMask  
= (wxTraceMask
)0; 
 645 wxArrayString   
wxLog::ms_aTraceMasks
; 
 647 // ---------------------------------------------------------------------------- 
 648 // stdout error logging helper 
 649 // ---------------------------------------------------------------------------- 
 651 // helper function: wraps the message and justifies it under given position 
 652 // (looks more pretty on the terminal). Also adds newline at the end. 
 654 // TODO this is now disabled until I find a portable way of determining the 
 655 //      terminal window size (ok, I found it but does anybody really cares?) 
 656 #ifdef LOG_PRETTY_WRAP 
 657 static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
) 
 659     size_t nMax 
= 80; // FIXME 
 660     size_t nStart 
= strlen(pszPrefix
); 
 664     while ( *psz 
!= '\0' ) { 
 665         for ( n 
= nStart
; (n 
< nMax
) && (*psz 
!= '\0'); n
++ ) 
 669         if ( *psz 
!= '\0' ) { 
 671             for ( n 
= 0; n 
< nStart
; n
++ ) 
 674             // as we wrapped, squeeze all white space 
 675             while ( isspace(*psz
) ) 
 682 #endif  //LOG_PRETTY_WRAP 
 684 // ---------------------------------------------------------------------------- 
 685 // error code/error message retrieval functions 
 686 // ---------------------------------------------------------------------------- 
 688 // get error code from syste 
 689 unsigned long wxSysErrorCode() 
 691 #if defined(__WXMSW__) && !defined(__WXMICROWIN__) 
 693     return ::GetLastError(); 
 695     // TODO what to do on Windows 3.1? 
 703 // get error message from system 
 704 const wxChar 
*wxSysErrorMsg(unsigned long nErrCode
) 
 707         nErrCode 
= wxSysErrorCode(); 
 709 #if defined(__WXMSW__) && !defined(__WXMICROWIN__) 
 711     static wxChar s_szBuf
[LOG_BUFFER_SIZE 
/ 2]; 
 713     // get error message from system 
 715     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER 
| FORMAT_MESSAGE_FROM_SYSTEM
, 
 717             MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), 
 721     // copy it to our buffer and free memory 
 722     wxStrncpy(s_szBuf
, (const wxChar 
*)lpMsgBuf
, WXSIZEOF(s_szBuf
) - 1); 
 723     s_szBuf
[WXSIZEOF(s_szBuf
) - 1] = wxT('\0'); 
 726     // returned string is capitalized and ended with '\r\n' - bad 
 727     s_szBuf
[0] = (wxChar
)wxTolower(s_szBuf
[0]); 
 728     size_t len 
= wxStrlen(s_szBuf
); 
 731         if ( s_szBuf
[len 
- 2] == wxT('\r') ) 
 732             s_szBuf
[len 
- 2] = wxT('\0'); 
 742     static wxChar s_szBuf
[LOG_BUFFER_SIZE 
/ 2]; 
 743     wxConvCurrent
->MB2WC(s_szBuf
, strerror(nErrCode
), WXSIZEOF(s_szBuf
) -1); 
 746     return strerror((int)nErrCode
); 
 751 // ---------------------------------------------------------------------------- 
 753 // ---------------------------------------------------------------------------- 
 758 bool wxAssertIsEqual(int x
, int y
) 
 763 // break into the debugger 
 766 #if defined(__WXMSW__) && !defined(__WXMICROWIN__) 
 768 #elif defined(__WXMAC__) 
 774 #elif defined(__UNIX__) 
 781 // this function is called when an assert fails 
 782 void wxOnAssert(const wxChar 
*szFile
, int nLine
, const wxChar 
*szMsg
) 
 784     // this variable can be set to true to suppress "assert failure" messages 
 785     static bool s_bNoAsserts 
= FALSE
; 
 786     static bool s_bInAssert 
= FALSE
;        // FIXME MT-unsafe 
 789         // He-e-e-e-elp!! we're trapped in endless loop 
 799     wxChar szBuf
[LOG_BUFFER_SIZE
]; 
 801     // make life easier for people using VC++ IDE: clicking on the message 
 802     // will take us immediately to the place of the failed assert 
 803     wxSnprintf(szBuf
, WXSIZEOF(szBuf
), 
 805                wxT("%s(%d): assert failed"), 
 807     // make the error message more clear for all the others 
 808                wxT("Assert failed in file %s at line %d"), 
 812     if ( szMsg 
!= NULL 
) { 
 813         wxStrcat(szBuf
, wxT(": ")); 
 814         wxStrcat(szBuf
, szMsg
); 
 817         wxStrcat(szBuf
, wxT(".")); 
 820     if ( !s_bNoAsserts 
) { 
 821         // send it to the normal log destination 
 824 #if (wxUSE_GUI && wxUSE_MSGDLG) || defined(__WXMSW__) 
 825         // this message is intentionally not translated - it is for 
 827         wxStrcat(szBuf
, wxT("\nDo you want to stop the program?\nYou can also choose [Cancel] to suppress further warnings.")); 
 829         // use the native message box if available: this is more robust than 
 831 #if defined(__WXMSW__) && !defined(__WXMICROWIN__) 
 832         switch ( ::MessageBox(NULL
, szBuf
, _T("Debug"), 
 833                               MB_YESNOCANCEL 
| MB_ICONSTOP 
) ) { 
 842             //case IDNO: nothing to do 
 845         switch ( wxMessageBox(szBuf
, wxT("Debug"), 
 846                               wxYES_NO 
| wxCANCEL 
| wxICON_STOP 
) ) { 
 855             //case wxNO: nothing to do