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 
  61   #include  "wx/msw/private.h"      // includes windows.h for OutputDebugString 
  66 // ---------------------------------------------------------------------------- 
  67 // non member functions 
  68 // ---------------------------------------------------------------------------- 
  70 // define this to enable wrapping of log messages 
  71 //#define LOG_PRETTY_WRAP 
  73 #ifdef  LOG_PRETTY_WRAP 
  74   static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
); 
  77 // ============================================================================ 
  79 // ============================================================================ 
  81 // ---------------------------------------------------------------------------- 
  83 // ---------------------------------------------------------------------------- 
  85 // log functions can't allocate memory (LogError("out of memory...") should 
  86 // work!), so we use a static buffer for all log messages 
  87 #define LOG_BUFFER_SIZE   (4096) 
  89 // static buffer for error messages 
  90 static wxChar s_szBuf
[LOG_BUFFER_SIZE
]; 
  94 // the critical section protecting the static buffer 
  95 static wxCriticalSection gs_csLogBuf
; 
  97 #endif // wxUSE_THREADS 
  99 // return true if we have a non NULL non disabled log target 
 100 static inline bool IsLoggingEnabled() 
 102     return wxLog::IsEnabled() && (wxLog::GetActiveTarget() != NULL
); 
 105 // ---------------------------------------------------------------------------- 
 106 // implementation of Log functions 
 108 // NB: unfortunately we need all these distinct functions, we can't make them 
 109 //     macros and not all compilers inline vararg functions. 
 110 // ---------------------------------------------------------------------------- 
 112 // generic log function 
 113 void wxLogGeneric(wxLogLevel level
, const wxChar 
*szFormat
, ...) 
 115     if ( IsLoggingEnabled() ) { 
 116         wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
); 
 119         va_start(argptr
, szFormat
); 
 120         wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
); 
 123         wxLog::OnLog(level
, s_szBuf
, time(NULL
)); 
 127 #define IMPLEMENT_LOG_FUNCTION(level)                               \ 
 128   void wxLog##level(const wxChar *szFormat, ...)                    \ 
 130     if ( IsLoggingEnabled() ) {                                     \ 
 131       wxCRIT_SECT_LOCKER(locker, gs_csLogBuf);                      \ 
 134       va_start(argptr, szFormat);                                   \ 
 135       wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr);    \ 
 138       wxLog::OnLog(wxLOG_##level, s_szBuf, time(NULL));             \ 
 142 IMPLEMENT_LOG_FUNCTION(FatalError
) 
 143 IMPLEMENT_LOG_FUNCTION(Error
) 
 144 IMPLEMENT_LOG_FUNCTION(Warning
) 
 145 IMPLEMENT_LOG_FUNCTION(Message
) 
 146 IMPLEMENT_LOG_FUNCTION(Info
) 
 147 IMPLEMENT_LOG_FUNCTION(Status
) 
 149 // same as info, but only if 'verbose' mode is on 
 150 void wxLogVerbose(const wxChar 
*szFormat
, ...) 
 152     if ( IsLoggingEnabled() ) { 
 153         wxLog 
*pLog 
= wxLog::GetActiveTarget(); 
 154         if ( pLog 
!= NULL 
&& pLog
->GetVerbose() ) { 
 155             wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
); 
 158             va_start(argptr
, szFormat
); 
 159             wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
); 
 162             wxLog::OnLog(wxLOG_Info
, s_szBuf
, time(NULL
)); 
 169 #define IMPLEMENT_LOG_DEBUG_FUNCTION(level)                         \ 
 170   void wxLog##level(const wxChar *szFormat, ...)                    \ 
 172     if ( IsLoggingEnabled() ) {                                     \ 
 173       wxCRIT_SECT_LOCKER(locker, gs_csLogBuf);                      \ 
 176       va_start(argptr, szFormat);                                   \ 
 177       wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr);    \ 
 180       wxLog::OnLog(wxLOG_##level, s_szBuf, time(NULL));             \ 
 184   void wxLogTrace(const wxChar 
*mask
, const wxChar 
*szFormat
, ...) 
 186     if ( IsLoggingEnabled() && wxLog::IsAllowedTraceMask(mask
) ) { 
 187       wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
); 
 190       size_t len 
= WXSIZEOF(s_szBuf
); 
 191       wxStrncpy(s_szBuf
, _T("("), len
); 
 192       len 
-= 1; // strlen("(") 
 194       wxStrncat(p
, mask
, len
); 
 195       size_t lenMask 
= wxStrlen(mask
); 
 199       wxStrncat(p
, _T(") "), len
); 
 204       va_start(argptr
, szFormat
); 
 205       wxVsnprintf(p
, len
, szFormat
, argptr
); 
 208       wxLog::OnLog(wxLOG_Trace
, s_szBuf
, time(NULL
)); 
 212   void wxLogTrace(wxTraceMask mask
, const wxChar 
*szFormat
, ...) 
 214     // we check that all of mask bits are set in the current mask, so 
 215     // that wxLogTrace(wxTraceRefCount | wxTraceOle) will only do something 
 216     // if both bits are set. 
 217     if ( IsLoggingEnabled() && ((wxLog::GetTraceMask() & mask
) == mask
) ) { 
 218       wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
); 
 221       va_start(argptr
, szFormat
); 
 222       wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
); 
 225       wxLog::OnLog(wxLOG_Trace
, s_szBuf
, time(NULL
)); 
 230   #define IMPLEMENT_LOG_DEBUG_FUNCTION(level) 
 233 IMPLEMENT_LOG_DEBUG_FUNCTION(Debug
) 
 234 IMPLEMENT_LOG_DEBUG_FUNCTION(Trace
) 
 236 // wxLogSysError: one uses the last error code, for other  you must give it 
 239 // common part of both wxLogSysError 
 240 void wxLogSysErrorHelper(long lErrCode
) 
 242     wxChar szErrMsg
[LOG_BUFFER_SIZE 
/ 2]; 
 243     wxSnprintf(szErrMsg
, WXSIZEOF(szErrMsg
), 
 244                _(" (error %ld: %s)"), lErrCode
, wxSysErrorMsg(lErrCode
)); 
 245     wxStrncat(s_szBuf
, szErrMsg
, WXSIZEOF(s_szBuf
) - wxStrlen(s_szBuf
)); 
 247     wxLog::OnLog(wxLOG_Error
, s_szBuf
, time(NULL
)); 
 250 void WXDLLEXPORT 
wxLogSysError(const wxChar 
*szFormat
, ...) 
 252     if ( IsLoggingEnabled() ) { 
 253         wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
); 
 256         va_start(argptr
, szFormat
); 
 257         wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
); 
 260         wxLogSysErrorHelper(wxSysErrorCode()); 
 264 void WXDLLEXPORT 
