src/corelib/ncbidiag.cpp

Go to the documentation of this file.
00001 /*  $Id: ncbidiag.cpp 177966 2009-12-07 19:28:10Z grichenk $
00002  * ===========================================================================
00003  *
00004  *                            PUBLIC DOMAIN NOTICE
00005  *               National Center for Biotechnology Information
00006  *
00007  *  This software/database is a "United States Government Work" under the
00008  *  terms of the United States Copyright Act.  It was written as part of
00009  *  the author's official duties as a United States Government employee and
00010  *  thus cannot be copyrighted.  This software/database is freely available
00011  *  to the public for use. The National Library of Medicine and the U.S.
00012  *  Government have not placed any restriction on its use or reproduction.
00013  *
00014  *  Although all reasonable efforts have been taken to ensure the accuracy
00015  *  and reliability of the software and data, the NLM and the U.S.
00016  *  Government do not and cannot warrant the performance or results that
00017  *  may be obtained by using this software or data. The NLM and the U.S.
00018  *  Government disclaim all warranties, express or implied, including
00019  *  warranties of performance, merchantability or fitness for any particular
00020  *  purpose.
00021  *
00022  *  Please cite the author in any work or product based on this material.
00023  *
00024  * ===========================================================================
00025  *
00026  * Authors:  Denis Vakatov et al.
00027  *
00028  * File Description:
00029  *   NCBI C++ diagnostic API
00030  *
00031  */
00032 
00033 
00034 #include <ncbi_pch.hpp>
00035 
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> // for pthread_atfork()
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 //  Output format parameters
00095 
00096 // Use old output format if the flag is set
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 // Auto-print context properties on set/change.
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 // Print system TID rather than CThread::GetSelf()
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 // Use assert() instead of abort() in Abort() function to show the assertion
00115 // dialog and allow to choose the action (stop/debug/continue).
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 // Limit log file size, rotate log when it reaches the limit.
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 //  Output rate control parameters
00130 
00131 // AppLog limit per period
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 // AppLog period, sec
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 // ErrLog limit per period
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 // ErrLog period, sec
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 // TraceLog limit per period
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 // TraceLog period, sec
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 // Duplicate messages to STDERR
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 // Minimum severity of the messages duplicated to STDERR
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     // the severities will be adjusted by x_Init()
00222     x_Init(eDiag_Critical, eDiag_Fatal, eDiscard);
00223 }
00224 
00225 
00226 CDiagCollectGuard::CDiagCollectGuard(EDiagSev print_severity)
00227 {
00228     // the severities will be adjusted by x_Init()
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     // the severities will be adjusted by x_Init()
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     // Get current print severity
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 //  Static variables for Trace and Post filters
00309 
00310 static CSafeStaticPtr<CDiagFilter> s_TraceFilter;
00311 static CSafeStaticPtr<CDiagFilter> s_PostFilter;
00312 
00313 
00314 // Analogue to strstr.
00315 // Returns a pointer to the last occurrence of a search string in a string
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 /// CDiagCompileInfo::
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     // Check for a file extension without creating of temporary string objects
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 // Skip matching l/r separators, return the last char before them
00444 // or null if the separators are unbalanced.
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     // Parse curr_funct
00473 
00474     // Skip function arguments
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         // Missing '('
00481         return;
00482     }
00483     if ( end_str ) {
00484         // Skip template arguments
00485         end_str = find_match('<', '>', m_CurrFunctName, end_str);
00486     }
00487     if ( !end_str ) {
00488         return;
00489     }
00490     // Get a function/method name
00491     const char* start_str = NULL;
00492 
00493     // Get a finction start position.
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     // Get a class name
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 //  CDiagRecycler::
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,   // before
00533                        s_NcbiDiagPostFork,  // after in parent
00534                        s_NcbiDiagPostFork); // after in child
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 //  CDiagContextThreadData::
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); // GCC 3.4.6 gives warning - ignore it.
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; // re-enable protection
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         // Copy properties from the main thread's TLS to the global properties.
00608         CMutexGuard LOCK(s_DiagMutex);
00609         CDiagContextThreadData::TProperties* props =
00610             value->GetProperties(CDiagContextThreadData::eProp_Get); /* NCBI_FAKE_WARNING */
00611         if ( props ) {
00612             GetDiagContext().m_Properties.insert(props->begin(),
00613                                                  props->end());
00614         }
00615         // Print stop message.
00616         if (!CDiagContext::IsSetOldPostFormat()  &&  s_FinishedSetupDiag) {
00617             GetDiagContext().PrintStop();
00618         }
00619         s_ThreadDataState = eDeinitialized; // re-enable protection
00620     }
00621     delete value;
00622 }
00623 
00624 
00625 CDiagContextThreadData& CDiagContextThreadData::GetThreadData(void)
00626 {
00627     // If any of this method's direct or indirect callees attempted to
00628     // report a (typically fatal) error, the result would ordinarily
00629     // be infinite recursion resulting in an undignified crash.  The
00630     // business with s_ThreadDataState allows the program to exit
00631     // (relatively) gracefully in such cases.
00632     //
00633     // In principle, such an event could happen at any stage; in
00634     // practice, however, the first call involves a superset of most
00635     // following calls' actions, at least until deep program
00636     // finalization.  Moreover, attempting to catch bad calls
00637     // mid-execution would both add overhead and open up uncatchable
00638     // opportunities for inappropriate recursion.
00639 
00640     static volatile CThreadSystemID s_LastThreadID
00641         = THREAD_SYSTEM_ID_INITIALIZER;
00642 
00643     if (s_ThreadDataState != eInitialized) {
00644         // Avoid false positives, while also taking care not to call
00645         // anything that might itself produce diagnostics.
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         // Cleanup data set to null for any thread except the main one.
00685         // This value is used as a flag to copy threads' properties to global
00686         // upon TLS cleanup.
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; // The guard has been already released
00740     }
00741     m_CollectGuards.erase(itg);
00742     if ( !m_CollectGuards.empty() ) {
00743         return;
00744         // Previously printing was done for each guard, discarding - only for
00745         // the last guard.
00746     }
00747     // If this is the last guard, perform its action
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 //  CDiagContext::
00816 
00817 
00818 // AppState formatting/parsing
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     // Throw to notify caller about invalid app state.
00837     NCBI_THROW(CException, eUnknown, "Invalid EDiagAppState value");
00838     /*NOTREACHED*/
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     // If the following properties are not set, take them from DiagContext.
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), // limit number of collected messages to 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         // Parent process does not need to update pid/guid
01109         return;
01110     }
01111     sm_PID = new_pid;
01112     CDiagContext& ctx = GetDiagContext();
01113     TUID old_uid = ctx.GetUID();
01114     // Update GUID to match the new PID
01115     ctx.x_CreateUID();
01116     ctx.Extra().
01117         Print("action", "fork").
01118         Print("parent_guid", ctx.GetStringUID(old_uid));
01119     //ctx.PrintExtra("New process created by fork(), "
01120     //    "parent GUID=" + );
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     // The low 4 bits are reserved as GUID generator version number.
01135     m_UID = (TUID(h) << 48) |
01136         ((TUID(pid) & 0xFFFF) << 32) |
01137         ((TUID(t) & 0xFFFFFFF) << 4) |
01138         1; // version #1 - fixed type conversion bug
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     // Clear old timestamp
01174     uid &= ~((TUID)0xFFFFFFF << 4);
01175     // Add current timestamp
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     // Check context properties
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     // UNIX - use uname()
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     // MSWIN - use COMPUTERNAME
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     // Server env. - use SERVER_ADDR
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     // Initialize m_Host, this does not change m_HostIP
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         // AppName can be set only once
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     // Global properties
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     // Request properties
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         // Can not set this property
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); /* NCBI_FAKE_WARNING */
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     // Global properties
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     // Request properties
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); /* NCBI_FAKE_WARNING */
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     // Check global properties
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); /* NCBI_FAKE_WARNING */
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     // Check global properties
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); /* NCBI_FAKE_WARNING */
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, // no message
01618                       0, 0, // file, line
01619                       CNcbiDiag::ForceImportantFlags(kApplogDiagPostFlags),
01620                       NULL,
01621                       0, 0, // err code/subcode
01622                       NULL,
01623                       0, 0, 0); // module/class/function
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     // This checks thread's state first, then calls GetAppState if necessary.
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 // Fixed fields' widths
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     // Print common fields
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* /*cleanup_data*/)
01826 {
01827     delete value;
01828 }
01829 
01830 
01831 void CDiagContext::x_StartRequest(void)
01832 {
01833     // Reset properties
01834     CRequestContext& ctx = GetRequestContext();
01835     if ( ctx.IsRunning() ) {
01836         // The request is already running -
01837         // duplicate request start or missing request stop
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                 // The request is not running -
01877                 // duplicate request stop or missing request start
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, // file, line
01898                       CNcbiDiag::ForceImportantFlags(kApplogDiagPostFlags),
01899                       NULL,
01900                       0, 0, // err code/subcode
01901                       NULL,
01902                       0, 0, 0); // module/class/function
01903     mess.m_Event = event;
01904     CDiagBuffer::DiagHandler(mess);
01905     ostr.rdbuf()->freeze(false);
01906     // Now it's safe to reset the request context
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     //ERR_POST_X(1, Message << "***** BEGIN COLLECTED MESSAGES *****");
01961     ITERATE(TMessages, it, *tmp.get()) {
01962         handler.Post(*it);
01963     }
01964     //ERR_POST_X(2, Message << "***** END COLLECTED MESSAGES *****");
01965     m_Messages.reset(tmp.release());
01966 }
01967 
01968 
01969 void CDiagContext::DiscardMessages(void)
01970 {
01971     m_Messages.reset();
01972 }
01973 
01974 
01975 // Diagnostics setup
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     // Find the first dir name corresponding to one of the entries
01999     ITERATE(list<string>, it, entries) {
02000         if (!it->empty()  &&  (*it)[0] != '/') {
02001             // not an absolute path
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             // absolute path
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     // Could not find a valid web-dir entry, use /log/port or empty string
02024     // to try /log/srv later.
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     // In eDS_User mode do not use config unless IgnoreEnvArg
02053     // is set to true.
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     // Try to switch to /log/ if available.
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     // Initialize message collecting
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; // By default consider it's a new name
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; // prevent flushing to memory
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             // log_switched = true;
02153             collect = eDCM_Discard;
02154             break;
02155         case eDS_AppSpecific:
02156             if ( app ) {
02157                 app->SetupDiag_AppSpecific(); /* NCBI_FAKE_WARNING */
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                     // fall through
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                         // Try /log/<port>
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                         // Try /log/srv if port is unknown or not writable
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                         // Try to switch to /log/fallback/
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                     // Try cwd/ for eDS_ToStdlog only
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                     // Try to switch to /log/fallback/UNKNOWN
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     // Allow to modify severity level
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); // No log size limit
02265         SetDiagPostLevel(eDiag_Warning);
02266         // Lock severity level
02267         DisableDiagPostLevelChange(true);
02268     }
02269     else {
02270         if ( s_MergeLinesSetBySetupDiag ) {
02271             UnsetDiagPostFlag(eDPF_PreMergeLines);
02272             UnsetDiagPostFlag(eDPF_MergeLines);
02273         }
02274         // Disable throttling
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         // Flush and discard
02283         if ( log_switched  &&  handler ) {
02284             ctx.FlushMessages(*handler);
02285         }
02286         collect = eDCM_Discard;
02287     }
02288     else if (collect == eDCM_NoChange) {
02289         // Flush but don't discard
02290         if ( log_switched  &&  handler ) {
02291             ctx.FlushMessages(*handler);
02292         }
02293     }
02294     if (collect == eDCM_Discard) {
02295         ctx.DiscardMessages();
02296     }
02297 
02298     // Refresh rate controls
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     // Make the context live longer than other diag safe-statics
02320     static CSafeStaticPtr<CDiagContext> s_DiagContext(0,
02321         CSafeStaticLifeSpan(CSafeStaticLifeSpan::eLifeSpan_Long));
02322 
02323     return s_DiagContext.Get();
02324 }
02325 
02326 
02327 ///////////////////////////////////////////////////////
02328 //  CDiagBuffer::
02329 
02330 #if defined(NDEBUG)
02331 EDiagSev       CDiagBuffer::sm_PostSeverity       = eDiag_Error;
02332 #else
02333 EDiagSev       CDiagBuffer::sm_PostSeverity       = eDiag_Warning;
02334 #endif /* else!NDEBUG */
02335 
02336 EDiagSevChange CDiagBuffer::sm_PostSeverityChange = eDiagSC_Unknown;
02337                                                   // to be set on first request
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;     // to be set on first request
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 // MT-safe initialization of the default handler
02395 CDiagHandler* CreateDefaultDiagHandler(void);
02396 
02397 
02398 // Use s_DefaultHandler only for purposes of comparison, as installing
02399 // another handler will normally delete it.
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 // For initialization only
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 // Note: Intel Thread Checker detects a memory leak at the line:
02425 //       m_Stream(new CNcbiOstrstream) below
02426 //       This is not a fault of the toolkit code as soon as a code like:
02427 //       int main() {
02428 //           ostrstream *  s = new ostrstream;
02429 //           delete s;
02430 //           return 0;
02431 //       }
02432 //       will also report memory leaks.
02433 // Test environment:
02434 // - Intel Thread Checker for Linux 3.1
02435 // - gcc 4.0.1, gcc 4.1.2, icc 10.1
02436 // - Linux64
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             // The mutex must be locked before approving.
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                 // Substitute the original message with the error.
02471                 // ERR_POST can not be used here since nested posts
02472                 // are blocked. Have to create the message manually.
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, // Error code
02497                     23,                                 // Err subcode
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; // trace is disabled
02527     }
02528     if (post_sev == eDiag_Trace  &&  allow_trace) {
02529         return false; // everything is enabled
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; // trace is disabled
02545     }
02546     if (post_sev == eDiag_Trace  &&  allow_trace) {
02547         return true; // everything is enabled
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     // Check severity level change status
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     // Do nothing if diag severity is lower than allowed
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         // normally only happens once, so might as well pull everything
02617         // in for the record...
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     // KCC's implementation of "freeze(false)" makes the ostrstream buffer
02654     // stuck.  We need to replace the frozen stream with the new one.
02655     delete ostr;
02656     m_Stream = new CNcbiOstrstream;
02657 #else
02658     // reset flags to initial value
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 //  CDiagMessage::
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,    // start position
02895                size_t        width,  // fixed width or 0
02896                char          sep)    // trailing separator (throw if not found)
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,              // start position
02926                        char          sep,              // separator
02927                        bool          optional = false) // do not throw if not found
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         // The separator is in the next position, no empty string allowed
02945         NCBI_THROW(CException, eUnknown,
02946             "Failed to parse diagnostic message");
02947     }
02948     // remember end position of the string, skip separators
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; // NStr::TruncateSpaces(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     // Do not use default URL-decoder since it does not check invalid chars
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         // Try to decode only the name, leave the value as-is.
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         // Fixed prefix
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             // Newer format, app state is present.
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             // Older format, no app state.
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         // Date and time. Try all known formats.
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         // Host
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         // Client
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         // Session ID
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         // Application name
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         // Severity or event type
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                 // Get the real severity
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             // Check event type rather than severity level
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             // The rest is the message (do not parse status, bytes etc.)
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         // Find message separator
03292         size_t sep_pos = message.find(" --- ", pos);
03293 
03294         // <module>, <module>(<err_code>.<err_subcode>) or <module>(<err_text>)
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                     // Space(s) inside the error text, try to find closing ')'
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                     // skip space(s)
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                     // Try to parse error code/subcode
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             // ["<file>", ][line <line>][:]
03355             pos++; // skip "
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             // Class:: Class::Function() ::Function()
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                     // Remove "()"
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         // All the rest goes to message - no way to parse prefix/error code.
03402         // [<prefix1>::<prefix2>::.....]
03403         // <message>
03404         // <err_code_message> and <err_code_explanation>
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         // Dirty check for PID/TID/RID
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                     // Not a valid prefix - append to the previous message
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     // GetDiagContext().PushMessage(*this);
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     // Copy to STDERR
03578     if ( TTeeToStderr::GetDefault() ) {
03579         bool visible = CompareDiagPostLevel(m_Severity,
03580             TTeeMinSeverity::GetDefault()) >= 0;
03581         if ( visible ) {
03582             // Reset flags - STDERR does not need to skip prefix or merge lines
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         // Just check the source string, it may contain only valid chars
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     // Encode value
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     // Date & time
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 /*HAVE_LOCALTIME_R*/
03673         NStr::strftime(datetime, sizeof(datetime), timefmt, tm);
03674         os << datetime;
03675     }
03676     // "<file>"
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     // , line <line>
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     // Get error code description
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     // <severity>:
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     // (<err_code>.<err_subcode>) or (err_text)
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     // Module::Class::Function -
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         // Module:: Module::Class Module::Class::Function()
03752         // ::Class ::Class::Function()
03753         // Module::Function() Function()
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     // [<prefix1>::<prefix2>::.....]
03782     if (m_Prefix  &&  *m_Prefix  &&  IsSetDiagPostFlag(eDPF_Prefix, m_Flags))
03783         os << '[' << m_Prefix << "] ";
03784 
03785     // <message>
03786     if (m_BufferLen)
03787         os.write(m_Buffer, m_BufferLen);
03788 
03789     // <err_code_message> and <err_code_explanation>
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     // Endl
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     // Get error code description
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     // <severity>:
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) // add 1 for space
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     // <module>-<err_code>.<err_subcode> or <module>-<err_text>
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     // "<file>"
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     // , line <line>
03882     if ( m_Line )
03883         os << (print_file ? ", line " : "line ") << m_Line;
03884     // :
03885     if (print_file  ||  m_Line)
03886         os << ": ";
03887 
03888     // Class::Function
03889     bool print_loc = (m_Class && *m_Class ) || (m_Function && *m_Function);
03890     if (print_loc) {
03891         // Class:: Class::Function() ::Function()
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     // [<prefix1>::<prefix2>::.....]
03907     if (m_Prefix  &&  *m_Prefix  &&  IsSetDiagPostFlag(eDPF_Prefix, m_Flags))
03908         os << '[' << m_Prefix << "] ";
03909 
03910     // <message>
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     // <err_code_message> and <err_code_explanation>
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     // Endl
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 //  CDiagAutoPrefix::
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 //  EXTERN
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     // Assume flag is set by user
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     // Assume flag is set by user
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             // special case
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             // ctx.PrintExtra("Switching diagnostics to " + new_name);
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         // ctx.PrintExtra("Switched diagnostics from " + old_name);
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 // CFileDiagHandler
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; // Reopen log every 60 seconds
04393 
04394 void CFileHandleDiagHandler::Reopen(TReopenFlags flags)
04395 {
04396     // Period is longer than for CFileDiagHandler to prevent double-reopening
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         // ERR_POST_X(6, Info << "Failed to reopen log: " << msg);
04448         return;
04449     }
04450     // Flush the collected messages, if any, once the handle if available
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     // Period is longer than for CFileDiagHandler to prevent double-reopening
04466     if (!m_ReopenTimer->IsRunning()  ||
04467         m_ReopenTimer->Elapsed() >= kLogReopenDelay + 5) {
04468         Reopen(fDefault);
04469     }
04470 
04471     // If the handle is not available, collect the messages until they
04472     // can be written.
04473     if ( m_Messages.get() ) {
04474         // Limit number of stored messages to 1000
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 // CFileDiagHandler
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             // The handler is also used by m_Err
04544             _ASSERT(!m_OwnErr);
04545             m_OwnErr = true; // now it's owned as m_Err
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         // Check if the handler is already owned
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         // GetMode() failed, use directory instead, must be writable
04628         string dir = entry.GetDir();
04629         entry = CDirEntry(dir.empty() ? "." : dir);
04630         if ( !entry.GetMode(&mode) ) {
04631             return false;
04632         }
04633     }
04634     // Need at least 20K of free space to write logs
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          /*quick_flush*/)
04667 {
04668     bool special = s_IsSpecialLogName(file_name);
04669     bool failed = false;
04670     switch ( file_type ) {
04671     case eDiagFile_All:
04672         {
04673             // Remove known extension if any
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;  // kEmptyStr
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         // Must set all three handlers
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     // Check time and re-open the streams
04812     if (!m_ReopenTimer->IsRunning()  ||
04813         m_ReopenTimer->Elapsed() >= kLogReopenDelay) {
04814         Reopen(fDefault);
04815     }
04816 
04817     // Output the message
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     // Check if a non-existing dir is specified
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         // Auto-split log file
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         // Check special filenames
04864         if ( file_name.empty()  ||  file_name == "/dev/null" ) {
04865             // no output
04866             SetDiagStream(0, quick_flush, 0, 0, kLogName_None);
04867         }
04868         else if (file_name == "-") {
04869             // output to stderr
04870             SetDiagStream(&NcbiCerr, quick_flush, 0, 0, kLogName_Stderr);
04871         }
04872         else {
04873             if ( !s_CanOpenLogFile(file_name) ) {
04874                 return false;
04875             }
04876             // output to file
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             // Install new handler, try to re-use the old one
04893             auto_ptr<CFileDiagHandler> fhandler(new CFileDiagHandler());
04894             // If we are going to set all three handlers, no need to save
04895             // the old one.
04896             if ( sub_handler  &&  file_type != eDiagFile_All) {
04897                 GetDiagHandler(true); // Take ownership!
04898                 // Set all three handlers to the old one.
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         // Update the existing handler
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     // check for trace filter
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     // check for post filter
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 //  CNcbiDiag::
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         // check for trace filter
05096         return  s_TraceFilter->Check(*this, this->GetSeverity())
05097                 != eDiagFilter_Reject;
05098     }
05099     
05100     // check for post filter and severity
05101     return  s_PostFilter->Check(*this, this->GetSeverity())
05102             != eDiagFilter_Reject;
05103 }
05104 
05105 
05106 // Formatted output of stack trace
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     // invert the order
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 << "    "; // indentation
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     // Digital value
05198     int nsev = NStr::StringToNumeric(str_sev);
05199 
05200     if (nsev > eDiagSevMax) {
05201         nsev = eDiagSevMax;
05202     } else if ( nsev == -1 ) {
05203         // String value
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     // Unknown value
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 //  CDiagRestorer::
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     // avoid premature cleanup
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 //  internal diag. handler classes for compatibility:
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     // This can also be CFileDiagHandler, check it later
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 //  abort handler
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     // If defined user abort handler then call it 
05408     if ( s_UserAbortHandler )
05409         s_UserAbortHandler();
05410     
05411     // If don't defined handler or application doesn't still terminated
05412 
05413     // Check environment variable for silent exit
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         // The condition in the following if statement is always 'true'.
05425         // It's a workaround for TotalView 6.5 (beta) to properly display
05426         // stacktrace at this point.
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); // Show assertion dialog
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 //  CDiagErrCodeInfo::
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 // Parse string for CDiagErrCodeInfo::Read()
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;    // List with line tokens
05485 
05486     try {
05487         // Get message text
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         // Split string on parts
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         // Mnemonic name (skip)
05504         tokens.pop_front();
05505 
05506         // Error code
05507         string token = NStr::TruncateSpaces(tokens.front());
05508         tokens.pop_front();
05509         x_code = NStr::StringToInt(token);
05510 
05511         // Severity
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;                      // The line being parsed
05539     SIZE_TYPE    line;                     // # of the line being parsed
05540     bool         err_ready       = false;  // Error data ready flag 
05541     int          err_code        = 0;      // First level error code
05542     int          err_subcode     = 0;      // Second level error code
05543     string       err_message;              // Short message
05544     string       err_text;                 // Error explanation
05545     int          err_severity    = -1;     // Use default severity if  
05546                                            // has not specified
05547     int          err_subseverity = -1;     // Use parents severity if  
05548                                            // has not specified
05549 
05550     for (line = 1;  NcbiGetlineEOL(is, str);  line++) {
05551         
05552         // This is a comment or empty line
05553         if (!str.length()  ||  NStr::StartsWith(str,"#")) {
05554             continue;
05555         }
05556         // Add error description
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             // Clean
05564             err_subseverity = -1;
05565             err_text     = kEmptyStr;
05566             err_ready    = false;
05567         }
05568 
05569         // Get error code
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         // Get error subcode
05578             s_ParseErrCodeInfoStr(str, line, err_subcode, err_subseverity,
05579                                   err_message, err_ready);
05580       
05581         } else if (err_ready) {
05582         // Get line of explanation message
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     // Find entry
05604     TInfo::const_iterator find_entry = m_Info.find(err_code);
05605     if (find_entry == m_Info.end()) {
05606         return false;
05607     }
05608     // Get entry value
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 

Generated on Wed Dec 9 04:14:31 2009 for NCBI C++ ToolKit by  doxygen 1.4.6
Modified on Wed Dec 09 08:17:56 2009 by modify_doxy.py rev. 173732