NCBI C++ ToolKit
perf_log.hpp
Go to the documentation of this file.
00001 #ifndef CORELIB___PERF_LOG__HPP
00002 #define CORELIB___PERF_LOG__HPP
00003 
00004 /*  $Id: perf_log.hpp 52765 2012-01-25 19:08:52Z grichenk $
00005  * ===========================================================================
00006  *
00007  *                            PUBLIC DOMAIN NOTICE
00008  *               National Center for Biotechnology Information
00009  *
00010  *  This software/database is a "United States Government Work" under the
00011  *  terms of the United States Copyright Act.  It was written as part of
00012  *  the author's official duties as a United States Government employee and
00013  *  thus cannot be copyrighted.  This software/database is freely available
00014  *  to the public for use. The National Library of Medicine and the U.S.
00015  *  Government have not placed any restriction on its use or reproduction.
00016  *
00017  *  Although all reasonable efforts have been taken to ensure the accuracy
00018  *  and reliability of the software and data, the NLM and the U.S.
00019  *  Government do not and cannot warrant the performance or results that
00020  *  may be obtained by using this software or data. The NLM and the U.S.
00021  *  Government disclaim all warranties, express or implied, including
00022  *  warranties of performance, merchantability or fitness for any particular
00023  *  purpose.
00024  *
00025  *  Please cite the author in any work or product based on this material.
00026  *
00027  * ===========================================================================
00028  *
00029  * Author:  Denis Vakatov, Vladimir Ivanov
00030  *
00031  *
00032  */
00033 
00034 /// @file perf_log.hpp
00035 ///
00036 ///   Defines NCBI C++ API for timing-and-logging, classes, and macros.
00037 ///
00038 
00039 #include <corelib/ncbitime.hpp>
00040 #include <corelib/ncbidiag.hpp>
00041 #include <corelib/request_status.hpp>
00042 
00043 
00044 /** @addtogroup Diagnostics
00045  *
00046  * @{
00047  */
00048 
00049 
00050 BEGIN_NCBI_SCOPE
00051 
00052 /////////////////////////////////////////////////////////////////////////////
00053 /// Forward declaration
00054 
00055 class CPerfLogGuard;
00056 
00057 
00058 /////////////////////////////////////////////////////////////////////////////
00059 ///
00060 /// CPerfLogger -- 
00061 ///
00062 /// The CPerfLogger measure time spend somewhere, executing some operation 
00063 /// and put result to the performance log. Each measurement will result 
00064 /// in printing a one-line record to the performance log.
00065 /// This class is designed to measure just one operation. Each measurement
00066 /// should be finished with calling Post() or Discard() method. You can call
00067 /// Start() and Suspend() methods in between as many times as you want.
00068 ///
00069 /// @attention
00070 ///   If the logging is off then neither logging nor timing will be done at all.
00071 ///   It will however check for incorrect usage and post errors, if any.
00072 ///   But if you use next construction
00073 ///     perf_logger.Post(...).Print(...)
00074 ///   that extra record will be put into the log if the logging is off.
00075 ///   Please use PERF_POST macro or PerfLogGuard class to avoid this.
00076 /// @sa
00077 ///   PERF_POST, PERF_POST_DB, PerfLogGuard
00078 
00079 class NCBI_XNCBI_EXPORT CPerfLogger
00080 {
00081 public:
00082     /// If to start the timing immediately
00083     enum EStart {
00084         eStart,   ///< Call Start() immediately after creating
00085         eSuspend  ///< Do not start timer (call Start() later)
00086     };
00087 
00088     /// Constructor. Starts the timer by default.
00089     CPerfLogger(EStart state = eStart);
00090 
00091     /// Activate and start (or, restart after Suspend()) the timer.
00092     /// @note
00093     ///   If the timer is already running, post an error (once).
00094     /// @sa Suspend
00095     void Start(void);
00096 
00097     /// Suspend the timer.
00098     /// Call Start() to continue to count time accured before.
00099     /// @sa Start
00100     void Suspend(void);
00101 
00102     /// Log the timing; stop and deactivate the timer.
00103     /// @param resource
00104     ///   Name of the resource (must be non-empty, else throws an exception).
00105     /// @param status
00106     ///   Status of the timed code.
00107     /// @param status_msg
00108     ///   Verbal description of the status of the timed code.
00109     /// @note
00110     ///   If the timer is already inactive, then post an error (once).
00111     /// @sa Discard
00112     CDiagContext_Extra Post(CRequestStatus::ECode status,
00113                             CTempString           resource,
00114                             CTempString           status_msg = CTempString());
00115 
00116     CDiagContext_Extra Post(int                   status,
00117                             CTempString           resource,
00118                             CTempString           status_msg = CTempString());
00119 
00120     /// Discard the timing results; stop and deactivate the timer.
00121     void Discard(void);
00122 
00123     /// If the timer is still active, then post an error (once).
00124     /// Usually each measurement should be finished with calling Post()
00125     /// or Discard() method.
00126     ~CPerfLogger();
00127 
00128     /// Is performance logging on, globally?
00129     /// Controlled by CParam(section="Log", entry="PerfLogging", default=false)
00130     static bool IsON(void);
00131 
00132     /// Turn performance logging on/off globally.
00133     static void SetON(bool enable = true);
00134 
00135 private:
00136     bool x_CheckValidity(const CTempString& err_msg) const;
00137     friend class CPerfLogGuard;
00138 
00139 private:
00140     CStopWatch         m_StopWatch;   ///< Internal timer
00141     CStopWatch::EStart m_TimerState;  ///< Internal timer state to save cycles 
00142     bool               m_IsDiscarded; ///< TRUE if Post() or Discard() is already called
00143 
00144 };
00145 
00146 
00147 /////////////////////////////////////////////////////////////////////////////
00148 
00149 /// Convenience macro that also saves cycles when the performance logging is
00150 /// globally turned off.
00151 ///
00152 /// CPerfLogger perf_logger;
00153 /// --- Here, running stored proc "123" on server "MSSQL444" which e.g.
00154 /// --- has finished successfully (code 200) in 2.345 seconds:
00155 /// PERF_POST(perf_logger, e200_Ok, "StoredProc123",
00156 ///           .Print("dbserver", "MSSQL444")
00157 ///           .Print("foo", "bar"));
00158 ///
00159 #define PERF_POST(perf_logger, status, resource, args)              \
00160     do { if ( CPerfLogger::IsON() )                                 \
00161         perf_logger.Post(CRequestStatus::status, resource) args;    \
00162     } while (false)
00163 
00164 
00165 /// Adaptation for logging database performance
00166 /// PERF_POST_DB(perf_logger, e200_Ok,
00167 ///              "StoredProc123", "MSSQL444")
00168 ///              .Print("foo", "bar"));
00169 ///
00170 #define PERF_POST_DB(perf_logger, status, resource, server, args)   \
00171     do { if ( CPerfLogger::IsON() )                                 \
00172         perf_logger.Post(CRequestStatus::status, resource)          \
00173                    .Print("dbserver", server) args;                 \
00174     } while (false)
00175 
00176 
00177 
00178 /////////////////////////////////////////////////////////////////////////////
00179 ///
00180 /// CPerfLogGuard -- 
00181 ///
00182 /// @attention
00183 ///   If the logging is off then neither logging nor timing will be done at all.
00184 ///   It will however check for incorrect usage and post errors, if any.
00185 ///
00186 ///   If a usage error is encountered, then an error will be posted -- only
00187 ///   once per an error type per process.
00188 
00189 class NCBI_XNCBI_EXPORT CPerfLogGuard
00190 {
00191 public:
00192     /// Constructor.
00193     /// @param resource
00194     ///   Name of the resource (must be non-empty, else throws an exception).
00195     /// @param state
00196     ///   Whether to start the timer by default.
00197     CPerfLogGuard(CTempString resource,
00198                   CPerfLogger::EStart state = CPerfLogger::eStart);
00199 
00200     /// Activate and start (or, restart after Suspend()) the timer.
00201     /// @note
00202     ///   If the timer is already running, post an error (once).
00203     /// @sa Suspend
00204     void Start(void);
00205 
00206     /// Suspend the timer.
00207     /// Call Start() to continue to count time accured before.
00208     /// @sa Start
00209     void Suspend(void);
00210 
00211     /// Add info to the resource's description
00212     CPerfLogGuard& AddParameter(CTempString name, CTempString value);
00213 
00214     /// Write the collected resource info and timing to the log.
00215     /// @param status
00216     ///   Status of the timed code.
00217     /// @param status_msg
00218     ///   Verbal description of the status of the timed code.
00219     /// @note
00220     ///   After this any action on this guard will be an error (and no-op).
00221     void Post(CRequestStatus::ECode status,
00222               CTempString           status_msg = CTempString());
00223 
00224     void Post(int                   status,
00225               CTempString           status_msg = CTempString());
00226 
00227     /// Discard the results.
00228     /// @note
00229     ///   After this any action on this guard will be an error (and no-op).
00230     void Discard(void);
00231 
00232     /// If Post() or Discard() have not been called, then log the collected
00233     /// info with status 500.
00234     ~CPerfLogGuard();
00235 
00236 private:
00237     CPerfLogger              m_Logger;
00238     string                   m_Resource;
00239     SDiagMessage::TExtraArgs m_Parameters;
00240 };
00241 
00242 
00243 /* @} */
00244 
00245 
00246 //=============================================================================
00247 //
00248 //  Inline class methods
00249 //
00250 //=============================================================================
00251 
00252 //
00253 //  CPerfLogger
00254 //
00255 
00256 inline
00257 CPerfLogger::CPerfLogger(EStart state)
00258 {
00259     m_IsDiscarded = false;
00260     m_TimerState  = CStopWatch::eStop;
00261     if ( state == eStart ) {
00262         Start();
00263     }
00264 }
00265 
00266 
00267 inline
00268 void CPerfLogger::Start()
00269 {
00270     if ( !x_CheckValidity("Start") ) {
00271         return;
00272     }
00273     if ( m_TimerState == CStopWatch::eStart ) {
00274         ERR_POST_ONCE(Error << "CPerfLogger timer is already started");
00275         return;
00276     }
00277     if ( CPerfLogger::IsON() ) {
00278         m_StopWatch.Start();
00279     }
00280     m_TimerState = CStopWatch::eStart;
00281 }
00282 
00283 
00284 inline
00285 void CPerfLogger::Suspend()
00286 {
00287     if ( !x_CheckValidity("Suspend") ) {
00288         return;
00289     }
00290     if ( CPerfLogger::IsON() ) {
00291         m_StopWatch.Stop();
00292     }
00293     m_TimerState = CStopWatch::eStop;
00294 }
00295 
00296 
00297 inline CDiagContext_Extra 
00298 CPerfLogger::Post(CRequestStatus::ECode status,
00299                   CTempString           resource,
00300                   CTempString           status_msg)
00301 {
00302     return Post((int)status, resource, status_msg);
00303 }
00304 
00305 
00306 inline
00307 void CPerfLogger::Discard()
00308 {
00309     // We don't need to "stop" CStopWatch here, it is nor really running.
00310     m_TimerState  = CStopWatch::eStop;
00311     m_IsDiscarded = true;
00312 }
00313 
00314 
00315 inline
00316 CPerfLogger::~CPerfLogger()
00317 {
00318     if ( !m_IsDiscarded  &&  m_TimerState != CStopWatch::eStop ) {
00319         ERR_POST_ONCE(Error << "CPerfLogger timer is still running");
00320     }
00321 }
00322 
00323 
00324 inline
00325 bool CPerfLogger::x_CheckValidity(const CTempString& err_msg) const
00326 {
00327     if ( m_IsDiscarded ) {
00328         ERR_POST_ONCE(Error << err_msg << "() cannot be done, " \
00329                       "CPerfLogger is already discarded");
00330         return false;
00331     }
00332     return true;
00333 }
00334 
00335 
00336 //
00337 //  CPerfLogGuard
00338 //
00339 
00340 inline
00341 CPerfLogGuard::CPerfLogGuard(CTempString resource, CPerfLogger::EStart state)
00342     : m_Logger(state), m_Resource(resource)   
00343 {
00344     if ( resource.empty() ) {
00345         NCBI_THROW(CCoreException, eInvalidArg,
00346             "CPerfLogGuard:: resource name is not specified");
00347     }
00348 }
00349 
00350 
00351 inline
00352 CPerfLogGuard::~CPerfLogGuard()
00353 {
00354     try {
00355         if ( !m_Logger.m_IsDiscarded ) {
00356             Post(CRequestStatus::e500_InternalServerError);
00357         }
00358     } 
00359     catch (CCoreException&) {
00360     }
00361 }
00362 
00363 
00364 inline
00365 void CPerfLogGuard::Start()
00366 {
00367     m_Logger.Start();
00368 }
00369 
00370 
00371 inline
00372 void CPerfLogGuard::Suspend()
00373 {
00374     m_Logger.Suspend();
00375 }
00376 
00377 
00378 inline
00379 CPerfLogGuard& CPerfLogGuard::AddParameter(CTempString name, CTempString value)
00380 {
00381     if ( m_Logger.m_IsDiscarded ) {
00382         ERR_POST_ONCE(Error << "AddParameter() cannot be done, " \
00383                       "CPerfLogGuard is already discarded");
00384     } else {
00385         m_Parameters.push_back(SDiagMessage::TExtraArg(name, value));
00386     }
00387     return *this;
00388 }
00389 
00390 
00391 inline
00392 void CPerfLogGuard::Post(CRequestStatus::ECode status,
00393                          CTempString           status_msg)
00394 {
00395     return Post((int)status, status_msg);
00396 }
00397 
00398 
00399 inline
00400 void CPerfLogGuard::Discard()
00401 {
00402     m_Logger.Discard();
00403 }
00404 
00405 
00406 
00407 END_NCBI_SCOPE
00408 
00409 
00410 #endif  /* CORELIB___PERF_LOG__HPP */
Modified on Wed May 23 12:56:01 2012 by modify_doxy.py rev. 337098