wxLogSysError(long lErrCode
, const wxChar 
*szFormat
, ...) 
 266     if ( IsLoggingEnabled() ) { 
 267         wxCRIT_SECT_LOCKER(locker
, gs_csLogBuf
); 
 270         va_start(argptr
, szFormat
); 
 271         wxVsnprintf(s_szBuf
, WXSIZEOF(s_szBuf
), szFormat
, argptr
); 
 274         wxLogSysErrorHelper(lErrCode
); 
 278 // ---------------------------------------------------------------------------- 
 279 // wxLog class implementation 
 280 // ---------------------------------------------------------------------------- 
 284     m_bHasMessages 
= FALSE
; 
 288 wxLog 
*wxLog::GetActiveTarget() 
 290     if ( ms_bAutoCreate 
&& ms_pLogger 
== NULL 
) { 
 291         // prevent infinite recursion if someone calls wxLogXXX() from 
 292         // wxApp::CreateLogTarget() 
 293         static bool s_bInGetActiveTarget 
= FALSE
; 
 294         if ( !s_bInGetActiveTarget 
) { 
 295             s_bInGetActiveTarget 
= TRUE
; 
 297             // ask the application to create a log target for us 
 298             if ( wxTheApp 
!= NULL 
) 
 299                 ms_pLogger 
= wxTheApp
->CreateLogTarget(); 
 301                 ms_pLogger 
= new wxLogStderr
; 
 303             s_bInGetActiveTarget 
= FALSE
; 
 305             // do nothing if it fails - what can we do? 
 312 wxLog 
*wxLog::SetActiveTarget(wxLog 
*pLogger
) 
 314     if ( ms_pLogger 
!= NULL 
) { 
 315         // flush the old messages before changing because otherwise they might 
 316         // get lost later if this target is not restored 
 320     wxLog 
*pOldLogger 
= ms_pLogger
; 
 321     ms_pLogger 
= pLogger
; 
 326 void wxLog::DontCreateOnDemand() 
 328     ms_bAutoCreate 
= FALSE
; 
 330     // this is usually called at the end of the program and we assume that it 
 331     // is *always* called at the end - so we free memory here to avoid false 
 332     // memory leak reports from wxWin  memory tracking code 
 336 void wxLog::RemoveTraceMask(const wxString
& str
) 
 338     int index 
= ms_aTraceMasks
.Index(str
); 
 339     if ( index 
!= wxNOT_FOUND 
) 
 340         ms_aTraceMasks
.Remove((size_t)index
); 
 343 void wxLog::ClearTraceMasks() 
 345     ms_aTraceMasks
.Clear(); 
 348 void wxLog::TimeStamp(wxString 
*str
) 
 354         (void)time(&timeNow
); 
 355         wxStrftime(buf
, WXSIZEOF(buf
), ms_timestamp
, localtime(&timeNow
)); 
 358         *str 
<< buf 
<< wxT(": "); 
 362 void wxLog::DoLog(wxLogLevel level
, const wxChar 
*szString
, time_t t
) 
 365         case wxLOG_FatalError
: 
 366             DoLogString(wxString(_("Fatal error: ")) + szString
, t
); 
 367             DoLogString(_("Program aborted."), t
); 
 373             DoLogString(wxString(_("Error: ")) + szString
, t
); 
 377             DoLogString(wxString(_("Warning: ")) + szString
, t
); 
 384         default:    // log unknown log levels too 
 385                 DoLogString(szString
, t
); 
 392                 wxString msg 
= level 
== wxLOG_Trace 
? wxT("Trace: ") 
 402 void wxLog::DoLogString(const wxChar 
*WXUNUSED(szString
), time_t WXUNUSED(t
)) 
 404     wxFAIL_MSG(wxT("DoLogString must be overriden if it's called.")); 
 412 // ---------------------------------------------------------------------------- 
 413 // wxLogStderr class implementation 
 414 // ---------------------------------------------------------------------------- 
 416 wxLogStderr::wxLogStderr(FILE *fp
) 
 424 #if defined(__WXMAC__) && !defined(__UNIX__) 
 425 #define kDebuggerSignature              'MWDB' 
 427 static Boolean 
FindProcessBySignature(OSType signature
, ProcessInfoRec
* info
) 
 430         ProcessSerialNumber psn
; 
 431         Boolean found 
= false; 
 432         psn
.highLongOfPSN 
= 0; 
 433         psn
.lowLongOfPSN 
= kNoProcess
; 
 435         if (!info
) return false; 
 437         info
->processInfoLength 
= sizeof(ProcessInfoRec
); 
 438         info
->processName 
= NULL
; 
 439         info
->processAppSpec 
= NULL
; 
 442         while (!found 
&& err 
== noErr
) 
 444                 err 
= GetNextProcess(&psn
); 
 447                         err 
= GetProcessInformation(&psn
, info
); 
 448                         found 
= err 
== noErr 
&& info
->processSignature 
== signature
; 
 454 pascal Boolean 
MWDebuggerIsRunning(void) 
 457         return FindProcessBySignature(kDebuggerSignature
, &info
); 
 460 pascal OSErr 
AmIBeingMWDebugged(Boolean
* result
) 
 463         ProcessSerialNumber psn
; 
 464         OSType sig 
= kDebuggerSignature
; 
 465         AppleEvent      theAE 
= {typeNull
, NULL
}; 
 466         AppleEvent      theReply 
= {typeNull
, NULL
}; 
 467         AEAddressDesc addr  
= {typeNull
, NULL
}; 
 471         if (!result
) return paramErr
; 
 473         err 
= AECreateDesc(typeApplSignature
, &sig
, sizeof(sig
), &addr
); 
 474         if (err 
!= noErr
) goto exit
; 
 476         err 
= AECreateAppleEvent('MWDB', 'Dbg?', &addr
, 
 477                                 kAutoGenerateReturnID
, kAnyTransactionID
, &theAE
); 
 478         if (err 
!= noErr
) goto exit
; 
 480         GetCurrentProcess(&psn
); 
 481         err 
= AEPutParamPtr(&theAE
, keyDirectObject
, typeProcessSerialNumber
, 
 483         if (err 
!= noErr
) goto exit
; 
 485         err 
= AESend(&theAE
, &theReply
, kAEWaitReply
, kAENormalPriority
, 
 486                                         kAEDefaultTimeout
, NULL
, NULL
); 
 487         if (err 
!= noErr
) goto exit
; 
 489         err 
= AEGetParamPtr(&theReply
, keyAEResult
, typeBoolean
, &actualType
, result
,  
 490                                 sizeof(Boolean
), &actualSize
); 
 494                 AEDisposeDesc(&addr
); 
 495         if (theAE
.dataHandle
) 
 496                 AEDisposeDesc(&theAE
); 
 497         if (theReply
.dataHandle
) 
 498                 AEDisposeDesc(&theReply
); 
 504 void wxLogStderr::DoLogString(const wxChar 
*szString
, time_t WXUNUSED(t
)) 
 510     fputs(str
.mb_str(), m_fp
); 
 511     fputc(_T('\n'), m_fp
); 
 514     // under Windows, programs usually don't have stderr at all, so show the 
 515     // messages also under debugger - unless it's a console program 
 516 #if defined(__WXMSW__) && wxUSE_GUI 
 518     OutputDebugString(str
.c_str()); 
 520 #if defined(__WXMAC__) && !defined(__WXMAC_X__) && wxUSE_GUI 
 522         strcpy( (char*) pstr 
, str
.c_str() ) ; 
 523         strcat( (char*) pstr 
, ";g" ) ; 
 524         c2pstr( (char*) pstr 
) ; 
 526         Boolean running 
= false ; 
 529         if ( MWDebuggerIsRunning() ) 
 531                 AmIBeingMWDebugged( &running ) ; 
 554 // ---------------------------------------------------------------------------- 
 555 // wxLogStream implementation 
 556 // ---------------------------------------------------------------------------- 
 558 #if wxUSE_STD_IOSTREAM 
 559 wxLogStream::wxLogStream(wxSTD ostream 
*ostr
) 
 562         m_ostr 
= &wxSTD cerr
; 
 567 void wxLogStream::DoLogString(const wxChar 
*szString
, time_t WXUNUSED(t
)) 
 571     (*m_ostr
) << str 
<< wxConvertWX2MB(szString
) << wxSTD endl
; 
 573 #endif // wxUSE_STD_IOSTREAM 
 575 // ============================================================================ 
 576 // Global functions/variables 
 577 // ============================================================================ 
 579 // ---------------------------------------------------------------------------- 
 581 // ---------------------------------------------------------------------------- 
 583 wxLog          
*wxLog::ms_pLogger      
= (wxLog 
*)NULL
; 
 584 bool            wxLog::ms_doLog        
= TRUE
; 
 585 bool            wxLog::ms_bAutoCreate  
= TRUE
; 
 587 size_t          wxLog::ms_suspendCount 
= 0; 
 590     const wxChar 
*wxLog::ms_timestamp  
= wxT("%X");  // time only, no date 
 592     const wxChar 
*wxLog::ms_timestamp  
= NULL
;       // save space 
 595 wxTraceMask     
wxLog::ms_ulTraceMask  
= (wxTraceMask
)0; 
 596 wxArrayString   
wxLog::ms_aTraceMasks
; 
 598 // ---------------------------------------------------------------------------- 
 599 // stdout error logging helper 
 600 // ---------------------------------------------------------------------------- 
 602 // helper function: wraps the message and justifies it under given position 
 603 // (looks more pretty on the terminal). Also adds newline at the end. 
 605 // TODO this is now disabled until I find a portable way of determining the 
 606 //      terminal window size (ok, I found it but does anybody really cares?) 
 607 #ifdef LOG_PRETTY_WRAP 
 608 static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
) 
 610     size_t nMax 
= 80; // FIXME 
 611     size_t nStart 
= strlen(pszPrefix
); 
 615     while ( *psz 
!= '\0' ) { 
 616         for ( n 
= nStart
; (n 
< nMax
) && (*psz 
!= '\0'); n
++ ) 
 620         if ( *psz 
!= '\0' ) { 
 622             for ( n 
= 0; n 
< nStart
; n
++ ) 
 625             // as we wrapped, squeeze all white space 
 626             while ( isspace(*psz
) ) 
 633 #endif  //LOG_PRETTY_WRAP 
 635 // ---------------------------------------------------------------------------- 
 636 // error code/error message retrieval functions 
 637 // ---------------------------------------------------------------------------- 
 639 // get error code from syste 
 640 unsigned long wxSysErrorCode() 
 644     return ::GetLastError(); 
 646     // TODO what to do on Windows 3.1? 
 654 // get error message from system 
 655 const wxChar 
*wxSysErrorMsg(unsigned long nErrCode
) 
 658         nErrCode 
= wxSysErrorCode(); 
 662     static wxChar s_szBuf
[LOG_BUFFER_SIZE 
/ 2]; 
 664     // get error message from system 
 666     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER 
| FORMAT_MESSAGE_FROM_SYSTEM
, 
 668             MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), 
 672     // copy it to our buffer and free memory 
 673     wxStrncpy(s_szBuf
, (const wxChar 
*)lpMsgBuf
, WXSIZEOF(s_szBuf
) - 1); 
 674     s_szBuf
[WXSIZEOF(s_szBuf
) - 1] = wxT('\0'); 
 677     // returned string is capitalized and ended with '\r\n' - bad 
 678     s_szBuf
[0] = (wxChar
)wxTolower(s_szBuf
[0]); 
 679     size_t len 
= wxStrlen(s_szBuf
); 
 682         if ( s_szBuf
[len 
- 2] == wxT('\r') ) 
 683             s_szBuf
[len 
- 2] = wxT('\0'); 
 693     static wxChar s_szBuf
[LOG_BUFFER_SIZE 
/ 2]; 
 694     wxConvCurrent
->MB2WC(s_szBuf
, strerror(nErrCode
), WXSIZEOF(s_szBuf
) -1); 
 697     return strerror((int)nErrCode
); 
 702 // ---------------------------------------------------------------------------- 
 704 // ---------------------------------------------------------------------------- 
 708 // break into the debugger 
 713 #elif defined(__WXMAC__) 
 719 #elif defined(__UNIX__) 
 726 // this function is called when an assert fails 
 727 void wxOnAssert(const wxChar 
*szFile
, int nLine
, const wxChar 
*szMsg
) 
 729     // this variable can be set to true to suppress "assert failure" messages 
 730     static bool s_bNoAsserts 
= FALSE
; 
 731     static bool s_bInAssert 
= FALSE
;        // FIXME MT-unsafe 
 734         // He-e-e-e-elp!! we're trapped in endless loop 
 744     wxChar szBuf
[LOG_BUFFER_SIZE
]; 
 746     // make life easier for people using VC++ IDE: clicking on the message 
 747     // will take us immediately to the place of the failed assert 
 748     wxSnprintf(szBuf
, WXSIZEOF(szBuf
), 
 750                wxT("%s(%d): assert failed"), 
 752     // make the error message more clear for all the others 
 753                wxT("Assert failed in file %s at line %d"), 
 757     if ( szMsg 
!= NULL 
) { 
 758         wxStrcat(szBuf
, wxT(": ")); 
 759         wxStrcat(szBuf
, szMsg
); 
 762         wxStrcat(szBuf
, wxT(".")); 
 765     if ( !s_bNoAsserts 
) { 
 766         // send it to the normal log destination 
 769 #if wxUSE_GUI || defined(__WXMSW__) 
 770         // this message is intentionally not translated - it is for 
 772         wxStrcat(szBuf
, wxT("\nDo you want to stop the program?\nYou can also choose [Cancel] to suppress further warnings.")); 
 774         // use the native message box if available: this is more robust than 
 777         switch ( ::MessageBox(NULL
, szBuf
, _T("Debug"), 
 778                               MB_YESNOCANCEL 
| MB_ICONSTOP 
) ) { 
 787             //case IDNO: nothing to do 
 790         switch ( wxMessageBox(szBuf
, wxT("Debug"), 
 791                               wxYES_NO 
| wxCANCEL 
| wxICON_STOP 
) ) { 
 800             //case wxNO: nothing to do