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