1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/common/log.cpp 
   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 licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  33     #include "wx/arrstr.h" 
  35     #include "wx/string.h" 
  39 #include "wx/apptrait.h" 
  40 #include "wx/datetime.h" 
  42 #include "wx/msgout.h" 
  43 #include "wx/textfile.h" 
  44 #include "wx/thread.h" 
  45 #include "wx/private/threadinfo.h" 
  47 #include "wx/vector.h" 
  49 // other standard headers 
  59 #include "wx/msw/wince/time.h" 
  62 #if defined(__WINDOWS__) 
  63     #include "wx/msw/private.h" // includes windows.h 
  66 #undef wxLOG_COMPONENT 
  67 const char *wxLOG_COMPONENT 
= ""; 
  69 // this macro allows to define an object which will be initialized before any 
  70 // other function in this file is called: this is necessary to allow log 
  71 // functions to be used during static initialization (this is not advisable 
  72 // anyhow but we should at least try to not crash) and to also ensure that they 
  73 // are initialized by the time static initialization is done, i.e. before any 
  74 // threads are created hopefully 
  76 // the net effect of all this is that you can use Get##name() function to 
  77 // access the object without worrying about it being not initialized 
  79 // see also WX_DEFINE_GLOBAL_CONV2() in src/common/strconv.cpp 
  80 #define WX_DEFINE_GLOBAL_VAR(type, name)                                      \ 
  81     inline type& Get##name()                                                  \ 
  83         static type s_##name;                                                 \ 
  87     type *gs_##name##Ptr = &Get##name() 
  91 wxTLS_TYPE(wxThreadSpecificInfo
) wxThreadInfoVar
; 
  96 // contains messages logged by the other threads and waiting to be shown until 
  97 // Flush() is called in the main one 
  98 typedef wxVector
<wxLogRecord
> wxLogRecords
; 
  99 wxLogRecords gs_bufferedLogRecords
; 
 101 #define WX_DEFINE_LOG_CS(name) WX_DEFINE_GLOBAL_VAR(wxCriticalSection, name##CS) 
 103 // this critical section is used for buffering the messages from threads other 
 104 // than main, i.e. it protects all accesses to gs_bufferedLogRecords above 
 105 WX_DEFINE_LOG_CS(BackgroundLog
); 
 107 // this one is used for protecting TraceMasks() from concurrent access 
 108 WX_DEFINE_LOG_CS(TraceMask
); 
 110 // and this one is used for GetComponentLevels() 
 111 WX_DEFINE_LOG_CS(Levels
); 
 113 } // anonymous namespace 
 115 #endif // wxUSE_THREADS 
 117 // ---------------------------------------------------------------------------- 
 118 // non member functions 
 119 // ---------------------------------------------------------------------------- 
 121 // define this to enable wrapping of log messages 
 122 //#define LOG_PRETTY_WRAP 
 124 #ifdef  LOG_PRETTY_WRAP 
 125   static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
); 
 128 // ---------------------------------------------------------------------------- 
 130 // ---------------------------------------------------------------------------- 
 135 // this struct is used to store information about the previous log message used 
 136 // by OnLog() to (optionally) avoid logging multiple copies of the same message 
 137 struct PreviousLogInfo
 
 145     // previous message itself 
 151     // other information about it 
 152     wxLogRecordInfo info
; 
 154     // the number of times it was already repeated 
 155     unsigned numRepeated
; 
 158 PreviousLogInfo gs_prevLog
; 
 161 // map containing all components for which log level was explicitly set 
 163 // NB: all accesses to it must be protected by GetLevelsCS() critical section 
 164 WX_DEFINE_GLOBAL_VAR(wxStringToNumHashMap
, ComponentLevels
); 
 166 // ---------------------------------------------------------------------------- 
 167 // wxLogOutputBest: wxLog wrapper around wxMessageOutputBest 
 168 // ---------------------------------------------------------------------------- 
 170 class wxLogOutputBest 
