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