|
NCBI C++ ToolKit
|
00001 /* $Id: ncbidiag.cpp 54513 2012-05-17 18:33:05Z grichenk $ 00002 * =========================================================================== 00003 * 00004 * PUBLIC DOMAIN NOTICE 00005 * National Center for Biotechnology Information 00006 * 00007 * This software/database is a "United States Government Work" under the 00008 * terms of the United States Copyright Act. It was written as part of 00009 * the author's official duties as a United States Government employee and 00010 * thus cannot be copyrighted. This software/database is freely available 00011 * to the public for use. The National Library of Medicine and the U.S. 00012 * Government have not placed any restriction on its use or reproduction. 00013 * 00014 * Although all reasonable efforts have been taken to ensure the accuracy 00015 * and reliability of the software and data, the NLM and the U.S. 00016 * Government do not and cannot warrant the performance or results that 00017 * may be obtained by using this software or data. The NLM and the U.S. 00018 * Government disclaim all warranties, express or implied, including 00019 * warranties of performance, merchantability or fitness for any particular 00020 * purpose. 00021 * 00022 * Please cite the author in any work or product based on this material. 00023 * 00024 * =========================================================================== 00025 * 00026 * Authors: Denis Vakatov et al. 00027 * 00028 * File Description: 00029 * NCBI C++ diagnostic API 00030 * 00031 */ 00032 00033 00034 #include <ncbi_pch.hpp> 00035 #include <corelib/ncbiexpt.hpp> 00036 #include <corelib/ncbi_process.hpp> 00037 #include <corelib/ncbifile.hpp> 00038 #include <corelib/syslog.hpp> 00039 #include <corelib/error_codes.hpp> 00040 #include <corelib/request_ctx.hpp> 00041 #include <corelib/request_control.hpp> 00042 #include "ncbidiag_p.hpp" 00043 #include "ncbisys.hpp" 00044 #include <fcntl.h> 00045 #include <stdlib.h> 00046 #include <stack> 00047 00048 #if defined(NCBI_OS_MSWIN) 00049 # include <io.h> 00050 #endif 00051 00052 #if defined(NCBI_OS_UNIX) 00053 # include <unistd.h> 00054 # include <sys/utsname.h> 00055 #endif 00056 00057 00058 #define NCBI_USE_ERRCODE_X Corelib_Diag 00059 00060 00061 BEGIN_NCBI_SCOPE 00062 00063 static bool s_DiagUseRWLock = false; 00064 DEFINE_STATIC_MUTEX(s_DiagMutex); 00065 static CSafeStaticPtr<CRWLock> s_DiagRWLock; 00066 static CSafeStaticPtr<CAtomicCounter_WithAutoInit> s_ReopenEntered; 00067 00068 00069 void g_Diag_Use_RWLock(void) 00070 { 00071 if (s_DiagUseRWLock) return; // already switched 00072 // Try to detect at least some dangerous situations. 00073 // This does not guarantee safety since some thread may 00074 // be waiting for the mutex while we switch to RW-lock. 00075 bool diag_mutex_unlocked = s_DiagMutex.TryLock(); 00076 // Mutex is locked - fail 00077 _ASSERT(diag_mutex_unlocked); 00078 if (!diag_mutex_unlocked) { 00079 _TROUBLE; 00080 NCBI_THROW(CCoreException, eCore, 00081 "Cannot switch diagnostic to RW-lock - mutex is locked."); 00082 } 00083 s_DiagUseRWLock = true; 00084 s_DiagMutex.Unlock(); 00085 } 00086 00087 00088 class CDiagLock 00089 { 00090 public: 00091 enum ELockType { 00092 eRead, // read lock 00093 eWrite, // write lock (modifying flags etc.) 00094 ePost // lock used by diag handlers to lock stream in Post() 00095 }; 00096 00097 CDiagLock(ELockType locktype) 00098 : m_UsedRWLock(false), m_LockType(locktype) 00099 { 00100 if (s_DiagUseRWLock) { 00101 if (m_LockType == eRead) { 00102 m_UsedRWLock = true; 00103 s_DiagRWLock->ReadLock(); 00104 return; 00105 } 00106 if (m_LockType == eWrite) { 00107 m_UsedRWLock = true; 00108 s_DiagRWLock->WriteLock(); 00109 return; 00110 } 00111 // For ePost use normal mutex below. 00112 } 00113 s_DiagMutex.Lock(); 00114 } 00115 00116 ~CDiagLock(void) 00117 { 00118 if (m_UsedRWLock) { 00119 s_DiagRWLock->Unlock(); 00120 } 00121 else { 00122 s_DiagMutex.Unlock(); 00123 } 00124 } 00125 00126 private: 00127 bool m_UsedRWLock; 00128 ELockType m_LockType; 00129 }; 00130 00131 00132 class CDiagFileHandleHolder : public CObject 00133 { 00134 public: 00135 CDiagFileHandleHolder(const string& fname, CDiagHandler::TReopenFlags flags); 00136 virtual ~CDiagFileHandleHolder(void); 00137 00138 int GetHandle(void) { return m_Handle; } 00139 00140 private: 00141 int m_Handle; 00142 }; 00143 00144 00145 // Special diag handler for duplicating error log messages on stderr. 00146 class CTeeDiagHandler : public CDiagHandler 00147 { 00148 public: 00149 CTeeDiagHandler(CDiagHandler* orig, bool own_orig); 00150 virtual void Post(const SDiagMessage& mess); 00151 00152 // Don't post duplicates to console. 00153 virtual void PostToConsole(const SDiagMessage& mess) {} 00154 00155 virtual string GetLogName(void) 00156 { 00157 return m_OrigHandler.get() ? 00158 m_OrigHandler->GetLogName() : "STDERR-TEE"; 00159 } 00160 virtual void Reopen(TReopenFlags flags) 00161 { 00162 if ( m_OrigHandler.get() ) { 00163 m_OrigHandler->Reopen(flags); 00164 } 00165 } 00166 00167 CDiagHandler* GetOriginalHandler(void) const 00168 { 00169 return m_OrigHandler.get(); 00170 } 00171 00172 private: 00173 EDiagSev m_MinSev; 00174 AutoPtr<CDiagHandler> m_OrigHandler; 00175 }; 00176 00177 00178 #if defined(NCBI_POSIX_THREADS) && defined(HAVE_PTHREAD_ATFORK) 00179 00180 #include <unistd.h> // for pthread_atfork() 00181 00182 extern "C" { 00183 static void s_NcbiDiagPreFork(void) 00184 { 00185 s_DiagMutex.Lock(); 00186 } 00187 static void s_NcbiDiagPostFork(void) 00188 { 00189 s_DiagMutex.Unlock(); 00190 } 00191 } 00192 00193 #endif 00194 00195 00196 /////////////////////////////////////////////////////// 00197 // Output format parameters 00198 00199 // Use old output format if the flag is set 00200 NCBI_PARAM_DECL(bool, Diag, Old_Post_Format); 00201 NCBI_PARAM_DEF_EX(bool, Diag, Old_Post_Format, true, eParam_NoThread, 00202 DIAG_OLD_POST_FORMAT); 00203 typedef NCBI_PARAM_TYPE(Diag, Old_Post_Format) TOldPostFormatParam; 00204 00205 // Auto-print context properties on set/change. 00206 NCBI_PARAM_DECL(bool, Diag, AutoWrite_Context); 00207 NCBI_PARAM_DEF_EX(bool, Diag, AutoWrite_Context, false, eParam_NoThread, 00208 DIAG_AUTOWRITE_CONTEXT); 00209 typedef NCBI_PARAM_TYPE(Diag, AutoWrite_Context) TAutoWrite_Context; 00210 00211 // Print system TID rather than CThread::GetSelf() 00212 NCBI_PARAM_DECL(bool, Diag, Print_System_TID); 00213 NCBI_PARAM_DEF_EX(bool, Diag, Print_System_TID, false, eParam_NoThread, 00214 DIAG_PRINT_SYSTEM_TID); 00215 typedef NCBI_PARAM_TYPE(Diag, Print_System_TID) TPrintSystemTID; 00216 00217 // Use assert() instead of abort() when printing fatal errors 00218 // to show the assertion dialog and allow to choose the action 00219 // (stop/debug/continue). 00220 NCBI_PARAM_DECL(bool, Diag, Assert_On_Abort); 00221 NCBI_PARAM_DEF_EX(bool, Diag, Assert_On_Abort, false, eParam_NoThread, 00222 DIAG_ASSERT_ON_ABORT); 00223 typedef NCBI_PARAM_TYPE(Diag, Assert_On_Abort) TAssertOnAbortParam; 00224 00225 // Limit log file size, rotate log when it reaches the limit. 00226 NCBI_PARAM_DECL(long, Diag, Log_Size_Limit); 00227 NCBI_PARAM_DEF_EX(long, Diag, Log_Size_Limit, 0, eParam_NoThread, 00228 DIAG_LOG_SIZE_LIMIT); 00229 typedef NCBI_PARAM_TYPE(Diag, Log_Size_Limit) TLogSizeLimitParam; 00230 00231 00232 /////////////////////////////////////////////////////// 00233 // Output rate control parameters 00234 00235 // AppLog limit per period 00236 NCBI_PARAM_DECL(unsigned int, Diag, AppLog_Rate_Limit); 00237 NCBI_PARAM_DEF_EX(unsigned int, Diag, AppLog_Rate_Limit, 50000, 00238 eParam_NoThread, DIAG_APPLOG_RATE_LIMIT); 00239 typedef NCBI_PARAM_TYPE(Diag, AppLog_Rate_Limit) TAppLogRateLimitParam; 00240 00241 // AppLog period, sec 00242 NCBI_PARAM_DECL(unsigned int, Diag, AppLog_Rate_Period); 00243 NCBI_PARAM_DEF_EX(unsigned int, Diag, AppLog_Rate_Period, 10, eParam_NoThread, 00244 DIAG_APPLOG_RATE_PERIOD); 00245 typedef NCBI_PARAM_TYPE(Diag, AppLog_Rate_Period) TAppLogRatePeriodParam; 00246 00247 // ErrLog limit per period 00248 NCBI_PARAM_DECL(unsigned int, Diag, ErrLog_Rate_Limit); 00249 NCBI_PARAM_DEF_EX(unsigned int, Diag, ErrLog_Rate_Limit, 5000, 00250 eParam_NoThread, DIAG_ERRLOG_RATE_LIMIT); 00251 typedef NCBI_PARAM_TYPE(Diag, ErrLog_Rate_Limit) TErrLogRateLimitParam; 00252 00253 // ErrLog period, sec 00254 NCBI_PARAM_DECL(unsigned int, Diag, ErrLog_Rate_Period); 00255 NCBI_PARAM_DEF_EX(unsigned int, Diag, ErrLog_Rate_Period, 1, eParam_NoThread, 00256 DIAG_ERRLOG_RATE_PERIOD); 00257 typedef NCBI_PARAM_TYPE(Diag, ErrLog_Rate_Period) TErrLogRatePeriodParam; 00258 00259 // TraceLog limit per period 00260 NCBI_PARAM_DECL(unsigned int, Diag, TraceLog_Rate_Limit); 00261 NCBI_PARAM_DEF_EX(unsigned int, Diag, TraceLog_Rate_Limit, 5000, 00262 eParam_NoThread, DIAG_TRACELOG_RATE_LIMIT); 00263 typedef NCBI_PARAM_TYPE(Diag, TraceLog_Rate_Limit) TTraceLogRateLimitParam; 00264 00265 // TraceLog period, sec 00266 NCBI_PARAM_DECL(unsigned int, Diag, TraceLog_Rate_Period); 00267 NCBI_PARAM_DEF_EX(unsigned int, Diag, TraceLog_Rate_Period, 1, eParam_NoThread, 00268 DIAG_TRACELOG_RATE_PERIOD); 00269 typedef NCBI_PARAM_TYPE(Diag, TraceLog_Rate_Period) TTraceLogRatePeriodParam; 00270 00271 // Duplicate messages to STDERR 00272 NCBI_PARAM_DECL(bool, Diag, Tee_To_Stderr); 00273 NCBI_PARAM_DEF_EX(bool, Diag, Tee_To_Stderr, false, eParam_NoThread, 00274 DIAG_TEE_TO_STDERR); 00275 typedef NCBI_PARAM_TYPE(Diag, Tee_To_Stderr) TTeeToStderr; 00276 00277 // Minimum severity of the messages duplicated to STDERR 00278 NCBI_PARAM_ENUM_DECL(EDiagSev, Diag, Tee_Min_Severity); 00279 NCBI_PARAM_ENUM_ARRAY(EDiagSev, Diag, Tee_Min_Severity) 00280 { 00281 {"Info", eDiag_Info}, 00282 {"Warning", eDiag_Warning}, 00283 {"Error", eDiag_Error}, 00284 {"Critical", eDiag_Critical}, 00285 {"Fatal", eDiag_Fatal}, 00286 {"Trace", eDiag_Trace} 00287 }; 00288 00289 const EDiagSev kTeeMinSeverityDef = 00290 #if defined(NDEBUG) 00291 eDiag_Error; 00292 #else 00293 eDiag_Warning; 00294 #endif 00295 00296 NCBI_PARAM_ENUM_DEF_EX(EDiagSev, Diag, Tee_Min_Severity, 00297 kTeeMinSeverityDef, 00298 eParam_NoThread, DIAG_TEE_MIN_SEVERITY); 00299 typedef NCBI_PARAM_TYPE(Diag, Tee_Min_Severity) TTeeMinSeverity; 00300 00301 00302 NCBI_PARAM_DECL(size_t, Diag, Collect_Limit); 00303 NCBI_PARAM_DEF_EX(size_t, Diag, Collect_Limit, 1000, eParam_NoThread, 00304 DIAG_COLLECT_LIMIT); 00305 typedef NCBI_PARAM_TYPE(Diag, Collect_Limit) TDiagCollectLimit; 00306 00307 00308 NCBI_PARAM_DECL(bool, Log, Truncate); 00309 NCBI_PARAM_DEF_EX(bool, Log, Truncate, false, eParam_NoThread, LOG_TRUNCATE); 00310 typedef NCBI_PARAM_TYPE(Log, Truncate) TLogTruncateParam; 00311 00312 00313 NCBI_PARAM_DECL(bool, Log, NoCreate); 00314 NCBI_PARAM_DEF_EX(bool, Log, NoCreate, false, eParam_NoThread, LOG_NOCREATE); 00315 typedef NCBI_PARAM_TYPE(Log, NoCreate) TLogNoCreate; 00316 00317 00318 // Logging of environment variables: space separated list of names which 00319 // should be logged after each request start. 00320 NCBI_PARAM_DECL(string, Log, LogEnvironment); 00321 NCBI_PARAM_DEF_EX(string, Log, LogEnvironment, kEmptyStr, 00322 eParam_NoThread, 00323 DIAG_LOG_ENVIRONMENT); 00324 typedef NCBI_PARAM_TYPE(Log, LogEnvironment) TLogEnvironment; 00325 00326 00327 // Logging of registry values: space separated list of 'section:name' strings. 00328 NCBI_PARAM_DECL(string, Log, LogRegistry); 00329 NCBI_PARAM_DEF_EX(string, Log, LogRegistry, kEmptyStr, 00330 eParam_NoThread, 00331 DIAG_LOG_REGISTRY); 00332 typedef NCBI_PARAM_TYPE(Log, LogRegistry) TLogRegistry; 00333 00334 00335 static bool s_UseRootLog = true; 00336 static bool s_FinishedSetupDiag = false; 00337 static bool s_MergeLinesSetBySetupDiag = false; 00338 00339 00340 CDiagCollectGuard::CDiagCollectGuard(void) 00341 { 00342 // the severities will be adjusted by x_Init() 00343 x_Init(eDiag_Critical, eDiag_Fatal, eDiscard); 00344 } 00345 00346 00347 CDiagCollectGuard::CDiagCollectGuard(EDiagSev print_severity) 00348 { 00349 // the severities will be adjusted by x_Init() 00350 x_Init(eDiag_Critical, print_severity, eDiscard); 00351 } 00352 00353 00354 CDiagCollectGuard::CDiagCollectGuard(EDiagSev print_severity, 00355 EDiagSev collect_severity, 00356 EAction action) 00357 { 00358 // the severities will be adjusted by x_Init() 00359 x_Init(print_severity, collect_severity, action); 00360 } 00361 00362 00363 void CDiagCollectGuard::x_Init(EDiagSev print_severity, 00364 EDiagSev collect_severity, 00365 EAction action) 00366 { 00367 // Get current print severity 00368 EDiagSev psev, csev; 00369 CDiagContextThreadData& thr_data = 00370 CDiagContextThreadData::GetThreadData(); 00371 if ( thr_data.GetCollectGuard() ) { 00372 psev = thr_data.GetCollectGuard()->GetPrintSeverity(); 00373 csev = thr_data.GetCollectGuard()->GetCollectSeverity(); 00374 } 00375 else { 00376 CDiagLock lock(CDiagLock::eRead); 00377 psev = CDiagBuffer::sm_PostSeverity; 00378 csev = CDiagBuffer::sm_PostSeverity; 00379 } 00380 psev = CompareDiagPostLevel(psev, print_severity) > 0 00381 ? psev : print_severity; 00382 csev = CompareDiagPostLevel(csev, collect_severity) < 0 00383 ? csev : collect_severity; 00384 00385 m_PrintSev = psev; 00386 m_CollectSev = csev; 00387 m_Action = action; 00388 thr_data.AddCollectGuard(this); 00389 } 00390 00391 00392 CDiagCollectGuard::~CDiagCollectGuard(void) 00393 { 00394 Release(); 00395 } 00396 00397 00398 void CDiagCollectGuard::Release(void) 00399 { 00400 CDiagContextThreadData& thr_data = CDiagContextThreadData::GetThreadData(); 00401 thr_data.RemoveCollectGuard(this); 00402 } 00403 00404 00405 void CDiagCollectGuard::Release(EAction action) 00406 { 00407 SetAction(action); 00408 Release(); 00409 } 00410 00411 00412 void CDiagCollectGuard::SetPrintSeverity(EDiagSev sev) 00413 { 00414 if ( CompareDiagPostLevel(m_PrintSev, sev) < 0 ) { 00415 m_PrintSev = sev; 00416 } 00417 } 00418 00419 00420 void CDiagCollectGuard::SetCollectSeverity(EDiagSev sev) 00421 { 00422 if ( CompareDiagPostLevel(m_CollectSev, sev) < 0 ) { 00423 m_CollectSev = sev; 00424 } 00425 } 00426 00427 00428 /////////////////////////////////////////////////////// 00429 // Static variables for Trace and Post filters 00430 00431 static CSafeStaticPtr<CDiagFilter> s_TraceFilter; 00432 static CSafeStaticPtr<CDiagFilter> s_PostFilter; 00433 00434 00435 // Analogue to strstr. 00436 // Returns a pointer to the last occurrence of a search string in a string 00437 const char* 00438 str_rev_str(const char* begin_str, const char* end_str, const char* str_search) 00439 { 00440 if (begin_str == NULL) 00441 return NULL; 00442 if (end_str == NULL) 00443 return NULL; 00444 if (str_search == NULL) 00445 return NULL; 00446 00447 const char* search_char = str_search + strlen(str_search); 00448 const char* cur_char = end_str; 00449 00450 do { 00451 --search_char; 00452 do { 00453 --cur_char; 00454 } while(*cur_char != *search_char && cur_char != begin_str); 00455 if (*cur_char != *search_char) 00456 return NULL; 00457 } 00458 while (search_char != str_search); 00459 00460 return cur_char; 00461 } 00462 00463 00464 00465 ///////////////////////////////////////////////////////////////////////////// 00466 /// CDiagCompileInfo:: 00467 00468 CDiagCompileInfo::CDiagCompileInfo(void) 00469 : m_File(""), 00470 m_Module(""), 00471 m_Line(0), 00472 m_CurrFunctName(0), 00473 m_Parsed(false), 00474 m_StrFile(0), 00475 m_StrModule(0), 00476 m_StrCurrFunctName(0) 00477 { 00478 } 00479 00480 CDiagCompileInfo::CDiagCompileInfo(const char* file, 00481 int line, 00482 const char* curr_funct, 00483 const char* module) 00484 : m_File(file), 00485 m_Module(""), 00486 m_Line(line), 00487 m_CurrFunctName(curr_funct), 00488 m_Parsed(false), 00489 m_StrFile(0), 00490 m_StrModule(0), 00491 m_StrCurrFunctName(0) 00492 { 00493 if (!file) { 00494 m_File = ""; 00495 return; 00496 } 00497 if (!module) 00498 return; 00499 if ( x_NeedModule() && 0 != strcmp(module, "NCBI_MODULE") ) { 00500 m_Module = module; 00501 } 00502 } 00503 00504 00505 CDiagCompileInfo::CDiagCompileInfo(const string& file, 00506 int line, 00507 const string& curr_funct, 00508 const string& module) 00509 : m_File(""), 00510 m_Module(""), 00511 m_Line(line), 00512 m_CurrFunctName(""), 00513 m_Parsed(false), 00514 m_StrFile(0), 00515 m_StrModule(0), 00516 m_StrCurrFunctName(0) 00517 { 00518 if ( !file.empty() ) { 00519 m_StrFile = new char[file.size() + 1]; 00520 strcpy(m_StrFile, file.c_str()); 00521 m_File = m_StrFile; 00522 } 00523 if ( m_File && !module.empty() && x_NeedModule() ) { 00524 m_StrModule = new char[module.size() + 1]; 00525 strcpy(m_StrModule, module.c_str()); 00526 m_Module = m_StrModule; 00527 } 00528 if ( !curr_funct.empty() ) { 00529 m_StrCurrFunctName = new char[curr_funct.size() + 1]; 00530 strcpy(m_StrCurrFunctName, curr_funct.c_str()); 00531 m_CurrFunctName = m_StrCurrFunctName; 00532 } 00533 } 00534 00535 00536 bool CDiagCompileInfo::x_NeedModule(void) const 00537 { 00538 // Check for a file extension without creating of temporary string objects 00539 const char* cur_extension = strrchr(m_File, '.'); 00540 if (cur_extension == NULL) 00541 return false; 00542 00543 if (*(cur_extension + 1) != '\0') { 00544 ++cur_extension; 00545 } else { 00546 return false; 00547 } 00548 00549 return strcmp(cur_extension, "cpp") == 0 || 00550 strcmp(cur_extension, "C") == 0 || 00551 strcmp(cur_extension, "c") == 0 || 00552 strcmp(cur_extension, "cxx") == 0; 00553 } 00554 00555 00556 CDiagCompileInfo::~CDiagCompileInfo(void) 00557 { 00558 delete[] m_StrFile; 00559 delete[] m_StrModule; 00560 delete[] m_StrCurrFunctName; 00561 } 00562 00563 00564 // Skip matching l/r separators, return the last char before them 00565 // or null if the separators are unbalanced. 00566 const char* find_match(char lsep, 00567 char rsep, 00568 const char* start, 00569 const char* stop) 00570 { 00571 if (*(stop - 1) != rsep) return stop; 00572 int balance = 1; 00573 const char* pos = stop - 2; 00574 for (; pos > start; pos--) { 00575 if (*pos == rsep) { 00576 balance++; 00577 } 00578 else if (*pos == lsep) { 00579 if (--balance == 0) break; 00580 } 00581 } 00582 return (pos <= start) ? NULL : pos; 00583 } 00584 00585 00586 void 00587 CDiagCompileInfo::ParseCurrFunctName(void) const 00588 { 00589 m_Parsed = true; 00590 if (!m_CurrFunctName || !(*m_CurrFunctName)) { 00591 return; 00592 } 00593 // Parse curr_funct 00594 00595 // Skip function arguments 00596 size_t len = strlen(m_CurrFunctName); 00597 const char* end_str = find_match('(', ')', 00598 m_CurrFunctName, 00599 m_CurrFunctName + len); 00600 if (end_str == m_CurrFunctName + len) { 00601 // Missing '(' 00602 return; 00603 } 00604 if ( end_str ) { 00605 // Skip template arguments 00606 end_str = find_match('<', '>', m_CurrFunctName, end_str); 00607 } 00608 if ( !end_str ) { 00609 return; 00610 } 00611 // Get a function/method name 00612 const char* start_str = NULL; 00613 00614 // Get a finction start position. 00615 const char* start_str_tmp = 00616 str_rev_str(m_CurrFunctName, end_str, "::"); 00617 bool has_class = start_str_tmp != NULL; 00618 if (start_str_tmp != NULL) { 00619 start_str = start_str_tmp + 2; 00620 } else { 00621 start_str_tmp = str_rev_str(m_CurrFunctName, end_str, " "); 00622 if (start_str_tmp != NULL) { 00623 start_str = start_str_tmp + 1; 00624 } 00625 } 00626 00627 const char* cur_funct_name = 00628 (start_str == NULL ? m_CurrFunctName : start_str); 00629 size_t cur_funct_name_len = end_str - cur_funct_name; 00630 m_FunctName = string(cur_funct_name, cur_funct_name_len); 00631 00632 // Get a class name 00633 if (has_class) { 00634 end_str = find_match('<', '>', m_CurrFunctName, start_str - 2); 00635 start_str = str_rev_str(m_CurrFunctName, end_str, " "); 00636 const char* cur_class_name = 00637 (start_str == NULL ? m_CurrFunctName : start_str + 1); 00638 size_t cur_class_name_len = end_str - cur_class_name; 00639 m_ClassName = string(cur_class_name, cur_class_name_len); 00640 } 00641 } 00642 00643 00644 00645 /////////////////////////////////////////////////////// 00646 // CDiagRecycler:: 00647 00648 class CDiagRecycler { 00649 public: 00650 CDiagRecycler(void) 00651 { 00652 #if defined(NCBI_POSIX_THREADS) && defined(HAVE_PTHREAD_ATFORK) 00653 pthread_atfork(s_NcbiDiagPreFork, // before 00654 s_NcbiDiagPostFork, // after in parent 00655 s_NcbiDiagPostFork); // after in child 00656 #endif 00657 } 00658 ~CDiagRecycler(void) 00659 { 00660 SetDiagHandler(0, false); 00661 SetDiagErrCodeInfo(0, false); 00662 } 00663 }; 00664 00665 static CSafeStaticPtr<CDiagRecycler> s_DiagRecycler; 00666 00667 00668 // Helper function to check if applog severity lock is set and 00669 // return the proper printable severity. 00670 00671 EDiagSev AdjustApplogPrintableSeverity(EDiagSev sev) 00672 { 00673 if ( !CDiagContext::IsApplogSeverityLocked() ) return sev; 00674 return CompareDiagPostLevel(sev, eDiag_Warning) < 0 00675 ? sev : eDiag_Warning; 00676 } 00677 00678 00679 /////////////////////////////////////////////////////// 00680 // CDiagContextThreadData:: 00681 00682 00683 struct SRequestCtxWrapper 00684 { 00685 CRef<CRequestContext> m_Ctx; 00686 }; 00687 00688 00689 inline Uint8 s_GetThreadId(void) 00690 { 00691 if (TPrintSystemTID::GetDefault()) { 00692 return (Uint8)(CThreadSystemID::GetCurrent().m_ID); // GCC 3.4.6 gives warning - ignore it. 00693 } else { 00694 return CThread::GetSelf(); 00695 } 00696 } 00697 00698 00699 enum EThreadDataState { 00700 eInitialized, 00701 eUninitialized, 00702 eInitializing, 00703 eDeinitialized, 00704 eReinitializing 00705 }; 00706 00707 static volatile EThreadDataState s_ThreadDataState = eUninitialized; 00708 00709 static void s_ThreadDataSafeStaticCleanup(void*) 00710 { 00711 s_ThreadDataState = eDeinitialized; // re-enable protection 00712 } 00713 00714 00715 CDiagContextThreadData::CDiagContextThreadData(void) 00716 : m_Properties(NULL), 00717 m_DiagBuffer(new CDiagBuffer), 00718 m_TID(s_GetThreadId()), 00719 m_ThreadPostNumber(0), 00720 m_DiagCollectionSize(0), 00721 m_RequestCtx(new SRequestCtxWrapper), 00722 m_DefaultRequestCtx(new SRequestCtxWrapper) 00723 { 00724 m_RequestCtx->m_Ctx = m_DefaultRequestCtx->m_Ctx = new CRequestContext; 00725 m_RequestCtx->m_Ctx->SetAutoIncRequestIDOnPost( 00726 CRequestContext::GetDefaultAutoIncRequestIDOnPost()); 00727 } 00728 00729 00730 CDiagContextThreadData::~CDiagContextThreadData(void) 00731 { 00732 } 00733 00734 00735 void CDiagContext::sx_ThreadDataTlsCleanup(CDiagContextThreadData* value, 00736 void* cleanup_data) 00737 { 00738 if ( cleanup_data ) { 00739 // Copy properties from the main thread's TLS to the global properties. 00740 CDiagLock lock(CDiagLock::eWrite); 00741 CDiagContextThreadData::TProperties* props = 00742 value->GetProperties(CDiagContextThreadData::eProp_Get); /* NCBI_FAKE_WARNING */ 00743 if ( props ) { 00744 GetDiagContext().m_Properties.insert(props->begin(), 00745 props->end()); 00746 } 00747 // Print stop message. 00748 if (!CDiagContext::IsSetOldPostFormat() && s_FinishedSetupDiag) { 00749 GetDiagContext().PrintStop(); 00750 } 00751 s_ThreadDataState = eDeinitialized; // re-enable protection 00752 } 00753 delete value; 00754 } 00755 00756 00757 CDiagContextThreadData& CDiagContextThreadData::GetThreadData(void) 00758 { 00759 // If any of this method's direct or indirect callees attempted to 00760 // report a (typically fatal) error, the result would ordinarily 00761 // be infinite recursion resulting in an undignified crash. The 00762 // business with s_ThreadDataState allows the program to exit 00763 // (relatively) gracefully in such cases. 00764 // 00765 // In principle, such an event could happen at any stage; in 00766 // practice, however, the first call involves a superset of most 00767 // following calls' actions, at least until deep program 00768 // finalization. Moreover, attempting to catch bad calls 00769 // mid-execution would both add overhead and open up uncatchable 00770 // opportunities for inappropriate recursion. 00771 00772 static volatile CThreadSystemID s_LastThreadID 00773 = THREAD_SYSTEM_ID_INITIALIZER; 00774 00775 if (s_ThreadDataState != eInitialized) { 00776 // Avoid false positives, while also taking care not to call 00777 // anything that might itself produce diagnostics. 00778 CThreadSystemID thread_id = CThreadSystemID::GetCurrent(); 00779 switch (s_ThreadDataState) { 00780 case eInitialized: 00781 break; 00782 00783 case eUninitialized: 00784 s_ThreadDataState = eInitializing; 00785 s_LastThreadID.Set(thread_id); 00786 break; 00787 00788 case eInitializing: 00789 if (s_LastThreadID.Is(thread_id)) { 00790 cerr << "FATAL ERROR: inappropriate recursion initializing NCBI" 00791 " diagnostic framework." << endl; 00792 Abort(); 00793 } 00794 break; 00795 00796 case eDeinitialized: 00797 s_ThreadDataState = eReinitializing; 00798 s_LastThreadID.Set(thread_id); 00799 break; 00800 00801 case eReinitializing: 00802 if (s_LastThreadID.Is(thread_id)) { 00803 cerr << "FATAL ERROR: NCBI diagnostic framework no longer" 00804 " initialized." << endl; 00805 Abort(); 00806 } 00807 break; 00808 } 00809 } 00810 00811 static CStaticTls<CDiagContextThreadData> 00812 s_ThreadData(s_ThreadDataSafeStaticCleanup, 00813 CSafeStaticLifeSpan(CSafeStaticLifeSpan::eLifeSpan_Long, 1)); 00814 CDiagContextThreadData* data = s_ThreadData.GetValue(); 00815 if ( !data ) { 00816 // Cleanup data set to null for any thread except the main one. 00817 // This value is used as a flag to copy threads' properties to global 00818 // upon TLS cleanup. 00819 data = new CDiagContextThreadData; 00820 s_ThreadData.SetValue(data, CDiagContext::sx_ThreadDataTlsCleanup, 00821 CThread::GetSelf() ? 0 : (void*)(1)); 00822 } 00823 00824 s_ThreadDataState = eInitialized; 00825 00826 return *data; 00827 } 00828 00829 00830 CRequestContext& CDiagContextThreadData::GetRequestContext(void) 00831 { 00832 _ASSERT(m_RequestCtx.get() && m_RequestCtx->m_Ctx); 00833 return *m_RequestCtx->m_Ctx; 00834 } 00835 00836 00837 void CDiagContextThreadData::SetRequestContext(CRequestContext* ctx) 00838 { 00839 m_RequestCtx->m_Ctx = ctx ? ctx : m_DefaultRequestCtx->m_Ctx; 00840 } 00841 00842 00843 CDiagContextThreadData::TProperties* 00844 CDiagContextThreadData::GetProperties(EGetProperties flag) 00845 { 00846 if ( !m_Properties.get() && flag == eProp_Create ) { 00847 m_Properties.reset(new TProperties); 00848 } 00849 return m_Properties.get(); 00850 } 00851 00852 00853 CDiagContextThreadData::TCount 00854 CDiagContextThreadData::GetThreadPostNumber(EPostNumberIncrement inc) 00855 { 00856 return inc == ePostNumber_Increment ? 00857 ++m_ThreadPostNumber : m_ThreadPostNumber; 00858 } 00859 00860 00861 void CDiagContextThreadData::AddCollectGuard(CDiagCollectGuard* guard) 00862 { 00863 m_CollectGuards.push_front(guard); 00864 } 00865 00866 00867 void CDiagContextThreadData::RemoveCollectGuard(CDiagCollectGuard* guard) 00868 { 00869 TCollectGuards::iterator itg = find( 00870 m_CollectGuards.begin(), m_CollectGuards.end(), guard); 00871 if (itg == m_CollectGuards.end()) { 00872 return; // The guard has been already released 00873 } 00874 m_CollectGuards.erase(itg); 00875 if ( !m_CollectGuards.empty() ) { 00876 return; 00877 // Previously printing was done for each guard, discarding - only for 00878 // the last guard. 00879 } 00880 // If this is the last guard, perform its action 00881 CDiagLock lock(CDiagLock::eWrite); 00882 if (guard->GetAction() == CDiagCollectGuard::ePrint) { 00883 CDiagHandler* handler = GetDiagHandler(); 00884 if ( handler ) { 00885 ITERATE(TDiagCollection, itc, m_DiagCollection) { 00886 if ((itc->m_Flags & eDPF_IsConsole) != 0) { 00887 // Print all messages to console 00888 handler->PostToConsole(*itc); 00889 // Make sure only messages with the severity above allowed 00890 // are printed to normal log. 00891 EDiagSev post_sev = AdjustApplogPrintableSeverity( 00892 guard->GetCollectSeverity()); 00893 bool allow_trace = post_sev == eDiag_Trace; 00894 if (itc->m_Severity == eDiag_Trace && !allow_trace) { 00895 continue; // trace is disabled 00896 } 00897 if (itc->m_Severity < post_sev) { 00898 continue; 00899 } 00900 } 00901 handler->Post(*itc); 00902 } 00903 size_t discarded = m_DiagCollectionSize - m_DiagCollection.size(); 00904 if (discarded > 0) { 00905 ERR_POST_X(18, Warning << "Discarded " << discarded << 00906 " messages due to collection limit. Set " 00907 "DIAG_COLLECT_LIMIT to increase the limit."); 00908 } 00909 } 00910 } 00911 m_DiagCollection.clear(); 00912 m_DiagCollectionSize = 0; 00913 } 00914 00915 00916 CDiagCollectGuard* CDiagContextThreadData::GetCollectGuard(void) 00917 { 00918 return m_CollectGuards.empty() ? NULL : m_CollectGuards.front(); 00919 } 00920 00921 00922 void CDiagContextThreadData::CollectDiagMessage(const SDiagMessage& mess) 00923 { 00924 if (m_DiagCollectionSize >= TDiagCollectLimit::GetDefault()) { 00925 m_DiagCollection.erase(m_DiagCollection.begin()); 00926 } 00927 m_DiagCollection.push_back(mess); 00928 m_DiagCollectionSize++; 00929 } 00930 00931 00932 CDiagContextThreadData::TCount 00933 CDiagContextThreadData::GetRequestId(void) 00934 { 00935 return GetRequestContext().GetRequestID(); 00936 } 00937 00938 00939 void CDiagContextThreadData::SetRequestId(TCount id) 00940 { 00941 GetRequestContext().SetRequestID(id); 00942 } 00943 00944 00945 void CDiagContextThreadData::IncRequestId(void) 00946 { 00947 GetRequestContext().SetRequestID(); 00948 } 00949 00950 00951 extern Uint8 GetDiagRequestId(void) 00952 { 00953 return GetDiagContext().GetRequestContext().GetRequestID(); 00954 } 00955 00956 00957 extern void SetDiagRequestId(Uint8 id) 00958 { 00959 GetDiagContext().GetRequestContext().SetRequestID(id); 00960 } 00961 00962 00963 /////////////////////////////////////////////////////// 00964 // CDiagContext:: 00965 00966 00967 // AppState formatting/parsing 00968 static const char* s_AppStateStr[] = { 00969 "NS", "AB", "A", "AE", "RB", "R", "RE" 00970 }; 00971 00972 const char* s_AppStateToStr(EDiagAppState state) 00973 { 00974 return s_AppStateStr[state]; 00975 } 00976 00977 EDiagAppState s_StrToAppState(const string& state) 00978 { 00979 for (int st = (int)eDiagAppState_AppBegin; 00980 st < eDiagAppState_RequestEnd; st++) { 00981 if (state == s_AppStateStr[st]) { 00982 return (EDiagAppState)st; 00983 } 00984 } 00985 // Throw to notify caller about invalid app state. 00986 NCBI_THROW(CException, eUnknown, "Invalid EDiagAppState value"); 00987 /*NOTREACHED*/ 00988 return eDiagAppState_NotSet; 00989 } 00990 00991 00992 struct SDiagMessageData 00993 { 00994 SDiagMessageData(void); 00995 ~SDiagMessageData(void) {} 00996 00997 string m_Message; 00998 string m_File; 00999 string m_Module; 01000 string m_Class; 01001 string m_Function; 01002 string m_Prefix; 01003 string m_ErrText; 01004 01005 CDiagContext::TUID m_UID; 01006 CTime m_Time; 01007 01008 // If the following properties are not set, take them from DiagContext. 01009 string m_Host; 01010 string m_Client; 01011 string m_Session; 01012 string m_AppName; 01013 EDiagAppState m_AppState; 01014 }; 01015 01016 01017 SDiagMessageData::SDiagMessageData(void) 01018 : m_UID(0), 01019 m_Time(GetFastLocalTime()), 01020 m_AppState(eDiagAppState_NotSet) 01021 { 01022 } 01023 01024 01025 CDiagContext* CDiagContext::sm_Instance = NULL; 01026 bool CDiagContext::sm_ApplogSeverityLocked = false; 01027 01028 01029 CDiagContext::CDiagContext(void) 01030 : m_UID(0), 01031 m_Host(new CEncodedString), 01032 m_Username(new CEncodedString), 01033 m_AppName(new CEncodedString), 01034 m_ExitCode(0), 01035 m_ExitSig(0), 01036 m_AppState(eDiagAppState_AppBegin), 01037 m_StopWatch(new CStopWatch(CStopWatch::eStart)), 01038 m_MaxMessages(100), // limit number of collected messages to 100 01039 m_AppLogRC(new CRequestRateControl( 01040 GetLogRate_Limit(eLogRate_App), 01041 CTimeSpan((long)GetLogRate_Period(eLogRate_App)), 01042 CTimeSpan((long)0), 01043 CRequestRateControl::eErrCode, 01044 CRequestRateControl::eDiscrete)), 01045 m_ErrLogRC(new CRequestRateControl( 01046 GetLogRate_Limit(eLogRate_Err), 01047 CTimeSpan((long)GetLogRate_Period(eLogRate_Err)), 01048 CTimeSpan((long)0), 01049 CRequestRateControl::eErrCode, 01050 CRequestRateControl::eDiscrete)), 01051 m_TraceLogRC(new CRequestRateControl( 01052 GetLogRate_Limit(eLogRate_Trace), 01053 CTimeSpan((long)GetLogRate_Period(eLogRate_Trace)), 01054 CTimeSpan((long)0), 01055 CRequestRateControl::eErrCode, 01056 CRequestRateControl::eDiscrete)), 01057 m_AppLogSuspended(false), 01058 m_ErrLogSuspended(false), 01059 m_TraceLogSuspended(false) 01060 { 01061 sm_Instance = this; 01062 } 01063 01064 01065 CDiagContext::~CDiagContext(void) 01066 { 01067 sm_Instance = NULL; 01068 } 01069 01070 01071 void CDiagContext::ResetLogRates(void) 01072 { 01073 m_AppLogRC->Reset(GetLogRate_Limit(eLogRate_App), 01074 CTimeSpan((long)GetLogRate_Period(eLogRate_App)), 01075 CTimeSpan((long)0), 01076 CRequestRateControl::eErrCode, 01077 CRequestRateControl::eDiscrete); 01078 m_ErrLogRC->Reset(GetLogRate_Limit(eLogRate_Err), 01079 CTimeSpan((long)GetLogRate_Period(eLogRate_Err)), 01080 CTimeSpan((long)0), 01081 CRequestRateControl::eErrCode, 01082 CRequestRateControl::eDiscrete); 01083 m_TraceLogRC->Reset(GetLogRate_Limit(eLogRate_Trace), 01084 CTimeSpan((long)GetLogRate_Period(eLogRate_Trace)), 01085 CTimeSpan((long)0), 01086 CRequestRateControl::eErrCode, 01087 CRequestRateControl::eDiscrete); 01088 m_AppLogSuspended = false; 01089 m_ErrLogSuspended = false; 01090 m_TraceLogSuspended = false; 01091 } 01092 01093 01094 unsigned int CDiagContext::GetLogRate_Limit(ELogRate_Type type) const 01095 { 01096 switch ( type ) { 01097 case eLogRate_App: 01098 return TAppLogRateLimitParam::GetDefault(); 01099 case eLogRate_Err: 01100 return TErrLogRateLimitParam::GetDefault(); 01101 case eLogRate_Trace: 01102 default: 01103 return TTraceLogRateLimitParam::GetDefault(); 01104 } 01105 } 01106 01107 void CDiagContext::SetLogRate_Limit(ELogRate_Type type, unsigned int limit) 01108 { 01109 switch ( type ) { 01110 case eLogRate_App: 01111 TAppLogRateLimitParam::SetDefault(limit); 01112 if ( m_AppLogRC.get() ) { 01113 m_AppLogRC->Reset(limit, 01114 CTimeSpan((long)GetLogRate_Period(type)), 01115 CTimeSpan((long)0), 01116 CRequestRateControl::eErrCode, 01117 CRequestRateControl::eDiscrete); 01118 } 01119 m_AppLogSuspended = false; 01120 break; 01121 case eLogRate_Err: 01122 TErrLogRateLimitParam::SetDefault(limit); 01123 if ( m_ErrLogRC.get() ) { 01124 m_ErrLogRC->Reset(limit, 01125 CTimeSpan((long)GetLogRate_Period(type)), 01126 CTimeSpan((long)0), 01127 CRequestRateControl::eErrCode, 01128 CRequestRateControl::eDiscrete); 01129 } 01130 m_ErrLogSuspended = false; 01131 break; 01132 case eLogRate_Trace: 01133 default: 01134 TTraceLogRateLimitParam::SetDefault(limit); 01135 if ( m_TraceLogRC.get() ) { 01136 m_TraceLogRC->Reset(limit, 01137 CTimeSpan((long)GetLogRate_Period(type)), 01138 CTimeSpan((long)0), 01139 CRequestRateControl::eErrCode, 01140 CRequestRateControl::eDiscrete); 01141 } 01142 m_TraceLogSuspended = false; 01143 break; 01144 } 01145 } 01146 01147 unsigned int CDiagContext::GetLogRate_Period(ELogRate_Type type) const 01148 { 01149 switch ( type ) { 01150 case eLogRate_App: 01151 return TAppLogRatePeriodParam::GetDefault(); 01152 case eLogRate_Err: 01153 return TErrLogRatePeriodParam::GetDefault(); 01154 case eLogRate_Trace: 01155 default: 01156 return TTraceLogRatePeriodParam::GetDefault(); 01157 } 01158 } 01159 01160 void CDiagContext::SetLogRate_Period(ELogRate_Type type, unsigned int period) 01161 { 01162 switch ( type ) { 01163 case eLogRate_App: 01164 TAppLogRatePeriodParam::SetDefault(period); 01165 if ( m_AppLogRC.get() ) { 01166 m_AppLogRC->Reset(GetLogRate_Limit(type), 01167 CTimeSpan((long)period), 01168 CTimeSpan((long)0), 01169 CRequestRateControl::eErrCode, 01170 CRequestRateControl::eDiscrete); 01171 } 01172 m_AppLogSuspended = false; 01173 break; 01174 case eLogRate_Err: 01175 TErrLogRatePeriodParam::SetDefault(period); 01176 if ( m_ErrLogRC.get() ) { 01177 m_ErrLogRC->Reset(GetLogRate_Limit(type), 01178 CTimeSpan((long)period), 01179 CTimeSpan((long)0), 01180 CRequestRateControl::eErrCode, 01181 CRequestRateControl::eDiscrete); 01182 } 01183 m_ErrLogSuspended = false; 01184 break; 01185 case eLogRate_Trace: 01186 default: 01187 TTraceLogRatePeriodParam::SetDefault(period); 01188 if ( m_TraceLogRC.get() ) { 01189 m_TraceLogRC->Reset(GetLogRate_Limit(type), 01190 CTimeSpan((long)period), 01191 CTimeSpan((long)0), 01192 CRequestRateControl::eErrCode, 01193 CRequestRateControl::eDiscrete); 01194 } 01195 m_TraceLogSuspended = false; 01196 break; 01197 } 01198 } 01199 01200 01201 bool CDiagContext::ApproveMessage(SDiagMessage& msg, 01202 bool* show_warning) 01203 { 01204 bool approved = true; 01205 if ( IsSetDiagPostFlag(eDPF_AppLog, msg.m_Flags) ) { 01206 approved = m_AppLogRC->Approve(); 01207 if ( approved ) { 01208 m_AppLogSuspended = false; 01209 } 01210 else { 01211 *show_warning = !m_AppLogSuspended; 01212 m_AppLogSuspended = true; 01213 } 01214 } 01215 else { 01216 switch ( msg.m_Severity ) { 01217 case eDiag_Info: 01218 case eDiag_Trace: 01219 approved = m_TraceLogRC->Approve(); 01220 if ( approved ) { 01221 m_TraceLogSuspended = false; 01222 } 01223 else { 01224 *show_warning = !m_TraceLogSuspended; 01225 m_TraceLogSuspended = true; 01226 } 01227 break; 01228 default: 01229 approved = m_ErrLogRC->Approve(); 01230 if ( approved ) { 01231 m_ErrLogSuspended = false; 01232 } 01233 else { 01234 *show_warning = !m_ErrLogSuspended; 01235 m_ErrLogSuspended = true; 01236 } 01237 } 01238 } 01239 return approved; 01240 } 01241 01242 01243 CDiagContext::TPID CDiagContext::sm_PID = 0; 01244 01245 CDiagContext::TPID CDiagContext::GetPID(void) 01246 { 01247 if ( !sm_PID ) { 01248 sm_PID = CProcess::GetCurrentPid(); 01249 } 01250 return sm_PID; 01251 } 01252 01253 01254 void CDiagContext::UpdatePID(void) 01255 { 01256 TPID new_pid = CProcess::GetCurrentPid(); 01257 if (sm_PID == new_pid) { 01258 // Parent process does not need to update pid/guid 01259 return; 01260 } 01261 sm_PID = new_pid; 01262 CDiagContext& ctx = GetDiagContext(); 01263 TUID old_uid = ctx.GetUID(); 01264 // Update GUID to match the new PID 01265 ctx.x_CreateUID(); 01266 ctx.Extra(). 01267 Print("action", "fork"). 01268 Print("parent_guid", ctx.GetStringUID(old_uid)); 01269 //ctx.PrintExtra("New process created by fork(), " 01270 // "parent GUID=" + ); 01271 } 01272 01273 01274 void CDiagContext::x_CreateUID(void) const 01275 { 01276 Int8 pid = GetPID(); 01277 time_t t = time(0); 01278 const string& host = GetHost(); 01279 TUID h = 212; 01280 ITERATE(string, s, host) { 01281 h = h*1265 + *s; 01282 } 01283 h &= 0xFFFF; 01284 // The low 4 bits are reserved as GUID generator version number. 01285 m_UID = (TUID(h) << 48) | 01286 ((TUID(pid) & 0xFFFF) << 32) | 01287 ((TUID(t) & 0xFFFFFFF) << 4) | 01288 1; // version #1 - fixed type conversion bug 01289 } 01290 01291 01292 CDiagContext::TUID CDiagContext::GetUID(void) const 01293 { 01294 if ( !m_UID ) { 01295 CDiagLock lock(CDiagLock::eWrite); 01296 if ( !m_UID ) { 01297 x_CreateUID(); 01298 } 01299 } 01300 return m_UID; 01301 } 01302 01303 01304 string CDiagContext::GetStringUID(TUID uid) const 01305 { 01306 char buf[18]; 01307 if (uid == 0) { 01308 uid = GetUID(); 01309 } 01310 int hi = int((uid >> 32) & 0xFFFFFFFF); 01311 int lo = int(uid & 0xFFFFFFFF); 01312 sprintf(buf, "%08X%08X", hi, lo); 01313 return string(buf); 01314 } 01315 01316 01317 CDiagContext::TUID CDiagContext::UpdateUID(TUID uid) const 01318 { 01319 if (uid == 0) { 01320 uid = GetUID(); 01321 } 01322 time_t t = time(0); 01323 // Clear old timestamp 01324 uid &= ~((TUID)0xFFFFFFF << 4); 01325 // Add current timestamp 01326 return uid | ((TUID(t) & 0xFFFFFFF) << 4); 01327 } 01328 01329 01330 string CDiagContext::GetNextHitID(void) const 01331 { 01332 Uint8 hi = GetUID(); 01333 Uint4 b3 = Uint4((hi >> 32) & 0xFFFFFFFF); 01334 Uint4 b2 = Uint4(hi & 0xFFFFFFFF); 01335 01336 CDiagContextThreadData& thr_data = CDiagContextThreadData::GetThreadData(); 01337 Uint8 tid = (thr_data.GetTID() & 0xFFFFFF) << 40; 01338 Uint8 rid = Uint8(thr_data.GetRequestContext().GetRequestID() & 0xFFFFFF) << 16; 01339 Uint8 us = (GetFastLocalTime().MicroSecond()/16) & 0xFFFF; 01340 Uint8 lo = tid | rid | us; 01341 Uint4 b1 = Uint4((lo >> 32) & 0xFFFFFFFF); 01342 Uint4 b0 = Uint4(lo & 0xFFFFFFFF); 01343 char buf[40]; 01344 sprintf(buf, "%08X%08X%08X%08X", b3, b2, b1, b0); 01345 return string(buf); 01346 } 01347 01348 01349 const string& CDiagContext::GetUsername(void) const 01350 { 01351 return m_Username->GetOriginalString(); 01352 } 01353 01354 01355 void CDiagContext::SetUsername(const string& username) 01356 { 01357 m_Username->SetString(username); 01358 } 01359 01360 01361 const string& CDiagContext::GetHost(void) const 01362 { 01363 // Check context properties 01364 if ( !m_Host->IsEmpty() ) { 01365 return m_Host->GetOriginalString(); 01366 } 01367 if ( !m_HostIP.empty() ) { 01368 return m_HostIP; 01369 } 01370 01371 #if defined(NCBI_OS_UNIX) 01372 // UNIX - use uname() 01373 {{ 01374 struct utsname buf; 01375 if (uname(&buf) >= 0) { 01376 m_Host->SetString(buf.nodename); 01377 return m_Host->GetOriginalString(); 01378 } 01379 }} 01380 #endif 01381 01382 #if defined(NCBI_OS_MSWIN) 01383 // MSWIN - use COMPUTERNAME 01384 const TXChar* compname = NcbiSys_getenv(_TX("COMPUTERNAME")); 01385 if ( compname && *compname ) { 01386 m_Host->SetString(_T_STDSTRING(compname)); 01387 return m_Host->GetOriginalString(); 01388 } 01389 #endif 01390 01391 // Server env. - use SERVER_ADDR 01392 const TXChar* servaddr = NcbiSys_getenv(_TX("SERVER_ADDR")); 01393 if ( servaddr && *servaddr ) { 01394 m_Host->SetString(_T_STDSTRING(servaddr)); 01395 } 01396 return m_Host->GetOriginalString(); 01397 } 01398 01399 01400 const string& CDiagContext::GetEncodedHost(void) const 01401 { 01402 if ( !m_Host->IsEmpty() ) { 01403 return m_Host->GetEncodedString(); 01404 } 01405 if ( !m_HostIP.empty() ) { 01406 return m_HostIP; 01407 } 01408 // Initialize m_Host, this does not change m_HostIP 01409 GetHost(); 01410 return m_Host->GetEncodedString(); 01411 } 01412 01413 01414 const string& CDiagContext::GetHostname(void) const 01415 { 01416 return m_Host->GetOriginalString(); 01417 } 01418 01419 01420 const string& CDiagContext::GetEncodedHostname(void) const 01421 { 01422 return m_Host->GetEncodedString(); 01423 } 01424 01425 01426 void CDiagContext::SetHostname(const string& hostname) 01427 { 01428 m_Host->SetString(hostname); 01429 } 01430 01431 01432 void CDiagContext::SetHostIP(const string& ip) 01433 { 01434 if ( !NStr::IsIPAddress(ip) ) { 01435 m_HostIP.clear(); 01436 ERR_POST("Bad host IP value: " << ip); 01437 return; 01438 } 01439 01440 m_HostIP = ip; 01441 } 01442 01443 01444 const string& CDiagContext::GetAppName(void) const 01445 { 01446 if ( m_AppName->IsEmpty() ) { 01447 m_AppName->SetString(CNcbiApplication::GetAppName()); 01448 } 01449 return m_AppName->GetOriginalString(); 01450 } 01451 01452 01453 const string& CDiagContext::GetEncodedAppName(void) const 01454 { 01455 return m_AppName->GetEncodedString(); 01456 } 01457 01458 01459 void CDiagContext::SetAppName(const string& app_name) 01460 { 01461 if ( !m_AppName->IsEmpty() ) { 01462 // AppName can be set only once 01463 ERR_POST("Application name cannot be changed."); 01464 return; 01465 } 01466 m_AppName->SetString(app_name); 01467 if ( m_AppName->IsEncoded() ) { 01468 ERR_POST("Illegal characters in application name: '" << app_name << 01469 "', using URL-encode."); 01470 } 01471 } 01472 01473 01474 CRequestContext& CDiagContext::GetRequestContext(void) 01475 { 01476 return CDiagContextThreadData::GetThreadData().GetRequestContext(); 01477 } 01478 01479 01480 void CDiagContext::SetRequestContext(CRequestContext* ctx) 01481 { 01482 CDiagContextThreadData::GetThreadData().SetRequestContext(ctx); 01483 } 01484 01485 01486 void CDiagContext::SetAutoWrite(bool value) 01487 { 01488 TAutoWrite_Context::SetDefault(value); 01489 } 01490 01491 01492 inline bool IsGlobalProperty(const string& name) 01493 { 01494 return 01495 name == CDiagContext::kProperty_UserName || 01496 name == CDiagContext::kProperty_HostName || 01497 name == CDiagContext::kProperty_HostIP || 01498 name == CDiagContext::kProperty_AppName || 01499 name == CDiagContext::kProperty_ExitSig || 01500 name == CDiagContext::kProperty_ExitCode; 01501 } 01502 01503 01504 void CDiagContext::SetProperty(const string& name, 01505 const string& value, 01506 EPropertyMode mode) 01507 { 01508 // Global properties 01509 if (name == kProperty_UserName) { 01510 SetUsername(value); 01511 return; 01512 } 01513 if (name == kProperty_HostName) { 01514 SetHostname(value); 01515 return; 01516 } 01517 if (name == kProperty_HostIP) { 01518 SetHostIP(value); 01519 return; 01520 } 01521 if (name == kProperty_AppName) { 01522 SetAppName(value); 01523 return; 01524 } 01525 if (name == kProperty_ExitCode) { 01526 SetExitCode(NStr::StringToInt(value, NStr::fConvErr_NoThrow)); 01527 return; 01528 } 01529 if (name == kProperty_ExitSig) { 01530 SetExitSignal(NStr::StringToInt(value, NStr::fConvErr_NoThrow)); 01531 return; 01532 } 01533 01534 // Request properties 01535 if (name == kProperty_AppState) { 01536 try { 01537 SetAppState(s_StrToAppState(value)); 01538 } 01539 catch (CException) { 01540 } 01541 return; 01542 } 01543 if (name == kProperty_ClientIP) { 01544 GetRequestContext().SetClientIP(value); 01545 return; 01546 } 01547 if (name == kProperty_SessionID) { 01548 GetRequestContext().SetSessionID(value); 01549 return; 01550 } 01551 if (name == kProperty_ReqStatus) { 01552 if ( !value.empty() ) { 01553 GetRequestContext().SetRequestStatus( 01554 NStr::StringToInt(value, NStr::fConvErr_NoThrow)); 01555 } 01556 else { 01557 GetRequestContext().UnsetRequestStatus(); 01558 } 01559 return; 01560 } 01561 if (name == kProperty_BytesRd) { 01562 GetRequestContext().SetBytesRd( 01563 NStr::StringToInt8(value, NStr::fConvErr_NoThrow)); 01564 return; 01565 } 01566 if (name == kProperty_BytesWr) { 01567 GetRequestContext().SetBytesWr( 01568 NStr::StringToInt8(value, NStr::fConvErr_NoThrow)); 01569 return; 01570 } 01571 if (name == kProperty_ReqTime) { 01572 // Cannot set this property 01573 return; 01574 } 01575 01576 if ( mode == eProp_Default ) { 01577 mode = IsGlobalProperty(name) ? eProp_Global : eProp_Thread; 01578 } 01579 01580 if ( mode == eProp_Global ) { 01581 CDiagLock lock(CDiagLock::eWrite); 01582 m_Properties[name] = value; 01583 } 01584 else { 01585 TProperties* props = 01586 CDiagContextThreadData::GetThreadData().GetProperties( 01587 CDiagContextThreadData::eProp_Create); /* NCBI_FAKE_WARNING */ 01588 _ASSERT(props); 01589 (*props)[name] = value; 01590 } 01591 if ( sm_Instance && TAutoWrite_Context::GetDefault() ) { 01592 CDiagLock lock(CDiagLock::eRead); 01593 x_PrintMessage(SDiagMessage::eEvent_Extra, name + "=" + value); 01594 } 01595 } 01596 01597 01598 string CDiagContext::GetProperty(const string& name, 01599 EPropertyMode mode) const 01600 { 01601 // Global properties 01602 if (name == kProperty_UserName) { 01603 return GetUsername(); 01604 } 01605 if (name == kProperty_HostName) { 01606 return GetHostname(); 01607 } 01608 if (name == kProperty_HostIP) { 01609 return GetHostIP(); 01610 } 01611 if (name == kProperty_AppName) { 01612 return GetAppName(); 01613 } 01614 if (name == kProperty_ExitCode) { 01615 return NStr::IntToString(m_ExitCode); 01616 } 01617 if (name == kProperty_ExitSig) { 01618 return NStr::IntToString(m_ExitSig); 01619 } 01620 01621 // Request properties 01622 if (name == kProperty_AppState) { 01623 return s_AppStateToStr(GetAppState()); 01624 } 01625 if (name == kProperty_ClientIP) { 01626 return GetRequestContext().GetClientIP(); 01627 } 01628 if (name == kProperty_SessionID) { 01629 return GetSessionID(); 01630 } 01631 if (name == kProperty_ReqStatus) { 01632 return GetRequestContext().IsSetRequestStatus() ? 01633 NStr::IntToString(GetRequestContext().GetRequestStatus()) 01634 : kEmptyStr; 01635 } 01636 if (name == kProperty_BytesRd) { 01637 return NStr::Int8ToString(GetRequestContext().GetBytesRd()); 01638 } 01639 if (name == kProperty_BytesWr) { 01640 return NStr::Int8ToString(GetRequestContext().GetBytesWr()); 01641 } 01642 if (name == kProperty_ReqTime) { 01643 return GetRequestContext().GetRequestTimer().AsString(); 01644 } 01645 01646 if (mode == eProp_Thread || 01647 (mode == eProp_Default && !IsGlobalProperty(name))) { 01648 TProperties* props = 01649 CDiagContextThreadData::GetThreadData().GetProperties( 01650 CDiagContextThreadData::eProp_Get); /* NCBI_FAKE_WARNING */ 01651 if ( props ) { 01652 TProperties::const_iterator tprop = props->find(name); 01653 if ( tprop != props->end() ) { 01654 return tprop->second; 01655 } 01656 } 01657 if (mode == eProp_Thread) { 01658 return kEmptyStr; 01659 } 01660 } 01661 // Check global properties 01662 CDiagLock lock(CDiagLock::eRead); 01663 TProperties::const_iterator gprop = m_Properties.find(name); 01664 return gprop != m_Properties.end() ? gprop->second : kEmptyStr; 01665 } 01666 01667 01668 void CDiagContext::DeleteProperty(const string& name, 01669 EPropertyMode mode) 01670 { 01671 if (mode == eProp_Thread || 01672 (mode == eProp_Default && !IsGlobalProperty(name))) { 01673 TProperties* props = 01674 CDiagContextThreadData::GetThreadData().GetProperties( 01675 CDiagContextThreadData::eProp_Get); /* NCBI_FAKE_WARNING */ 01676 if ( props ) { 01677 TProperties::iterator tprop = props->find(name); 01678 if ( tprop != props->end() ) { 01679 props->erase(tprop); 01680 return; 01681 } 01682 } 01683 if (mode == eProp_Thread) { 01684 return; 01685 } 01686 } 01687 // Check global properties 01688 CDiagLock lock(CDiagLock::eRead); 01689 TProperties::iterator gprop = m_Properties.find(name); 01690 if (gprop != m_Properties.end()) { 01691 m_Properties.erase(gprop); 01692 } 01693 } 01694 01695 01696 void CDiagContext::PrintProperties(void) 01697 { 01698 {{ 01699 CDiagLock lock(CDiagLock::eRead); 01700 ITERATE(TProperties, gprop, m_Properties) { 01701 x_PrintMessage(SDiagMessage::eEvent_Extra, 01702 gprop->first + "=" + gprop->second); 01703 } 01704 }} 01705 TProperties* props = 01706 CDiagContextThreadData::GetThreadData().GetProperties( 01707 CDiagContextThreadData::eProp_Get); /* NCBI_FAKE_WARNING */ 01708 if ( !props ) { 01709 return; 01710 } 01711 ITERATE(TProperties, tprop, *props) { 01712 x_PrintMessage(SDiagMessage::eEvent_Extra, 01713 tprop->first + "=" + tprop->second); 01714 } 01715 } 01716 01717 01718 void CDiagContext::PrintStart(const string& message) 01719 { 01720 x_PrintMessage(SDiagMessage::eEvent_Start, message); 01721 } 01722 01723 01724 void CDiagContext::PrintStop(void) 01725 { 01726 x_PrintMessage(SDiagMessage::eEvent_Stop, kEmptyStr); 01727 } 01728 01729 01730 void CDiagContext::PrintExtra(const string& message) 01731 { 01732 x_PrintMessage(SDiagMessage::eEvent_Extra, message); 01733 } 01734 01735 01736 CDiagContext_Extra::CDiagContext_Extra(SDiagMessage::EEventType event_type) 01737 : m_EventType(event_type), 01738 m_Args(0), 01739 m_Counter(new int(1)), 01740 m_Typed(false), 01741 m_PerfStatus(0), 01742 m_PerfTime(0) 01743 { 01744 } 01745 01746 01747 CDiagContext_Extra::CDiagContext_Extra(int status, 01748 double timespan, 01749 TExtraArgs& args) 01750 : m_EventType(SDiagMessage::eEvent_PerfLog), 01751 m_Args(0), 01752 m_Counter(new int(1)), 01753 m_Typed(false), 01754 m_PerfStatus(status), 01755 m_PerfTime(timespan) 01756 { 01757 if (args.empty()) return; 01758 m_Args = new TExtraArgs; 01759 m_Args->splice(m_Args->end(), args); 01760 } 01761 01762 01763 CDiagContext_Extra::CDiagContext_Extra(const CDiagContext_Extra& args) 01764 : m_EventType(const_cast<CDiagContext_Extra&>(args).m_EventType), 01765 m_Args(const_cast<CDiagContext_Extra&>(args).m_Args), 01766 m_Counter(const_cast<CDiagContext_Extra&>(args).m_Counter), 01767 m_Typed(args.m_Typed), 01768 m_PerfStatus(args.m_PerfStatus), 01769 m_PerfTime(args.m_PerfTime) 01770 { 01771 (*m_Counter)++; 01772 } 01773 01774 01775 const TDiagPostFlags kApplogDiagPostFlags = 01776 eDPF_OmitInfoSev | eDPF_OmitSeparator | eDPF_AppLog; 01777 01778 void CDiagContext_Extra::Flush(void) 01779 { 01780 if ( !m_Args || m_Args->empty() || 01781 CDiagContext::IsSetOldPostFormat() ) { 01782 return; 01783 } 01784 01785 if (m_EventType == SDiagMessage::eEvent_RequestStart) { 01786 CDiagContext::x_StartRequest(); 01787 } 01788 01789 auto_ptr<CNcbiOstrstream> ostr; 01790 char* buf = 0; 01791 size_t buflen = 0; 01792 if (m_EventType == SDiagMessage::eEvent_PerfLog) { 01793 ostr.reset(new CNcbiOstrstream); 01794 *ostr << m_PerfStatus << " " << 01795 NStr::DoubleToString(m_PerfTime, -1, NStr::fDoubleFixed); 01796 buf = ostr->str(); 01797 buflen = size_t(ostr->pcount()); 01798 } 01799 01800 SDiagMessage mess(eDiag_Info, 01801 buf, buflen, 01802 0, 0, // file, line 01803 CNcbiDiag::ForceImportantFlags(kApplogDiagPostFlags), 01804 NULL, 01805 0, 0, // err code/subcode 01806 NULL, 01807 0, 0, 0); // module/class/function 01808 mess.m_Event = m_EventType; 01809 mess.m_ExtraArgs.splice(mess.m_ExtraArgs.end(), *m_Args); 01810 mess.m_TypedExtra = m_Typed; 01811 01812 GetDiagBuffer().DiagHandler(mess); 01813 if ( ostr.get() ) { 01814 ostr->rdbuf()->freeze(false); 01815 } 01816 } 01817 01818 01819 void CDiagContext_Extra::x_Release(void) 01820 { 01821 if ( m_Counter && --(*m_Counter) == 0) { 01822 Flush(); 01823 delete m_Args; 01824 m_Args = 0; 01825 } 01826 } 01827 01828 01829 CDiagContext_Extra& 01830 CDiagContext_Extra::operator=(const CDiagContext_Extra& args) 01831 { 01832 if (this != &args) { 01833 x_Release(); 01834 m_Args = const_cast<CDiagContext_Extra&>(args).m_Args; 01835 m_Counter = const_cast<CDiagContext_Extra&>(args).m_Counter; 01836 m_Typed = args.m_Typed; 01837 m_PerfStatus = args.m_PerfStatus; 01838 m_PerfTime = args.m_PerfTime; 01839 (*m_Counter)++; 01840 } 01841 return *this; 01842 } 01843 01844 01845 CDiagContext_Extra::~CDiagContext_Extra(void) 01846 { 01847 x_Release(); 01848 if ( *m_Counter == 0) { 01849 delete m_Counter; 01850 } 01851 } 01852 01853 CDiagContext_Extra& 01854 CDiagContext_Extra::Print(const string& name, const string& value) 01855 { 01856 if ( !m_Args ) { 01857 m_Args = new TExtraArgs; 01858 } 01859 // Optimize inserting new pair into the args list, it is the same as: 01860 // m_Args->push_back(TExtraArg(name, value)); 01861 m_Args->push_back(TExtraArg(kEmptyStr, kEmptyStr)); 01862 m_Args->rbegin()->first.assign(name); 01863 m_Args->rbegin()->second.assign(value); 01864 return *this; 01865 } 01866 01867 CDiagContext_Extra& 01868 CDiagContext_Extra::Print(const string& name, const char* value) 01869 { 01870 return Print(name, string(value)); 01871 } 01872 01873 CDiagContext_Extra& 01874 CDiagContext_Extra::Print(const string& name, int value) 01875 { 01876 return Print(name, NStr::Int8ToString(value)); 01877 } 01878 01879 CDiagContext_Extra& 01880 CDiagContext_Extra::Print(const string& name, unsigned int value) 01881 { 01882 return Print(name, NStr::UInt8ToString(value)); 01883 } 01884 01885 #if (SIZEOF_INT < 8) 01886 CDiagContext_Extra& 01887 CDiagContext_Extra::Print(const string& name, Int8 value) 01888 { 01889 return Print(name, NStr::Int8ToString(value)); 01890 } 01891 CDiagContext_Extra& 01892 CDiagContext_Extra::Print(const string& name, Uint8 value) 01893 { 01894 return Print(name, NStr::UInt8ToString(value)); 01895 } 01896 #endif 01897 01898 CDiagContext_Extra& 01899 CDiagContext_Extra::Print(const string& name, char value) 01900 { 01901 return Print(name, string(1,value)); 01902 } 01903 01904 CDiagContext_Extra& 01905 CDiagContext_Extra::Print(const string& name, signed char value) 01906 { 01907 return Print(name, string(1,value)); 01908 } 01909 01910 CDiagContext_Extra& 01911 CDiagContext_Extra::Print(const string& name, unsigned char value) 01912 { 01913 return Print(name, string(1,value)); 01914 } 01915 01916 CDiagContext_Extra& 01917 CDiagContext_Extra::Print(const string& name, double value) 01918 { 01919 return Print(name, NStr::DoubleToString(value)); 01920 } 01921 01922 CDiagContext_Extra& 01923 CDiagContext_Extra::Print(const string& name, bool value) 01924 { 01925 return Print(name, NStr::BoolToString(value)); 01926 } 01927 01928 CDiagContext_Extra& 01929 CDiagContext_Extra::Print(SDiagMessage::TExtraArgs& args) 01930 { 01931 if ( !m_Args ) { 01932 m_Args = new TExtraArgs; 01933 } 01934 m_Args->splice(m_Args->end(), args); 01935 return *this; 01936 } 01937 01938 01939 static const char* kExtraTypeArgName = "NCBIEXTRATYPE"; 01940 01941 CDiagContext_Extra& CDiagContext_Extra::SetType(const string& type) 01942 { 01943 m_Typed = true; 01944 Print(kExtraTypeArgName, type); 01945 return *this; 01946 } 01947 01948 01949 void CDiagContext::PrintRequestStart(const string& message) 01950 { 01951 x_PrintMessage(SDiagMessage::eEvent_RequestStart, message); 01952 } 01953 01954 01955 void CDiagContext::PrintRequestStop(void) 01956 { 01957 x_PrintMessage(SDiagMessage::eEvent_RequestStop, kEmptyStr); 01958 } 01959 01960 01961 EDiagAppState CDiagContext::GetGlobalAppState(void) const 01962 { 01963 CDiagLock lock(CDiagLock::eRead); 01964 return m_AppState; 01965 } 01966 01967 01968 EDiagAppState CDiagContext::GetAppState(void) const 01969 { 01970 // This checks thread's state first, then calls GetAppState if necessary. 01971 return GetRequestContext().GetAppState(); 01972 } 01973 01974 01975 void CDiagContext::SetGlobalAppState(EDiagAppState state) 01976 { 01977 CDiagLock lock(CDiagLock::eWrite); 01978 m_AppState = state; 01979 } 01980 01981 01982 void CDiagContext::SetAppState(EDiagAppState state) 01983 { 01984 CRequestContext& ctx = GetRequestContext(); 01985 switch ( state ) { 01986 case eDiagAppState_AppBegin: 01987 case eDiagAppState_AppRun: 01988 case eDiagAppState_AppEnd: 01989 { 01990 ctx.SetAppState(eDiagAppState_NotSet); 01991 CDiagLock lock(CDiagLock::eWrite); 01992 m_AppState = state; 01993 break; 01994 } 01995 case eDiagAppState_RequestBegin: 01996 case eDiagAppState_Request: 01997 case eDiagAppState_RequestEnd: 01998 ctx.SetAppState(state); 01999 break; 02000 default: 02001 ERR_POST_X(17, Warning << "Invalid EDiagAppState value"); 02002 } 02003 } 02004 02005 02006 void CDiagContext::SetAppState(EDiagAppState state, EPropertyMode mode) 02007 { 02008 switch ( mode ) { 02009 case eProp_Default: 02010 SetAppState(state); 02011 break; 02012 case eProp_Global: 02013 SetGlobalAppState(state); 02014 break; 02015 case eProp_Thread: 02016 GetRequestContext().SetAppState(state); 02017 break; 02018 } 02019 } 02020 02021 02022 NCBI_PARAM_DECL(string, Log, Session_Id); 02023 NCBI_PARAM_DEF_EX(string, Log, Session_Id, kEmptyStr, eParam_NoThread, 02024 NCBI_LOG_SESSION_ID); 02025 typedef NCBI_PARAM_TYPE(Log, Session_Id) TParamDefaultSessionId; 02026 02027 02028 const string& CDiagContext::GetDefaultSessionID(void) const 02029 { 02030 CDiagLock lock(CDiagLock::eRead); 02031 if ( !m_DefaultSessionId.get() ) { 02032 m_DefaultSessionId.reset(new CEncodedString); 02033 } 02034 if ( m_DefaultSessionId->IsEmpty() ) { 02035 m_DefaultSessionId->SetString(TParamDefaultSessionId::GetDefault()); 02036 } 02037 return m_DefaultSessionId->GetOriginalString(); 02038 } 02039 02040 02041 void CDiagContext::SetDefaultSessionID(const string& session_id) 02042 { 02043 CDiagLock lock(CDiagLock::eWrite); 02044 if ( !m_DefaultSessionId.get() ) { 02045 m_DefaultSessionId.reset(new CEncodedString); 02046 } 02047 m_DefaultSessionId->SetString(session_id); 02048 } 02049 02050 02051 const string& CDiagContext::GetSessionID(void) const 02052 { 02053 CRequestContext& rctx = GetRequestContext(); 02054 if ( rctx.IsSetExplicitSessionID() ) { 02055 return rctx.GetSessionID(); 02056 } 02057 return GetDefaultSessionID(); 02058 } 02059 02060 02061 const string& CDiagContext::GetEncodedSessionID(void) const 02062 { 02063 CRequestContext& rctx = GetRequestContext(); 02064 if ( rctx.IsSetExplicitSessionID() ) { 02065 return rctx.GetEncodedSessionID(); 02066 } 02067 GetDefaultSessionID(); // Make sure the default value is initialized. 02068 _ASSERT(m_DefaultSessionId.get()); 02069 return m_DefaultSessionId->GetEncodedString(); 02070 } 02071 02072 02073 NCBI_PARAM_DECL(string, Log, Client_Ip); 02074 NCBI_PARAM_DEF_EX(string, Log, Client_Ip, kEmptyStr, eParam_NoThread, 02075 NCBI_LOG_CLIENT_IP); 02076 typedef NCBI_PARAM_TYPE(Log, Client_Ip) TParamDefaultClientIp; 02077 02078 02079 const string CDiagContext::GetDefaultClientIP(void) 02080 { 02081 return TParamDefaultClientIp::GetDefault(); 02082 } 02083 02084 02085 void CDiagContext::SetDefaultClientIP(const string& client_ip) 02086 { 02087 TParamDefaultClientIp::SetDefault(client_ip); 02088 } 02089 02090 02091 const char* CDiagContext::kProperty_UserName = "user"; 02092 const char* CDiagContext::kProperty_HostName = "host"; 02093 const char* CDiagContext::kProperty_HostIP = "host_ip_addr"; 02094 const char* CDiagContext::kProperty_ClientIP = "client_ip"; 02095 const char* CDiagContext::kProperty_SessionID = "session_id"; 02096 const char* CDiagContext::kProperty_AppName = "app_name"; 02097 const char* CDiagContext::kProperty_AppState = "app_state"; 02098 const char* CDiagContext::kProperty_ExitSig = "exit_signal"; 02099 const char* CDiagContext::kProperty_ExitCode = "exit_code"; 02100 const char* CDiagContext::kProperty_ReqStatus = "request_status"; 02101 const char* CDiagContext::kProperty_ReqTime = "request_time"; 02102 const char* CDiagContext::kProperty_BytesRd = "bytes_rd"; 02103 const char* CDiagContext::kProperty_BytesWr = "bytes_wr"; 02104 02105 static const char* kDiagTimeFormat = "Y-M-DTh:m:s.l"; 02106 // Fixed fields' widths 02107 static const int kDiagW_PID = 5; 02108 static const int kDiagW_TID = 3; 02109 static const int kDiagW_RID = 4; 02110 static const int kDiagW_AppState = 2; 02111 static const int kDiagW_SN = 4; 02112 static const int kDiagW_UID = 16; 02113 static const int kDiagW_Host = 15; 02114 static const int kDiagW_Client = 15; 02115 static const int kDiagW_Session = 24; 02116 02117 static const char* kUnknown_Host = "UNK_HOST"; 02118 static const char* kUnknown_Client = "UNK_CLIENT"; 02119 static const char* kUnknown_Session = "UNK_SESSION"; 02120 static const char* kUnknown_App = "UNK_APP"; 02121 02122 02123 void CDiagContext::WriteStdPrefix(CNcbiOstream& ostr, 02124 const SDiagMessage& msg) const 02125 { 02126 string uid = GetStringUID(msg.GetUID()); 02127 const string& host = msg.GetHost(); 02128 const string& client = msg.GetClient(); 02129 const string& session = msg.GetSession(); 02130 const string& app = msg.GetAppName(); 02131 const char* app_state = s_AppStateToStr(msg.GetAppState()); 02132 02133 // Print common fields 02134 ostr << setfill('0') << setw(kDiagW_PID) << msg.m_PID << '/' 02135 << setw(kDiagW_TID) << msg.m_TID << '/' 02136 << setw(kDiagW_RID) << msg.m_RequestId 02137 << "/" 02138 << setfill(' ') << setw(kDiagW_AppState) << setiosflags(IOS_BASE::left) 02139 << app_state << resetiosflags(IOS_BASE::left) 02140 << ' ' << setw(0) << setfill(' ') << uid << ' ' 02141 << setfill('0') << setw(kDiagW_SN) << msg.m_ProcPost << '/' 02142 << setw(kDiagW_SN) << msg.m_ThrPost << ' ' 02143 << setw(0) << msg.GetTime().AsString(kDiagTimeFormat) << ' ' 02144 << setfill(' ') << setiosflags(IOS_BASE::left) 02145 << setw(kDiagW_Host) 02146 << (host.empty() ? kUnknown_Host : host.c_str()) << ' ' 02147 << setw(kDiagW_Client) 02148 << (client.empty() ? kUnknown_Client : client.c_str()) << ' ' 02149 << setw(kDiagW_Session) 02150 << (session.empty() ? kUnknown_Session : session.c_str()) << ' ' 02151 << resetiosflags(IOS_BASE::left) << setw(0) 02152 << (app.empty() ? kUnknown_App : app.c_str()) << ' '; 02153 } 02154 02155 02156 void RequestStopWatchTlsCleanup(CStopWatch* value, void* /*cleanup_data*/) 02157 { 02158 delete value; 02159 } 02160 02161 02162 void CDiagContext::x_StartRequest(void) 02163 { 02164 // Reset properties 02165 CRequestContext& ctx = GetRequestContext(); 02166 if ( ctx.IsRunning() ) { 02167 // The request is already running - 02168 // duplicate request start or missing request stop 02169 ERR_POST_ONCE( 02170 "Duplicate request-start or missing request-stop"); 02171 } 02172 02173 // Use the default client ip if no other value is set. 02174 if ( !ctx.IsSetClientIP() ) { 02175 string ip = GetDefaultClientIP(); 02176 if ( !ip.empty() ) { 02177 ctx.SetClientIP(ip); 02178 } 02179 } 02180 02181 ctx.StartRequest(); 02182 02183 // Print selected environment and registry values. 02184 CNcbiApplication* app = CNcbiApplication::Instance(); 02185 if ( !app ) return; 02186 string log_args = TLogEnvironment::GetDefault(); 02187 if ( !log_args.empty() ) { 02188 list<string> log_args_list; 02189 NStr::Split(log_args, " ", log_args_list); 02190 CDiagContext_Extra extra = GetDiagContext().Extra(); 02191 extra.Print("LogEnvironment", "true"); 02192 const CNcbiEnvironment& env = app->GetEnvironment(); 02193 ITERATE(list<string>, it, log_args_list) { 02194 const string& val = env.Get(*it); 02195 extra.Print(*it, val); 02196 } 02197 extra.Flush(); 02198 } 02199 log_args = TLogRegistry::GetDefault(); 02200 if ( !log_args.empty() ) { 02201 list<string> log_args_list; 02202 NStr::Split(log_args, " ", log_args_list); 02203 CDiagContext_Extra extra = GetDiagContext().Extra(); 02204 extra.Print("LogRegistry", "true"); 02205 const CNcbiRegistry& reg = app->GetConfig(); 02206 ITERATE(list<string>, it, log_args_list) { 02207 string section, name; 02208 NStr::SplitInTwo(*it, ":", section, name); 02209 const string& val = reg.Get(section, name); 02210 extra.Print(*it, val); 02211 } 02212 extra.Flush(); 02213 } 02214 } 02215 02216 02217 void CDiagContext::x_PrintMessage(SDiagMessage::EEventType event, 02218 const string& message) 02219 { 02220 if ( IsSetOldPostFormat() ) { 02221 return; 02222 } 02223 CNcbiOstrstream ostr; 02224 string prop; 02225 bool need_space = false; 02226 CRequestContext& ctx = GetRequestContext(); 02227 02228 switch ( event ) { 02229 case SDiagMessage::eEvent_Start: 02230 case SDiagMessage::eEvent_Extra: 02231 break; 02232 case SDiagMessage::eEvent_RequestStart: 02233 { 02234 x_StartRequest(); 02235 break; 02236 } 02237 case SDiagMessage::eEvent_Stop: 02238 ostr << NStr::IntToString(GetExitCode()) 02239 << " " << m_StopWatch->AsString(); 02240 if (GetExitSignal() != 0) { 02241 ostr << " SIG=" << GetExitSignal(); 02242 } 02243 need_space = true; 02244 break; 02245 case SDiagMessage::eEvent_RequestStop: 02246 { 02247 if ( !ctx.IsRunning() ) { 02248 // The request is not running - 02249 // duplicate request stop or missing request start 02250 ERR_POST_ONCE( 02251 "Duplicate request-stop or missing request-start"); 02252 } 02253 ostr << ctx.GetRequestStatus() << " " 02254 << ctx.GetRequestTimer().AsString() << " " 02255 << ctx.GetBytesRd() << " " 02256 << ctx.GetBytesWr(); 02257 need_space = true; 02258 break; 02259 } 02260 default: 02261 return; // Prevent warning about other event types. 02262 } 02263 if ( !message.empty() ) { 02264 if (need_space) { 02265 ostr << " "; 02266 } 02267 ostr << message; 02268 } 02269 SDiagMessage mess(eDiag_Info, 02270 ostr.str(), size_t(ostr.pcount()), 02271 0, 0, // file, line 02272 CNcbiDiag::ForceImportantFlags(kApplogDiagPostFlags), 02273 NULL, 02274 0, 0, // err code/subcode 02275 NULL, 02276 0, 0, 0); // module/class/function 02277 mess.m_Event = event; 02278 CDiagBuffer::DiagHandler(mess); 02279 ostr.rdbuf()->freeze(false); 02280 // Now it's safe to reset the request context 02281 if (event == SDiagMessage::eEvent_RequestStop) { 02282 ctx.StopRequest(); 02283 } 02284 } 02285 02286 02287 bool CDiagContext::IsSetOldPostFormat(void) 02288 { 02289 return TOldPostFormatParam::GetDefault(); 02290 } 02291 02292 02293 void CDiagContext::SetOldPostFormat(bool value) 02294 { 02295 TOldPostFormatParam::SetDefault(value); 02296 } 02297 02298 02299 bool CDiagContext::IsUsingSystemThreadId(void) 02300 { 02301 return TPrintSystemTID::GetDefault(); 02302 } 02303 02304 02305 void CDiagContext::UseSystemThreadId(bool value) 02306 { 02307 TPrintSystemTID::SetDefault(value); 02308 } 02309 02310 02311 void CDiagContext::InitMessages(size_t max_size) 02312 { 02313 if ( !m_Messages.get() ) { 02314 m_Messages.reset(new TMessages); 02315 } 02316 m_MaxMessages = max_size; 02317 } 02318 02319 02320 void CDiagContext::PushMessage(const SDiagMessage& message) 02321 { 02322 if ( m_Messages.get() && m_Messages->size() < m_MaxMessages) { 02323 m_Messages->push_back(message); 02324 } 02325 } 02326 02327 02328 void CDiagContext::FlushMessages(CDiagHandler& handler) 02329 { 02330 if ( !m_Messages.get() || m_Messages->empty() ) { 02331 return; 02332 } 02333 CTeeDiagHandler* tee = dynamic_cast<CTeeDiagHandler*>(&handler); 02334 if (tee && !tee->GetOriginalHandler()) { 02335 // Tee over STDERR - flushing will create duplicate messages 02336 return; 02337 } 02338 auto_ptr<TMessages> tmp(m_Messages.release()); 02339 //ERR_POST_X(1, Message << "***** BEGIN COLLECTED MESSAGES *****"); 02340 NON_CONST_ITERATE(TMessages, it, *tmp.get()) { 02341 it->m_NoTee = true; // Do not tee duplicate messages to console. 02342 handler.Post(*it); 02343 if (it->m_Flags & eDPF_IsConsole) { 02344 handler.PostToConsole(*it); 02345 } 02346 } 02347 //ERR_POST_X(2, Message << "***** END COLLECTED MESSAGES *****"); 02348 m_Messages.reset(tmp.release()); 02349 } 02350 02351 02352 void CDiagContext::DiscardMessages(void) 02353 { 02354 m_Messages.reset(); 02355 } 02356 02357 02358 // Diagnostics setup 02359 02360 static const char* kLogName_None = "NONE"; 02361 static const char* kLogName_Unknown = "UNKNOWN"; 02362 static const char* kLogName_Stdout = "STDOUT"; 02363 static const char* kLogName_Stderr = "STDERR"; 02364 static const char* kLogName_Stream = "STREAM"; 02365 static const char* kLogName_Memory = "MEMORY"; 02366 02367 string GetDefaultLogLocation(CNcbiApplication& app) 02368 { 02369 static const char* kToolkitRcPath = "/etc/toolkitrc"; 02370 static const char* kWebDirToPort = "Web_dir_to_port"; 02371 02372 string log_path = "/log/"; 02373 02374 string exe_path = CFile(app.GetProgramExecutablePath()).GetDir(); 02375 CNcbiIfstream is(kToolkitRcPath, ios::binary); 02376 CNcbiRegistry reg(is); 02377 list<string> entries; 02378 reg.EnumerateEntries(kWebDirToPort, &entries); 02379 size_t min_pos = exe_path.length(); 02380 string web_dir; 02381 // Find the first dir name corresponding to one of the entries 02382 ITERATE(list<string>, it, entries) { 02383 if (!it->empty() && (*it)[0] != '/') { 02384 // not an absolute path 02385 string mask = "/" + *it; 02386 if (mask[mask.length() - 1] != '/') { 02387 mask += "/"; 02388 } 02389 size_t pos = exe_path.find(mask); 02390 if (pos < min_pos) { 02391 min_pos = pos; 02392 web_dir = *it; 02393 } 02394 } 02395 else { 02396 // absolute path 02397 if (exe_path.substr(0, it->length()) == *it) { 02398 web_dir = *it; 02399 break; 02400 } 02401 } 02402 } 02403 if ( !web_dir.empty() ) { 02404 return log_path + reg.GetString(kWebDirToPort, web_dir, kEmptyStr); 02405 } 02406 // Could not find a valid web-dir entry, use /log/port or empty string 02407 // to try /log/srv later. 02408 const TXChar* port = NcbiSys_getenv(_TX("SERVER_PORT")); 02409 return port ? log_path + string(_T_CSTRING(port)) : kEmptyStr; 02410 } 02411 02412 02413 bool CDiagContext::GetLogTruncate(void) 02414 { 02415 return TLogTruncateParam::GetDefault(); 02416 } 02417 02418 02419 void CDiagContext::SetLogTruncate(bool value) 02420 { 02421 TLogTruncateParam::SetDefault(value); 02422 } 02423 02424 02425 ios::openmode s_GetLogOpenMode(void) 02426 { 02427 return ios::out | 02428 (CDiagContext::GetLogTruncate() ? ios::trunc : ios::app); 02429 } 02430 02431 02432 bool OpenLogFileFromConfig(CNcbiRegistry& config, string* new_name) 02433 { 02434 string logname = config.GetString("LOG", "File", kEmptyStr); 02435 // In eDS_User mode do not use config unless IgnoreEnvArg 02436 // is set to true. 02437 if ( !logname.empty() ) { 02438 if ( TLogNoCreate::GetDefault() && !CDirEntry(logname).Exists() ) { 02439 return false; 02440 } 02441 if ( new_name ) { 02442 *new_name = logname; 02443 } 02444 return SetLogFile(logname, eDiagFile_All, true); 02445 } 02446 return false; 02447 } 02448 02449 02450 void CDiagContext::SetUseRootLog(void) 02451 { 02452 if (s_FinishedSetupDiag) { 02453 return; 02454 } 02455 s_UseRootLog = true; 02456 // Try to switch to /log/ if available. 02457 SetupDiag(); 02458 } 02459 02460 02461 void CDiagContext::x_FinalizeSetupDiag(void) 02462 { 02463 _ASSERT(!s_FinishedSetupDiag); 02464 s_FinishedSetupDiag = true; 02465 } 02466 02467 02468 // Helper function to set log file with forced splitting. 02469 bool SetApplogFile(const string& file_name) 02470 { 02471 bool old_split = GetSplitLogFile(); 02472 SetSplitLogFile(true); 02473 bool res = SetLogFile(file_name); 02474 if ( !res ) { 02475 SetSplitLogFile(old_split); 02476 } 02477 return res; 02478 } 02479 02480 02481 void CDiagContext::SetupDiag(EAppDiagStream ds, 02482 CNcbiRegistry* config, 02483 EDiagCollectMessages collect) 02484 { 02485 CDiagContext& ctx = GetDiagContext(); 02486 // Initialize message collecting 02487 if (collect == eDCM_Init) { 02488 ctx.InitMessages(); 02489 } 02490 else if (collect == eDCM_InitNoLimit) { 02491 ctx.InitMessages(size_t(-1)); 02492 } 02493 02494 bool log_switched = false; 02495 bool name_changed = true; // By default consider it's a new name 02496 bool to_applog = false; 02497 bool try_root_log_first = false; 02498 if ( config ) { 02499 try_root_log_first = config->GetBool("LOG", "TryRootLogFirst", false) 02500 && (ds == eDS_ToStdlog || ds == eDS_Default); 02501 bool force_config = config->GetBool("LOG", "IgnoreEnvArg", false); 02502 if ( force_config ) { 02503 try_root_log_first = false; 02504 } 02505 if (force_config || (ds != eDS_User && !try_root_log_first)) { 02506 log_switched = OpenLogFileFromConfig(*config, NULL); 02507 } 02508 } 02509 02510 if ( !log_switched ) { 02511 string old_log_name; 02512 CDiagHandler* handler = GetDiagHandler(); 02513 if ( handler ) { 02514 old_log_name = handler->GetLogName(); 02515 } 02516 CNcbiApplication* app = CNcbiApplication::Instance(); 02517 02518 switch ( ds ) { 02519 case eDS_ToStdout: 02520 if (old_log_name != kLogName_Stdout) { 02521 SetDiagHandler(new CStreamDiagHandler(&cout, 02522 true, kLogName_Stdout), true); 02523 log_switched = true; 02524 } 02525 break; 02526 case eDS_ToStderr: 02527 if (old_log_name != kLogName_Stderr) { 02528 SetDiagHandler(new CStreamDiagHandler(&cerr, 02529 true, kLogName_Stderr), true); 02530 log_switched = true; 02531 } 02532 break; 02533 case eDS_ToMemory: 02534 if (old_log_name != kLogName_Memory) { 02535 ctx.InitMessages(size_t(-1)); 02536 SetDiagStream(0, false, 0, 0, kLogName_Memory); 02537 log_switched = true; 02538 } 02539 collect = eDCM_NoChange; // prevent flushing to memory 02540 break; 02541 case eDS_Disable: 02542 if (old_log_name != kLogName_None) { 02543 SetDiagStream(0, false, 0, 0, kLogName_None); 02544 log_switched = true; 02545 } 02546 break; 02547 case eDS_User: 02548 // log_switched = true; 02549 collect = eDCM_Discard; 02550 break; 02551 case eDS_AppSpecific: 02552 if ( app ) { 02553 app->SetupDiag_AppSpecific(); /* NCBI_FAKE_WARNING */ 02554 } 02555 collect = eDCM_Discard; 02556 break; 02557 case eDS_ToSyslog: 02558 if (old_log_name != CSysLog::kLogName_Syslog) { 02559 try { 02560 SetDiagHandler(new CSysLog); 02561 log_switched = true; 02562 break; 02563 } catch (...) { 02564 // fall through 02565 } 02566 } else { 02567 break; 02568 } 02569 case eDS_ToStdlog: 02570 case eDS_Default: 02571 { 02572 string log_base = app ? 02573 app->GetProgramExecutablePath() : kEmptyStr; 02574 if ( !log_base.empty() ) { 02575 log_base = CFile(log_base).GetBase() + ".log"; 02576 string log_name; 02577 if ( s_UseRootLog ) { 02578 string def_log_dir = GetDefaultLogLocation(*app); 02579 // Try /log/<port> 02580 if ( !def_log_dir.empty() ) { 02581 log_name = CFile::ConcatPath(def_log_dir, log_base); 02582 if ( SetApplogFile(log_name) ) { 02583 log_switched = true; 02584 name_changed = log_name != old_log_name; 02585 to_applog = true; 02586 break; 02587 } 02588 } 02589 // Try /log/srv if port is unknown or not writable 02590 log_name = CFile::ConcatPath("/log/srv", log_base); 02591 if ( SetApplogFile(log_name) ) { 02592 log_switched = true; 02593 name_changed = log_name != old_log_name; 02594 to_applog = true; 02595 break; 02596 } 02597 if (try_root_log_first && 02598 OpenLogFileFromConfig(*config, &log_name)) { 02599 log_switched = true; 02600 name_changed = log_name != old_log_name; 02601 break; 02602 } 02603 // Try to switch to /log/fallback/ 02604 log_name = CFile::ConcatPath("/log/fallback/", log_base); 02605 if ( SetApplogFile(log_name) ) { 02606 log_switched = true; 02607 name_changed = log_name != old_log_name; 02608 to_applog = true; 02609 break; 02610 } 02611 } 02612 // Try cwd/ for eDS_ToStdlog only 02613 if (ds == eDS_ToStdlog) { 02614 log_name = CFile::ConcatPath(".", log_base); 02615 log_switched = SetLogFile(log_name, eDiagFile_All); 02616 name_changed = log_name != old_log_name; 02617 } 02618 if ( !log_switched ) { 02619 ERR_POST_X(3, Info << "Failed to set log file to " + 02620 CFile::NormalizePath(log_name)); 02621 } 02622 } 02623 else { 02624 static const char* kDefaultFallback = "/log/fallback/UNKNOWN"; 02625 // Try to switch to /log/fallback/UNKNOWN 02626 if ( s_UseRootLog ) { 02627 if ( SetApplogFile(kDefaultFallback) ) { 02628 log_switched = true; 02629 name_changed = kDefaultFallback != old_log_name; 02630 to_applog = true; 02631 } 02632 else { 02633 ERR_POST_X_ONCE(4, Info << 02634 "Failed to set log file to " << 02635 CFile::NormalizePath(kDefaultFallback)); 02636 } 02637 } 02638 } 02639 if (!log_switched && old_log_name != kLogName_Stderr) { 02640 SetDiagHandler(new CStreamDiagHandler(&cerr, 02641 true, kLogName_Stderr), true); 02642 log_switched = true; 02643 } 02644 break; 02645 } 02646 default: 02647 ERR_POST_X(5, Warning << "Unknown EAppDiagStream value"); 02648 _ASSERT(0); 02649 break; 02650 } 02651 } 02652 02653 // Unlock severity level 02654 SetApplogSeverityLocked(false); 02655 if ( to_applog ) { 02656 ctx.SetOldPostFormat(false); 02657 SetDiagPostFlag(eDPF_PreMergeLines); 02658 SetDiagPostFlag(eDPF_MergeLines); 02659 s_MergeLinesSetBySetupDiag = true; 02660 TLogSizeLimitParam::SetDefault(0); // No log size limit 02661 SetDiagPostLevel(eDiag_Warning); 02662 // Lock severity level 02663 SetApplogSeverityLocked(true); 02664 } 02665 else { 02666 if ( s_MergeLinesSetBySetupDiag ) { 02667 UnsetDiagPostFlag(eDPF_PreMergeLines); 02668 UnsetDiagPostFlag(eDPF_MergeLines); 02669 } 02670 // Disable throttling 02671 ctx.SetLogRate_Limit(eLogRate_App, CRequestRateControl::kNoLimit); 02672 ctx.SetLogRate_Limit(eLogRate_Err, CRequestRateControl::kNoLimit); 02673 ctx.SetLogRate_Limit(eLogRate_Trace, CRequestRateControl::kNoLimit); 02674 } 02675 log_switched &= name_changed; 02676 CDiagHandler* handler = GetDiagHandler(); 02677 if (collect == eDCM_Flush) { 02678 // Flush and discard 02679 if ( log_switched && handler ) { 02680 ctx.FlushMessages(*handler); 02681 } 02682 collect = eDCM_Discard; 02683 } 02684 else if (collect == eDCM_NoChange) { 02685 // Flush but don't discard 02686 if ( log_switched && handler ) { 02687 ctx.FlushMessages(*handler); 02688 } 02689 } 02690 if (collect == eDCM_Discard) { 02691 ctx.DiscardMessages(); 02692 } 02693 02694 // Refresh rate controls 02695 ctx.ResetLogRates(); 02696 } 02697 02698 02699 CDiagContext::TCount CDiagContext::GetProcessPostNumber(EPostNumberIncrement inc) 02700 { 02701 static CAtomicCounter s_ProcessPostCount; 02702 return (TCount)(inc == ePostNumber_Increment ? 02703 s_ProcessPostCount.Add(1) : s_ProcessPostCount.Get()); 02704 } 02705 02706 02707 bool CDiagContext::IsUsingRootLog(void) 02708 { 02709 return GetLogFile().substr(0, 5) == "/log/"; 02710 } 02711 02712 02713 CDiagContext& GetDiagContext(void) 02714 { 02715 // Make the context live longer than other diag safe-statics 02716 static CSafeStaticPtr<CDiagContext> s_DiagContext(0, 02717 CSafeStaticLifeSpan(CSafeStaticLifeSpan::eLifeSpan_Long)); 02718 02719 return s_DiagContext.Get(); 02720 } 02721 02722 02723 /////////////////////////////////////////////////////// 02724 // CDiagBuffer:: 02725 02726 #if defined(NDEBUG) 02727 EDiagSev CDiagBuffer::sm_PostSeverity = eDiag_Error; 02728 #else 02729 EDiagSev CDiagBuffer::sm_PostSeverity = eDiag_Warning; 02730 #endif /* else!NDEBUG */ 02731 02732 EDiagSevChange CDiagBuffer::sm_PostSeverityChange = eDiagSC_Unknown; 02733 // to be set on first request 02734 02735 static const TDiagPostFlags s_OldDefaultPostFlags = 02736 eDPF_Prefix | eDPF_Severity | eDPF_ErrorID | 02737 eDPF_ErrCodeMessage | eDPF_ErrCodeExplanation | 02738 eDPF_ErrCodeUseSeverity | eDPF_AtomicWrite; 02739 static const TDiagPostFlags s_NewDefaultPostFlags = 02740 s_OldDefaultPostFlags | 02741 #if defined(NCBI_THREADS) 02742 eDPF_TID | eDPF_SerialNo_Thread | 02743 #endif 02744 eDPF_PID | eDPF_SerialNo | eDPF_AtomicWrite; 02745 static TDiagPostFlags s_PostFlags = 0; 02746 static bool s_DiagPostFlagsInitialized = false; 02747 02748 inline 02749 TDiagPostFlags& CDiagBuffer::sx_GetPostFlags(void) 02750 { 02751 if (!s_DiagPostFlagsInitialized) { 02752 s_PostFlags = TOldPostFormatParam::GetDefault() ? 02753 s_OldDefaultPostFlags : s_NewDefaultPostFlags; 02754 s_DiagPostFlagsInitialized = true; 02755 } 02756 return s_PostFlags; 02757 } 02758 02759 02760 TDiagPostFlags& CDiagBuffer::s_GetPostFlags(void) 02761 { 02762 return sx_GetPostFlags(); 02763 } 02764 02765 02766 TDiagPostFlags CDiagBuffer::sm_TraceFlags = eDPF_Trace; 02767 02768 bool CDiagBuffer::sm_IgnoreToDie = false; 02769 EDiagSev CDiagBuffer::sm_DieSeverity = eDiag_Fatal; 02770 02771 EDiagTrace CDiagBuffer::sm_TraceDefault = eDT_Default; 02772 bool CDiagBuffer::sm_TraceEnabled; // to be set on first request 02773 02774 02775 const char* CDiagBuffer::sm_SeverityName[eDiag_Trace+1] = { 02776 "Info", "Warning", "Error", "Critical", "Fatal", "Trace" }; 02777 02778 02779 void* InitDiagHandler(void) 02780 { 02781 static bool s_DiagInitialized = false; 02782 if ( !s_DiagInitialized ) { 02783 CDiagContext::SetupDiag(eDS_Default, 0, eDCM_Init); 02784 s_DiagInitialized = true; 02785 } 02786 return 0; 02787 } 02788 02789 02790 // MT-safe initialization of the default handler 02791 static CDiagHandler* s_CreateDefaultDiagHandler(void); 02792 02793 02794 // Use s_DefaultHandler only for purposes of comparison, as installing 02795 // another handler will normally delete it. 02796 CDiagHandler* s_DefaultHandler = s_CreateDefaultDiagHandler(); 02797 CDiagHandler* CDiagBuffer::sm_Handler = s_DefaultHandler; 02798 bool CDiagBuffer::sm_CanDeleteHandler = true; 02799 CDiagErrCodeInfo* CDiagBuffer::sm_ErrCodeInfo = 0; 02800 bool CDiagBuffer::sm_CanDeleteErrCodeInfo = false; 02801 02802 // For initialization only 02803 void* s_DiagHandlerInitializer = InitDiagHandler(); 02804 02805 02806 static CDiagHandler* s_CreateDefaultDiagHandler(void) 02807 { 02808 CDiagLock lock(CDiagLock::eWrite); 02809 static bool s_DefaultDiagHandlerInitialized = false; 02810 if ( !s_DefaultDiagHandlerInitialized ) { 02811 s_DefaultDiagHandlerInitialized = true; 02812 CDiagHandler* handler = new CStreamDiagHandler(&NcbiCerr, true, kLogName_Stderr); 02813 if ( TTeeToStderr::GetDefault() ) { 02814 // Need to tee? 02815 handler = new CTeeDiagHandler(handler, true); 02816 } 02817 return handler; 02818 } 02819 return s_DefaultHandler; 02820 } 02821 02822 02823 02824 // Note: Intel Thread Checker detects a memory leak at the line: 02825 // m_Stream(new CNcbiOstrstream) below 02826 // This is not a fault of the toolkit code as soon as a code like: 02827 // int main() { 02828 // ostrstream * s = new ostrstream; 02829 // delete s; 02830 // return 0; 02831 // } 02832 // will also report memory leaks. 02833 // Test environment: 02834 // - Intel Thread Checker for Linux 3.1 02835 // - gcc 4.0.1, gcc 4.1.2, icc 10.1 02836 // - Linux64 02837 CDiagBuffer::CDiagBuffer(void) 02838 : m_Stream(new CNcbiOstrstream), 02839 m_InitialStreamFlags(m_Stream->flags()), 02840 m_InUse(false) 02841 { 02842 m_Diag = 0; 02843 } 02844 02845 CDiagBuffer::~CDiagBuffer(void) 02846 { 02847 #if (_DEBUG > 1) 02848 if (m_Diag || m_Stream->pcount()) 02849 Abort(); 02850 #endif 02851 delete m_Stream; 02852 m_Stream = 0; 02853 } 02854 02855 void CDiagBuffer::DiagHandler(SDiagMessage& mess) 02856 { 02857 bool is_console = (mess.m_Flags & eDPF_IsConsole) > 0; 02858 bool applog = (mess.m_Flags & eDPF_AppLog) > 0; 02859 bool is_printable = applog || SeverityPrintable(mess.m_Severity); 02860 if (!is_console && !is_printable) { 02861 return; 02862 } 02863 if ( CDiagBuffer::sm_Handler ) { 02864 CDiagLock lock(CDiagLock::eRead); 02865 if ( CDiagBuffer::sm_Handler ) { 02866 // The mutex must be locked before approving. 02867 CDiagBuffer& diag_buf = GetDiagBuffer(); 02868 bool show_warning = false; 02869 CDiagContext& ctx = GetDiagContext(); 02870 mess.m_Prefix = diag_buf.m_PostPrefix.empty() ? 02871 0 : diag_buf.m_PostPrefix.c_str(); 02872 if (is_console) { 02873 // No throttling for console 02874 CDiagBuffer::sm_Handler->PostToConsole(mess); 02875 if ( !is_printable ) { 02876 return; 02877 } 02878 } 02879 if ( ctx.ApproveMessage(mess, &show_warning) ) { 02880 CDiagBuffer::sm_Handler->Post(mess); 02881 } 02882 else if ( show_warning ) { 02883 // Substitute the original message with the error. 02884 // ERR_POST cannot be used here since nested posts 02885 // are blocked. Have to create the message manually. 02886 string limit_name = "error"; 02887 CDiagContext::ELogRate_Type limit_type = 02888 CDiagContext::eLogRate_Err; 02889 if ( IsSetDiagPostFlag(eDPF_AppLog, mess.m_Flags) ) { 02890 limit_name = "applog"; 02891 limit_type = CDiagContext::eLogRate_App; 02892 } 02893 else if (mess.m_Severity == eDiag_Info || 02894 mess.m_Severity == eDiag_Trace) { 02895 limit_name = "trace"; 02896 limit_type = CDiagContext::eLogRate_Trace; 02897 } 02898 string txt = "Maximum logging rate for " + limit_name + " (" 02899 + NStr::UIntToString(ctx.GetLogRate_Limit(limit_type)) 02900 + " messages per " 02901 + NStr::UIntToString(ctx.GetLogRate_Period(limit_type)) 02902 + " sec) exceeded, suspending the output."; 02903 const CNcbiDiag diag(DIAG_COMPILE_INFO); 02904 SDiagMessage err_msg(eDiag_Error, 02905 txt.c_str(), txt.length(), 02906 diag.GetFile(), 02907 diag.GetLine(), 02908 diag.GetPostFlags(), 02909 NULL, 02910 err_code_x::eErrCodeX_Corelib_Diag, // Error code 02911 23, // Err subcode 02912 NULL, 02913 diag.GetModule(), 02914 diag.GetClass(), 02915 diag.GetFunction()); 02916 CDiagBuffer::sm_Handler->Post(err_msg); 02917 return; 02918 } 02919 } 02920 } 02921 GetDiagContext().PushMessage(mess); 02922 } 02923 02924 02925 inline 02926 bool CDiagBuffer::SeverityDisabled(EDiagSev sev) 02927 { 02928 CDiagContextThreadData& thr_data = 02929 CDiagContextThreadData::GetThreadData(); 02930 CDiagCollectGuard* guard = thr_data.GetCollectGuard(); 02931 EDiagSev post_sev = AdjustApplogPrintableSeverity(sm_PostSeverity); 02932 bool allow_trace = GetTraceEnabled(); 02933 if ( guard ) { 02934 post_sev = guard->GetCollectSeverity(); 02935 allow_trace = post_sev == eDiag_Trace; 02936 } 02937 if (sev == eDiag_Trace && !allow_trace) { 02938 return true; // trace is disabled 02939 } 02940 if (post_sev == eDiag_Trace && allow_trace) { 02941 return false; // everything is enabled 02942 } 02943 return (sev < post_sev) && (sev < sm_DieSeverity || sm_IgnoreToDie); 02944 } 02945 02946 02947 inline 02948 bool CDiagBuffer::SeverityPrintable(EDiagSev sev) 02949 { 02950 CDiagContextThreadData& thr_data = 02951 CDiagContextThreadData::GetThreadData(); 02952 CDiagCollectGuard* guard = thr_data.GetCollectGuard(); 02953 EDiagSev post_sev = AdjustApplogPrintableSeverity(sm_PostSeverity); 02954 bool allow_trace = GetTraceEnabled(); 02955 if ( guard ) { 02956 post_sev = AdjustApplogPrintableSeverity(guard->GetPrintSeverity()); 02957 allow_trace = post_sev == eDiag_Trace; 02958 } 02959 if (sev == eDiag_Trace && !allow_trace) { 02960 return false; // trace is disabled 02961 } 02962 if (post_sev == eDiag_Trace && allow_trace) { 02963 return true; // everything is enabled 02964 } 02965 return !((sev < post_sev) && (sev < sm_DieSeverity || sm_IgnoreToDie)); 02966 } 02967 02968 02969 bool CDiagBuffer::SetDiag(const CNcbiDiag& diag) 02970 { 02971 if ( m_InUse || !m_Stream ) { 02972 return false; 02973 } 02974 02975 // Check severity level change status 02976 if ( sm_PostSeverityChange == eDiagSC_Unknown ) { 02977 GetSeverityChangeEnabledFirstTime(); 02978 } 02979 02980 EDiagSev sev = diag.GetSeverity(); 02981 bool is_console = (diag.GetPostFlags() & eDPF_IsConsole) > 0; 02982 if (!is_console && SeverityDisabled(sev)) { 02983 return false; 02984 } 02985 02986 if (m_Diag != &diag) { 02987 if ( m_Stream->pcount() ) { 02988 Flush(); 02989 } 02990 m_Diag = &diag; 02991 } 02992 02993 return true; 02994 } 02995 02996 02997 class CRecursionGuard 02998 { 02999 public: 03000 CRecursionGuard(bool& flag) : m_Flag(flag) { m_Flag = true; } 03001 ~CRecursionGuard(void) { m_Flag = false; } 03002 private: 03003 bool& m_Flag; 03004 }; 03005 03006 03007 void CDiagBuffer::Flush(void) 03008 { 03009 if ( m_InUse ) { 03010 return; 03011 } 03012 CRecursionGuard guard(m_InUse); 03013 03014 EDiagSev sev = m_Diag->GetSeverity(); 03015 bool is_console = (m_Diag->GetPostFlags() & eDPF_IsConsole) != 0; 03016 bool is_disabled = SeverityDisabled(sev); 03017 // Do nothing if diag severity is lower than allowed 03018 if ((!is_console && is_disabled) || !m_Stream->pcount()) { 03019 return; 03020 } 03021 03022 const char* message = m_Stream->str(); 03023 size_t size = size_t(m_Stream->pcount()); 03024 m_Stream->rdbuf()->freeze(false); 03025 03026 TDiagPostFlags flags = m_Diag->GetPostFlags(); 03027 if (sev == eDiag_Trace) { 03028 flags |= sm_TraceFlags; 03029 } else if (sev == eDiag_Fatal) { 03030 // normally only happens once, so might as well pull everything 03031 // in for the record... 03032 flags |= sm_TraceFlags | eDPF_Trace; 03033 } 03034 03035 if ( m_Diag->CheckFilters() ) { 03036 string dest; 03037 if (IsSetDiagPostFlag(eDPF_PreMergeLines, flags)) { 03038 string src(message, size); 03039 NStr::Replace(NStr::Replace(src,"\r",""),"\n",";", dest); 03040 message = dest.c_str(); 03041 size = dest.length(); 03042 } 03043 SDiagMessage mess(sev, message, size, 03044 m_Diag->GetFile(), 03045 m_Diag->GetLine(), 03046 flags, 03047 NULL, 03048 m_Diag->GetErrorCode(), 03049 m_Diag->GetErrorSubCode(), 03050 NULL, 03051 m_Diag->GetModule(), 03052 m_Diag->GetClass(), 03053 m_Diag->GetFunction()); 03054 PrintMessage(mess, *m_Diag); 03055 } 03056 03057 #if defined(NCBI_COMPILER_KCC) 03058 // KCC's implementation of "freeze(false)" makes the ostrstream buffer 03059 // stuck. We need to replace the frozen stream with the new one. 03060 delete ostr; 03061 m_Stream = new CNcbiOstrstream; 03062 #else 03063 // reset flags to initial value 03064 m_Stream->flags(m_InitialStreamFlags); 03065 #endif 03066 03067 Reset(*m_Diag); 03068 03069 if (sev >= sm_DieSeverity && sev != eDiag_Trace && !sm_IgnoreToDie) { 03070 m_Diag = 0; 03071 03072 #ifdef NCBI_COMPILER_MSVC 03073 if ( TAssertOnAbortParam::GetDefault() ) { 03074 int old_mode = _set_error_mode(_OUT_TO_MSGBOX); 03075 _ASSERT(false); // Show assertion dialog 03076 _set_error_mode(old_mode); 03077 } 03078 else { 03079 Abort(); 03080 } 03081 #else // NCBI_COMPILER_MSVC 03082 Abort(); 03083 #endif // NCBI_COMPILER_MSVC 03084 } 03085 } 03086 03087 03088 void CDiagBuffer::PrintMessage(SDiagMessage& mess, const CNcbiDiag& diag) 03089 { 03090 EDiagSev sev = diag.GetSeverity(); 03091 if (!SeverityPrintable(sev)) { 03092 CDiagContextThreadData& thr_data = 03093 CDiagContextThreadData::GetThreadData(); 03094 bool can_collect = thr_data.GetCollectGuard() != NULL; 03095 bool is_console = (diag.GetPostFlags() & eDPF_IsConsole) != 0; 03096 bool is_disabled = SeverityDisabled(sev); 03097 if (!is_disabled || (is_console && can_collect)) { 03098 thr_data.CollectDiagMessage(mess); 03099 Reset(diag); 03100 // The message has been collected, don't print to 03101 // the console now. 03102 return; 03103 } 03104 } 03105 DiagHandler(mess); 03106 } 03107 03108 03109 bool CDiagBuffer::GetTraceEnabledFirstTime(void) 03110 { 03111 CDiagLock lock(CDiagLock::eWrite); 03112 const TXChar* str = NcbiSys_getenv(_T_XCSTRING(DIAG_TRACE)); 03113 if (str && *str) { 03114 sm_TraceDefault = eDT_Enable; 03115 } else { 03116 sm_TraceDefault = eDT_Disable; 03117 } 03118 sm_TraceEnabled = (sm_TraceDefault == eDT_Enable); 03119 return sm_TraceEnabled; 03120 } 03121 03122 03123 bool CDiagBuffer::GetSeverityChangeEnabledFirstTime(void) 03124 { 03125 CDiagLock lock(CDiagLock::eWrite); 03126 if ( sm_PostSeverityChange != eDiagSC_Unknown ) { 03127 return sm_PostSeverityChange == eDiagSC_Enable; 03128 } 03129 const TXChar* str = NcbiSys_getenv(_T_XCSTRING(DIAG_POST_LEVEL)); 03130 EDiagSev sev; 03131 if (str && *str && CNcbiDiag::StrToSeverityLevel(_T_CSTRING(str), sev)) { 03132 SetDiagFixedPostLevel(sev); 03133 } else { 03134 sm_PostSeverityChange = eDiagSC_Enable; 03135 } 03136 return sm_PostSeverityChange == eDiagSC_Enable; 03137 } 03138 03139 03140 void CDiagBuffer::UpdatePrefix(void) 03141 { 03142 m_PostPrefix.erase(); 03143 ITERATE(TPrefixList, prefix, m_PrefixList) { 03144 if (prefix != m_PrefixList.begin()) { 03145 m_PostPrefix += "::"; 03146 } 03147 m_PostPrefix += *prefix; 03148 } 03149 } 03150 03151 03152 /////////////////////////////////////////////////////// 03153 // CDiagMessage:: 03154 03155 03156 SDiagMessage::SDiagMessage(EDiagSev severity, 03157 const char* buf, size_t len, 03158 const char* file, size_t line, 03159 TDiagPostFlags flags, const char* prefix, 03160 int err_code, int err_subcode, 03161 const char* err_text, 03162 const char* module, 03163 const char* nclass, 03164 const char* function) 03165 : m_Event(eEvent_Start), 03166 m_TypedExtra(false), 03167 m_NoTee(false), 03168 m_Data(0), 03169 m_Format(eFormat_Auto) 03170 { 03171 m_Severity = severity; 03172 m_Buffer = buf; 03173 m_BufferLen = len; 03174 m_File = file; 03175 m_Line = line; 03176 m_Flags = flags; 03177 m_Prefix = prefix; 03178 m_ErrCode = err_code; 03179 m_ErrSubCode = err_subcode; 03180 m_ErrText = err_text; 03181 m_Module = module; 03182 m_Class = nclass; 03183 m_Function = function; 03184 03185 CDiagContextThreadData& thr_data = 03186 CDiagContextThreadData::GetThreadData(); 03187 CRequestContext& rq_ctx = thr_data.GetRequestContext(); 03188 m_PID = CDiagContext::GetPID(); 03189 m_TID = thr_data.GetTID(); 03190 if ( rq_ctx.GetAutoIncRequestIDOnPost() ) { 03191 rq_ctx.SetRequestID(); 03192 } 03193 m_RequestId = rq_ctx.GetRequestID(); 03194 m_ProcPost = CDiagContext::GetProcessPostNumber(ePostNumber_Increment); 03195 m_ThrPost = thr_data.GetThreadPostNumber(ePostNumber_Increment); 03196 } 03197 03198 03199 SDiagMessage::SDiagMessage(const string& message, bool* result) 03200 : m_Severity(eDiagSevMin), 03201 m_Buffer(0), 03202 m_BufferLen(0), 03203 m_File(0), 03204 m_Module(0), 03205 m_Class(0), 03206 m_Function(0), 03207 m_Line(0), 03208 m_ErrCode(0), 03209 m_ErrSubCode(0), 03210 m_Flags(0), 03211 m_Prefix(0), 03212 m_ErrText(0), 03213 m_PID(0), 03214 m_TID(0), 03215 m_ProcPost(0), 03216 m_ThrPost(0), 03217 m_RequestId(0), 03218 m_Event(eEvent_Start), 03219 m_TypedExtra(false), 03220 m_NoTee(false), 03221 m_Data(0), 03222 m_Format(eFormat_Auto) 03223 { 03224 bool res = ParseMessage(message); 03225 if ( result ) { 03226 *result = res; 03227 } 03228 } 03229 03230 03231 SDiagMessage::~SDiagMessage(void) 03232 { 03233 if ( m_Data ) { 03234 delete m_Data; 03235 } 03236 } 03237 03238 03239 SDiagMessage::SDiagMessage(const SDiagMessage& message) 03240 : m_Severity(eDiagSevMin), 03241 m_Buffer(0), 03242 m_BufferLen(0), 03243 m_File(0), 03244 m_Module(0), 03245 m_Class(0), 03246 m_Function(0), 03247 m_Line(0), 03248 m_ErrCode(0), 03249 m_ErrSubCode(0), 03250 m_Flags(0), 03251 m_Prefix(0), 03252 m_ErrText(0), 03253 m_PID(0), 03254 m_TID(0), 03255 m_ProcPost(0), 03256 m_ThrPost(0), 03257 m_RequestId(0), 03258 m_Event(eEvent_Start), 03259 m_TypedExtra(false), 03260 m_NoTee(false), 03261 m_Data(0), 03262 m_Format(eFormat_Auto) 03263 { 03264 *this = message; 03265 } 03266 03267 03268 SDiagMessage& SDiagMessage::operator=(const SDiagMessage& message) 03269 { 03270 if (&message != this) { 03271 m_Format = message.m_Format; 03272 if ( message.m_Data ) { 03273 m_Data = new SDiagMessageData(*message.m_Data); 03274 m_Data->m_Host = message.m_Data->m_Host; 03275 m_Data->m_Client = message.m_Data->m_Client; 03276 m_Data->m_Session = message.m_Data->m_Session; 03277 m_Data->m_AppName = message.m_Data->m_AppName; 03278 m_Data->m_AppState = message.m_Data->m_AppState; 03279 } 03280 else { 03281 x_SaveContextData(); 03282 if (message.m_Buffer) { 03283 m_Data->m_Message = 03284 string(message.m_Buffer, message.m_BufferLen); 03285 } 03286 if ( message.m_File ) { 03287 m_Data->m_File = message.m_File; 03288 } 03289 if ( message.m_Module ) { 03290 m_Data->m_Module = message.m_Module; 03291 } 03292 if ( message.m_Class ) { 03293 m_Data->m_Class = message.m_Class; 03294 } 03295 if ( message.m_Function ) { 03296 m_Data->m_Function = message.m_Function; 03297 } 03298 if ( message.m_Prefix ) { 03299 m_Data->m_Prefix = message.m_Prefix; 03300 } 03301 if ( message.m_ErrText ) { 03302 m_Data->m_ErrText = message.m_ErrText; 03303 } 03304 } 03305 m_Severity = message.m_Severity; 03306 m_Line = message.m_Line; 03307 m_ErrCode = message.m_ErrCode; 03308 m_ErrSubCode = message.m_ErrSubCode; 03309 m_Flags = message.m_Flags; 03310 m_PID = message.m_PID; 03311 m_TID = message.m_TID; 03312 m_ProcPost = message.m_ProcPost; 03313 m_ThrPost = message.m_ThrPost; 03314 m_RequestId = message.m_RequestId; 03315 m_Event = message.m_Event; 03316 m_TypedExtra = message.m_TypedExtra; 03317 m_ExtraArgs.assign(message.m_ExtraArgs.begin(), 03318 message.m_ExtraArgs.end()); 03319 03320 m_Buffer = m_Data->m_Message.empty() ? 0 : m_Data->m_Message.c_str(); 03321 m_BufferLen = m_Data->m_Message.empty() ? 0 : m_Data->m_Message.length(); 03322 m_File = m_Data->m_File.empty() ? 0 : m_Data->m_File.c_str(); 03323 m_Module = m_Data->m_Module.empty() ? 0 : m_Data->m_Module.c_str(); 03324 m_Class = m_Data->m_Class.empty() ? 0 : m_Data->m_Class.c_str(); 03325 m_Function = m_Data->m_Function.empty() 03326 ? 0 : m_Data->m_Function.c_str(); 03327 m_Prefix = m_Data->m_Prefix.empty() ? 0 : m_Data->m_Prefix.c_str(); 03328 m_ErrText = m_Data->m_ErrText.empty() ? 0 : m_Data->m_ErrText.c_str(); 03329 } 03330 return *this; 03331 } 03332 03333 03334 Uint8 s_ParseInt(const string& message, 03335 size_t& pos, // start position 03336 size_t width, // fixed width or 0 03337 char sep) // trailing separator (throw if not found) 03338 { 03339 if (pos >= message.length()) { 03340 NCBI_THROW(CException, eUnknown, 03341 "Failed to parse diagnostic message"); 03342 } 03343 Uint8 ret = 0; 03344 if (width > 0) { 03345 if (message[pos + width] != sep) { 03346 NCBI_THROW(CException, eUnknown, 03347 "Missing separator after integer"); 03348 } 03349 } 03350 else { 03351 width = message.find(sep, pos); 03352 if (width == NPOS) { 03353 NCBI_THROW(CException, eUnknown, 03354 "Missing separator after integer"); 03355 } 03356 width -= pos; 03357 } 03358 03359 ret = NStr::StringToUInt8(CTempString(message.c_str() + pos, width)); 03360 pos += width + 1; 03361 return ret; 03362 } 03363 03364 03365 CTempString s_ParseStr(const string& message, 03366 size_t& pos, // start position 03367 char sep, // separator 03368 bool optional = false) // do not throw if not found 03369 { 03370 if (pos >= message.length()) { 03371 NCBI_THROW(CException, eUnknown, 03372 "Failed to parse diagnostic message"); 03373 } 03374 size_t pos1 = pos; 03375 pos = message.find(sep, pos1); 03376 if (pos == NPOS) { 03377 if ( !optional ) { 03378 NCBI_THROW(CException, eUnknown, 03379 "Failed to parse diagnostic message"); 03380 } 03381 pos = pos1; 03382 return kEmptyStr; 03383 } 03384 if ( pos == pos1 + 1 && !optional ) { 03385 // The separator is in the next position, no empty string allowed 03386 NCBI_THROW(CException, eUnknown, 03387 "Failed to parse diagnostic message"); 03388 } 03389 // remember end position of the string, skip separators 03390 size_t pos2 = pos; 03391 pos = message.find_first_not_of(sep, pos); 03392 if (pos == NPOS) { 03393 pos = message.length(); 03394 } 03395 return CTempString(message.c_str() + pos1, pos2 - pos1); 03396 } 03397 03398 03399 static const char s_ExtraEncodeChars[256][4] = { 03400 "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", 03401 "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F", 03402 "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", 03403 "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F", 03404 "+", "!", "\"", "#", "$", "%25", "%26", "'", 03405 "(", ")", "*", "%2B", ",", "-", ".", "/", 03406 "0", "1", "2", "3", "4", "5", "6", "7", 03407 "8", "9", ":", ";", "<", "%3D", ">", "?", 03408 "@", "A", "B", "C", "D", "E", "F", "G", 03409 "H", "I", "J", "K", "L", "M", "N", "O", 03410 "P", "Q", "R", "S", "T", "U", "V", "W", 03411 "X", "Y", "Z", "[", "\\", "]", "^", "_", 03412 "`", "a", "b", "c", "d", "e", "f", "g", 03413 "h", "i", "j", "k", "l", "m", "n", "o", 03414 "p", "q", "r", "s", "t", "u", "v", "w", 03415 "x", "y", "z", "{", "|", "}", "~", "%7F", 03416 "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87", 03417 "%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F", 03418 "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", 03419 "%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F", 03420 "%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7", 03421 "%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF", 03422 "%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7", 03423 "%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE", "%BF", 03424 "%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7", 03425 "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF", 03426 "%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7", 03427 "%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE", "%DF", 03428 "%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6", "%E7", 03429 "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF", 03430 "%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7", 03431 "%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF" 03432 }; 03433 03434 03435 inline 03436 bool x_IsEncodableChar(char c) 03437 { 03438 return s_ExtraEncodeChars[(unsigned char)c][0] != c || 03439 s_ExtraEncodeChars[(unsigned char)c][1] != 0; 03440 } 03441 03442 03443 class CExtraDecoder : public IStringDecoder 03444 { 03445 public: 03446 virtual string Decode(const CTempString& src, EStringType stype) const; 03447 }; 03448 03449 03450 string CExtraDecoder::Decode(const CTempString& src, EStringType stype) const 03451 { 03452 string str = src; // NStr::TruncateSpaces(src); 03453 size_t len = str.length(); 03454 if ( !len && stype == eName ) { 03455 NCBI_THROW2(CStringException, eFormat, 03456 "Empty name in extra-arg", 0); 03457 } 03458 03459 size_t dst = 0; 03460 for (size_t p = 0; p < len; dst++) { 03461 switch ( str[p] ) { 03462 case '%': { 03463 if (p + 2 > len) { 03464 NCBI_THROW2(CStringException, eFormat, 03465 "Inavild char in extra arg", p); 03466 } 03467 int n1 = NStr::HexChar(str[p+1]); 03468 int n2 = NStr::HexChar(str[p+2]); 03469 if (n1 < 0 || n2 < 0) { 03470 NCBI_THROW2(CStringException, eFormat, 03471 "Inavild char in extra arg", p); 03472 } 03473 str[dst] = (n1 << 4) | n2; 03474 p += 3; 03475 break; 03476 } 03477 case '+': 03478 str[dst] = ' '; 03479 p++; 03480 break; 03481 default: 03482 str[dst] = str[p++]; 03483 if ( x_IsEncodableChar(str[dst]) ) { 03484 NCBI_THROW2(CStringException, eFormat, 03485 "Unencoded special char in extra arg", p); 03486 } 03487 } 03488 } 03489 if (dst < len) { 03490 str[dst] = '\0'; 03491 str.resize(dst); 03492 } 03493 return str; 03494 } 03495 03496 03497 bool SDiagMessage::x_ParseExtraArgs(const string& str, size_t pos) 03498 { 03499 m_ExtraArgs.clear(); 03500 if (str.find('&', pos) == NPOS && str.find('=', pos) == NPOS) { 03501 return false; 03502 } 03503 CStringPairs<TExtraArgs> parser("&", "=", new CExtraDecoder()); 03504 try { 03505 parser.Parse(CTempString(str.c_str() + pos)); 03506 } 03507 catch (CStringException) { 03508 string n, v; 03509 NStr::SplitInTwo(CTempString(str.c_str() + pos), "=", n, v); 03510 // Try to decode only the name, leave the value as-is. 03511 try { 03512 n = parser.GetDecoder()->Decode(n, CExtraDecoder::eName); 03513 if (n == kExtraTypeArgName) { 03514 m_TypedExtra = true; 03515 } 03516 m_ExtraArgs.push_back(TExtraArg(n, v)); 03517 return true; 03518 } 03519 catch (CStringException) { 03520 return false; 03521 } 03522 } 03523 ITERATE(TExtraArgs, it, parser.GetPairs()) { 03524 if (it->first == kExtraTypeArgName) { 03525 m_TypedExtra = true; 03526 } 03527 m_ExtraArgs.push_back(TExtraArg(it->first, it->second)); 03528 } 03529 return true; 03530 } 03531 03532 03533 bool SDiagMessage::ParseMessage(const string& message) 03534 { 03535 m_Severity = eDiagSevMin; 03536 m_Buffer = 0; 03537 m_BufferLen = 0; 03538 m_File = 0; 03539 m_Module = 0; 03540 m_Class = 0; 03541 m_Function = 0; 03542 m_Line = 0; 03543 m_ErrCode = 0; 03544 m_ErrSubCode = 0; 03545 m_Flags = 0; 03546 m_Prefix = 0; 03547 m_ErrText = 0; 03548 m_PID = 0; 03549 m_TID = 0; 03550 m_ProcPost = 0; 03551 m_ThrPost = 0; 03552 m_RequestId = 0; 03553 m_Event = eEvent_Start; 03554 m_TypedExtra = false; 03555 m_Format = eFormat_Auto; 03556 if ( m_Data ) { 03557 delete m_Data; 03558 m_Data = 0; 03559 } 03560 m_Data = new SDiagMessageData; 03561 03562 size_t pos = 0; 03563 try { 03564 // Fixed prefix 03565 m_PID = s_ParseInt(message, pos, 0, '/'); 03566 m_TID = s_ParseInt(message, pos, 0, '/'); 03567 size_t sl_pos = message.find('/', pos); 03568 size_t sp_pos = message.find(' ', pos); 03569 if (sl_pos < sp_pos) { 03570 // Newer format, app state is present. 03571 m_RequestId = s_ParseInt(message, pos, 0, '/'); 03572 m_Data->m_AppState = 03573 s_StrToAppState(s_ParseStr(message, pos, ' ', true)); 03574 } 03575 else { 03576 // Older format, no app state. 03577 m_RequestId = s_ParseInt(message, pos, 0, ' '); 03578 m_Data->m_AppState = eDiagAppState_AppRun; 03579 } 03580 03581 if (message[pos + kDiagW_UID] != ' ') { 03582 return false; 03583 } 03584 m_Data->m_UID = NStr::StringToUInt8( 03585 CTempString(message.c_str() + pos, kDiagW_UID), 0, 16); 03586 pos += kDiagW_UID + 1; 03587 03588 m_ProcPost = s_ParseInt(message, pos, 0, '/'); 03589 m_ThrPost = s_ParseInt(message, pos, 0, ' '); 03590 03591 // Date and time. Try all known formats. 03592 CTempString tmp = s_ParseStr(message, pos, ' '); 03593 static const char* s_TimeFormats[3] = { 03594 kDiagTimeFormat, "Y-M-DTh:m:s", "Y/M/D:h:m:s" 03595 }; 03596 const char* fmt = s_TimeFormats[1]; 03597 if (tmp.find('T') == NPOS) { 03598 fmt = s_TimeFormats[2]; 03599 } 03600 else if (tmp.find('.') != NPOS) { 03601 fmt = s_TimeFormats[0]; 03602 } 03603 try { 03604 m_Data->m_Time = CTime(tmp, fmt); 03605 } 03606 catch (CTimeException) { 03607 return false; 03608 } 03609 03610 // Host 03611 m_Data->m_Host = s_ParseStr(message, pos, ' '); 03612 if (m_Data->m_Host == kUnknown_Host) { 03613 m_Data->m_Host.clear(); 03614 } 03615 // Client 03616 m_Data->m_Client = s_ParseStr(message, pos, ' '); 03617 if (m_Data->m_Client == kUnknown_Client) { 03618 m_Data->m_Client.clear(); 03619 } 03620 // Session ID 03621 m_Data->m_Session = s_ParseStr(message, pos, ' '); 03622 if (m_Data->m_Session == kUnknown_Session) { 03623 m_Data->m_Session.clear(); 03624 } 03625 // Application name 03626 m_Data->m_AppName = s_ParseStr(message, pos, ' '); 03627 if (m_Data->m_AppName == kUnknown_App) { 03628 m_Data->m_AppName.clear(); 03629 } 03630 03631 // Severity or event type 03632 bool have_severity = false; 03633 size_t severity_pos = pos; 03634 tmp = s_ParseStr(message, pos, ':', true); 03635 if ( !tmp.empty() ) { 03636 if (tmp.length() == 10 && tmp.find("Message[") == 0) { 03637 // Get the real severity 03638 switch ( tmp[8] ) { 03639 case 'T': 03640 m_Severity = eDiag_Trace; 03641 break; 03642 case 'I': 03643 m_Severity = eDiag_Info; 03644 break; 03645 case 'W': 03646 m_Severity = eDiag_Warning; 03647 break; 03648 case 'E': 03649 m_Severity = eDiag_Error; 03650 break; 03651 case 'C': 03652 m_Severity = eDiag_Critical; 03653 break; 03654 case 'F': 03655 m_Severity = eDiag_Fatal; 03656 break; 03657 default: 03658 return false; 03659 } 03660 m_Flags |= eDPF_IsMessage; 03661 have_severity = true; 03662 } 03663 else { 03664 have_severity = 03665 CNcbiDiag::StrToSeverityLevel(string(tmp).c_str(), m_Severity); 03666 } 03667 } 03668 if ( have_severity ) { 03669 pos = message.find_first_not_of(' ', pos); 03670 if (pos == NPOS) { 03671 pos = message.length(); 03672 } 03673 } 03674 else { 03675 // Check event type rather than severity level 03676 pos = severity_pos; 03677 tmp = s_ParseStr(message, pos, ' ', true); 03678 if (tmp.empty() && severity_pos < message.length()) { 03679 tmp = CTempString(message.c_str() + severity_pos); 03680 pos = message.length(); 03681 } 03682 if (tmp == GetEventName(eEvent_Start)) { 03683 m_Event = eEvent_Start; 03684 } 03685 else if (tmp == GetEventName(eEvent_Stop)) { 03686 m_Event = eEvent_Stop; 03687 } 03688 else if (tmp == GetEventName(eEvent_RequestStart)) { 03689 m_Event = eEvent_RequestStart; 03690 if (pos < message.length()) { 03691 if ( x_ParseExtraArgs(message, pos) ) { 03692 pos = message.length(); 03693 } 03694 } 03695 } 03696 else if (tmp == GetEventName(eEvent_RequestStop)) { 03697 m_Event = eEvent_RequestStop; 03698 } 03699 else if (tmp == GetEventName(eEvent_Extra)) { 03700 m_Event = eEvent_Extra; 03701 if (pos < message.length()) { 03702 if ( x_ParseExtraArgs(message, pos) ) { 03703 pos = message.length(); 03704 } 03705 } 03706 } 03707 else if (tmp == GetEventName(eEvent_PerfLog)) { 03708 m_Event = eEvent_PerfLog; 03709 if (pos < message.length()) { 03710 // Put status and time to the message, 03711 // parse all the rest as extra. 03712 size_t msg_end = message.find_first_not_of(' ', pos); 03713 msg_end = message.find_first_of(' ', msg_end); 03714 msg_end = message.find_first_not_of(' ', msg_end); 03715 msg_end = message.find_first_of(' ', msg_end); 03716 size_t extra_pos = message.find_first_not_of(' ', msg_end); 03717 m_Data->m_Message = string(message.c_str() + pos). 03718 substr(0, msg_end - pos); 03719 m_BufferLen = m_Data->m_Message.length(); 03720 m_Buffer = m_Data->m_Message.empty() ? 03721 0 : &m_Data->m_Message[0]; 03722 if ( x_ParseExtraArgs(message, extra_pos) ) { 03723 pos = message.length(); 03724 } 03725 } 03726 } 03727 else { 03728 return false; 03729 } 03730 m_Flags |= eDPF_AppLog; 03731 // The rest is the message (do not parse status, bytes etc.) 03732 if (pos < message.length()) { 03733 m_Data->m_Message = message.c_str() + pos; 03734 m_BufferLen = m_Data->m_Message.length(); 03735 m_Buffer = m_Data->m_Message.empty() ? 03736 0 : &m_Data->m_Message[0]; 03737 } 03738 m_Format = eFormat_New; 03739 return true; 03740 } 03741 03742 // Find message separator 03743 size_t sep_pos = message.find(" --- ", pos); 03744 03745 // <module>, <module>(<err_code>.<err_subcode>) or <module>(<err_text>) 03746 if (pos < sep_pos && message[pos] != '"') { 03747 size_t mod_pos = pos; 03748 tmp = s_ParseStr(message, pos, ' '); 03749 size_t lbr = tmp.find("("); 03750 if (lbr != NPOS) { 03751 if (tmp[tmp.length() - 1] != ')') { 03752 // Space(s) inside the error text, try to find closing ')' 03753 int open_br = 1; 03754 while (open_br > 0 && pos < message.length()) { 03755 if (message[pos] == '(') { 03756 open_br++; 03757 } 03758 else if (message[pos] == ')') { 03759 open_br--; 03760 } 03761 pos++; 03762 } 03763 if (message[pos] != ' ' || pos >= message.length()) { 03764 return false; 03765 } 03766 tmp = CTempString(message.c_str() + mod_pos, pos - mod_pos); 03767 // skip space(s) 03768 pos = message.find_first_not_of(' ', pos); 03769 if (pos == NPOS) { 03770 pos = message.length(); 03771 } 03772 } 03773 m_Data->m_Module = tmp.substr(0, lbr); 03774 tmp = tmp.substr(lbr + 1, tmp.length() - lbr - 2); 03775 size_t dot_pos = tmp.find('.'); 03776 if (dot_pos != NPOS) { 03777 // Try to parse error code/subcode 03778 try { 03779 m_ErrCode = NStr::StringToInt(tmp.substr(0, dot_pos)); 03780 m_ErrSubCode = NStr::StringToInt(tmp.substr(dot_pos + 1)); 03781 } 03782 catch (CStringException) { 03783 m_ErrCode = 0; 03784 m_ErrSubCode = 0; 03785 } 03786 } 03787 if (!m_ErrCode && !m_ErrSubCode) { 03788 m_Data->m_ErrText = tmp; 03789 m_ErrText = m_Data->m_ErrText.empty() ? 03790 0 : m_Data->m_ErrText.c_str(); 03791 } 03792 } 03793 else { 03794 m_Data->m_Module = tmp; 03795 } 03796 if ( !m_Data->m_Module.empty() ) { 03797 m_Module = m_Data->m_Module.c_str(); 03798 } 03799 } 03800 03801 if (pos < sep_pos && message[pos] == '"') { 03802 // ["<file>", ][line <line>][:] 03803 pos++; // skip " 03804 tmp = s_ParseStr(message, pos, '"'); 03805 m_Data->m_File = tmp; 03806 m_File = m_Data->m_File.empty() ? 0 : m_Data->m_File.c_str(); 03807 if (CTempString(message.c_str() + pos, 7) != ", line ") { 03808 return false; 03809 } 03810 pos += 7; 03811 m_Line = (size_t)s_ParseInt(message, pos, 0, ':'); 03812 pos = message.find_first_not_of(' ', pos); 03813 if (pos == NPOS) { 03814 pos = message.length(); 03815 } 03816 } 03817 03818 if (pos < sep_pos) { 03819 // Class:: Class::Function() ::Function() 03820 if (message.find("::", pos) != NPOS) { 03821 size_t tmp_pos = sep_pos; 03822 while (tmp_pos > pos && message[tmp_pos - 1] == ' ') 03823 --tmp_pos; 03824 tmp.assign(message.data() + pos, tmp_pos - pos); 03825 size_t dcol = tmp.find("::"); 03826 if (dcol == NPOS) { 03827 goto parse_unk_func; 03828 } 03829 pos = sep_pos + 1; 03830 if (dcol > 0) { 03831 m_Data->m_Class = tmp.substr(0, dcol); 03832 m_Class = m_Data->m_Class.empty() ? 03833 0 : m_Data->m_Class.c_str(); 03834 } 03835 dcol += 2; 03836 if (dcol < tmp.length() - 2) { 03837 // Remove "()" 03838 if (tmp[tmp.length() - 2] != '(' || tmp[tmp.length() - 1] != ')') { 03839 return false; 03840 } 03841 m_Data->m_Function = tmp.substr(dcol, 03842 tmp.length() - dcol - 2); 03843 m_Function = m_Data->m_Function.empty() ? 03844 0 : m_Data->m_Function.c_str(); 03845 } 03846 } 03847 else { 03848 parse_unk_func: 03849 size_t unkf = message.find("UNK_FUNC", pos); 03850 if (unkf == pos) { 03851 pos += 9; 03852 } 03853 } 03854 } 03855 03856 if (CTempString(message.c_str() + pos, 4) == "--- ") { 03857 pos += 4; 03858 } 03859 03860 // All the rest goes to message - no way to parse prefix/error code. 03861 // [<prefix1>::<prefix2>::.....] 03862 // <message> 03863 // <err_code_message> and <err_code_explanation> 03864 m_Data->m_Message = message.c_str() + pos; 03865 m_BufferLen = m_Data->m_Message.length(); 03866 m_Buffer = m_Data->m_Message.empty() ? 0 : &m_Data->m_Message[0]; 03867 } 03868 catch (CException) { 03869 return false; 03870 } 03871 03872 m_Format = eFormat_New; 03873 return true; 03874 } 03875 03876 03877 void SDiagMessage::ParseDiagStream(CNcbiIstream& in, 03878 INextDiagMessage& func) 03879 { 03880 string msg_str, line, last_msg_str; 03881 bool res = false; 03882 auto_ptr<SDiagMessage> msg; 03883 auto_ptr<SDiagMessage> last_msg; 03884 while ( in.good() ) { 03885 getline(in, line); 03886 // Dirty check for PID/TID/RID 03887 if (line.size() < 15) { 03888 if ( !line.empty() ) { 03889 msg_str += "\n" + line; 03890 line.erase(); 03891 } 03892 continue; 03893 } 03894 else { 03895 for (size_t i = 0; i < 15; i++) { 03896 if (line[i] != '/' && (line[i] < '0' || line[i] > '9')) { 03897 // Not a valid prefix - append to the previous message 03898 msg_str += "\n" + line; 03899 line.erase(); 03900 break; 03901 } 03902 } 03903 if ( line.empty() ) { 03904 continue; 03905 } 03906 } 03907 if ( msg_str.empty() ) { 03908 msg_str = line; 03909 continue; 03910 } 03911 msg.reset(new SDiagMessage(msg_str, &res)); 03912 if ( res ) { 03913 if ( last_msg.get() ) { 03914 func(*last_msg); 03915 } 03916 last_msg_str = msg_str; 03917 last_msg.reset(msg.release()); 03918 } 03919 else if ( !last_msg_str.empty() ) { 03920 last_msg_str += "\n" + msg_str; 03921 last_msg.reset(new SDiagMessage(last_msg_str, &res)); 03922 if ( !res ) { 03923 ERR_POST_X(19, 03924 Error << "Failed to parse message: " << last_msg_str); 03925 } 03926 } 03927 else { 03928 ERR_POST_X(20, Error << "Failed to parse message: " << msg_str); 03929 } 03930 msg_str = line; 03931 } 03932 if ( !msg_str.empty() ) { 03933 msg.reset(new SDiagMessage(msg_str, &res)); 03934 if ( res ) { 03935 if ( last_msg.get() ) { 03936 func(*last_msg); 03937 } 03938 func(*msg); 03939 } 03940 else if ( !last_msg_str.empty() ) { 03941 last_msg_str += "\n" + msg_str; 03942 msg.reset(new SDiagMessage(last_msg_str, &res)); 03943 if ( res ) { 03944 func(*msg); 03945 } 03946 else { 03947 ERR_POST_X(21, 03948 Error << "Failed to parse message: " << last_msg_str); 03949 } 03950 } 03951 else { 03952 ERR_POST_X(22, 03953 Error << "Failed to parse message: " << msg_str); 03954 } 03955 } 03956 } 03957 03958 03959 string SDiagMessage::GetEventName(EEventType event) 03960 { 03961 switch ( event ) { 03962 case eEvent_Start: 03963 return "start"; 03964 case eEvent_Stop: 03965 return "stop"; 03966 case eEvent_Extra: 03967 return "extra"; 03968 case eEvent_RequestStart: 03969 return "request-start"; 03970 case eEvent_RequestStop: 03971 return "request-stop"; 03972 case eEvent_PerfLog: 03973 return "perf"; 03974 } 03975 return kEmptyStr; 03976 } 03977 03978 03979 CDiagContext::TUID SDiagMessage::GetUID(void) const 03980 { 03981 return m_Data ? m_Data->m_UID : GetDiagContext().GetUID(); 03982 } 03983 03984 03985 CTime SDiagMessage::GetTime(void) const 03986 { 03987 return m_Data ? m_Data->m_Time : GetFastLocalTime(); 03988 } 03989 03990 03991 inline 03992 bool SDiagMessage::x_IsSetOldFormat(void) const 03993 { 03994 return m_Format == eFormat_Auto ? GetDiagContext().IsSetOldPostFormat() 03995 : m_Format == eFormat_Old; 03996 } 03997 03998 03999 void SDiagMessage::Write(string& str, TDiagWriteFlags flags) const 04000 { 04001 CNcbiOstrstream ostr; 04002 Write(ostr, flags); 04003 ostr.put('\0'); 04004 str = ostr.str(); 04005 ostr.rdbuf()->freeze(false); 04006 } 04007 04008 04009 CNcbiOstream& SDiagMessage::Write(CNcbiOstream& os, 04010 TDiagWriteFlags flags) const 04011 { 04012 // GetDiagContext().PushMessage(*this); 04013 04014 if (IsSetDiagPostFlag(eDPF_MergeLines, m_Flags)) { 04015 CNcbiOstrstream ostr; 04016 string src, dest; 04017 x_Write(ostr, fNoEndl); 04018 ostr.put('\0'); 04019 src = ostr.str(); 04020 ostr.rdbuf()->freeze(false); 04021 NStr::Replace(NStr::Replace(src,"\r",""),"\n","", dest); 04022 os << dest; 04023 if ((flags & fNoEndl) == 0) { 04024 os << NcbiEndl; 04025 } 04026 return os; 04027 } else { 04028 return x_Write(os, flags); 04029 } 04030 } 04031 04032 04033 CNcbiOstream& SDiagMessage::x_Write(CNcbiOstream& os, 04034 TDiagWriteFlags flags) const 04035 { 04036 CNcbiOstream& res = 04037 x_IsSetOldFormat() ? x_OldWrite(os, flags) : x_NewWrite(os, flags); 04038 return res; 04039 } 04040 04041 04042 string SDiagMessage::x_GetModule(void) const 04043 { 04044 if ( m_Module && *m_Module ) { 04045 return string(m_Module); 04046 } 04047 if ( x_IsSetOldFormat() ) { 04048 return kEmptyStr; 04049 } 04050 if ( !m_File || !(*m_File) ) { 04051 return kEmptyStr; 04052 } 04053 char sep_chr = CDirEntry::GetPathSeparator(); 04054 const char* last_sep = m_File; 04055 while (last_sep && *last_sep) { 04056 if (*last_sep == sep_chr) break; 04057 last_sep++; 04058 } 04059 if (last_sep == m_File) { 04060 return kEmptyStr; 04061 } 04062 const char* cur = last_sep - 1; 04063 while (cur && *cur && cur >= m_File) { 04064 if (*cur == sep_chr) { 04065 string ret(cur + 1, last_sep - cur - 1); 04066 NStr::ToUpper(ret); 04067 return ret; 04068 } 04069 cur--; 04070 } 04071 return kEmptyStr; 04072 } 04073 04074 04075 class CExtraEncoder : public IStringEncoder 04076 { 04077 public: 04078 virtual string Encode(const CTempString& src, EStringType stype) const; 04079 }; 04080 04081 04082 string CExtraEncoder::Encode(const CTempString& src, EStringType stype) const 04083 { 04084 if (stype == eName) { 04085 // Just check the source string, it may contain only valid chars 04086 ITERATE(CTempString, c, src) { 04087 const char* enc = s_ExtraEncodeChars[(unsigned char)(*c)]; 04088 if (enc[1] != 0 || enc[0] != *c) { 04089 NCBI_THROW(CCoreException, eInvalidArg, 04090 "Invalid char in extra args name: " + string(src)); 04091 } 04092 } 04093 return src; 04094 } 04095 // Encode value 04096 string dst; 04097 ITERATE(CTempString, c, src) { 04098 dst += s_ExtraEncodeChars[(unsigned char)(*c)]; 04099 } 04100 return dst; 04101 } 04102 04103 04104 string SDiagMessage::FormatExtraMessage(void) const 04105 { 04106 return CStringPairs<TExtraArgs>::Merge(m_ExtraArgs, 04107 "&", "=", new CExtraEncoder); 04108 } 04109 04110 04111 CNcbiOstream& SDiagMessage::x_OldWrite(CNcbiOstream& os, 04112 TDiagWriteFlags flags) const 04113 { 04114 // Date & time 04115 if (IsSetDiagPostFlag(eDPF_DateTime, m_Flags)) { 04116 os << CFastLocalTime().GetLocalTime().AsString("M/D/y h:m:s "); 04117 } 04118 // "<file>" 04119 bool print_file = (m_File && *m_File && 04120 IsSetDiagPostFlag(eDPF_File, m_Flags)); 04121 if ( print_file ) { 04122 const char* x_file = m_File; 04123 if ( !IsSetDiagPostFlag(eDPF_LongFilename, m_Flags) ) { 04124 for (const char* s = m_File; *s; s++) { 04125 if (*s == '/' || *s == '\\' || *s == ':') 04126 x_file = s + 1; 04127 } 04128 } 04129 os << '"' << x_file << '"'; 04130 } 04131 04132 // , line <line> 04133 bool print_line = (m_Line && IsSetDiagPostFlag(eDPF_Line, m_Flags)); 04134 if ( print_line ) 04135 os << (print_file ? ", line " : "line ") << m_Line; 04136 04137 // : 04138 if (print_file || print_line) 04139 os << ": "; 04140 04141 // Get error code description 04142 bool have_description = false; 04143 SDiagErrCodeDescription description; 04144 if ((m_ErrCode || m_ErrSubCode) && 04145 (IsSetDiagPostFlag(eDPF_ErrCodeMessage, m_Flags) || 04146 IsSetDiagPostFlag(eDPF_ErrCodeExplanation, m_Flags) || 04147 IsSetDiagPostFlag(eDPF_ErrCodeUseSeverity, m_Flags)) && 04148 IsSetDiagErrCodeInfo()) { 04149 04150 CDiagErrCodeInfo* info = GetDiagErrCodeInfo(); 04151 if ( info && 04152 info->GetDescription(ErrCode(m_ErrCode, m_ErrSubCode), 04153 &description) ) { 04154 have_description = true; 04155 if (IsSetDiagPostFlag(eDPF_ErrCodeUseSeverity, m_Flags) && 04156 description.m_Severity != -1 ) 04157 m_Severity = (EDiagSev)description.m_Severity; 04158 } 04159 } 04160 04161 // <severity>: 04162 if (IsSetDiagPostFlag(eDPF_Severity, m_Flags) && 04163 (m_Severity != eDiag_Info || !IsSetDiagPostFlag(eDPF_OmitInfoSev))) { 04164 if ( IsSetDiagPostFlag(eDPF_IsMessage, m_Flags) ) { 04165 os << "Message: "; 04166 } 04167 else { 04168 os << CNcbiDiag::SeverityName(m_Severity) << ": "; 04169 } 04170 } 04171 04172 // (<err_code>.<err_subcode>) or (err_text) 04173 if ((m_ErrCode || m_ErrSubCode || m_ErrText) && 04174 IsSetDiagPostFlag(eDPF_ErrorID, m_Flags)) { 04175 os << '('; 04176 if (m_ErrText) { 04177 os << m_ErrText; 04178 } else { 04179 os << m_ErrCode << '.' << m_ErrSubCode; 04180 } 04181 os << ") "; 04182 } 04183 04184 // Module::Class::Function - 04185 bool have_module = (m_Module && *m_Module); 04186 bool print_location = 04187 ( have_module || 04188 (m_Class && *m_Class ) || 04189 (m_Function && *m_Function)) 04190 && IsSetDiagPostFlag(eDPF_Location, m_Flags); 04191 04192 if (print_location) { 04193 // Module:: Module::Class Module::Class::Function() 04194 // ::Class ::Class::Function() 04195 // Module::Function() Function() 04196 bool need_double_colon = false; 04197 04198 if ( have_module ) { 04199 os << x_GetModule(); 04200 need_double_colon = true; 04201 } 04202 04203 if (m_Class && *m_Class) { 04204 if (need_double_colon) 04205 os << "::"; 04206 os << m_Class; 04207 need_double_colon = true; 04208 } 04209 04210 if (m_Function && *m_Function) { 04211 if (need_double_colon) 04212 os << "::"; 04213 need_double_colon = false; 04214 os << m_Function << "()"; 04215 } 04216 04217 if( need_double_colon ) 04218 os << "::"; 04219 04220 os << " - "; 04221 } 04222 04223 // [<prefix1>::<prefix2>::.....] 04224 if (m_Prefix && *m_Prefix && IsSetDiagPostFlag(eDPF_Prefix, m_Flags)) 04225 os << '[' << m_Prefix << "] "; 04226 04227 // <message> 04228 if (m_BufferLen) 04229 os.write(m_Buffer, m_BufferLen); 04230 04231 // <err_code_message> and <err_code_explanation> 04232 if (have_description) { 04233 if (IsSetDiagPostFlag(eDPF_ErrCodeMessage, m_Flags) && 04234 !description.m_Message.empty()) 04235 os << NcbiEndl << description.m_Message << ' '; 04236 if (IsSetDiagPostFlag(eDPF_ErrCodeExplanation, m_Flags) && 04237 !description.m_Explanation.empty()) 04238 os << NcbiEndl << description.m_Explanation; 04239 } 04240 04241 // Endl 04242 if ((flags & fNoEndl) == 0) { 04243 os << NcbiEndl; 04244 } 04245 04246 return os; 04247 } 04248 04249 04250 CNcbiOstream& SDiagMessage::x_NewWrite(CNcbiOstream& os, 04251 TDiagWriteFlags flags) const 04252 { 04253 if ((flags & fNoPrefix) == 0) { 04254 GetDiagContext().WriteStdPrefix(os, *this); 04255 } 04256 04257 // Get error code description 04258 bool have_description = false; 04259 SDiagErrCodeDescription description; 04260 if ((m_ErrCode || m_ErrSubCode) && 04261 IsSetDiagPostFlag(eDPF_ErrCodeUseSeverity, m_Flags) && 04262 IsSetDiagErrCodeInfo()) { 04263 04264 CDiagErrCodeInfo* info = GetDiagErrCodeInfo(); 04265 if ( info && 04266 info->GetDescription(ErrCode(m_ErrCode, m_ErrSubCode), 04267 &description) ) { 04268 have_description = true; 04269 if (description.m_Severity != -1) 04270 m_Severity = (EDiagSev)description.m_Severity; 04271 } 04272 } 04273 04274 // <severity>: 04275 if ( IsSetDiagPostFlag(eDPF_AppLog, m_Flags) ) { 04276 os << setfill(' ') << setw(13) << setiosflags(IOS_BASE::left) 04277 << GetEventName(m_Event) << resetiosflags(IOS_BASE::left) 04278 << setw(0); 04279 } 04280 else { 04281 string sev = CNcbiDiag::SeverityName(m_Severity); 04282 os << setfill(' ') << setw(13) // add 1 for space 04283 << setiosflags(IOS_BASE::left) << setw(0); 04284 if ( IsSetDiagPostFlag(eDPF_IsMessage, m_Flags) ) { 04285 os << "Message[" << sev[0] << "]:"; 04286 } 04287 else { 04288 os << sev << ':'; 04289 } 04290 os << resetiosflags(IOS_BASE::left); 04291 } 04292 os << ' '; 04293 04294 // <module>-<err_code>.<err_subcode> or <module>-<err_text> 04295 bool have_module = (m_Module && *m_Module) || (m_File && *m_File); 04296 bool print_err_id = have_module || m_ErrCode || m_ErrSubCode || m_ErrText; 04297 04298 if (print_err_id) { 04299 os << (have_module ? x_GetModule() : "UNK_MODULE"); 04300 if (m_ErrCode || m_ErrSubCode || m_ErrText) { 04301 if (m_ErrText) { 04302 os << '(' << m_ErrText << ')'; 04303 } else { 04304 os << '(' << m_ErrCode << '.' << m_ErrSubCode << ')'; 04305 } 04306 } 04307 os << ' '; 04308 } 04309 04310 // "<file>" 04311 if ( !IsSetDiagPostFlag(eDPF_AppLog, m_Flags) ) { 04312 bool print_file = m_File && *m_File; 04313 if ( print_file ) { 04314 const char* x_file = m_File; 04315 if ( !IsSetDiagPostFlag(eDPF_LongFilename, m_Flags) ) { 04316 for (const char* s = m_File; *s; s++) { 04317 if (*s == '/' || *s == '\\' || *s == ':') 04318 x_file = s + 1; 04319 } 04320 } 04321 os << '"' << x_file << '"'; 04322 } 04323 else { 04324 os << "\"UNK_FILE\""; 04325 } 04326 // , line <line> 04327 os << ", line " << m_Line; 04328 os << ": "; 04329 04330 // Class::Function 04331 bool print_loc = (m_Class && *m_Class ) || (m_Function && *m_Function); 04332 if (print_loc) { 04333 // Class:: Class::Function() ::Function() 04334 if (m_Class && *m_Class) { 04335 os << m_Class; 04336 } 04337 os << "::"; 04338 if (m_Function && *m_Function) { 04339 os << m_Function << "() "; 04340 } 04341 } 04342 else { 04343 os << "UNK_FUNC "; 04344 } 04345 04346 if ( !IsSetDiagPostFlag(eDPF_OmitSeparator, m_Flags) && 04347 !IsSetDiagPostFlag(eDPF_AppLog, m_Flags) ) { 04348 os << "--- "; 04349 } 04350 } 04351 04352 // [<prefix1>::<prefix2>::.....] 04353 if (m_Prefix && *m_Prefix && IsSetDiagPostFlag(eDPF_Prefix, m_Flags)) 04354 os << '[' << m_Prefix << "] "; 04355 04356 // <message> 04357 if (m_BufferLen) { 04358 os.write(m_Buffer, m_BufferLen); 04359 } 04360 04361 if ( IsSetDiagPostFlag(eDPF_AppLog, m_Flags) ) { 04362 if ( !m_ExtraArgs.empty() ) { 04363 if ( m_BufferLen ) { 04364 os << ' '; 04365 } 04366 os << FormatExtraMessage(); 04367 } 04368 } 04369 04370 // <err_code_message> and <err_code_explanation> 04371 if (have_description) { 04372 if (IsSetDiagPostFlag(eDPF_ErrCodeMessage, m_Flags) && 04373 !description.m_Message.empty()) 04374 os << NcbiEndl << description.m_Message << ' '; 04375 if (IsSetDiagPostFlag(eDPF_ErrCodeExplanation, m_Flags) && 04376 !description.m_Explanation.empty()) 04377 os << NcbiEndl << description.m_Explanation; 04378 } 04379 04380 // Endl 04381 if ((flags & fNoEndl) == 0) { 04382 os << NcbiEndl; 04383 } 04384 04385 return os; 04386 } 04387 04388 04389 void SDiagMessage::x_InitData(void) const 04390 { 04391 if ( !m_Data ) { 04392 m_Data = new SDiagMessageData; 04393 } 04394 if (m_Data->m_Message.empty() && m_Buffer) { 04395 m_Data->m_Message = string(m_Buffer, m_BufferLen); 04396 } 04397 if (m_Data->m_File.empty() && m_File) { 04398 m_Data->m_File = m_File; 04399 } 04400 if (m_Data->m_Module.empty() && m_Module) { 04401 m_Data->m_Module = m_Module; 04402 } 04403 if (m_Data->m_Class.empty() && m_Class) { 04404 m_Data->m_Class = m_Class; 04405 } 04406 if (m_Data->m_Function.empty() && m_Function) { 04407 m_Data->m_Function = m_Function; 04408 } 04409 if (m_Data->m_Prefix.empty() && m_Prefix) { 04410 m_Data->m_Prefix = m_Prefix; 04411 } 04412 if (m_Data->m_ErrText.empty() && m_ErrText) { 04413 m_Data->m_ErrText = m_ErrText; 04414 } 04415 04416 if ( !m_Data->m_UID ) { 04417 m_Data->m_UID = GetDiagContext().GetUID(); 04418 } 04419 if ( m_Data->m_Time.IsEmpty() ) { 04420 m_Data->m_Time = GetFastLocalTime(); 04421 } 04422 } 04423 04424 04425 void SDiagMessage::x_SaveContextData(void) const 04426 { 04427 if ( m_Data ) { 04428 return; 04429 } 04430 x_InitData(); 04431 CDiagContext& dctx = GetDiagContext(); 04432 m_Data->m_Host = dctx.GetEncodedHost(); 04433 m_Data->m_AppName = dctx.GetEncodedAppName(); 04434 m_Data->m_AppState = dctx.GetAppState(); 04435 04436 CRequestContext& rctx = dctx.GetRequestContext(); 04437 m_Data->m_Client = rctx.GetClientIP(); 04438 m_Data->m_Session = dctx.GetEncodedSessionID(); 04439 } 04440 04441 04442 const string& SDiagMessage::GetHost(void) const 04443 { 04444 if ( m_Data ) { 04445 return m_Data->m_Host; 04446 } 04447 return GetDiagContext().GetEncodedHost(); 04448 } 04449 04450 04451 const string& SDiagMessage::GetClient(void) const 04452 { 04453 return m_Data ? m_Data->m_Client 04454 : CDiagContext::GetRequestContext().GetClientIP(); 04455 } 04456 04457 04458 const string& SDiagMessage::GetSession(void) const 04459 { 04460 return m_Data ? m_Data->m_Session 04461 : GetDiagContext().GetEncodedSessionID(); 04462 } 04463 04464 04465 const string& SDiagMessage::GetAppName(void) const 04466 { 04467 if ( m_Data ) { 04468 return m_Data->m_AppName; 04469 } 04470 return GetDiagContext().GetEncodedAppName(); 04471 } 04472 04473 04474 EDiagAppState SDiagMessage::GetAppState(void) const 04475 { 04476 return m_Data ? m_Data->m_AppState : GetDiagContext().GetAppState(); 04477 } 04478 04479 04480 /////////////////////////////////////////////////////// 04481 // CDiagAutoPrefix:: 04482 04483 CDiagAutoPrefix::CDiagAutoPrefix(const string& prefix) 04484 { 04485 PushDiagPostPrefix(prefix.c_str()); 04486 } 04487 04488 CDiagAutoPrefix::CDiagAutoPrefix(const char* prefix) 04489 { 04490 PushDiagPostPrefix(prefix); 04491 } 04492 04493 CDiagAutoPrefix::~CDiagAutoPrefix(void) 04494 { 04495 PopDiagPostPrefix(); 04496 } 04497 04498 04499 /////////////////////////////////////////////////////// 04500 // EXTERN 04501 04502 04503 static TDiagPostFlags s_SetDiagPostAllFlags(TDiagPostFlags& flags, 04504 TDiagPostFlags new_flags) 04505 { 04506 CDiagLock lock(CDiagLock::eWrite); 04507 04508 TDiagPostFlags prev_flags = flags; 04509 if (new_flags & eDPF_Default) { 04510 new_flags |= prev_flags; 04511 new_flags &= ~eDPF_Default; 04512 } 04513 flags = new_flags; 04514 return prev_flags; 04515 } 04516 04517 04518 static void s_SetDiagPostFlag(TDiagPostFlags& flags, EDiagPostFlag flag) 04519 { 04520 if (flag == eDPF_Default) 04521 return; 04522 04523 CDiagLock lock(CDiagLock::eWrite); 04524 flags |= flag; 04525 // Assume flag is set by user 04526 s_MergeLinesSetBySetupDiag = false; 04527 } 04528 04529 04530 static void s_UnsetDiagPostFlag(TDiagPostFlags& flags, EDiagPostFlag flag) 04531 { 04532 if (flag == eDPF_Default) 04533 return; 04534 04535 CDiagLock lock(CDiagLock::eWrite); 04536 flags &= ~flag; 04537 // Assume flag is set by user 04538 s_MergeLinesSetBySetupDiag = false; 04539 } 04540 04541 04542 extern TDiagPostFlags SetDiagPostAllFlags(TDiagPostFlags flags) 04543 { 04544 return s_SetDiagPostAllFlags(CDiagBuffer::sx_GetPostFlags(), flags); 04545 } 04546 04547 extern void SetDiagPostFlag(EDiagPostFlag flag) 04548 { 04549 s_SetDiagPostFlag(CDiagBuffer::sx_GetPostFlags(), flag); 04550 } 04551 04552 extern void UnsetDiagPostFlag(EDiagPostFlag flag) 04553 { 04554 s_UnsetDiagPostFlag(CDiagBuffer::sx_GetPostFlags(), flag); 04555 } 04556 04557 04558 extern TDiagPostFlags SetDiagTraceAllFlags(TDiagPostFlags flags) 04559 { 04560 return s_SetDiagPostAllFlags(CDiagBuffer::sm_TraceFlags, flags); 04561 } 04562 04563 extern void SetDiagTraceFlag(EDiagPostFlag flag) 04564 { 04565 s_SetDiagPostFlag(CDiagBuffer::sm_TraceFlags, flag); 04566 } 04567 04568 extern void UnsetDiagTraceFlag(EDiagPostFlag flag) 04569 { 04570 s_UnsetDiagPostFlag(CDiagBuffer::sm_TraceFlags, flag); 04571 } 04572 04573 04574 extern void SetDiagPostPrefix(const char* prefix) 04575 { 04576 CDiagBuffer& buf = GetDiagBuffer(); 04577 if ( prefix ) { 04578 buf.m_PostPrefix = prefix; 04579 } else { 04580 buf.m_PostPrefix.erase(); 04581 } 04582 buf.m_PrefixList.clear(); 04583 } 04584 04585 04586 extern void PushDiagPostPrefix(const char* prefix) 04587 { 04588 if (prefix && *prefix) { 04589 CDiagBuffer& buf = GetDiagBuffer(); 04590 buf.m_PrefixList.push_back(prefix); 04591 buf.UpdatePrefix(); 04592 } 04593 } 04594 04595 04596 extern void PopDiagPostPrefix(void) 04597 { 04598 CDiagBuffer& buf = GetDiagBuffer(); 04599 if ( !buf.m_PrefixList.empty() ) { 04600 buf.m_PrefixList.pop_back(); 04601 buf.UpdatePrefix(); 04602 } 04603 } 04604 04605 04606 extern EDiagSev SetDiagPostLevel(EDiagSev post_sev) 04607 { 04608 if (post_sev < eDiagSevMin || post_sev > eDiagSevMax) { 04609 NCBI_THROW(CCoreException, eInvalidArg, 04610 "SetDiagPostLevel() -- Severity must be in the range " 04611 "[eDiagSevMin..eDiagSevMax]"); 04612 } 04613 04614 CDiagLock lock(CDiagLock::eWrite); 04615 EDiagSev sev = CDiagBuffer::sm_PostSeverity; 04616 if ( CDiagBuffer::sm_PostSeverityChange != eDiagSC_Disable) { 04617 if (post_sev == eDiag_Trace) { 04618 // special case 04619 SetDiagTrace(eDT_Enable); 04620 post_sev = eDiag_Info; 04621 } 04622 CDiagBuffer::sm_PostSeverity = post_sev; 04623 } 04624 return sev; 04625 } 04626 04627 04628 extern int CompareDiagPostLevel(EDiagSev sev1, EDiagSev sev2) 04629 { 04630 if (sev1 == sev2) return 0; 04631 if (sev1 == eDiag_Trace) return -1; 04632 if (sev2 == eDiag_Trace) return 1; 04633 return sev1 - sev2; 04634 } 04635 04636 04637 extern bool IsVisibleDiagPostLevel(EDiagSev sev) 04638 { 04639 if (sev == eDiag_Trace) { 04640 return CDiagBuffer::GetTraceEnabled(); 04641 } 04642 EDiagSev sev2; 04643 {{ 04644 CDiagLock lock(CDiagLock::eRead); 04645 sev2 = AdjustApplogPrintableSeverity(CDiagBuffer::sm_PostSeverity); 04646 }} 04647 return CompareDiagPostLevel(sev, sev2) >= 0; 04648 } 04649 04650 04651 extern void SetDiagFixedPostLevel(const EDiagSev post_sev) 04652 { 04653 SetDiagPostLevel(post_sev); 04654 DisableDiagPostLevelChange(); 04655 } 04656 04657 04658 extern bool DisableDiagPostLevelChange(bool disable_change) 04659 { 04660 CDiagLock lock(CDiagLock::eWrite); 04661 bool prev_status = (CDiagBuffer::sm_PostSeverityChange == eDiagSC_Enable); 04662 CDiagBuffer::sm_PostSeverityChange = disable_change ? eDiagSC_Disable : 04663 eDiagSC_Enable; 04664 return prev_status; 04665 } 04666 04667 04668 extern EDiagSev SetDiagDieLevel(EDiagSev die_sev) 04669 { 04670 if (die_sev < eDiagSevMin || die_sev > eDiag_Fatal) { 04671 NCBI_THROW(CCoreException, eInvalidArg, 04672 "SetDiagDieLevel() -- Severity must be in the range " 04673 "[eDiagSevMin..eDiag_Fatal]"); 04674 } 04675 04676 CDiagLock lock(CDiagLock::eWrite); 04677 EDiagSev sev = CDiagBuffer::sm_DieSeverity; 04678 CDiagBuffer::sm_DieSeverity = die_sev; 04679 return sev; 04680 } 04681 04682 04683 extern EDiagSev GetDiagDieLevel(void) 04684 { 04685 return CDiagBuffer::sm_DieSeverity; 04686 } 04687 04688 04689 extern bool IgnoreDiagDieLevel(bool ignore) 04690 { 04691 CDiagLock lock(CDiagLock::eWrite); 04692 bool retval = CDiagBuffer::sm_IgnoreToDie; 04693 CDiagBuffer::sm_IgnoreToDie = ignore; 04694 return retval; 04695 } 04696 04697 04698 extern void SetDiagTrace(EDiagTrace how, EDiagTrace dflt) 04699 { 04700 CDiagLock lock(CDiagLock::eWrite); 04701 (void) CDiagBuffer::GetTraceEnabled(); 04702 04703 if (dflt != eDT_Default) 04704 CDiagBuffer::sm_TraceDefault = dflt; 04705 04706 if (how == eDT_Default) 04707 how = CDiagBuffer::sm_TraceDefault; 04708 CDiagBuffer::sm_TraceEnabled = (how == eDT_Enable); 04709 } 04710 04711 04712 CTeeDiagHandler::CTeeDiagHandler(CDiagHandler* orig, bool own_orig) 04713 : m_MinSev(TTeeMinSeverity::GetDefault()), 04714 m_OrigHandler(orig, own_orig ? eTakeOwnership : eNoOwnership) 04715 { 04716 // Prevent recursion 04717 CTeeDiagHandler* tee = dynamic_cast<CTeeDiagHandler*>(m_OrigHandler.get()); 04718 if ( tee ) { 04719 m_OrigHandler = tee->m_OrigHandler; 04720 } 04721 CStreamDiagHandler* str = dynamic_cast<CStreamDiagHandler*>(m_OrigHandler.get()); 04722 if (str && str->GetLogName() == kLogName_Stderr) { 04723 m_OrigHandler.reset(); 04724 } 04725 } 04726 04727 04728 void CTeeDiagHandler::Post(const SDiagMessage& mess) 04729 { 04730 if ( m_OrigHandler.get() ) { 04731 m_OrigHandler->Post(mess); 04732 } 04733 04734 if ( mess.m_NoTee ) { 04735 // The message has been printed. 04736 return; 04737 } 04738 04739 // Ignore posts below the min severity and applog messages 04740 if ((mess.m_Flags & eDPF_AppLog) || 04741 CompareDiagPostLevel(mess.m_Severity, m_MinSev) < 0) { 04742 return; 04743 } 04744 04745 CNcbiOstrstream str_os; 04746 mess.x_OldWrite(str_os); 04747 CDiagLock lock(CDiagLock::ePost); 04748 cerr.write(str_os.str(), str_os.pcount()); 04749 str_os.rdbuf()->freeze(false); 04750 cerr << NcbiFlush; 04751 } 04752 04753 04754 extern void SetDiagHandler(CDiagHandler* handler, bool can_delete) 04755 { 04756 CDiagLock lock(CDiagLock::eWrite); 04757 CDiagContext& ctx = GetDiagContext(); 04758 bool report_switch = ctx.IsSetOldPostFormat() && 04759 CDiagContext::GetProcessPostNumber(ePostNumber_NoIncrement) > 0; 04760 string old_name, new_name; 04761 04762 if ( CDiagBuffer::sm_Handler ) { 04763 old_name = CDiagBuffer::sm_Handler->GetLogName(); 04764 } 04765 if ( handler ) { 04766 new_name = handler->GetLogName(); 04767 if (report_switch && new_name != old_name) { 04768 ctx.Extra().Print("switch_diag_to", new_name); 04769 } 04770 } 04771 if ( CDiagBuffer::sm_CanDeleteHandler ) 04772 delete CDiagBuffer::sm_Handler; 04773 if ( TTeeToStderr::GetDefault() ) { 04774 // Need to tee? 04775 handler = new CTeeDiagHandler(handler, can_delete); 04776 can_delete = true; 04777 } 04778 CDiagBuffer::sm_Handler = handler; 04779 CDiagBuffer::sm_CanDeleteHandler = can_delete; 04780 if (report_switch && !old_name.empty() && new_name != old_name) { 04781 ctx.Extra().Print("switch_diag_from", old_name); 04782 } 04783 // Unlock severity 04784 CDiagContext::SetApplogSeverityLocked(false); 04785 } 04786 04787 04788 extern bool IsSetDiagHandler(void) 04789 { 04790 return (CDiagBuffer::sm_Handler != s_DefaultHandler); 04791 } 04792 04793 extern CDiagHandler* GetDiagHandler(bool take_ownership) 04794 { 04795 CDiagLock lock(CDiagLock::eRead); 04796 if (take_ownership) { 04797 _ASSERT(CDiagBuffer::sm_CanDeleteHandler); 04798 CDiagBuffer::sm_CanDeleteHandler = false; 04799 } 04800 return CDiagBuffer::sm_Handler; 04801 } 04802 04803 04804 extern void DiagHandler_Reopen(void) 04805 { 04806 CDiagHandler* handler = GetDiagHandler(); 04807 if ( handler ) { 04808 handler->Reopen(CDiagHandler::fCheck); 04809 } 04810 } 04811 04812 04813 extern CDiagBuffer& GetDiagBuffer(void) 04814 { 04815 return CDiagContextThreadData::GetThreadData().GetDiagBuffer(); 04816 } 04817 04818 04819 void CDiagHandler::PostToConsole(const SDiagMessage& mess) 04820 { 04821 if (GetLogName() == kLogName_Stderr && 04822 IsVisibleDiagPostLevel(mess.m_Severity)) { 04823 // Already posted to console. 04824 return; 04825 } 04826 CDiagLock lock(CDiagLock::ePost); 04827 if ( IsSetDiagPostFlag(eDPF_AtomicWrite, mess.m_Flags) ) { 04828 CNcbiOstrstream str_os; 04829 str_os << mess; 04830 cerr.write(str_os.str(), str_os.pcount()); 04831 str_os.rdbuf()->freeze(false); 04832 } 04833 else { 04834 cerr << mess; 04835 } 04836 cerr << NcbiFlush; 04837 } 04838 04839 04840 string CDiagHandler::GetLogName(void) 04841 { 04842 string name = typeid(*this).name(); 04843 return name.empty() ? kLogName_Unknown 04844 : string(kLogName_Unknown) + "(" + name + ")"; 04845 } 04846 04847 04848 CStreamDiagHandler_Base::CStreamDiagHandler_Base(void) 04849 { 04850 SetLogName(kLogName_Stream); 04851 } 04852 04853 04854 string CStreamDiagHandler_Base::GetLogName(void) 04855 { 04856 return m_LogName; 04857 } 04858 04859 04860 void CStreamDiagHandler_Base::SetLogName(const string& log_name) 04861 { 04862 size_t len = min(log_name.length(), sizeof(m_LogName) - 1); 04863 memcpy(m_LogName, log_name.data(), len); 04864 m_LogName[len] = '\0'; 04865 } 04866 04867 04868 CStreamDiagHandler::CStreamDiagHandler(CNcbiOstream* os, 04869 bool quick_flush, 04870 const string& stream_name) 04871 : m_Stream(os), 04872 m_QuickFlush(quick_flush) 04873 { 04874 if ( !stream_name.empty() ) { 04875 SetLogName(stream_name); 04876 } 04877 } 04878 04879 04880 void CStreamDiagHandler::Post(const SDiagMessage& mess) 04881 { 04882 if ( !m_Stream ) { 04883 return; 04884 } 04885 CDiagLock lock(CDiagLock::ePost); 04886 if ( IsSetDiagPostFlag(eDPF_AtomicWrite, mess.m_Flags) ) { 04887 CNcbiOstrstream str_os; 04888 str_os << mess; 04889 m_Stream->write(str_os.str(), str_os.pcount()); 04890 str_os.rdbuf()->freeze(false); 04891 } 04892 else { 04893 *m_Stream << mess; 04894 } 04895 if (m_QuickFlush) { 04896 *m_Stream << NcbiFlush; 04897 } 04898 } 04899 04900 04901 CDiagFileHandleHolder::CDiagFileHandleHolder(const string& fname, 04902 CDiagHandler::TReopenFlags flags) 04903 : m_Handle(-1) 04904 { 04905 int mode = O_WRONLY | O_APPEND | O_CREAT; 04906 if (flags & CDiagHandler::fTruncate) { 04907 mode |= O_TRUNC; 04908 } 04909 04910 mode_t perm = CDirEntry::MakeModeT( 04911 CDirEntry::fRead | CDirEntry::fWrite, 04912 CDirEntry::fRead | CDirEntry::fWrite, 04913 CDirEntry::fRead | CDirEntry::fWrite, 04914 0); 04915 m_Handle = NcbiSys_open( 04916 _T_XCSTRING(CFile::ConvertToOSPath(fname)), 04917 mode, perm); 04918 } 04919 04920 CDiagFileHandleHolder::~CDiagFileHandleHolder(void) 04921 { 04922 if (m_Handle >= 0) { 04923 close(m_Handle); 04924 } 04925 } 04926 04927 04928 // CFileDiagHandler 04929 04930 CFileHandleDiagHandler::CFileHandleDiagHandler(const string& fname) 04931 : m_LowDiskSpace(false), 04932 m_Handle(NULL), 04933 m_HandleLock(new CSpinLock()), 04934 m_ReopenTimer(new CStopWatch()) 04935 { 04936 SetLogName(fname); 04937 Reopen(CDiagContext::GetLogTruncate() ? fTruncate : fDefault); 04938 } 04939 04940 04941 CFileHandleDiagHandler::~CFileHandleDiagHandler(void) 04942 { 04943 delete m_ReopenTimer; 04944 delete m_HandleLock; 04945 if (m_Handle) 04946 m_Handle->RemoveReference(); 04947 } 04948 04949 04950 void CFileHandleDiagHandler::SetLogName(const string& log_name) 04951 { 04952 string abs_name = CDirEntry::IsAbsolutePath(log_name) ? log_name 04953 : CDirEntry::CreateAbsolutePath(log_name); 04954 TParent::SetLogName(abs_name); 04955 } 04956 04957 04958 const int kLogReopenDelay = 60; // Reopen log every 60 seconds 04959 04960 void CFileHandleDiagHandler::Reopen(TReopenFlags flags) 04961 { 04962 s_ReopenEntered->Add(1); 04963 CDiagLock lock(CDiagLock::ePost); 04964 // Period is longer than for CFileDiagHandler to prevent double-reopening 04965 if (flags & fCheck && m_ReopenTimer->IsRunning()) { 04966 if (m_ReopenTimer->Elapsed() < kLogReopenDelay + 5) { 04967 s_ReopenEntered->Add(-1); 04968 return; 04969 } 04970 } 04971 04972 if (m_Handle) { 04973 // This feature of automatic log rotation will work correctly only on 04974 // Unix with only one CFileHandleDiagHandler for each physical file. 04975 // This is how it was requested to work by Denis Vakatov. 04976 long pos = lseek(m_Handle->GetHandle(), 0, SEEK_CUR); 04977 long limit = TLogSizeLimitParam::GetDefault(); 04978 if (limit > 0 && pos > limit) { 04979 CFile f(GetLogName()); 04980 f.Rename(GetLogName() + "-backup", CDirEntry::fRF_Overwrite); 04981 } 04982 } 04983 04984 m_LowDiskSpace = false; 04985 CDiagFileHandleHolder* new_handle; 04986 new_handle = new CDiagFileHandleHolder(GetLogName(), flags); 04987 new_handle->AddReference(); 04988 if (new_handle->GetHandle() == -1) { 04989 new_handle->RemoveReference(); 04990 new_handle = NULL; 04991 } 04992 else { 04993 // Need at least 20K of free space to write logs 04994 try { 04995 CDirEntry entry(GetLogName()); 04996 m_LowDiskSpace = CFileUtil::GetFreeDiskSpace(entry.GetDir()) < 1024*20; 04997 } 04998 catch (CException) { 04999 // Ignore error - could not check free space for some reason. 05000 // Try to open the file anyway. 05001 } 05002 if (m_LowDiskSpace) { 05003 new_handle->RemoveReference(); 05004 new_handle = NULL; 05005 } 05006 } 05007 05008 CDiagFileHandleHolder* old_handle; 05009 {{ 05010 CSpinGuard guard(*m_HandleLock); 05011 // Restart the timer even if failed to reopen the file. 05012 m_ReopenTimer->Restart(); 05013 old_handle = m_Handle; 05014 m_Handle = new_handle; 05015 }} 05016 05017 if (old_handle) 05018 old_handle->RemoveReference(); 05019 05020 if (!new_handle) { 05021 if ( !m_Messages.get() ) { 05022 m_Messages.reset(new TMessages); 05023 } 05024 } 05025 else if ( m_Messages.get() ) { 05026 // Flush the collected messages, if any, once the handle if available 05027 ITERATE(TMessages, it, *m_Messages) { 05028 CNcbiOstrstream str_os; 05029 str_os << *it; 05030 if (write(new_handle->GetHandle(), str_os.str(), 05031 (unsigned int)str_os.pcount())) {/*dummy*/}; 05032 str_os.rdbuf()->freeze(false); 05033 } 05034 m_Messages.reset(); 05035 } 05036 05037 s_ReopenEntered->Add(-1); 05038 } 05039 05040 05041 void CFileHandleDiagHandler::Post(const SDiagMessage& mess) 05042 { 05043 // Period is longer than for CFileDiagHandler to prevent double-reopening 05044 if (!m_ReopenTimer->IsRunning() || 05045 m_ReopenTimer->Elapsed() >= kLogReopenDelay + 5) 05046 { 05047 if (s_ReopenEntered->Add(1) == 1 || !m_ReopenTimer->IsRunning()) { 05048 CDiagLock lock(CDiagLock::ePost); 05049 if (!m_ReopenTimer->IsRunning() || 05050 m_ReopenTimer->Elapsed() >= kLogReopenDelay + 5) 05051 { 05052 Reopen(fDefault); 05053 } 05054 } 05055 s_ReopenEntered->Add(-1); 05056 } 05057 05058 // If the handle is not available, collect the messages until they 05059 // can be written. 05060 if ( m_Messages.get() ) { 05061 CDiagLock lock(CDiagLock::ePost); 05062 // Check again to make sure m_Messages still exists. 05063 if ( m_Messages.get() ) { 05064 // Limit number of stored messages to 1000 05065 if ( m_Messages->size() < 1000 ) { 05066 m_Messages->push_back(mess); 05067 } 05068 return; 05069 } 05070 } 05071 05072 CDiagFileHandleHolder* handle; 05073 {{ 05074 CSpinGuard guard(*m_HandleLock); 05075 handle = m_Handle; 05076 if (handle) 05077 handle->AddReference(); 05078 }} 05079 05080 if (handle) { 05081 CNcbiOstrstream str_os; 05082 str_os << mess; 05083 if (write(handle->GetHandle(), str_os.str(), 05084 (unsigned int)str_os.pcount())) {/*dummy*/}; 05085 str_os.rdbuf()->freeze(false); 05086 05087 handle->RemoveReference(); 05088 } 05089 } 05090 05091 05092 // CFileDiagHandler 05093 05094 static bool s_SplitLogFile = false; 05095 05096 extern void SetSplitLogFile(bool value) 05097 { 05098 s_SplitLogFile = value; 05099 } 05100 05101 05102 extern bool GetSplitLogFile(void) 05103 { 05104 return s_SplitLogFile; 05105 } 05106 05107 05108 bool s_IsSpecialLogName(const string& name) 05109 { 05110 return name.empty() 05111 || name == "-" 05112 || name == "/dev/null"; 05113 } 05114 05115 05116 CFileDiagHandler::CFileDiagHandler(void) 05117 : m_Err(0), 05118 m_OwnErr(false), 05119 m_Log(0), 05120 m_OwnLog(false), 05121 m_Trace(0), 05122 m_OwnTrace(false), 05123 m_Perf(0), 05124 m_OwnPerf(false), 05125 m_ReopenTimer(new CStopWatch()) 05126 { 05127 SetLogFile("-", eDiagFile_All, true); 05128 } 05129 05130 05131 CFileDiagHandler::~CFileDiagHandler(void) 05132 { 05133 x_ResetHandler(&m_Err, &m_OwnErr); 05134 x_ResetHandler(&m_Log, &m_OwnLog); 05135 x_ResetHandler(&m_Trace, &m_OwnTrace); 05136 x_ResetHandler(&m_Perf, &m_OwnPerf); 05137 delete m_ReopenTimer; 05138 } 05139 05140 05141 void CFileDiagHandler::SetLogName(const string& log_name) 05142 { 05143 string abs_name = CDirEntry::IsAbsolutePath(log_name) ? log_name 05144 : CDirEntry::CreateAbsolutePath(log_name); 05145 TParent::SetLogName(abs_name); 05146 } 05147 05148 05149 void CFileDiagHandler::x_ResetHandler(CStreamDiagHandler_Base** ptr, 05150 bool* owned) 05151 { 05152 if (!ptr || !(*ptr)) { 05153 return; 05154 } 05155 _ASSERT(owned); 05156 if ( *owned ) { 05157 if (ptr != &m_Err && *ptr == m_Err) { 05158 // The handler is also used by m_Err 05159 _ASSERT(!m_OwnErr); 05160 m_OwnErr = true; // now it's owned as m_Err 05161 *owned = false; 05162 } 05163 else if (ptr != &m_Log && *ptr == m_Log) { 05164 _ASSERT(!m_OwnLog); 05165 m_OwnLog = true; 05166 *owned = false; 05167 } 05168 else if (ptr != &m_Trace && *ptr == m_Trace) { 05169 _ASSERT(!m_OwnTrace); 05170 m_OwnTrace = true; 05171 *owned = false; 05172 } 05173 else if (ptr != &m_Perf && *ptr == m_Perf) { 05174 _ASSERT(!m_OwnPerf); 05175 m_OwnPerf = true; 05176 *owned = false; 05177 } 05178 if (*owned) { 05179 delete *ptr; 05180 } 05181 } 05182 *owned = false; 05183 *ptr = 0; 05184 } 05185 05186 05187 void CFileDiagHandler::x_SetHandler(CStreamDiagHandler_Base** member, 05188 bool* own_member, 05189 CStreamDiagHandler_Base* handler, 05190 bool own) 05191 { 05192 if (*member == handler) { 05193 *member = 0; 05194 *own_member = false; 05195 } 05196 else { 05197 x_ResetHandler(member, own_member); 05198 } 05199 if (handler && own) { 05200 // Check if the handler is already owned 05201 if (member != &m_Err) { 05202 if (handler == m_Err && m_OwnErr) { 05203 own = false; 05204 } 05205 } 05206 if (member != &m_Log) { 05207 if (handler == m_Log && m_OwnLog) { 05208 own = false; 05209 } 05210 } 05211 if (member != &m_Trace) { 05212 if (handler == m_Trace && m_OwnTrace) { 05213 own = false; 05214 } 05215 } 05216 if (member != &m_Perf) { 05217 if (handler == m_Perf && m_OwnPerf) { 05218 own = false; 05219 } 05220 } 05221 } 05222 *member = handler; 05223 *own_member = own; 05224 } 05225 05226 05227 void CFileDiagHandler::SetOwnership(CStreamDiagHandler_Base* handler, bool own) 05228 { 05229 if (!handler) { 05230 return; 05231 } 05232 if (m_Err == handler) { 05233 m_OwnErr = own; 05234 own = false; 05235 } 05236 if (m_Log == handler) { 05237 m_OwnLog = own; 05238 own = false; 05239 } 05240 if (m_Trace == handler) { 05241 m_OwnTrace = own; 05242 own = false; 05243 } 05244 if (m_Perf == handler) { 05245 m_OwnPerf = own; 05246 own = false; 05247 } 05248 } 05249 05250 05251 static bool 05252 s_CreateHandler(const string& fname, 05253 auto_ptr<CStreamDiagHandler_Base>& handler) 05254 { 05255 if ( fname.empty() || fname == "/dev/null") { 05256 handler.reset(); 05257 return true; 05258 } 05259 if (fname == "-") { 05260 handler.reset(new CStreamDiagHandler(&NcbiCerr, true, kLogName_Stderr)); 05261 return true; 05262 } 05263 auto_ptr<CFileHandleDiagHandler> fh(new CFileHandleDiagHandler(fname)); 05264 if ( !fh->Valid() ) { 05265 ERR_POST_X(7, Info << "Failed to open log file: " << fname); 05266 return false; 05267 } 05268 handler.reset(fh.release()); 05269 return true; 05270 } 05271 05272 05273 bool CFileDiagHandler::SetLogFile(const string& file_name, 05274 EDiagFileType file_type, 05275 bool /*quick_flush*/) 05276 { 05277 bool special = s_IsSpecialLogName(file_name); 05278 auto_ptr<CStreamDiagHandler_Base> err_handler, log_handler, 05279 trace_handler, perf_handler; 05280 switch ( file_type ) { 05281 case eDiagFile_All: 05282 { 05283 // Remove known extension if any 05284 string adj_name = file_name; 05285 if ( !special ) { 05286 CDirEntry entry(file_name); 05287 string ext = entry.GetExt(); 05288 if (ext == ".log" || 05289 ext == ".err" || 05290 ext == ".trace" || 05291 ext == ".perf") { 05292 adj_name = entry.GetDir() + entry.GetBase(); 05293 } 05294 } 05295 string err_name = special ? adj_name : adj_name + ".err"; 05296 string log_name = special ? adj_name : adj_name + ".log"; 05297 string trace_name = special ? adj_name : adj_name + ".trace"; 05298 string perf_name = special ? adj_name : adj_name + ".perf"; 05299 05300 if (!s_CreateHandler(err_name, err_handler)) 05301 return false; 05302 if (!s_CreateHandler(log_name, log_handler)) 05303 return false; 05304 if (!s_CreateHandler(trace_name, trace_handler)) 05305 return false; 05306 if (!s_CreateHandler(perf_name, perf_handler)) 05307 return false; 05308 05309 x_SetHandler(&m_Err, &m_OwnErr, err_handler.release(), true); 05310 x_SetHandler(&m_Log, &m_OwnLog, log_handler.release(), true); 05311 x_SetHandler(&m_Trace, &m_OwnTrace, trace_handler.release(), true); 05312 x_SetHandler(&m_Perf, &m_OwnPerf, perf_handler.release(), true); 05313 m_ReopenTimer->Restart(); 05314 break; 05315 } 05316 case eDiagFile_Err: 05317 if (!s_CreateHandler(file_name, err_handler)) 05318 return false; 05319 x_SetHandler(&m_Err, &m_OwnErr, err_handler.release(), true); 05320 break; 05321 case eDiagFile_Log: 05322 if (!s_CreateHandler(file_name, log_handler)) 05323 return false; 05324 x_SetHandler(&m_Log, &m_OwnLog, log_handler.release(), true); 05325 break; 05326 case eDiagFile_Trace: 05327 if (!s_CreateHandler(file_name, trace_handler)) 05328 return false; 05329 x_SetHandler(&m_Trace, &m_OwnTrace, trace_handler.release(), true); 05330 break; 05331 case eDiagFile_Perf: 05332 if (!s_CreateHandler(file_name, perf_handler)) 05333 return false; 05334 x_SetHandler(&m_Perf, &m_OwnPerf, perf_handler.release(), true); 05335 break; 05336 } 05337 if (file_name == "") { 05338 SetLogName(kLogName_None); 05339 } 05340 else if (file_name == "-") { 05341 SetLogName(kLogName_Stderr); 05342 } 05343 else { 05344 SetLogName(file_name); 05345 } 05346 return true; 05347 } 05348 05349 05350 string CFileDiagHandler::GetLogFile(EDiagFileType file_type) const 05351 { 05352 switch ( file_type ) { 05353 case eDiagFile_Err: 05354 return m_Err->GetLogName(); 05355 case eDiagFile_Log: 05356 return m_Log->GetLogName(); 05357 case eDiagFile_Trace: 05358 return m_Trace->GetLogName(); 05359 case eDiagFile_Perf: 05360 return m_Perf->GetLogName(); 05361 case eDiagFile_All: 05362 break; // kEmptyStr 05363 } 05364 return kEmptyStr; 05365 } 05366 05367 05368 CNcbiOstream* CFileDiagHandler::GetLogStream(EDiagFileType file_type) 05369 { 05370 CStreamDiagHandler_Base* handler = 0; 05371 switch ( file_type ) { 05372 case eDiagFile_Err: 05373 handler = m_Err; 05374 case eDiagFile_Log: 05375 handler = m_Log; 05376 case eDiagFile_Trace: 05377 handler = m_Trace; 05378 case eDiagFile_Perf: 05379 handler = m_Perf; 05380 case eDiagFile_All: 05381 return 0; 05382 } 05383 return handler ? handler->GetStream() : 0; 05384 } 05385 05386 05387 void CFileDiagHandler::SetSubHandler(CStreamDiagHandler_Base* handler, 05388 EDiagFileType file_type, 05389 bool own) 05390 { 05391 switch ( file_type ) { 05392 case eDiagFile_All: 05393 // Must set all handlers 05394 case eDiagFile_Err: 05395 x_SetHandler(&m_Err, &m_OwnErr, handler, own); 05396 if (file_type != eDiagFile_All) break; 05397 case eDiagFile_Log: 05398 x_SetHandler(&m_Log, &m_OwnLog, handler, own); 05399 if (file_type != eDiagFile_All) break; 05400 case eDiagFile_Trace: 05401 x_SetHandler(&m_Trace, &m_OwnTrace, handler, own); 05402 if (file_type != eDiagFile_All) break; 05403 case eDiagFile_Perf: 05404 x_SetHandler(&m_Perf, &m_OwnPerf, handler, own); 05405 if (file_type != eDiagFile_All) break; 05406 } 05407 } 05408 05409 05410 void CFileDiagHandler::Reopen(TReopenFlags flags) 05411 { 05412 s_ReopenEntered->Add(1); 05413 05414 if (flags & fCheck && m_ReopenTimer->IsRunning()) { 05415 if (m_ReopenTimer->Elapsed() < kLogReopenDelay) { 05416 s_ReopenEntered->Add(-1); 05417 return; 05418 } 05419 } 05420 if ( m_Err ) { 05421 m_Err->Reopen(flags); 05422 } 05423 if ( m_Log ) { 05424 m_Log->Reopen(flags); 05425 } 05426 if ( m_Trace ) { 05427 m_Trace->Reopen(flags); 05428 } 05429 if ( m_Perf ) { 05430 m_Perf->Reopen(flags); 05431 } 05432 m_ReopenTimer->Restart(); 05433 05434 s_ReopenEntered->Add(-1); 05435 } 05436 05437 05438 void CFileDiagHandler::Post(const SDiagMessage& mess) 05439 { 05440 // Check time and re-open the streams 05441 if (!m_ReopenTimer->IsRunning() || 05442 m_ReopenTimer->Elapsed() >= kLogReopenDelay) 05443 { 05444 if (s_ReopenEntered->Add(1) == 1 || !m_ReopenTimer->IsRunning()) { 05445 CDiagLock lock(CDiagLock::ePost); 05446 if (!m_ReopenTimer->IsRunning() || 05447 m_ReopenTimer->Elapsed() >= kLogReopenDelay) 05448 { 05449 Reopen(fDefault); 05450 } 05451 } 05452 s_ReopenEntered->Add(-1); 05453 } 05454 05455 // Output the message 05456 CStreamDiagHandler_Base* handler = 0; 05457 if ( IsSetDiagPostFlag(eDPF_AppLog, mess.m_Flags) ) { 05458 handler = mess.m_Event == SDiagMessage::eEvent_PerfLog 05459 ? m_Perf : m_Log; 05460 } 05461 else { 05462 switch ( mess.m_Severity ) { 05463 case eDiag_Info: 05464 case eDiag_Trace: 05465 handler = m_Trace; 05466 break; 05467 default: 05468 handler = m_Err; 05469 } 05470 } 05471 if (handler) 05472 handler->Post(mess); 05473 } 05474 05475 05476 class CAsyncDiagThread : public CThread 05477 { 05478 public: 05479 CAsyncDiagThread(void); 05480 virtual ~CAsyncDiagThread(void); 05481 05482 virtual void* Main(void); 05483 void Stop(void); 05484 05485 05486 bool m_NeedStop; 05487 Uint2 m_CntWaiters; 05488 CAtomicCounter m_MsgsInQueue; 05489 CDiagHandler* m_SubHandler; 05490 CFastMutex m_QueueLock; 05491 #ifdef NCBI_HAVE_CONDITIONAL_VARIABLE 05492 CConditionVariable m_QueueCond; 05493 CConditionVariable m_DequeueCond; 05494 #else 05495 CSemaphore m_QueueSem; 05496 CSemaphore m_DequeueSem; 05497 #endif 05498 deque<SDiagMessage*> m_MsgQueue; 05499 }; 05500 05501 05502 /// Maximum number of messages that allowed to be in the queue for 05503 /// asynchronous processing. 05504 NCBI_PARAM_DECL(Uint4, Diag, Max_Async_Queue_Size); 05505 NCBI_PARAM_DEF_EX(Uint4, Diag, Max_Async_Queue_Size, 10000, eParam_NoThread, 05506 DIAG_MAX_ASYNC_QUEUE_SIZE); 05507 typedef NCBI_PARAM_TYPE(Diag, Max_Async_Queue_Size) TMaxAsyncQueueSizeParam; 05508 05509 05510 CAsyncDiagHandler::CAsyncDiagHandler(void) 05511 : m_AsyncThread(NULL) 05512 {} 05513 05514 CAsyncDiagHandler::~CAsyncDiagHandler(void) 05515 { 05516 _ASSERT(!m_AsyncThread); 05517 } 05518 05519 void 05520 CAsyncDiagHandler::InstallToDiag(void) 05521 { 05522 m_AsyncThread = new CAsyncDiagThread(); 05523 m_AsyncThread->AddReference(); 05524 try { 05525 m_AsyncThread->Run(); 05526 } 05527 catch (CThreadException&) { 05528 m_AsyncThread->RemoveReference(); 05529 m_AsyncThread = NULL; 05530 throw; 05531 } 05532 m_AsyncThread->m_SubHandler = GetDiagHandler(true); 05533 SetDiagHandler(this, false); 05534 } 05535 05536 void 05537 CAsyncDiagHandler::RemoveFromDiag(void) 05538 { 05539 if (!m_AsyncThread) 05540 return; 05541 05542 _ASSERT(GetDiagHandler(false) == this); 05543 SetDiagHandler(m_AsyncThread->m_SubHandler); 05544 m_AsyncThread->Stop(); 05545 m_AsyncThread->RemoveReference(); 05546 m_AsyncThread = NULL; 05547 } 05548 05549 string 05550 CAsyncDiagHandler::GetLogName(void) 05551 { 05552 return m_AsyncThread->m_SubHandler->GetLogName(); 05553 } 05554 05555 void 05556 CAsyncDiagHandler::Reopen(TReopenFlags flags) 05557 { 05558 m_AsyncThread->m_SubHandler->Reopen(flags); 05559 } 05560 05561 void 05562 CAsyncDiagHandler::Post(const SDiagMessage& mess) 05563 { 05564 CAsyncDiagThread* thr = m_AsyncThread; 05565 SDiagMessage* save_msg = new SDiagMessage(mess); 05566 05567 if (save_msg->m_Severity < GetDiagDieLevel()) { 05568 CFastMutexGuard guard(thr->m_QueueLock); 05569 while (Uint4(thr->m_MsgsInQueue.Get()) >= TMaxAsyncQueueSizeParam::GetDefault()) 05570 { 05571 ++thr->m_CntWaiters; 05572 #ifdef NCBI_HAVE_CONDITIONAL_VARIABLE 05573 thr->m_DequeueCond.WaitForSignal(thr->m_QueueLock); 05574 #else 05575 guard.Release(); 05576 thr->m_QueueSem.Wait(); 05577 guard.Guard(thr->m_QueueLock); 05578 #endif 05579 --thr->m_CntWaiters; 05580 } 05581 thr->m_MsgQueue.push_back(save_msg); 05582 if (thr->m_MsgsInQueue.Add(1) == 1) { 05583 #ifdef NCBI_HAVE_CONDITIONAL_VARIABLE 05584 thr->m_QueueCond.SignalSome(); 05585 #else 05586 thr->m_QueueSem.Post(); 05587 #endif 05588 } 05589 } 05590 else { 05591 thr->Stop(); 05592 thr->m_SubHandler->Post(*save_msg); 05593 } 05594 } 05595 05596 05597 CAsyncDiagThread::CAsyncDiagThread(void) 05598 : m_NeedStop(false), 05599 m_CntWaiters(0), 05600 m_SubHandler(NULL) 05601 #ifndef NCBI_HAVE_CONDITIONAL_VARIABLE 05602 , m_QueueSem(0, 100) 05603 , m_DequeueSem(0, 10000000) 05604 #endif 05605 { 05606 m_MsgsInQueue.Set(0); 05607 } 05608 05609 CAsyncDiagThread::~CAsyncDiagThread(void) 05610 {} 05611 05612 void* 05613 CAsyncDiagThread::Main(void) 05614 { 05615 deque<SDiagMessage*> save_msgs; 05616 while (!m_NeedStop) { 05617 {{ 05618 CFastMutexGuard guard(m_QueueLock); 05619 while (m_MsgQueue.size() == 0 && !m_NeedStop) { 05620 if (m_MsgsInQueue.Get() != 0) 05621 abort(); 05622 #ifdef NCBI_HAVE_CONDITIONAL_VARIABLE 05623 m_QueueCond.WaitForSignal(m_QueueLock); 05624 #else 05625 guard.Release(); 05626 m_QueueSem.Wait(); 05627 guard.Guard(m_QueueLock); 05628 #endif 05629 } 05630 save_msgs.swap(m_MsgQueue); 05631 }} 05632 05633 drain_messages: 05634 while (!save_msgs.empty()) { 05635 SDiagMessage* msg = save_msgs.front(); 05636 save_msgs.pop_front(); 05637 m_SubHandler->Post(*msg); 05638 delete msg; 05639 m_MsgsInQueue.Add(-1); 05640 if (m_CntWaiters != 0) { 05641 #ifdef NCBI_HAVE_CONDITIONAL_VARIABLE 05642 m_DequeueCond.SignalSome(); 05643 #else 05644 m_DequeueSem.Post(); 05645 #endif 05646 } 05647 } 05648 } 05649 if (m_MsgQueue.size() != 0) { 05650 save_msgs.swap(m_MsgQueue); 05651 goto drain_messages; 05652 } 05653 return NULL; 05654 } 05655 05656 void 05657 CAsyncDiagThread::Stop(void) 05658 { 05659 m_NeedStop = true; 05660 try { 05661 #ifdef NCBI_HAVE_CONDITIONAL_VARIABLE 05662 m_QueueCond.SignalAll(); 05663 #else 05664 m_QueueSem.Post(10); 05665 #endif 05666 Join(); 05667 } 05668 catch (CException& ex) { 05669 ERR_POST_X(24, Critical 05670 << "Error while stopping thread for AsyncDiagHandler: " << ex); 05671 } 05672 } 05673 05674 05675 extern bool SetLogFile(const string& file_name, 05676 EDiagFileType file_type, 05677 bool quick_flush) 05678 { 05679 // Check if a non-existing dir is specified 05680 if ( !s_IsSpecialLogName(file_name) ) { 05681 string dir = CFile(file_name).GetDir(); 05682 if ( !dir.empty() && !CDir(dir).Exists() ) { 05683 return false; 05684 } 05685 } 05686 05687 if (file_type != eDiagFile_All) { 05688 // Auto-split log file 05689 SetSplitLogFile(true); 05690 } 05691 bool no_split = !s_SplitLogFile; 05692 if ( no_split ) { 05693 if (file_type != eDiagFile_All) { 05694 ERR_POST_X(8, Info << 05695 "Failed to set log file for the selected event type: " 05696 "split log is disabled"); 05697 return false; 05698 } 05699 // Check special filenames 05700 if ( file_name.empty() || file_name == "/dev/null" ) { 05701 // no output 05702 SetDiagStream(0, quick_flush, 0, 0, kLogName_None); 05703 } 05704 else if (file_name == "-") { 05705 // output to stderr 05706 SetDiagStream(&NcbiCerr, quick_flush, 0, 0, kLogName_Stderr); 05707 } 05708 else { 05709 // output to file 05710 auto_ptr<CFileHandleDiagHandler> fhandler( 05711 new CFileHandleDiagHandler(file_name)); 05712 if ( !fhandler->Valid() ) { 05713 ERR_POST_X(9, Info << "Failed to initialize log: " << file_name); 05714 return false; 05715 } 05716 SetDiagHandler(fhandler.release()); 05717 } 05718 } 05719 else { 05720 CFileDiagHandler* handler = 05721 dynamic_cast<CFileDiagHandler*>(GetDiagHandler()); 05722 if ( !handler ) { 05723 CStreamDiagHandler_Base* sub_handler = 05724 dynamic_cast<CStreamDiagHandler_Base*>(GetDiagHandler()); 05725 // Install new handler, try to re-use the old one 05726 auto_ptr<CFileDiagHandler> fhandler(new CFileDiagHandler()); 05727 // If we are going to set all three handlers, no need to save 05728 // the old one. 05729 if ( sub_handler && file_type != eDiagFile_All) { 05730 GetDiagHandler(true); // Take ownership! 05731 // Set all three handlers to the old one. 05732 fhandler->SetSubHandler(sub_handler, eDiagFile_All, false); 05733 } 05734 if ( fhandler->SetLogFile(file_name, file_type, quick_flush) ) { 05735 SetDiagHandler(fhandler.release()); 05736 return true; 05737 } 05738 else { 05739 return false; 05740 } 05741 } 05742 // Update the existing handler 05743 CDiagContext::SetApplogSeverityLocked(false); 05744 return handler->SetLogFile(file_name, file_type, quick_flush); 05745 } 05746 return true; 05747 } 05748 05749 05750 extern string GetLogFile(EDiagFileType file_type) 05751 { 05752 CDiagHandler* handler = GetDiagHandler(); 05753 CFileDiagHandler* fhandler = 05754 dynamic_cast<CFileDiagHandler*>(handler); 05755 if ( fhandler ) { 05756 return fhandler->GetLogFile(file_type); 05757 } 05758 CFileHandleDiagHandler* fhhandler = 05759 dynamic_cast<CFileHandleDiagHandler*>(handler); 05760 if ( fhhandler ) { 05761 return fhhandler->GetLogName(); 05762 } 05763 return kEmptyStr; 05764 } 05765 05766 05767 extern string GetLogFile(void) 05768 { 05769 CDiagHandler* handler = GetDiagHandler(); 05770 return handler ? handler->GetLogName() : kEmptyStr; 05771 } 05772 05773 05774 extern bool IsDiagStream(const CNcbiOstream* os) 05775 { 05776 CStreamDiagHandler_Base* sdh 05777 = dynamic_cast<CStreamDiagHandler_Base*>(CDiagBuffer::sm_Handler); 05778 return (sdh && sdh->GetStream() == os); 05779 } 05780 05781 05782 extern void SetDiagErrCodeInfo(CDiagErrCodeInfo* info, bool can_delete) 05783 { 05784 CDiagLock lock(CDiagLock::eWrite); 05785 if ( CDiagBuffer::sm_CanDeleteErrCodeInfo && 05786 CDiagBuffer::sm_ErrCodeInfo ) 05787 delete CDiagBuffer::sm_ErrCodeInfo; 05788 CDiagBuffer::sm_ErrCodeInfo = info; 05789 CDiagBuffer::sm_CanDeleteErrCodeInfo = can_delete; 05790 } 05791 05792 extern bool IsSetDiagErrCodeInfo(void) 05793 { 05794 return (CDiagBuffer::sm_ErrCodeInfo != 0); 05795 } 05796 05797 extern CDiagErrCodeInfo* GetDiagErrCodeInfo(bool take_ownership) 05798 { 05799 CDiagLock lock(CDiagLock::eRead); 05800 if (take_ownership) { 05801 _ASSERT(CDiagBuffer::sm_CanDeleteErrCodeInfo); 05802 CDiagBuffer::sm_CanDeleteErrCodeInfo = false; 05803 } 05804 return CDiagBuffer::sm_ErrCodeInfo; 05805 } 05806 05807 05808 extern void SetDiagFilter(EDiagFilter what, const char* filter_str) 05809 { 05810 CDiagLock lock(CDiagLock::eWrite); 05811 if (what == eDiagFilter_Trace || what == eDiagFilter_All) 05812 s_TraceFilter->Fill(filter_str); 05813 05814 if (what == eDiagFilter_Post || what == eDiagFilter_All) 05815 s_PostFilter->Fill(filter_str); 05816 } 05817 05818 05819 05820 /////////////////////////////////////////////////////// 05821 // CNcbiDiag:: 05822 05823 CNcbiDiag::CNcbiDiag(EDiagSev sev, TDiagPostFlags post_flags) 05824 : m_Severity(sev), 05825 m_ErrCode(0), 05826 m_ErrSubCode(0), 05827 m_Buffer(GetDiagBuffer()), 05828 m_PostFlags(ForceImportantFlags(post_flags)), 05829 m_Line(0), 05830 m_ValChngFlags(0) 05831 { 05832 } 05833 05834 05835 CNcbiDiag::CNcbiDiag(const CDiagCompileInfo &info, 05836 EDiagSev sev, TDiagPostFlags post_flags) 05837 : m_Severity(sev), 05838 m_ErrCode(0), 05839 m_ErrSubCode(0), 05840 m_Buffer(GetDiagBuffer()), 05841 m_PostFlags(ForceImportantFlags(post_flags)), 05842 m_CompileInfo(info), 05843 m_Line(info.GetLine()), 05844 m_ValChngFlags(0) 05845 { 05846 SetFile( info.GetFile() ); 05847 SetModule( info.GetModule() ); 05848 } 05849 05850 CNcbiDiag::~CNcbiDiag(void) 05851 { 05852 m_Buffer.Detach(this); 05853 } 05854 05855 TDiagPostFlags CNcbiDiag::ForceImportantFlags(TDiagPostFlags flags) 05856 { 05857 if ( !IsSetDiagPostFlag(eDPF_UseExactUserFlags, flags) ) { 05858 flags = (flags & (~eDPF_ImportantFlagsMask)) | 05859 (CDiagBuffer::s_GetPostFlags() & eDPF_ImportantFlagsMask); 05860 } 05861 return flags; 05862 } 05863 05864 const CNcbiDiag& CNcbiDiag::SetFile(const char* file) const 05865 { 05866 m_File = file; 05867 m_ValChngFlags |= fFileIsChanged; 05868 return *this; 05869 } 05870 05871 05872 const CNcbiDiag& CNcbiDiag::SetModule(const char* module) const 05873 { 05874 m_Module = module; 05875 m_ValChngFlags |= fModuleIsChanged; 05876 return *this; 05877 } 05878 05879 05880 const CNcbiDiag& CNcbiDiag::SetClass(const char* nclass ) const 05881 { 05882 m_Class = nclass; 05883 m_ValChngFlags |= fClassIsChanged; 05884 return *this; 05885 } 05886 05887 05888 const CNcbiDiag& CNcbiDiag::SetFunction(const char* function) const 05889 { 05890 m_Function = function; 05891 m_ValChngFlags |= fFunctionIsChanged; 05892 return *this; 05893 } 05894 05895 05896 bool CNcbiDiag::CheckFilters(void) const 05897 { 05898 EDiagSev current_sev = GetSeverity(); 05899 if (current_sev == eDiag_Fatal) 05900 return true; 05901 05902 CDiagLock lock(CDiagLock::eRead); 05903 if (GetSeverity() == eDiag_Trace) { 05904 // check for trace filter 05905 return s_TraceFilter->Check(*this, this->GetSeverity()) 05906 != eDiagFilter_Reject; 05907 } 05908 05909 // check for post filter and severity 05910 return s_PostFilter->Check(*this, this->GetSeverity()) 05911 != eDiagFilter_Reject; 05912 } 05913 05914 05915 // Formatted output of stack trace 05916 void s_FormatStackTrace(CNcbiOstream& os, const CStackTrace& trace) 05917 { 05918 string old_prefix = trace.GetPrefix(); 05919 trace.SetPrefix(" "); 05920 os << "\n Stack trace:\n" << trace; 05921 trace.SetPrefix(old_prefix); 05922 } 05923 05924 05925 const CNcbiDiag& CNcbiDiag::Put(const CStackTrace*, 05926 const CStackTrace& stacktrace) const 05927 { 05928 if ( !stacktrace.Empty() ) { 05929 stacktrace.SetPrefix(" "); 05930 ostrstream os; 05931 s_FormatStackTrace(os, stacktrace); 05932 *this << (string) CNcbiOstrstreamToString(os); 05933 } 05934 return *this; 05935 } 05936 05937 static string 05938 s_GetExceptionText(const CException* pex) 05939 { 05940 string text(pex->GetMsg()); 05941 ostrstream os; 05942 pex->ReportExtra(os); 05943 if (os.pcount() != 0) { 05944 text += " ("; 05945 text += (string) CNcbiOstrstreamToString(os); 05946 text += ')'; 05947 } 05948 return text; 05949 } 05950 05951 const CNcbiDiag& CNcbiDiag::x_Put(const CException& ex) const 05952 { 05953 if (m_Buffer.SeverityDisabled(GetSeverity()) || !CheckFilters()) 05954 return *this; 05955 05956 CDiagContextThreadData& thr_data = 05957 CDiagContextThreadData::GetThreadData(); 05958 CDiagCollectGuard* guard = thr_data.GetCollectGuard(); 05959 EDiagSev print_sev = AdjustApplogPrintableSeverity(CDiagBuffer::sm_PostSeverity); 05960 EDiagSev collect_sev = print_sev; 05961 if ( guard ) { 05962 print_sev = AdjustApplogPrintableSeverity(guard->GetPrintSeverity()); 05963 collect_sev = guard->GetCollectSeverity(); 05964 } 05965 05966 const CException* pex; 05967 const CException* main_pex = NULL; 05968 stack<const CException*> pile; 05969 // invert the order 05970 for (pex = &ex; pex; pex = pex->GetPredecessor()) { 05971 pile.push(pex); 05972 if (!main_pex && pex->HasMainText()) 05973 main_pex = pex; 05974 } 05975 if (!main_pex) 05976 main_pex = pile.top(); 05977 if (m_Buffer.m_Stream->pcount()) { 05978 *this << "(" << main_pex->GetType() << "::" 05979 << main_pex->GetErrCodeString() << ") " 05980 << s_GetExceptionText(main_pex); 05981 } 05982 for (; !pile.empty(); pile.pop()) { 05983 pex = pile.top(); 05984 string text(s_GetExceptionText(pex)); 05985 const CStackTrace* stacktrace = pex->GetStackTrace(); 05986 if ( stacktrace ) { 05987 ostrstream os; 05988 s_FormatStackTrace(os, *stacktrace); 05989 text += (string) CNcbiOstrstreamToString(os); 05990 } 05991 string err_type(pex->GetType()); 05992 err_type += "::"; 05993 err_type += pex->GetErrCodeString(); 05994 05995 EDiagSev pex_sev = pex->GetSeverity(); 05996 if (CompareDiagPostLevel(GetSeverity(), print_sev) < 0) { 05997 if (CompareDiagPostLevel(pex_sev, collect_sev) < 0) 05998 pex_sev = collect_sev; 05999 } 06000 else { 06001 if (CompareDiagPostLevel(pex_sev, print_sev) < 0) 06002 pex_sev = print_sev; 06003 } 06004 if (CompareDiagPostLevel(GetSeverity(), pex_sev) < 0) 06005 pex_sev = GetSeverity(); 06006 06007 SDiagMessage diagmsg 06008 (pex_sev, 06009 text.c_str(), 06010 text.size(), 06011 pex->GetFile().c_str(), 06012 pex->GetLine(), 06013 GetPostFlags(), 06014 NULL, 06015 pex->GetErrCode(), 06016 0, 06017 err_type.c_str(), 06018 pex->GetModule().c_str(), 06019 pex->GetClass().c_str(), 06020 pex->GetFunction().c_str()); 06021 06022 m_Buffer.PrintMessage(diagmsg, *this); 06023 } 06024 06025 06026 return *this; 06027 } 06028 06029 06030 bool CNcbiDiag::StrToSeverityLevel(const char* str_sev, EDiagSev& sev) 06031 { 06032 if (!str_sev || !*str_sev) { 06033 return false; 06034 } 06035 // Digital value 06036 int nsev = NStr::StringToNonNegativeInt(str_sev); 06037 06038 if (nsev > eDiagSevMax) { 06039 nsev = eDiagSevMax; 06040 } else if ( nsev == -1 ) { 06041 // String value 06042 for (int s = eDiagSevMin; s <= eDiagSevMax; s++) { 06043 if (NStr::CompareNocase(CNcbiDiag::SeverityName(EDiagSev(s)), 06044 str_sev) == 0) { 06045 nsev = s; 06046 break; 06047 } 06048 } 06049 } 06050 sev = EDiagSev(nsev); 06051 // Unknown value 06052 return sev >= eDiagSevMin && sev <= eDiagSevMax; 06053 } 06054 06055 void CNcbiDiag::DiagFatal(const CDiagCompileInfo& info, 06056 const char* message) 06057 { 06058 CNcbiDiag(info, NCBI_NS_NCBI::eDiag_Fatal) << message << Endm; 06059 } 06060 06061 void CNcbiDiag::DiagTrouble(const CDiagCompileInfo& info, 06062 const char* message) 06063 { 06064 DiagFatal(info, message); 06065 } 06066 06067 void CNcbiDiag::DiagAssert(const CDiagCompileInfo& info, 06068 const char* expression, 06069 const char* message) 06070 { 06071 CNcbiDiag(info, NCBI_NS_NCBI::eDiag_Fatal, eDPF_Trace) << 06072 "Assertion failed: (" << 06073 (expression ? expression : "") << ") " << 06074 (message ? message : "") << Endm; 06075 } 06076 06077 void CNcbiDiag::DiagAssertIfSuppressedSystemMessageBox( 06078 const CDiagCompileInfo& info, 06079 const char* expression, 06080 const char* message) 06081 { 06082 if ( IsSuppressedDebugSystemMessageBox() ) { 06083 DiagAssert(info, expression, message); 06084 } 06085 } 06086 06087 void CNcbiDiag::DiagValidate(const CDiagCompileInfo& info, 06088 const char* _DEBUG_ARG(expression), 06089 const char* message) 06090 { 06091 #ifdef _DEBUG 06092 if ( xncbi_GetValidateAction() != eValidate_Throw ) { 06093 DiagAssert(info, expression, message); 06094 } 06095 #endif 06096 throw CCoreException(info, 0, CCoreException::eCore, message); 06097 } 06098 06099 /////////////////////////////////////////////////////// 06100 // CDiagRestorer:: 06101 06102 CDiagRestorer::CDiagRestorer(void) 06103 { 06104 CDiagLock lock(CDiagLock::eWrite); 06105 const CDiagBuffer& buf = GetDiagBuffer(); 06106 m_PostPrefix = buf.m_PostPrefix; 06107 m_PrefixList = buf.m_PrefixList; 06108 m_PostFlags = buf.sx_GetPostFlags(); 06109 m_PostSeverity = buf.sm_PostSeverity; 06110 m_PostSeverityChange = buf.sm_PostSeverityChange; 06111 m_IgnoreToDie = buf.sm_IgnoreToDie; 06112 m_DieSeverity = buf.sm_DieSeverity; 06113 m_TraceDefault = buf.sm_TraceDefault; 06114 m_TraceEnabled = buf.sm_TraceEnabled; 06115 m_Handler = buf.sm_Handler; 06116 m_CanDeleteHandler = buf.sm_CanDeleteHandler; 06117 m_ErrCodeInfo = buf.sm_ErrCodeInfo; 06118 m_CanDeleteErrCodeInfo = buf.sm_CanDeleteErrCodeInfo; 06119 m_ApplogSeverityLocked = CDiagContext::IsApplogSeverityLocked(); 06120 06121 // avoid premature cleanup 06122 buf.sm_CanDeleteHandler = false; 06123 buf.sm_CanDeleteErrCodeInfo = false; 06124 } 06125 06126 CDiagRestorer::~CDiagRestorer(void) 06127 { 06128 {{ 06129 CDiagLock lock(CDiagLock::eWrite); 06130 CDiagBuffer& buf = GetDiagBuffer(); 06131 buf.m_PostPrefix = m_PostPrefix; 06132 buf.m_PrefixList = m_PrefixList; 06133 buf.sx_GetPostFlags() = m_PostFlags; 06134 buf.sm_PostSeverity = m_PostSeverity; 06135 buf.sm_PostSeverityChange = m_PostSeverityChange; 06136 buf.sm_IgnoreToDie = m_IgnoreToDie; 06137 buf.sm_DieSeverity = m_DieSeverity; 06138 buf.sm_TraceDefault = m_TraceDefault; 06139 buf.sm_TraceEnabled = m_TraceEnabled; 06140 }} 06141 SetDiagHandler(m_Handler, m_CanDeleteHandler); 06142 SetDiagErrCodeInfo(m_ErrCodeInfo, m_CanDeleteErrCodeInfo); 06143 CDiagContext::SetApplogSeverityLocked(m_ApplogSeverityLocked); 06144 } 06145 06146 06147 ////////////////////////////////////////////////////// 06148 // internal diag. handler classes for compatibility: 06149 06150 class CCompatDiagHandler : public CDiagHandler 06151 { 06152 public: 06153 CCompatDiagHandler(FDiagHandler func, void* data, FDiagCleanup cleanup) 06154 : m_Func(func), m_Data(data), m_Cleanup(cleanup) 06155 { } 06156 ~CCompatDiagHandler(void) 06157 { 06158 if (m_Cleanup) { 06159 m_Cleanup(m_Data); 06160 } 06161 } 06162 virtual void Post(const SDiagMessage& mess) { m_Func(mess); } 06163 06164 private: 06165 FDiagHandler m_Func; 06166 void* m_Data; 06167 FDiagCleanup m_Cleanup; 06168 }; 06169 06170 06171 extern void SetDiagHandler(FDiagHandler func, 06172 void* data, 06173 FDiagCleanup cleanup) 06174 { 06175 SetDiagHandler(new CCompatDiagHandler(func, data, cleanup)); 06176 } 06177 06178 06179 class CCompatStreamDiagHandler : public CStreamDiagHandler 06180 { 06181 public: 06182 CCompatStreamDiagHandler(CNcbiOstream* os, 06183 bool quick_flush = true, 06184 FDiagCleanup cleanup = 0, 06185 void* cleanup_data = 0, 06186 const string& stream_name = kEmptyStr) 06187 : CStreamDiagHandler(os, quick_flush, stream_name), 06188 m_Cleanup(cleanup), m_CleanupData(cleanup_data) 06189 { 06190 } 06191 06192 ~CCompatStreamDiagHandler(void) 06193 { 06194 if (m_Cleanup) { 06195 m_Cleanup(m_CleanupData); 06196 } 06197 } 06198 06199 private: 06200 FDiagCleanup m_Cleanup; 06201 void* m_CleanupData; 06202 }; 06203 06204 06205 extern void SetDiagStream(CNcbiOstream* os, 06206 bool quick_flush, 06207 FDiagCleanup cleanup, 06208 void* cleanup_data, 06209 const string& stream_name) 06210 { 06211 string str_name = stream_name; 06212 if ( str_name.empty() ) { 06213 if (os == &cerr) { 06214 str_name = kLogName_Stderr; 06215 } 06216 else if (os == &cout) { 06217 str_name = kLogName_Stdout; 06218 } 06219 else { 06220 str_name = kLogName_Stream; 06221 } 06222 } 06223 SetDiagHandler(new CCompatStreamDiagHandler(os, quick_flush, 06224 cleanup, cleanup_data, str_name)); 06225 } 06226 06227 06228 extern CNcbiOstream* GetDiagStream(void) 06229 { 06230 CDiagHandler* diagh = GetDiagHandler(); 06231 if ( !diagh ) { 06232 return 0; 06233 } 06234 CStreamDiagHandler_Base* sh = 06235 dynamic_cast<CStreamDiagHandler_Base*>(diagh); 06236 // This can also be CFileDiagHandler, check it later 06237 if ( sh && sh->GetStream() ) { 06238 return sh->GetStream(); 06239 } 06240 CFileDiagHandler* fh = 06241 dynamic_cast<CFileDiagHandler*>(diagh); 06242 if ( fh ) { 06243 return fh->GetLogStream(eDiagFile_Err); 06244 } 06245 return 0; 06246 } 06247 06248 06249 extern void SetDoubleDiagHandler(void) 06250 { 06251 ERR_POST_X(10, Error << "SetDoubleDiagHandler() is not implemented"); 06252 } 06253 06254 06255 ////////////////////////////////////////////////////// 06256 // abort handler 06257 06258 06259 static FAbortHandler s_UserAbortHandler = 0; 06260 06261 extern void SetAbortHandler(FAbortHandler func) 06262 { 06263 s_UserAbortHandler = func; 06264 } 06265 06266 06267 extern void Abort(void) 06268 { 06269 // If defined user abort handler then call it 06270 if ( s_UserAbortHandler ) 06271 s_UserAbortHandler(); 06272 06273 // If don't defined handler or application doesn't still terminated 06274 06275 // Check environment variable for silent exit 06276 const TXChar* value = NcbiSys_getenv(_TX("DIAG_SILENT_ABORT")); 06277 if (value && (*value == _TX('Y') || *value == _TX('y') || *value == _TX('1'))) { 06278 ::exit(255); 06279 } 06280 else if (value && (*value == _TX('N') || *value == _TX('n') || *value == _TX('0'))) { 06281 ::abort(); 06282 } 06283 else 06284 #define NCBI_TOTALVIEW_ABORT_WORKAROUND 1 06285 #if defined(NCBI_TOTALVIEW_ABORT_WORKAROUND) 06286 // The condition in the following if statement is always 'true'. 06287 // It's a workaround for TotalView 6.5 (beta) to properly display 06288 // stacktrace at this point. 06289 // if ( !(value && *value == 'Y') ) 06290 #endif 06291 { 06292 #if defined(_DEBUG) 06293 ::abort(); 06294 #else 06295 ::exit(255); 06296 #endif 06297 } 06298 } 06299 06300 06301 /////////////////////////////////////////////////////// 06302 // CDiagErrCodeInfo:: 06303 // 06304 06305 SDiagErrCodeDescription::SDiagErrCodeDescription(void) 06306 : m_Message(kEmptyStr), 06307 m_Explanation(kEmptyStr), 06308 m_Severity(-1) 06309 { 06310 return; 06311 } 06312 06313 06314 bool CDiagErrCodeInfo::Read(const string& file_name) 06315 { 06316 CNcbiIfstream is(file_name.c_str()); 06317 if ( !is.good() ) { 06318 return false; 06319 } 06320 return Read(is); 06321 } 06322 06323 06324 // Parse string for CDiagErrCodeInfo::Read() 06325 06326 bool s_ParseErrCodeInfoStr(string& str, 06327 const SIZE_TYPE line, 06328 int& x_code, 06329 int& x_severity, 06330 string& x_message, 06331 bool& x_ready) 06332 { 06333 list<string> tokens; // List with line tokens 06334 06335 try { 06336 // Get message text 06337 SIZE_TYPE pos = str.find_first_of(':'); 06338 if (pos == NPOS) { 06339 x_message = kEmptyStr; 06340 } else { 06341 x_message = NStr::TruncateSpaces(str.substr(pos+1)); 06342 str.erase(pos); 06343 } 06344 06345 // Split string on parts 06346 NStr::Split(str, ",", tokens); 06347 if (tokens.size() < 2) { 06348 ERR_POST_X(11, "Error message file parsing: Incorrect file format " 06349 ", line " + NStr::UInt8ToString(line)); 06350 return false; 06351 } 06352 // Mnemonic name (skip) 06353 tokens.pop_front(); 06354 06355 // Error code 06356 string token = NStr::TruncateSpaces(tokens.front()); 06357 tokens.pop_front(); 06358 x_code = NStr::StringToInt(token); 06359 06360 // Severity 06361 if ( !tokens.empty() ) { 06362 token = NStr::TruncateSpaces(tokens.front()); 06363 EDiagSev sev; 06364 if (CNcbiDiag::StrToSeverityLevel(token.c_str(), sev)) { 06365 x_severity = sev; 06366 } else { 06367 ERR_POST_X(12, Warning << "Error message file parsing: " 06368 "Incorrect severity level in the verbose " 06369 "message file, line " + NStr::UInt8ToString(line)); 06370 } 06371 } else { 06372 x_severity = -1; 06373 } 06374 } 06375 catch (CException& e) { 06376 ERR_POST_X(13, Warning << "Error message file parsing: " << e.GetMsg() << 06377 ", line " + NStr::UInt8ToString(line)); 06378 return false; 06379 } 06380 x_ready = true; 06381 return true; 06382 } 06383 06384 06385 bool CDiagErrCodeInfo::Read(CNcbiIstream& is) 06386 { 06387 string str; // The line being parsed 06388 SIZE_TYPE line; // # of the line being parsed 06389 bool err_ready = false; // Error data ready flag 06390 int err_code = 0; // First level error code 06391 int err_subcode = 0; // Second level error code 06392 string err_message; // Short message 06393 string err_text; // Error explanation 06394 int err_severity = -1; // Use default severity if 06395 // has not specified 06396 int err_subseverity = -1; // Use parents severity if 06397 // has not specified 06398 06399 for (line = 1; NcbiGetlineEOL(is, str); line++) { 06400 06401 // This is a comment or empty line 06402 if (!str.length() || NStr::StartsWith(str,"#")) { 06403 continue; 06404 } 06405 // Add error description 06406 if (err_ready && str[0] == '$') { 06407 if (err_subseverity == -1) 06408 err_subseverity = err_severity; 06409 SetDescription(ErrCode(err_code, err_subcode), 06410 SDiagErrCodeDescription(err_message, err_text, 06411 err_subseverity)); 06412 // Clean 06413 err_subseverity = -1; 06414 err_text = kEmptyStr; 06415 err_ready = false; 06416 } 06417 06418 // Get error code 06419 if (NStr::StartsWith(str,"$$")) { 06420 if (!s_ParseErrCodeInfoStr(str, line, err_code, err_severity, 06421 err_message, err_ready)) 06422 continue; 06423 err_subcode = 0; 06424 06425 } else if (NStr::StartsWith(str,"$^")) { 06426 // Get error subcode 06427 s_ParseErrCodeInfoStr(str, line, err_subcode, err_subseverity, 06428 err_message, err_ready); 06429 06430 } else if (err_ready) { 06431 // Get line of explanation message 06432 if (!err_text.empty()) { 06433 err_text += '\n'; 06434 } 06435 err_text += str; 06436 } 06437 } 06438 if (err_ready) { 06439 if (err_subseverity == -1) 06440 err_subseverity = err_severity; 06441 SetDescription(ErrCode(err_code, err_subcode), 06442 SDiagErrCodeDescription(err_message, err_text, err_subseverity)); 06443 } 06444 return true; 06445 } 06446 06447 06448 bool CDiagErrCodeInfo::GetDescription(const ErrCode& err_code, 06449 SDiagErrCodeDescription* description) 06450 const 06451 { 06452 // Find entry 06453 TInfo::const_iterator find_entry = m_Info.find(err_code); 06454 if (find_entry == m_Info.end()) { 06455 return false; 06456 } 06457 // Get entry value 06458 const SDiagErrCodeDescription& entry = find_entry->second; 06459 if (description) { 06460 *description = entry; 06461 } 06462 return true; 06463 } 06464 06465 06466 const char* g_DiagUnknownFunction(void) 06467 { 06468 return kEmptyCStr; 06469 } 06470 06471 06472 CDiagContext_Extra g_PostPerf(int status, 06473 double timespan, 06474 SDiagMessage::TExtraArgs& args) 06475 { 06476 return CDiagContext_Extra(status, timespan, args); 06477 } 06478 06479 06480 END_NCBI_SCOPE
1.7.5.1
Modified on Wed May 23 12:57:42 2012 by modify_doxy.py rev. 337098