: public wxLog
 
 173     wxLogOutputBest() { } 
 176     virtual void DoLogText(const wxString
& msg
) 
 178         wxMessageOutputBest().Output(msg
); 
 182     wxDECLARE_NO_COPY_CLASS(wxLogOutputBest
); 
 185 } // anonymous namespace 
 187 // ============================================================================ 
 189 // ============================================================================ 
 191 // ---------------------------------------------------------------------------- 
 192 // helper global functions 
 193 // ---------------------------------------------------------------------------- 
 195 void wxSafeShowMessage(const wxString
& title
, const wxString
& text
) 
 198     ::MessageBox(NULL
, text
.t_str(), title
.t_str(), MB_OK 
| MB_ICONSTOP
); 
 200     wxFprintf(stderr
, wxS("%s: %s\n"), title
.c_str(), text
.c_str()); 
 205 // ---------------------------------------------------------------------------- 
 206 // wxLogFormatter class implementation 
 207 // ---------------------------------------------------------------------------- 
 210 wxLogFormatter::Format(wxLogLevel level
, 
 212                        const wxLogRecordInfo
& info
) const 
 216     // don't time stamp debug messages under MSW as debug viewers usually 
 217     // already have an option to do it 
 219     if ( level 
!= wxLOG_Debug 
&& level 
!= wxLOG_Trace 
) 
 220 #endif // __WINDOWS__ 
 221         prefix 
= FormatTime(info
.timestamp
); 
 226         prefix 
+= _("Error: "); 
 230         prefix 
+= _("Warning: "); 
 233         // don't prepend "debug/trace" prefix under MSW as it goes to the debug 
 234         // window anyhow and so can't be confused with something else 
 237         // this prefix (as well as the one below) is intentionally not 
 238         // translated as nobody translates debug messages anyhow 
 245 #endif // !__WINDOWS__ 
 252 wxLogFormatter::FormatTime(time_t t
) const 
 255     wxLog::TimeStamp(&str
, t
); 
 261 // ---------------------------------------------------------------------------- 
 262 // wxLog class implementation 
 263 // ---------------------------------------------------------------------------- 
 265 unsigned wxLog::LogLastRepeatIfNeeded() 
 267     const unsigned count 
= gs_prevLog
.numRepeated
; 
 269     if ( gs_prevLog
.numRepeated 
) 
 273         if ( gs_prevLog
.numRepeated 
== 1 ) 
 275             // We use a separate message for this case as "repeated 1 time" 
 276             // looks somewhat strange. 
 277             msg 
= _("The previous message repeated once."); 
 281             // Notice that we still use wxPLURAL() to ensure that multiple 
 282             // numbers of times are correctly formatted, even though we never 
 283             // actually use the singular string. 
 284             msg
.Printf(wxPLURAL("The previous message repeated %lu time.", 
 285                                 "The previous message repeated %lu times.", 
 286                                 gs_prevLog
.numRepeated
), 
 287                        gs_prevLog
.numRepeated
); 
 290         msg
.Printf(wxS("The previous message was repeated %lu time(s)."), 
 291                    gs_prevLog
.numRepeated
); 
 293         gs_prevLog
.numRepeated 
= 0; 
 294         gs_prevLog
.msg
.clear(); 
 295         DoLogRecord(gs_prevLog
.level
, msg
, gs_prevLog
.info
); 
 303     // Flush() must be called before destroying the object as otherwise some 
 304     // messages could be lost 
 305     if ( gs_prevLog
.numRepeated 
) 
 307         wxMessageOutputDebug().Printf
 
 312                 "Last repeated message (\"%s\", %lu time) wasn't output", 
 313                 "Last repeated message (\"%s\", %lu times) wasn't output", 
 314                 gs_prevLog
.numRepeated
 
 317             wxS("Last repeated message (\"%s\", %lu time(s)) wasn't output"), 
 320             gs_prevLog
.numRepeated
 
 327 // ---------------------------------------------------------------------------- 
 328 // wxLog logging functions 
 329 // ---------------------------------------------------------------------------- 
 333 wxLog::OnLog(wxLogLevel level
, const wxString
& msg
, time_t t
) 
 335     wxLogRecordInfo info
; 
 338     info
.threadId 
= wxThread::GetCurrentId(); 
 339 #endif // wxUSE_THREADS 
 341     OnLog(level
, msg
, info
); 
 346 wxLog::OnLog(wxLogLevel level
, 
 348              const wxLogRecordInfo
& info
) 
 350     // fatal errors can't be suppressed nor handled by the custom log target 
 351     // and always terminate the program 
 352     if ( level 
== wxLOG_FatalError 
) 
 354         wxSafeShowMessage(wxS("Fatal Error"), msg
); 
 362     if ( !wxThread::IsMain() ) 
 364         logger 
