cpLog.cxx
上传用户:sy_wanhua
上传日期:2013-07-25
资源大小:3048k
文件大小:18k
源码类别:

流媒体/Mpeg4/MP4

开发平台:

C/C++

  1. /* ====================================================================
  2.  * The Vovida Software License, Version 1.0 
  3.  * 
  4.  * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
  5.  * 
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions
  8.  * are met:
  9.  * 
  10.  * 1. Redistributions of source code must retain the above copyright
  11.  *    notice, this list of conditions and the following disclaimer.
  12.  * 
  13.  * 2. Redistributions in binary form must reproduce the above copyright
  14.  *    notice, this list of conditions and the following disclaimer in
  15.  *    the documentation and/or other materials provided with the
  16.  *    distribution.
  17.  * 
  18.  * 3. The names "VOCAL", "Vovida Open Communication Application Library",
  19.  *    and "Vovida Open Communication Application Library (VOCAL)" must
  20.  *    not be used to endorse or promote products derived from this
  21.  *    software without prior written permission. For written
  22.  *    permission, please contact vocal@vovida.org.
  23.  *
  24.  * 4. Products derived from this software may not be called "VOCAL", nor
  25.  *    may "VOCAL" appear in their name, without prior written
  26.  *    permission of Vovida Networks, Inc.
  27.  * 
  28.  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
  29.  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  30.  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
  31.  * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
  32.  * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
  33.  * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
  34.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  35.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  36.  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  37.  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  38.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  39.  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  40.  * DAMAGE.
  41.  * 
  42.  * ====================================================================
  43.  * 
  44.  * This software consists of voluntary contributions made by Vovida
  45.  * Networks, Inc. and many individuals on behalf of Vovida Networks,
  46.  * Inc.  For more information on Vovida Networks, Inc., please see
  47.  * <http://www.vovida.org/>.
  48.  *
  49.  */
  50. static const char* const cpLog_cxx_Version =
  51.     "$Id: cpLog.cxx,v 1.8 2001/07/06 09:29:44 chok Exp $";
  52. #ifdef WIN32
  53. #pragma warning (disable: 4786)
  54. #else
  55. #include <syslog.h>
  56. #endif
  57. #include <sys/types.h>
  58. #include <sys/stat.h>
  59. #include <sys/unistd.h>
  60. #include <stdarg.h>
  61. #include <stdlib.h>
  62. #include <stdio.h>
  63. #include <string.h>
  64. #include <time.h>
  65. #include <errno.h>
  66. #include <cassert>
  67. #include <string>
  68. #include <map>
  69. #include "cpLog.h"
  70. #include "global.h"
  71. #include "VMutex.h"
  72. #include "VThread.hxx"
  73. #include "LockHelper.hxx"
  74. #include "support.hxx"
  75. /* String sizes */
  76. #define DATEBUF_SIZE 256
  77. #define FILEBUF_SIZE 256
  78. #define LOG_FNAME_MAX_SIZE 256
  79. /* The trailing 0 is a nil pointer, for use by code that iterates over
  80. the array and needs to know where to stop. */
  81. static const char* priName[] =
  82.     {
  83.         "EMERG",
  84.         "ALERT",
  85.         "CRIT",
  86.         "ERR",
  87.         "WARNING",
  88.         "NOTICE",
  89.         "INFO",
  90.         "DEBUG",
  91.         "DEBUG_STACK",
  92.         "DEBUG_OPER",
  93.         "DEBUG_HB",
  94.         0
  95.     };
  96. /* The trailing 0 is a nil pointer, for use by code that iterates over
  97. the array and needs to know where to stop. */
  98. static const char* priNameShort[] =
  99.     {
  100.         "EMRG",
  101.         "ALRT",
  102.         "CRIT",
  103.         "ERR ",
  104.         "WARN",
  105.         "NOTC",
  106.         "INFO",
  107.         "DBUG",
  108.         "DSTK",
  109.         "DOP ",
  110.         "DHB ",
  111.         0
  112.     };
  113. VMutex fileRotationMutex;
  114. static char cpLogFilename[LOG_FNAME_MAX_SIZE + 1] = "";
  115. static FILE* cpLogFd = stderr;
  116. static const int numberOfBackupFilesToKeep = 6;
  117. /* 1 if we're using syslog, 0 if we're not */
  118. int usingSyslog = 0;
  119. int openTheLogFile();
  120. void handleCriticalError (char* fmt, ...);
  121. inline void rotateFilesIfNecessary();
  122. void rotateFiles();
  123. static VMutex cpLogMutex;
  124. class CpLogPriority
  125. {
  126.     public:
  127.         static int getPriority();
  128.         static void setPriority(int pri);
  129.         static void setPriorityThread(vthread_t threadId, int pri);
  130.         static void clearPriorityThread(vthread_t threadId);
  131.         static const char* getLabel();
  132.         static void setLabel(const char* label);
  133.         static void setLabelThread(vthread_t threadId, const char* label);
  134.         static void clearLabelThread(vthread_t threadId);
  135.     protected:
  136.         CpLogPriority();
  137.     private:
  138.         static VMutex logMutex;
  139.         static CpLogPriority* getInstance();
  140.         static CpLogPriority* instance_;
  141.         int logPriority;
  142.         map < vthread_t, int > logPriorityMap;
  143.         map < vthread_t, string > logLabelMap;
  144.         string logLabel;
  145.         /* This is a singleton class, so it makes no sense for it
  146.         to have an assignment operator or a copy constructor.  Keep the
  147.         compiler from auto-generating by declaring them as private
  148.         methods, and then refusing to define them. */
  149.         CpLogPriority (const CpLogPriority& x);
  150.         CpLogPriority& operator= (const CpLogPriority& x);
  151. };
  152. CpLogPriority* CpLogPriority::instance_ = 0;
  153. VMutex CpLogPriority::logMutex;
  154. CpLogPriority::CpLogPriority()
  155.         : logPriority(LOG_ERR),
  156.         logLabel("")
  157. {}
  158. CpLogPriority* CpLogPriority::getInstance()
  159. {
  160.     if (!instance_)
  161.     {
  162.         instance_ = new CpLogPriority;
  163.     }
  164.     return instance_;
  165. }
  166. int CpLogPriority::getPriority()
  167. {
  168.     map < vthread_t, int > ::iterator i;
  169.     LockHelper lock(logMutex);
  170.     i = getInstance()->logPriorityMap.find(VThread::selfId());
  171.     if (i != getInstance()->logPriorityMap.end())
  172.     {
  173.         // found it!
  174.         return i->second;
  175.     }
  176.     else
  177.     {
  178.         // not found, use default
  179.         return getInstance()->logPriority;
  180.     }
  181. }
  182. void
  183. CpLogPriority::setPriority(int pri)
  184. {
  185.     assert (pri >= 0 && pri <= LAST_PRIORITY);
  186.     LockHelper lock(logMutex);
  187.     getInstance()->logPriority = pri;
  188. }
  189. void
  190. CpLogPriority::setPriorityThread(vthread_t threadId, int pri)
  191. {
  192.     assert (pri >= 0 && pri <= LAST_PRIORITY);
  193.     LockHelper lock(logMutex);
  194.     getInstance()->logPriorityMap[threadId] = pri;
  195. }
  196. void
  197. CpLogPriority::clearPriorityThread(vthread_t threadId)
  198. {
  199.     map < vthread_t, int > ::iterator i;
  200.     LockHelper lock(logMutex);
  201.     i = getInstance()->logPriorityMap.find(threadId);
  202.     if (i != getInstance()->logPriorityMap.end())
  203.     {
  204.         getInstance()->logPriorityMap.erase(i);
  205.     }
  206. }
  207. const char* CpLogPriority::getLabel()
  208. {
  209.     map < vthread_t, string > ::iterator i;
  210.     LockHelper lock(logMutex);
  211.     i = getInstance()->logLabelMap.find(VThread::selfId());
  212.     if (i != getInstance()->logLabelMap.end())
  213.     {
  214.         // found it!
  215.         return i->second.c_str();
  216.     }
  217.     else
  218.     {
  219.         // not found, use default
  220.         return getInstance()->logLabel.c_str();
  221.     }
  222. }
  223. void
  224. CpLogPriority::setLabel(const char* label)
  225. {
  226.     LockHelper lock(logMutex);
  227.     getInstance()->logLabel = label;
  228. }
  229. void
  230. CpLogPriority::setLabelThread(vthread_t threadId, const char* label)
  231. {
  232.     LockHelper lock(logMutex);
  233.     getInstance()->logLabelMap[threadId] = label;
  234. }
  235. void
  236. CpLogPriority::clearLabelThread(vthread_t threadId)
  237. {
  238.     map < vthread_t, string > ::iterator i;
  239.     LockHelper lock(logMutex);
  240.     i = getInstance()->logLabelMap.find(threadId);
  241.     if (i != getInstance()->logLabelMap.end())
  242.     {
  243.         getInstance()->logLabelMap.erase(i);
  244.     }
  245. }
  246. void cpLogOpenSyslog()
  247. {
  248. #ifndef WIN32
  249.     /* Use LOG_LOCAL7, which is reserved for non-system programs, to avoid
  250.     gratuitously intermingling our logs with system logs.  LOG_CONS means
  251.     to print messages to the system console if there is some error in sending
  252.     them to syslog.  LOG_NDELAY means we will try to open syslog now, not
  253.     after the first message is received. */
  254.     openlog (CpLogPriority::getLabel(), LOG_CONS | LOG_NDELAY, LOG_LOCAL7);
  255.     usingSyslog = 1;
  256. #endif // !WIN32
  257. }
  258. int
  259. cpLogOpen (const char* filename)
  260. {
  261.     assert (strlen (filename) <= FILEBUF_SIZE);
  262.     strncpy (cpLogFilename, filename, FILEBUF_SIZE);
  263.     usingSyslog = 0;
  264.     return openTheLogFile();
  265. }
  266. int
  267. openTheLogFile()
  268. {
  269.     if (!(cpLogFd = fopen (cpLogFilename, "a+")))
  270.     {
  271.         handleCriticalError ("cpLog: Cannot open log file %s:  %s", cpLogFilename, strerror(errno));
  272.         return 0;
  273.     }
  274.     cpLog (LOG_INFO, "Opened new log file - %s", cpLogFilename);
  275.     return 1;
  276. }
  277. #ifndef __GNUC__
  278. #ifdef cpLog
  279. #undef cpLog
  280. #endif
  281. void
  282. cpLog(int pri, const char* fmt, ...)
  283. {
  284.     if (pri < 0)
  285.         return ;
  286.     va_list ap;
  287.     if (pri <= CpLogPriority::getPriority())
  288.     {
  289.         va_start(ap, fmt);
  290.         vCpLog(pri, "", 0, fmt, ap);
  291.         va_end(ap);
  292.     }
  293. }  /* cpLog  */
  294. #endif
  295. void cpLog_impl_(int pri, const char* file, int line, const char* fmt, ...)
  296. {
  297.     va_list ap;
  298.     if (pri <= CpLogPriority::getPriority())
  299.     {
  300. // cpLogMutex.lock();
  301.         va_start(ap, fmt);
  302.         vCpLog(pri, file, line, fmt, ap);
  303.         va_end(ap);
  304. // cpLogMutex.unlock();
  305.     }
  306. }
  307. void
  308. vCpLog(int pri, const char* file, int line, const char* fmt, va_list ap)
  309. {
  310.     assert (pri >= 0 && pri <= LAST_PRIORITY);
  311.     char datebuf [DATEBUF_SIZE];
  312.     int datebufCharsRemaining;
  313. #ifndef WIN32
  314.     struct timeval tv;
  315.     struct timezone tz;
  316.     int result = gettimeofday (&tv, &tz);
  317. #else
  318. struct timeval tv;
  319. int result = gettimeofday (&tv, NULL);
  320. #endif
  321.     
  322.     if (result == -1)
  323.     {
  324.         /* If we can't get the time of day, don't print a timestamp.
  325.         (Under Unix, this will never happen:  gettimeofday can fail only
  326.         if the timezone is invalid [which it can't be, since it is
  327.         uninitialized] or if &tv or &tz are invalid pointers.) */
  328.         datebuf [0] = '';
  329.     }
  330.     else
  331.     {
  332.         /* The tv_sec field represents the number of seconds passed since
  333.         the Epoch, which is exactly the argument gettimeofday needs. */
  334.         const time_t timeInSeconds = (time_t) tv.tv_sec;
  335.         strftime (datebuf,
  336.                   DATEBUF_SIZE,
  337.                   "%Y%m%d-%H%M%S", /* guaranteed to fit in 256 chars,
  338.                                       hence don't check return code */
  339.                   localtime (&timeInSeconds));
  340.     }
  341.     char msbuf[5];
  342.     /* Dividing (without remainder) by 1000 rounds the microseconds
  343.     measure to the nearest millisecond. */
  344.     sprintf(msbuf, ".%3.3ld", (tv.tv_usec / 1000));
  345.     datebufCharsRemaining = DATEBUF_SIZE - strlen (datebuf);
  346.     strncat (datebuf, msbuf, datebufCharsRemaining - 1);
  347.     datebuf[DATEBUF_SIZE - 1] = ''; /* Just in case strncat truncated msbuf,
  348.                                      thereby leaving its last character at
  349.                                      the end, instead of a null terminator */
  350.     if (usingSyslog)
  351.     {
  352. #if !defined(WIN32)
  353.         /* syslog does not recognize priorities conceptually lower (numerically
  354.         greater) than LOG_DEBUG.  If our current priority is lower, "promote"
  355.         it to LOG_DEBUG. */
  356.         int coercedPriority = (pri <= LOG_DEBUG) ? pri : LOG_DEBUG;
  357.         syslog (coercedPriority,
  358.                 "%s [%5.5ld] %s %s: %s:%d ",
  359.                 datebuf,
  360.                 VThread::selfId(),
  361.                 priNameShort[pri],
  362.                 CpLogPriority::getLabel(),
  363.                 file,
  364.                 line);
  365. #else
  366. printf("syslog not implemented for win32");
  367. assert(0);
  368. #endif
  369.     }
  370.     else
  371.     {
  372.         fprintf (cpLogFd,
  373.                  "%s [%5.5ld] %s %s: %s:%d ",
  374.                  datebuf,
  375.                  VThread::selfId(),
  376.                  priNameShort[pri],
  377.                  CpLogPriority::getLabel(),
  378.                  file,
  379.                  line);
  380.         vfprintf (cpLogFd, fmt, ap);
  381.         fprintf (cpLogFd, "n");
  382.         fflush(cpLogFd);
  383.         /* in case we just pushed the current file past the size limit... */
  384.         rotateFilesIfNecessary();
  385.     }
  386. }
  387. /* Handle a critical error in cpLog itself (an error so bad, by definition,
  388. it prevents logging in the normal way).  Do this by reverting to using
  389. standard error as the log "file" and immediately printing a warning about
  390. the situation.*/
  391. void
  392. handleCriticalError (char* fmt, ...)
  393. {
  394.     cpLogFd = stderr;
  395.     strcpy (cpLogFilename, "");
  396.     fprintf (cpLogFd, "nCRITICAL LOGGING ERROR:n");
  397.     
  398.     va_list ap;
  399.     va_start (ap, fmt);
  400.     vfprintf (cpLogFd, fmt, ap);
  401.     va_end (ap);
  402.     fprintf (cpLogFd, "ncpLog has reverted to logging to standard error...nn");
  403. }
  404. inline
  405. void
  406. rotateFilesIfNecessary()
  407. {
  408.     /* If we are logging to standard error, there are no files to rotate. */
  409.     if (cpLogFd == stderr)
  410.     {
  411.         return;
  412.     }
  413.     /* If we are logging to syslog, log rotation is somebody else's problem
  414.     (SEP); the log file name is outside of our knowledge, and the file itself
  415.     may be outside of our permissions */
  416.     if (usingSyslog)
  417.     {
  418.         return;
  419.     }
  420.     /* Test to see if the present log file has exceeded the maximum
  421.        size.  (If it has, rotate it.) */
  422.     struct stat fileInfo;
  423.     
  424.     if (stat (cpLogFilename, &fileInfo))
  425.     {
  426.         /* What?  We can't see the log file? */
  427.         handleCriticalError ("cpLog could not stat its own current log file, %s:  %s", cpLogFilename, strerror (errno));
  428.             return;
  429.     }
  430.     
  431.     if (fileInfo.st_size >= SIZE_PER_LOGFILE)
  432.     {
  433.         rotateFiles();
  434.     }
  435. }
  436. /* Move the file names, cascading down, so that logfile.1 is renamed
  437. to logfile.2, logfile.2 is renamed to logfile.3, et cetera.  logfile.6,
  438. if it exists, will be overwritten. */
  439. void
  440. rotateFiles()
  441. {
  442.     LockHelper lock (fileRotationMutex);
  443.     /* First double-check the log file size, to avoid a race condition.
  444.     It is possible that, between the time rotateFiles was called and
  445.     the present moment, some other thread has attempted to log a message
  446.     (using vCpLog), noticed that fileInfo.st_size +. SIZE_PER_LOGFILE
  447.     (in rotateFilesIfNecessary), and rotated the logs out from under us.
  448.     */
  449.     struct stat fileInfo;
  450.     if (stat (cpLogFilename, &fileInfo) != 0)
  451.     {
  452.         handleCriticalError ("rotateFiles could not stat the log file (%s)", cpLogFilename);
  453.     }
  454.     if (fileInfo.st_size < SIZE_PER_LOGFILE)
  455.     {
  456.         /* The race condition occurred; our files have already been
  457.         moved for us. */
  458.         return;
  459.     }
  460.     /* Close the current log file */
  461.     if( fclose (cpLogFd) )
  462.     {
  463.         handleCriticalError ("Could not close the log file:  %s", strerror (errno));
  464.     }
  465.     /* Make room for the new log file */
  466.     for( int i = numberOfBackupFilesToKeep - 1; i >= 0; i-- )
  467.     {
  468.         string oldFilename( cpLogFilename );
  469.         if( i > 0 )
  470.         {
  471.             oldFilename += "." + itos( i );
  472.         }
  473.         const char* oldFilename_c_str = oldFilename.c_str();
  474.         if (stat (oldFilename_c_str, &fileInfo) == 0) /* if the file _does_ exist... */
  475.         {
  476.             string newFilename( cpLogFilename );
  477.             newFilename += "." + itos( i+1 );
  478.             const char* newFilename_c_str = newFilename.c_str();
  479.             if (rename (oldFilename_c_str, newFilename_c_str) != 0) /* If rename() fails... */
  480.             {
  481.                 handleCriticalError ("cpLog could not rename %s to %s:  %s", oldFilename_c_str, newFilename_c_str, strerror (errno));
  482.                 return;
  483.             }
  484.         }
  485.         /* The only reason the file should be un-stat-able is that it
  486.         does not exist.  That is a legitimate condition, since rotation may
  487.         not yet have created a file with that number (i).  Any other failure
  488.         is an error. */
  489.         else if (errno != ENOENT)
  490.         {
  491.             handleCriticalError ("cpLog could not stat %s:  %s", oldFilename_c_str, strerror (errno));
  492.             return;
  493.         }
  494.     }
  495.     /* Open the log file for writing once more.  (The current log file will
  496.     always have the name stored in cpLogFilename, without a numeric extension.)*/
  497.     openTheLogFile();
  498. }
  499. void
  500. cpLogSetPriority (int pri)
  501. {
  502.     CpLogPriority::setPriority(pri);
  503. }
  504. int
  505. cpLogGetPriority ()
  506. {
  507.     return CpLogPriority::getPriority();
  508. }
  509. void
  510. cpLogSetPriorityThread (vthread_t thread_id, int pri)
  511. {
  512.     if (pri < 0)
  513.         return ;
  514.     CpLogPriority::setPriorityThread(thread_id, pri);
  515. }
  516. void
  517. cpLogClearPriorityThread (vthread_t thread_id)
  518. {
  519.     CpLogPriority::clearPriorityThread(thread_id);
  520. }
  521. void
  522. cpLogSetLabel (const char* label)
  523. {
  524.     CpLogPriority::setLabel(label);
  525. }
  526. void
  527. cpLogSetLabelThread (vthread_t thread_id, const char* label)
  528. {
  529.     CpLogPriority::setLabelThread(thread_id, label);
  530. }
  531. void
  532. cpLogClearLabelThread (vthread_t thread_id)
  533. {
  534.     CpLogPriority::clearLabelThread(thread_id);
  535. }
  536. void
  537. cpLogShow (void)
  538. {
  539.     fprintf (stderr, "tLabel    : %sn", CpLogPriority::getLabel());
  540.     fprintf (stderr, "tPriority : %sn", priName[CpLogPriority::getPriority()]);
  541.     fprintf (stderr, "tFile     : %s (cpLogFd = %d)n", cpLogFilename, fileno(cpLogFd));
  542. }
  543. int
  544. cpLogStrToPriority(const char* priority)
  545. {
  546.     string pri = priority;
  547.     if (pri.find("LOG_", 0) == 0)
  548.     {
  549.         pri.erase(0, 4);
  550.     }
  551.     int i = 0;
  552.     while (priName[i] != 0)
  553.     {
  554.         if (pri == priName[i])
  555.         {
  556.             return i;
  557.         }
  558.         i++;
  559.     }
  560.     return -1;  // invalid
  561. }
  562. const char*
  563. cpLogPriorityToStr(int priority)
  564. {
  565.     int priorityCount = 0;
  566.     while (priName[priorityCount] != 0)
  567.     {
  568.         priorityCount++;
  569.     }
  570.     if ((priority >= 0) && (priority < priorityCount))
  571.     {
  572.         return priName[priority];
  573.     }
  574.     else
  575.     {
  576.         return 0;
  577.     }
  578. }
  579. /* this function is not called, it is just compiled to make sure that
  580.    the cpLog Macro expansion works */
  581. void
  582. testCpLogMacroExpansion()
  583. {
  584.     if (true)
  585.         cpLog(LOG_DEBUG, "this is a test");
  586.     else
  587.         cpLog(LOG_DEBUG, "and a second");
  588. }