= wxThreadInfo
.logger
; 
 369                 // buffer the messages until they can be shown from the main 
 371                 wxCriticalSectionLocker 
lock(GetBackgroundLogCS()); 
 373                 gs_bufferedLogRecords
.push_back(wxLogRecord(level
, msg
, info
)); 
 375                 // ensure that our Flush() will be called soon 
 378             //else: we don't have any logger at all, there is no need to log 
 383         //else: we have a thread-specific logger, we can send messages to it 
 387 #endif // wxUSE_THREADS 
 389         logger 
= GetMainThreadActiveTarget(); 
 394     logger
->CallDoLogNow(level
, msg
, info
); 
 398 wxLog::CallDoLogNow(wxLogLevel level
, 
 400                     const wxLogRecordInfo
& info
) 
 402     if ( GetRepetitionCounting() ) 
 404         if ( msg 
== gs_prevLog
.msg 
) 
 406             gs_prevLog
.numRepeated
++; 
 408             // nothing else to do, in particular, don't log the 
 413         LogLastRepeatIfNeeded(); 
 415         // reset repetition counter for a new message 
 416         gs_prevLog
.msg 
= msg
; 
 417         gs_prevLog
.level 
= level
; 
 418         gs_prevLog
.info 
= info
; 
 421     // handle extra data which may be passed to us by wxLogXXX() 
 422     wxString prefix
, suffix
; 
 424     if ( info
.GetNumValue(wxLOG_KEY_SYS_ERROR_CODE
, &num
) ) 
 426         const long err 
= static_cast<long>(num
); 
 428         suffix
.Printf(_(" (error %ld: %s)"), err
, wxSysErrorMsg(err
)); 
 433     if ( level 
== wxLOG_Trace 
&& info
.GetStrValue(wxLOG_KEY_TRACE_MASK
, &str
) ) 
 435         prefix 
= "(" + str 
+ ") "; 
 437 #endif // wxUSE_LOG_TRACE 
 439     DoLogRecord(level
, prefix 
+ msg 
+ suffix
, info
); 
 442 void wxLog::DoLogRecord(wxLogLevel level
, 
 444                              const wxLogRecordInfo
& info
) 
 446 #if WXWIN_COMPATIBILITY_2_8 
 447     // call the old DoLog() to ensure that existing custom log classes still 
 450     // as the user code could have defined it as either taking "const char *" 
 451     // (in ANSI build) or "const wxChar *" (in ANSI/Unicode), we have no choice 
 452     // but to call both of them 
 453     DoLog(level
, (const char*)msg
.mb_str(), info
.timestamp
); 
 454     DoLog(level
, (const wchar_t*)msg
.wc_str(), info
.timestamp
); 
 455 #else // !WXWIN_COMPATIBILITY_2_8 
 457 #endif // WXWIN_COMPATIBILITY_2_8/!WXWIN_COMPATIBILITY_2_8 
 459     // Use wxLogFormatter to format the message 
 460     DoLogTextAtLevel(level
, m_formatter
->Format (level
, msg
, info
)); 
 463 void wxLog::DoLogTextAtLevel(wxLogLevel level
, const wxString
& msg
) 
 465     // we know about debug messages (because using wxMessageOutputDebug is the 
 466     // right thing to do in 99% of all cases and also for compatibility) but 
 467     // anything else needs to be handled in the derived class 
 468     if ( level 
== wxLOG_Debug 
|| level 
== wxLOG_Trace 
) 
 470         wxMessageOutputDebug().Output(msg 
+ wxS('\n')); 
 478 void wxLog::DoLogText(const wxString
& WXUNUSED(msg
)) 
 480     // in 2.8-compatible build the derived class might override DoLog() or 
 481     // DoLogString() instead so we can't have this assert there 
 482 #if !WXWIN_COMPATIBILITY_2_8 
 483     wxFAIL_MSG( "must be overridden if it is called" ); 
 484 #endif // WXWIN_COMPATIBILITY_2_8 
 487 #if WXWIN_COMPATIBILITY_2_8 
 489 void wxLog::DoLog(wxLogLevel 
WXUNUSED(level
), const char *szString
, time_t t
) 
 491     DoLogString(szString
, t
); 
 494 void wxLog::DoLog(wxLogLevel 
WXUNUSED(level
), const wchar_t *wzString
, time_t t
) 
 496     DoLogString(wzString
, t
); 
 499 #endif // WXWIN_COMPATIBILITY_2_8 
 501 // ---------------------------------------------------------------------------- 
 502 // wxLog active target management 
 503 // ---------------------------------------------------------------------------- 
 505 wxLog 
*wxLog::GetActiveTarget() 
 508     if ( !wxThread::IsMain() ) 
 510         // check if we have a thread-specific log target 
 511         wxLog 
* const logger 
= wxThreadInfo
.logger
; 
 513         // the code below should be only executed for the main thread as 
 514         // CreateLogTarget() is not meant for auto-creating log targets for 
 515         // worker threads so skip it in any case 
 516         return logger 
? logger 
: ms_pLogger
; 
 518 #endif // wxUSE_THREADS 
 520     return GetMainThreadActiveTarget(); 
 524 wxLog 
*wxLog::GetMainThreadActiveTarget() 
 526     if ( ms_bAutoCreate 
&& ms_pLogger 
== NULL 
) { 
 527         // prevent infinite recursion if someone calls wxLogXXX() from 
 528         // wxApp::CreateLogTarget() 
 529         static bool s_bInGetActiveTarget 
= false; 
 530         if ( !s_bInGetActiveTarget 
) { 
 531             s_bInGetActiveTarget 
= true; 
 533             // ask the application to create a log target for us 
 534             if ( wxTheApp 
!= NULL 
) 
 535                 ms_pLogger 
= wxTheApp
->GetTraits()->CreateLogTarget(); 
 537                 ms_pLogger 
= new wxLogOutputBest
; 
 539             s_bInGetActiveTarget 
= false; 
 541             // do nothing if it fails - what can we do? 
 548 wxLog 
*wxLog::SetActiveTarget(wxLog 
*pLogger
) 
 550     if ( ms_pLogger 
!= NULL 
) { 
 551         // flush the old messages before changing because otherwise they might 
 552         // get lost later if this target is not restored 
 556     wxLog 
*pOldLogger 
= ms_pLogger
; 
 557     ms_pLogger 
= pLogger
; 
 564 wxLog 
*wxLog::SetThreadActiveTarget(wxLog 
*logger
) 
 566     wxASSERT_MSG( !wxThread::IsMain(), "use SetActiveTarget() for main thread" ); 
 568     wxLog 
* const oldLogger 
= wxThreadInfo
.logger
; 
 572     wxThreadInfo
.logger 
= logger
; 
 576 #endif // wxUSE_THREADS 
 578 void wxLog::DontCreateOnDemand() 
 580     ms_bAutoCreate 
= false; 
 582     // this is usually called at the end of the program and we assume that it 
 583     // is *always* called at the end - so we free memory here to avoid false 
 584     // memory leak reports from wxWin  memory tracking code 
 588 void wxLog::DoCreateOnDemand() 
 590     ms_bAutoCreate 
= true; 
 593 // ---------------------------------------------------------------------------- 
 594 // wxLog components levels 
 595 // ---------------------------------------------------------------------------- 
 598 void wxLog::SetComponentLevel(const wxString
& component
, wxLogLevel level
) 
 600     if ( component
.empty() ) 
 606         wxCRIT_SECT_LOCKER(lock
, GetLevelsCS()); 
 608         GetComponentLevels()[component
] = level
; 
 613 wxLogLevel 
wxLog::GetComponentLevel(wxString component
) 
 615     wxCRIT_SECT_LOCKER(lock
, GetLevelsCS()); 
 617     const wxStringToNumHashMap
& componentLevels 
= GetComponentLevels(); 
 618     while ( !component
.empty() ) 
 620         wxStringToNumHashMap::const_iterator
 
 621             it 
= componentLevels
.find(component
); 
 622         if ( it 
!= componentLevels
.end() ) 
 623             return static_cast<wxLogLevel
>(it
->second
); 
 625         component 
= component
.BeforeLast('/'); 
 628     return GetLogLevel(); 
 631 // ---------------------------------------------------------------------------- 
 633 // ---------------------------------------------------------------------------- 
 638 // because IsAllowedTraceMask() may be called during static initialization 
 639 // (this is not recommended but it may still happen, see #11592) we can't use a 
 640 // simple static variable which might be not initialized itself just yet to 
 641 // store the trace masks, but need this accessor function which will ensure 
 642 // that the variable is always correctly initialized before being accessed 
 644 // notice that this doesn't make accessing it MT-safe, of course, you need to 
 645 // serialize accesses to it using GetTraceMaskCS() for this 
 646 wxArrayString
& TraceMasks() 
 648     static wxArrayString s_traceMasks
; 
 653 } // anonymous namespace 
 655 /* static */ const wxArrayString
& wxLog::GetTraceMasks() 
 657     // because of this function signature (it returns a reference, not the 
 658     // object), it is inherently MT-unsafe so there is no need to acquire the 
 664 void wxLog::AddTraceMask(const wxString
& str
) 
 666     wxCRIT_SECT_LOCKER(lock
, GetTraceMaskCS()); 
 668     TraceMasks().push_back(str
); 
 671 void wxLog::RemoveTraceMask(const wxString
& str
) 
 673     wxCRIT_SECT_LOCKER(lock
, GetTraceMaskCS()); 
 675     int index 
= TraceMasks().Index(str
); 
 676     if ( index 
!= wxNOT_FOUND 
) 
 677         TraceMasks().RemoveAt((size_t)index
); 
 680 void wxLog::ClearTraceMasks() 
 682     wxCRIT_SECT_LOCKER(lock
, GetTraceMaskCS()); 
 684     TraceMasks().Clear(); 
 687 /*static*/ bool wxLog::IsAllowedTraceMask(const wxString
& mask
) 
 689     wxCRIT_SECT_LOCKER(lock
, GetTraceMaskCS()); 
 691     const wxArrayString
& masks 
= GetTraceMasks(); 
 692     for ( wxArrayString::const_iterator it 
= masks
.begin(), 
 704 // ---------------------------------------------------------------------------- 
 705 // wxLog miscellaneous other methods 
 706 // ---------------------------------------------------------------------------- 
 710 void wxLog::TimeStamp(wxString 
*str
) 
 712     if ( !ms_timestamp
.empty() ) 
 714         *str 
= wxDateTime::UNow().Format(ms_timestamp
); 
 719 void wxLog::TimeStamp(wxString 
*str
, time_t t
) 
 721     if ( !ms_timestamp
.empty() ) 
 723         *str 
= wxDateTime(t
).Format(ms_timestamp
); 
 728 #else // !wxUSE_DATETIME 
 730 void wxLog::TimeStamp(wxString
*) 
 734 void wxLog::TimeStamp(wxString
*, time_t) 
 738 #endif // wxUSE_DATETIME/!wxUSE_DATETIME 
 742 void wxLog::FlushThreadMessages() 
 744     // check if we have queued messages from other threads 
 745     wxLogRecords bufferedLogRecords
; 
 748         wxCriticalSectionLocker 
lock(GetBackgroundLogCS()); 
 749         bufferedLogRecords
.swap(gs_bufferedLogRecords
); 
 751         // release the lock now to not keep it while we are logging the 
 752         // messages below, allowing background threads to run 
 755     if ( !bufferedLogRecords
.empty() ) 
 757         for ( wxLogRecords::const_iterator it 
= bufferedLogRecords
.begin(); 
 758               it 
!= bufferedLogRecords
.end(); 
 761             CallDoLogNow(it
->level
, it
->msg
, it
->info
); 
 767 bool wxLog::IsThreadLoggingEnabled() 
 769     return !wxThreadInfo
.loggingDisabled
; 
 773 bool wxLog::EnableThreadLogging(bool enable
) 
 775     const bool wasEnabled 
= !wxThreadInfo
.loggingDisabled
; 
 776     wxThreadInfo
.loggingDisabled 
= !enable
; 
 780 #endif // wxUSE_THREADS 
 782 wxLogFormatter 
*wxLog::SetFormatter(wxLogFormatter
* formatter
) 
 784     wxLogFormatter
* formatterOld 
= m_formatter
; 
 785     m_formatter 
= formatter 
? formatter 
: new wxLogFormatter
; 
 792     LogLastRepeatIfNeeded(); 
 796 void wxLog::FlushActive() 
 798     if ( ms_suspendCount 
) 
 801     wxLog 
* const log 
= GetActiveTarget(); 
 805         if ( wxThread::IsMain() ) 
 806             log
->FlushThreadMessages(); 
 807 #endif // wxUSE_THREADS 
 813 // ---------------------------------------------------------------------------- 
 814 // wxLogBuffer implementation 
 815 // ---------------------------------------------------------------------------- 
 817 void wxLogBuffer::Flush() 
 821     if ( !m_str
.empty() ) 
 823         wxMessageOutputBest out
; 
 824         out
.Printf(wxS("%s"), m_str
.c_str()); 
 829 void wxLogBuffer::DoLogTextAtLevel(wxLogLevel level
, const wxString
& msg
) 
 831     // don't put debug messages in the buffer, we don't want to show 
 832     // them to the user in a msg box, log them immediately 
 837             wxLog::DoLogTextAtLevel(level
, msg
); 
 841             m_str 
<< msg 
<< wxS("\n"); 
 845 // ---------------------------------------------------------------------------- 
 846 // wxLogStderr class implementation 
 847 // ---------------------------------------------------------------------------- 
 849 wxLogStderr::wxLogStderr(FILE *fp
) 
 857 void wxLogStderr::DoLogText(const wxString
& msg
) 
 859     wxFputs(msg 
+ '\n', m_fp
); 
 862     // under GUI systems such as Windows or Mac, programs usually don't have 
 863     // stderr at all, so show the messages also somewhere else, typically in 
 864     // the debugger window so that they go at least somewhere instead of being 
 866     if ( m_fp 
== stderr 
) 
 868         wxAppTraits 
*traits 
= wxTheApp 
? wxTheApp
->GetTraits() : NULL
; 
 869         if ( traits 
&& !traits
->HasStderr() ) 
 871             wxMessageOutputDebug().Output(msg 
+ wxS('\n')); 
 876 // ---------------------------------------------------------------------------- 
 877 // wxLogStream implementation 
 878 // ---------------------------------------------------------------------------- 
 880 #if wxUSE_STD_IOSTREAM 
 881 #include "wx/ioswrap.h" 
 882 wxLogStream::wxLogStream(wxSTD ostream 
*ostr
) 
 885         m_ostr 
= &wxSTD cerr
; 
 890 void wxLogStream::DoLogText(const wxString
& msg
) 
 892     (*m_ostr
) << msg 
<< wxSTD endl
; 
 894 #endif // wxUSE_STD_IOSTREAM 
 896 // ---------------------------------------------------------------------------- 
 898 // ---------------------------------------------------------------------------- 
 900 wxLogChain::wxLogChain(wxLog 
*logger
) 
 902     m_bPassMessages 
= true; 
 905     m_logOld 
= wxLog::SetActiveTarget(this); 
 908 wxLogChain::~wxLogChain() 
 910     wxLog::SetActiveTarget(m_logOld
); 
 912     if ( m_logNew 
!= this ) 
 916 void wxLogChain::SetLog(wxLog 
*logger
) 
 918     if ( m_logNew 
!= this ) 
 924 void wxLogChain::Flush() 
 929     // be careful to avoid infinite recursion 
 930     if ( m_logNew 
&& m_logNew 
!= this ) 
 934 void wxLogChain::DoLogRecord(wxLogLevel level
, 
 936                              const wxLogRecordInfo
& info
) 
 938     // let the previous logger show it 
 939     if ( m_logOld 
&& IsPassingMessages() ) 
 940         m_logOld
->LogRecord(level
, msg
, info
); 
 942     // and also send it to the new one 
 945         // don't call m_logNew->LogRecord() to avoid infinite recursion when 
 946         // m_logNew is this object itself 
 947         if ( m_logNew 
!= this ) 
 948             m_logNew
->LogRecord(level
, msg
, info
); 
 950             wxLog::DoLogRecord(level
, msg
, info
); 
 955     // "'this' : used in base member initializer list" - so what? 
 956     #pragma warning(disable:4355) 
 959 // ---------------------------------------------------------------------------- 
 961 // ---------------------------------------------------------------------------- 
 963 wxLogInterposer::wxLogInterposer() 
 968 // ---------------------------------------------------------------------------- 
 969 // wxLogInterposerTemp 
 970 // ---------------------------------------------------------------------------- 
 972 wxLogInterposerTemp::wxLogInterposerTemp() 
 979     #pragma warning(default:4355) 
 982 // ============================================================================ 
 983 // Global functions/variables 
 984 // ============================================================================ 
 986 // ---------------------------------------------------------------------------- 
 988 // ---------------------------------------------------------------------------- 
 990 bool            wxLog::ms_bRepetCounting 
= false; 
 992 wxLog          
*wxLog::ms_pLogger      
= NULL
; 
 993 bool            wxLog::ms_doLog        
= true; 
 994 bool            wxLog::ms_bAutoCreate  
= true; 
 995 bool            wxLog::ms_bVerbose     
= false; 
 997 wxLogLevel      
wxLog::ms_logLevel     
= wxLOG_Max
;  // log everything by default 
 999 size_t          wxLog::ms_suspendCount 
= 0; 
1001 wxString        
wxLog::ms_timestamp(wxS("%X"));  // time only, no date 
1003 #if WXWIN_COMPATIBILITY_2_8 
1004 wxTraceMask     
wxLog::ms_ulTraceMask  
= (wxTraceMask
)0; 
1005 #endif // wxDEBUG_LEVEL 
1007 // ---------------------------------------------------------------------------- 
1008 // stdout error logging helper 
1009 // ---------------------------------------------------------------------------- 
1011 // helper function: wraps the message and justifies it under given position 
1012 // (looks more pretty on the terminal). Also adds newline at the end. 
1014 // TODO this is now disabled until I find a portable way of determining the 
1015 //      terminal window size (ok, I found it but does anybody really cares?) 
1016 #ifdef LOG_PRETTY_WRAP 
1017 static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
) 
1019     size_t nMax 
= 80; // FIXME 
1020     size_t nStart 
= strlen(pszPrefix
); 
1021     fputs(pszPrefix
, f
); 
1024     while ( *psz 
!= '\0' ) { 
1025         for ( n 
= nStart
; (n 
< nMax
) && (*psz 
!= '\0'); n
++ ) 
1029         if ( *psz 
!= '\0' ) { 
1031             for ( n 
= 0; n 
< nStart
; n
++ ) 
1034             // as we wrapped, squeeze all white space 
1035             while ( isspace(*psz
) ) 
1042 #endif  //LOG_PRETTY_WRAP 
1044 // ---------------------------------------------------------------------------- 
1045 // error code/error message retrieval functions 
1046 // ---------------------------------------------------------------------------- 
1048 // get error code from syste 
1049 unsigned long wxSysErrorCode() 
1051 #if defined(__WINDOWS__) && !defined(__WXMICROWIN__) 
1052     return ::GetLastError(); 
1058 // get error message from system 
1059 const wxChar 
*wxSysErrorMsg(unsigned long nErrCode
) 
1061     if ( nErrCode 
== 0 ) 
1062         nErrCode 
= wxSysErrorCode(); 
1064 #if defined(__WINDOWS__) && !defined(__WXMICROWIN__) 
1065     static wxChar s_szBuf
[1024]; 
1067     // get error message from system 
1069     if ( ::FormatMessage
 
1071             FORMAT_MESSAGE_ALLOCATE_BUFFER 
| FORMAT_MESSAGE_FROM_SYSTEM
, 
1074             MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), 
1080         // if this happens, something is seriously wrong, so don't use _() here 
1082         wxSprintf(s_szBuf
, wxS("unknown error %lx"), nErrCode
); 
1087     // copy it to our buffer and free memory 
1088     // Crashes on SmartPhone (FIXME) 
1089 #if !defined(__SMARTPHONE__) /* of WinCE */ 
1092         wxStrlcpy(s_szBuf
, (const wxChar 
*)lpMsgBuf
, WXSIZEOF(s_szBuf
)); 
1094         LocalFree(lpMsgBuf
); 
1096         // returned string is capitalized and ended with '\r\n' - bad 
1097         s_szBuf
[0] = (wxChar
)wxTolower(s_szBuf
[0]); 
1098         size_t len 
= wxStrlen(s_szBuf
); 
1101             if ( s_szBuf
[len 
- 2] == wxS('\r') ) 
1102                 s_szBuf
[len 
- 2] = wxS('\0'); 
1106 #endif // !__SMARTPHONE__ 
1108         s_szBuf
[0] = wxS('\0'); 
1112 #else // !__WINDOWS__ 
1114         static wchar_t s_wzBuf
[1024]; 
1115         wxConvCurrent
->MB2WC(s_wzBuf
, strerror((int)nErrCode
), 
1116                              WXSIZEOF(s_wzBuf
) - 1); 
1119         return strerror((int)nErrCode
); 
1121 #endif  // __WINDOWS__/!__WINDOWS__