VirtualBox

Changeset 36344 in vbox for trunk/src/VBox/Runtime/common


Ignore:
Timestamp:
Mar 22, 2011 2:29:37 PM (14 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
70679
Message:

Runtime/log: implement log rotation, adapt all code creating log files and make use of it in the webservice

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/log/log.cpp

    r33595 r36344  
    55
    66/*
    7  * Copyright (C) 2006-2010 Oracle Corporation
     7 * Copyright (C) 2006-2011 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    8989static void rtR0LogLoggerExFallback(uint32_t fDestFlags, uint32_t fFlags, const char *pszFormat, va_list va);
    9090#endif
     91#ifdef IN_RING3
     92static int rtlogFileOpen(PRTLOGGER pLogger, char *pszErrorMsg, size_t cchErrorMsg);
     93static void rtlogRotate(PRTLOGGER pLogger, uint32_t uTimeSlot, bool fFirst);
     94#endif
    9195static void rtlogFlush(PRTLOGGER pLogger);
    9296static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars);
    9397static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars);
     98static void rtlogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args);
    9499
    95100
     
    190195    { "file",     sizeof("file"    ) - 1,  RTLOGDEST_FILE }, /* Must be 1st! */
    191196    { "dir",      sizeof("dir"     ) - 1,  RTLOGDEST_FILE }, /* Must be 2nd! */
     197    { "history",  sizeof("history" ) - 1,  0 },              /* Must be 3rd! */
     198    { "histsize", sizeof("histsize") - 1,  0 },              /* Must be 4th! */
     199    { "histtime", sizeof("histtime") - 1,  0 },              /* Must be 5th! */
    192200    { "stdout",   sizeof("stdout"  ) - 1,  RTLOGDEST_STDOUT },
    193201    { "stderr",   sizeof("stderr"  ) - 1,  RTLOGDEST_STDERR },
     
    225233{
    226234#ifndef IN_RC
    227     if (pLogger->hSpinMtx != NIL_RTSEMFASTMUTEX)
     235    if (pLogger->hSpinMtx != NIL_RTSEMSPINMUTEX)
    228236        RTSemSpinMutexRelease(pLogger->hSpinMtx);
    229237#endif
     
    233241
    234242#ifndef IN_RC
     243# ifdef IN_RING3
     244/**
     245 * Logging to file, output callback.
     246 *
     247 * @param  pvArg        User argument.
     248 * @param  pachChars    Pointer to an array of utf-8 characters.
     249 * @param  cbChars      Number of bytes in the character array pointed to by pachChars.
     250 */
     251static DECLCALLBACK(size_t) rtlogPhaseWrite(void *pvArg, const char *pachChars, size_t cbChars)
     252{
     253    PRTLOGGER pLogger = (PRTLOGGER)pvArg;
     254    RTFileWrite(pLogger->pFile->File, pachChars, cbChars, NULL);
     255    return cbChars;
     256}
     257
     258/**
     259 * Callback to format VBox formatting extentions.
     260 * See @ref pg_rt_str_format for a reference on the format types.
     261 *
     262 * @returns The number of bytes formatted.
     263 * @param   pvArg           Formatter argument.
     264 * @param   pfnOutput       Pointer to output function.
     265 * @param   pvArgOutput     Argument for the output function.
     266 * @param   ppszFormat      Pointer to the format string pointer. Advance this till the char
     267 *                          after the format specifier.
     268 * @param   pArgs           Pointer to the argument list. Use this to fetch the arguments.
     269 * @param   cchWidth        Format Width. -1 if not specified.
     270 * @param   cchPrecision    Format Precision. -1 if not specified.
     271 * @param   fFlags          Flags (RTSTR_NTFS_*).
     272 * @param   chArgSize       The argument size specifier, 'l' or 'L'.
     273 */
     274static DECLCALLBACK(size_t) rtlogPhaseFormatStr(void *pvArg, PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
     275                                                const char **ppszFormat, va_list *pArgs, int cchWidth,
     276                                                int cchPrecision, unsigned fFlags, char chArgSize)
     277{
     278    char ch = *(*ppszFormat)++;
     279
     280    AssertMsgFailed(("Invalid logger phase format type '%%%c%.10s'!\n", ch, *ppszFormat)); NOREF(ch);
     281
     282    return 0;
     283}
     284
     285/**
     286 * Log phase callback function, assumes the lock is already held
     287 *
     288 * @param   pLogger     The logger instance.
     289 * @param   pszFormat   Format string.
     290 * @param   ...         Optional arguments as specified in the format string.
     291 */
     292static DECLCALLBACK(void) rtlogPhaseMsgLocked(PRTLOGGER pLogger, const char *pszFormat, ...)
     293{
     294    va_list args;
     295    AssertPtrReturnVoid(pLogger);
     296    AssertPtrReturnVoid(pLogger->pFile);
     297    Assert(pLogger->hSpinMtx != NIL_RTSEMSPINMUTEX);
     298
     299    va_start(args, pszFormat);
     300    rtlogLoggerExV(pLogger, 0, ~0, pszFormat, args);
     301    va_end(args);
     302}
     303
     304/**
     305 * Log phase callback function, assumes the lock is not held
     306 *
     307 * @param   pLogger     The logger instance.
     308 * @param   pszFormat   Format string.
     309 * @param   ...         Optional arguments as specified in the format string.
     310 */
     311static DECLCALLBACK(void) rtlogPhaseMsgNormal(PRTLOGGER pLogger, const char *pszFormat, ...)
     312{
     313    va_list args;
     314    AssertPtrReturnVoid(pLogger);
     315    AssertPtrReturnVoid(pLogger->pFile);
     316    Assert(pLogger->hSpinMtx != NIL_RTSEMSPINMUTEX);
     317
     318    va_start(args, pszFormat);
     319    RTLogLoggerExV(pLogger, 0, ~0, pszFormat, args);
     320    va_end(args);
     321}
     322# endif /* IN_RING3 */
     323
    235324/**
    236325 * Create a logger instance, comprehensive version.
     
    246335 *                              logger instance.
    247336 * @param   fDestFlags          The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
     337 * @param   pfnPhase            Callback function for starting logging and for ending or starting a new file for log history rotation.
     338 * @param   cHistory            Number of old log files to keep when performing log history rotation.
     339 * @param   cbHistoryFileMax    Maximum size of log file when performing history rotation. 0=no size limit.
     340 * @param   uHistoryTimeSlotLength     Maximum time interval per log file when performing history rotation, in seconds. 0=no time limit.
    248341 * @param   pszErrorMsg         A buffer which is filled with an error message if something fails. May be NULL.
    249342 * @param   cchErrorMsg         The size of the error message buffer.
     
    253346RTDECL(int) RTLogCreateExV(PRTLOGGER *ppLogger, uint32_t fFlags, const char *pszGroupSettings,
    254347                           const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
    255                            uint32_t fDestFlags, char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, va_list args)
     348                           uint32_t fDestFlags, PFNRTLOGPHASE pfnPhase, uint32_t cHistory,
     349                           uint64_t cbHistoryFileMax, uint32_t uHistoryTimeSlotLength,
     350                           char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, va_list args)
    256351{
    257352    int        rc;
     
    278373     */
    279374    cb = RT_OFFSETOF(RTLOGGER, afGroups[cGroups + 1]) + RTPATH_MAX;
     375#ifdef IN_RING3
     376    cb += sizeof(RTLOGGERFILE);
     377#endif
    280378    pLogger = (PRTLOGGER)RTMemAllocZVar(cb);
    281379    if (pLogger)
     
    289387        pLogger->cMaxGroups  = cGroups;
    290388        pLogger->cGroups     = cGroups;
    291         pLogger->pszFilename = (char *)&pLogger->afGroups[cGroups + 1];
    292         pLogger->File        = NIL_RTFILE;
     389#ifdef IN_RING3
     390        pLogger->pFile       = (PRTLOGGERFILE)((char *)&pLogger->afGroups[cGroups + 1] + RTPATH_MAX);
     391        pLogger->pFile->File        = NIL_RTFILE;
     392        pLogger->pFile->pszFilename = (char *)&pLogger->afGroups[cGroups + 1];
     393        pLogger->pFile->pfnPhase    = pfnPhase;
     394        pLogger->pFile->cHistory    = cHistory;
     395        if (cbHistoryFileMax == 0)
     396            pLogger->pFile->cbHistoryFileMax = UINT64_MAX;
     397        else
     398            pLogger->pFile->cbHistoryFileMax = cbHistoryFileMax;
     399        if (uHistoryTimeSlotLength == 0)
     400            pLogger->pFile->uHistoryTimeSlotLength = UINT32_MAX;
     401        else
     402            pLogger->pFile->uHistoryTimeSlotLength = uHistoryTimeSlotLength;
     403#else /* !IN_RING3 */
     404        pLogger->pFile       = NULL;
     405#endif /* !IN_RING3 */
    293406        pLogger->fFlags      = fFlags;
    294407        pLogger->fDestFlags  = fDestFlags;
     
    337450            if (pszFilenameFmt)
    338451            {
    339                 RTStrPrintfV(pLogger->pszFilename, RTPATH_MAX, pszFilenameFmt, args);
     452                RTStrPrintfV(pLogger->pFile->pszFilename, RTPATH_MAX, pszFilenameFmt, args);
    340453                pLogger->fDestFlags |= RTLOGDEST_FILE;
    341454            }
     
    384497            if (pLogger->fDestFlags & RTLOGDEST_FILE)
    385498            {
    386                 uint32_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
    387499                if (pLogger->fFlags & RTLOGFLAGS_APPEND)
    388                     fOpen |= RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND;
     500                {
     501                    rc = rtlogFileOpen(pLogger, pszErrorMsg, cchErrorMsg);
     502                    /* Rotate in case of appending to a too big log file,
     503                     * otherwise this simply doesn't do anything. */
     504                    rtlogRotate(pLogger, 0, true /* fFirst */);
     505                }
    389506                else
    390                     fOpen |= RTFILE_O_CREATE_REPLACE;
    391                 if (pLogger->fFlags & RTLOGFLAGS_WRITE_THROUGH)
    392                     fOpen |= RTFILE_O_WRITE_THROUGH;
    393                 rc = RTFileOpen(&pLogger->File, pLogger->pszFilename, fOpen);
    394                 if (RT_FAILURE(rc) && pszErrorMsg)
    395                     RTStrPrintf(pszErrorMsg, cchErrorMsg, N_("could not open file '%s' (fOpen=%#x)"), pLogger->pszFilename, fOpen);
     507                {
     508                    /* Force rotation if it is configured. */
     509                    pLogger->pFile->cbHistoryFileWritten = UINT64_MAX;
     510                    rtlogRotate(pLogger, 0, true /* fFirst */);
     511                    /* If the file is not open then rotation is not set up. */
     512                    if (pLogger->pFile->File == NIL_RTFILE)
     513                    {
     514                        pLogger->pFile->cbHistoryFileWritten = 0;
     515                        rc = rtlogFileOpen(pLogger, pszErrorMsg, cchErrorMsg);
     516                    }
     517                }
    396518            }
     519
     520            /* Use the callback to generate some initial log contents. */
     521            Assert(VALID_PTR(pLogger->pFile->pfnPhase) || pLogger->pFile->pfnPhase == NULL);
     522            if (pLogger->pFile->pfnPhase)
     523                pfnPhase(pLogger, RTLOGPHASE_BEGIN, rtlogPhaseMsgNormal);
    397524#endif  /* IN_RING3 */
    398525
     
    425552            }
    426553#ifdef IN_RING3
    427             RTFileClose(pLogger->File);
     554            RTFileClose(pLogger->pFile->File);
    428555#endif
    429556#if defined(LOG_USE_C99) && defined(RT_WITHOUT_EXEC_ALLOC)
     
    456583 *                              logger instance.
    457584 * @param   fDestFlags          The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
     585 * @param   pfnPhase            Callback function for starting logging and for ending or starting a new file for log history rotation.
     586 * @param   cHistory            Number of old log files to keep when performing log history rotation.
     587 * @param   cbHistoryFileMax    Maximum size of log file when performing history rotation. 0=no size limit.
     588 * @param   uHistoryTimeSlotLength     Maximum time interval per log file when performing history rotation, in seconds. 0=no time limit.
    458589 * @param   pszFilenameFmt      Log filename format string. Standard RTStrFormat().
    459590 * @param   ...                 Format arguments.
     
    461592RTDECL(int) RTLogCreate(PRTLOGGER *ppLogger, uint32_t fFlags, const char *pszGroupSettings,
    462593                        const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
    463                         uint32_t fDestFlags, const char *pszFilenameFmt, ...)
     594                        uint32_t fDestFlags, PFNRTLOGPHASE pfnPhase, uint32_t cHistory,
     595                        uint64_t cbHistoryFileMax, uint32_t uHistoryTimeSlotLength,
     596                        const char *pszFilenameFmt, ...)
    464597{
    465598    va_list args;
     
    467600
    468601    va_start(args, pszFilenameFmt);
    469     rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, NULL, 0, pszFilenameFmt, args);
     602    rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, pfnPhase, cHistory, cbHistoryFileMax, uHistoryTimeSlotLength, NULL, 0, pszFilenameFmt, args);
    470603    va_end(args);
    471604    return rc;
     
    487620 *                              logger instance.
    488621 * @param   fDestFlags          The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
     622 * @param   pfnPhase            Callback function for starting logging and for ending or starting a new file for log history rotation.
     623 * @param   cHistory            Number of old log files to keep when performing log history rotation.
     624 * @param   cbHistoryFileMax    Maximum size of log file when performing history rotation. 0=no size limit.
     625 * @param   uHistoryTimeSlotLength     Maximum time interval per log file when performing history rotation, in seconds. 0=no time limit.
    489626 * @param   pszErrorMsg         A buffer which is filled with an error message if something fails. May be NULL.
    490627 * @param   cchErrorMsg         The size of the error message buffer.
     
    494631RTDECL(int) RTLogCreateEx(PRTLOGGER *ppLogger, uint32_t fFlags, const char *pszGroupSettings,
    495632                          const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
    496                           uint32_t fDestFlags,  char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, ...)
     633                          uint32_t fDestFlags, PFNRTLOGPHASE pfnPhase, uint32_t cHistory,
     634                          uint64_t cbHistoryFileMax, uint32_t uHistoryTimeSlotLength,
     635                          char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, ...)
    497636{
    498637    va_list args;
     
    500639
    501640    va_start(args, pszFilenameFmt);
    502     rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, pszErrorMsg, cchErrorMsg, pszFilenameFmt, args);
     641    rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, pfnPhase, cHistory, cbHistoryFileMax, uHistoryTimeSlotLength, pszErrorMsg, cchErrorMsg, pszFilenameFmt, args);
    503642    va_end(args);
    504643    return rc;
     
    545684    rtlogFlush(pLogger);
    546685
     686#ifdef IN_RING3
     687    /*
     688     * Add end of logging message.
     689     */
     690    if (pLogger->fDestFlags & RTLOGDEST_FILE && pLogger->pFile->File != NIL_RTFILE)
     691        pLogger->pFile->pfnPhase(pLogger, RTLOGPHASE_END, rtlogPhaseMsgLocked);
     692
    547693    /*
    548694     * Close output stuffs.
    549695     */
    550 #ifdef IN_RING3
    551     if (pLogger->File != NIL_RTFILE)
    552     {
    553         int rc2 = RTFileClose(pLogger->File);
     696    if (pLogger->pFile->File != NIL_RTFILE)
     697    {
     698        int rc2 = RTFileClose(pLogger->pFile->File);
    554699        AssertRC(rc2);
    555700        if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
    556701            rc = rc2;
    557         pLogger->File = NIL_RTFILE;
     702        pLogger->pFile->File = NIL_RTFILE;
    558703    }
    559704#endif
     
    766911    pLogger->fFlags       = fFlags;
    767912    pLogger->fDestFlags   = fDestFlags & ~RTLOGDEST_FILE;
    768     pLogger->File         = NIL_RTFILE;
    769     pLogger->pszFilename  = NULL;
     913    pLogger->pFile        = NULL;
    770914    pLogger->papszGroups  = NULL;
    771915    pLogger->cMaxGroups   = (uint32_t)((cbLogger - RT_OFFSETOF(RTLOGGER, afGroups[0])) / sizeof(pLogger->afGroups[0]));
     
    16041748                    {
    16051749                        AssertReturn(cch < RTPATH_MAX, VERR_OUT_OF_RANGE);
    1606                         memcpy(pLogger->pszFilename, pszVar, cch);
    1607                         pLogger->pszFilename[cch] = '\0';
     1750                        memcpy(pLogger->pFile->pszFilename, pszVar, cch);
     1751                        pLogger->pFile->pszFilename[cch] = '\0';
    16081752                    }
    16091753                    /* log directory */
     
    16111755                    {
    16121756                        char        szTmp[RTPATH_MAX];
    1613                         const char *pszFile = RTPathFilename(pLogger->pszFilename);
     1757                        const char *pszFile = RTPathFilename(pLogger->pFile->pszFilename);
    16141758                        size_t      cchFile = pszFile ? strlen(pszFile) : 0;
    16151759                        AssertReturn(cchFile + cch + 1 < RTPATH_MAX, VERR_OUT_OF_RANGE);
    16161760                        memcpy(szTmp, cchFile ? pszFile : "", cchFile + 1);
    16171761
    1618                         memcpy(pLogger->pszFilename, pszVar, cch);
    1619                         pLogger->pszFilename[cch] = '\0';
    1620                         RTPathStripTrailingSlash(pLogger->pszFilename);
    1621 
    1622                         cch = strlen(pLogger->pszFilename);
    1623                         pLogger->pszFilename[cch++] = '/';
    1624                         memcpy(&pLogger->pszFilename[cch], szTmp, cchFile);
    1625                         pLogger->pszFilename[cch+cchFile] = '\0';
     1762                        memcpy(pLogger->pFile->pszFilename, pszVar, cch);
     1763                        pLogger->pFile->pszFilename[cch] = '\0';
     1764                        RTPathStripTrailingSlash(pLogger->pFile->pszFilename);
     1765
     1766                        cch = strlen(pLogger->pFile->pszFilename);
     1767                        pLogger->pFile->pszFilename[cch++] = '/';
     1768                        memcpy(&pLogger->pFile->pszFilename[cch], szTmp, cchFile);
     1769                        pLogger->pFile->pszFilename[cch+cchFile] = '\0';
     1770                    }
     1771                    else if (i == 2 /* history */)
     1772                    {
     1773                        if (!fNo)
     1774                        {
     1775                            char szTmp[32];
     1776                            int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszVar, cch);
     1777                            if (RT_SUCCESS(rc))
     1778                                rc = RTStrToUInt32Full(szTmp, 0, &pLogger->pFile->cHistory);
     1779                            if (RT_FAILURE(rc))
     1780                                AssertMsgFailedReturn(("Invalid history value %s (%Rrc)!\n",
     1781                                                       szTmp, rc), rc);
     1782                        }
     1783                        else
     1784                            pLogger->pFile->cHistory = 0;
     1785                    }
     1786                    else if (i == 3 /* histsize */)
     1787                    {
     1788                        if (!fNo)
     1789                        {
     1790                            char szTmp[32];
     1791                            int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszVar, cch);
     1792                            if (RT_SUCCESS(rc))
     1793                                rc = RTStrToUInt64Full(szTmp, 0, &pLogger->pFile->cbHistoryFileMax);
     1794                            if (RT_FAILURE(rc))
     1795                                AssertMsgFailedReturn(("Invalid history file size value %s (%Rrc)!\n",
     1796                                                       szTmp, rc), rc);
     1797                            if (pLogger->pFile->cbHistoryFileMax == 0)
     1798                                pLogger->pFile->cbHistoryFileMax = UINT64_MAX;
     1799                        }
     1800                        else
     1801                            pLogger->pFile->cbHistoryFileMax = UINT64_MAX;
     1802                    }
     1803                    else if (i == 4 /* histtime */)
     1804                    {
     1805                        if (!fNo)
     1806                        {
     1807                            char szTmp[32];
     1808                            int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszVar, cch);
     1809                            if (RT_SUCCESS(rc))
     1810                                rc = RTStrToUInt32Full(szTmp, 0, &pLogger->pFile->uHistoryTimeSlotLength);
     1811                            if (RT_FAILURE(rc))
     1812                                AssertMsgFailedReturn(("Invalid history timespan value %s (%Rrc)!\n",
     1813                                                       szTmp, rc), rc);
     1814                            if (pLogger->pFile->uHistoryTimeSlotLength == 0)
     1815                                pLogger->pFile->uHistoryTimeSlotLength = UINT32_MAX;
     1816                        }
     1817                        else
     1818                            pLogger->pFile->uHistoryTimeSlotLength = UINT32_MAX;
    16261819                    }
    16271820                    else
     
    17041897        }
    17051898
     1899#ifdef IN_RING3
    17061900    /*
    17071901     * Add the filename.
    17081902     */
    17091903    if (    (fDestFlags & RTLOGDEST_FILE)
    1710         &&  VALID_PTR(pLogger->pszFilename)
     1904        &&  VALID_PTR(pLogger->pFile->pszFilename)
    17111905        &&  RT_SUCCESS(rc))
    17121906    {
    1713         size_t cchFilename = strlen(pLogger->pszFilename);
     1907        size_t cchFilename = strlen(pLogger->pFile->pszFilename);
    17141908        if (cchFilename + sizeof("file=") - 1 + fNotFirst + 1 <= cchBuf)
    17151909        {
     
    17181912            else
    17191913                APPEND_SZ("file=");
    1720             APPEND_PSZ(pLogger->pszFilename, cchFilename);
     1914            APPEND_PSZ(pLogger->pFile->pszFilename, cchFilename);
    17211915        }
    17221916        else
    17231917            rc = VERR_BUFFER_OVERFLOW;
    17241918    }
     1919#endif
    17251920
    17261921#undef APPEND_PSZ
     
    20322227
    20332228    /*
    2034      * Format the message and perhaps flush it.
    2035      */
    2036     if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF))
    2037     {
    2038         RTLOGOUTPUTPREFIXEDARGS OutputArgs;
    2039         OutputArgs.pLogger = pLogger;
    2040         OutputArgs.iGroup = iGroup;
    2041         OutputArgs.fFlags = fFlags;
    2042         RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);
    2043     }
    2044     else
    2045         RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);
    2046     if (    !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)
    2047         &&  pLogger->offScratch)
    2048         rtlogFlush(pLogger);
     2229     * Call worker.
     2230     */
     2231    rtlogLoggerExV(pLogger, fFlags, iGroup, pszFormat, args);
    20492232
    20502233    /*
     
    22172400
    22182401
     2402#ifdef IN_RING3
     2403/**
     2404 * Opens/creates the log file.
     2405 *
     2406 * @param   pLogger     The logger instance to update. NULL is not allowed!
     2407 * @param   pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.
     2408 * @param   cchErrorMsg The size of the error message buffer.
     2409 */
     2410static int rtlogFileOpen(PRTLOGGER pLogger, char *pszErrorMsg, size_t cchErrorMsg)
     2411{
     2412    uint32_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
     2413    if (pLogger->fFlags & RTLOGFLAGS_APPEND)
     2414        fOpen |= RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND;
     2415    else
     2416        fOpen |= RTFILE_O_CREATE_REPLACE;
     2417    if (pLogger->fFlags & RTLOGFLAGS_WRITE_THROUGH)
     2418        fOpen |= RTFILE_O_WRITE_THROUGH;
     2419    int rc = RTFileOpen(&pLogger->pFile->File, pLogger->pFile->pszFilename, fOpen);
     2420    if (RT_FAILURE(rc))
     2421    {
     2422        pLogger->pFile->File = NIL_RTFILE;
     2423        if (pszErrorMsg)
     2424            RTStrPrintf(pszErrorMsg, cchErrorMsg, N_("could not open file '%s' (fOpen=%#x)"), pLogger->pFile->pszFilename, fOpen);
     2425    }
     2426    else
     2427    {
     2428        rc = RTFileGetSize(pLogger->pFile->File, &pLogger->pFile->cbHistoryFileWritten);
     2429        if (RT_FAILURE(rc))
     2430        {
     2431            /* Don't complain if this fails, assume the file is empty. */
     2432            pLogger->pFile->cbHistoryFileWritten = 0;
     2433            rc = VINF_SUCCESS;
     2434        }
     2435    }
     2436    return rc;
     2437}
     2438
     2439
     2440/**
     2441 * Closes, rotates and opens the log files if necessary.
     2442 * Used by the rtlogFlush() function.
     2443 *
     2444 * @param   pLogger     The logger instance to update. NULL is not allowed!
     2445 * @param   uTimeSlit   Current time slot (for tikme based rotation).
     2446 * @param   fFirst      Flag whether this is the beginning of logging.
     2447 */
     2448static void rtlogRotate(PRTLOGGER pLogger, uint32_t uTimeSlot, bool fFirst)
     2449{
     2450    /* Suppress rotating empty log files simply because the time elapsed. */
     2451    if (RT_UNLIKELY(!pLogger->pFile->cbHistoryFileWritten))
     2452        pLogger->pFile->uHistoryTimeSlotStart = uTimeSlot;
     2453
     2454    /* Check rotation condition: file still small enough and not too old? */
     2455    if (RT_LIKELY(   pLogger->pFile->cbHistoryFileWritten < pLogger->pFile->cbHistoryFileMax
     2456                  && uTimeSlot == pLogger->pFile->uHistoryTimeSlotStart))
     2457        return;
     2458
     2459    /* Save "disabled" log flag and make sure logging is disabled.
     2460     * The logging in the functions called during log file history
     2461     * rotation would cause severe trouble otherwise. */
     2462    uint32_t fOFlags = pLogger->fFlags;
     2463    pLogger->fFlags |= RTLOGFLAGS_DISABLED;
     2464
     2465    /* Disable log rotation temporarily, otherwise with extreme settings and
     2466     * chatty phase logging we could run into endless rotation. */
     2467    uint32_t cOHistory = pLogger->pFile->cHistory;
     2468    pLogger->pFile->cHistory = 0;
     2469
     2470    /* Close the old log file. */
     2471    if (pLogger->pFile->File != NIL_RTFILE)
     2472    {
     2473        /* Use the callback to generate some final log contents, but only if
     2474         * this is a rotation with a fully set up logger. Leave the other case
     2475         * to the RTLogCreateExV function. */
     2476        if (pLogger->pFile->pfnPhase && !fFirst)
     2477        {
     2478            uint32_t fODestFlags = pLogger->fDestFlags;
     2479            pLogger->fDestFlags &= RTLOGDEST_FILE;
     2480            pLogger->pFile->pfnPhase(pLogger, RTLOGPHASE_PREROTATE, rtlogPhaseMsgLocked);
     2481            pLogger->fDestFlags = fODestFlags;
     2482        }
     2483        RTFileClose(pLogger->pFile->File);
     2484        pLogger->pFile->File = NIL_RTFILE;
     2485    }
     2486
     2487    /* Rotate the log files. */
     2488    if (cOHistory)
     2489    {
     2490        for (uint32_t i = cOHistory - 1; i + 1 > 0; i--)
     2491        {
     2492            char szOldName[RTPATH_MAX];
     2493            if (i > 0)
     2494                RTStrPrintf(szOldName, sizeof(szOldName), "%s.%u", pLogger->pFile->pszFilename, i);
     2495            else
     2496                RTStrCopy(szOldName, sizeof(szOldName), pLogger->pFile->pszFilename);
     2497            char szNewName[RTPATH_MAX];
     2498            RTStrPrintf(szNewName, sizeof(szNewName), "%s.%u", pLogger->pFile->pszFilename, i + 1);
     2499            if (RTFileRename(szOldName, szNewName,
     2500                             RTFILEMOVE_FLAGS_REPLACE) == VERR_FILE_NOT_FOUND)
     2501                RTFileDelete(szNewName);
     2502        }
     2503    }
     2504    /* Delete excess log files. */
     2505    for (uint32_t i = cOHistory + 1; i++; )
     2506    {
     2507        char szExcessName[RTPATH_MAX];
     2508        RTStrPrintf(szExcessName, sizeof(szExcessName), "%s.%u", pLogger->pFile->pszFilename, i);
     2509        int rc = RTFileDelete(szExcessName);
     2510        if (RT_FAILURE(rc))
     2511            break;
     2512    }
     2513
     2514    /* Update logger state and create new log file. */
     2515    pLogger->pFile->cbHistoryFileWritten = 0;
     2516    pLogger->pFile->uHistoryTimeSlotStart = uTimeSlot;
     2517    rtlogFileOpen(pLogger, NULL, 0);
     2518
     2519    /* Use the callback to generate some initial log contents, but only if this
     2520     * is a rotation with a fully set up logger. Leave the other case to the
     2521     * RTLogCreateExV function. */
     2522    if (pLogger->pFile->pfnPhase && !fFirst)
     2523    {
     2524        uint32_t fODestFlags = pLogger->fDestFlags;
     2525        pLogger->fDestFlags &= RTLOGDEST_FILE;
     2526        pLogger->pFile->pfnPhase(pLogger, RTLOGPHASE_POSTROTATE, rtlogPhaseMsgLocked);
     2527        pLogger->fDestFlags = fODestFlags;
     2528    }
     2529
     2530    /* Restore log rotation to the previous value. */
     2531    pLogger->pFile->cHistory = cOHistory;
     2532
     2533    /* Restore the log flags to the previous value. */
     2534    pLogger->fFlags = fOFlags;
     2535}
     2536#endif /* IN_RING3 */
     2537
     2538
    22192539/**
    22202540 * Writes the buffer to the given log device without checking for buffered
     
    22392559    if (pLogger->fDestFlags & RTLOGDEST_FILE)
    22402560    {
    2241         RTFileWrite(pLogger->File, pLogger->achScratch, pLogger->offScratch, NULL);
    2242         if (pLogger->fFlags & RTLOGFLAGS_FLUSH)
    2243             RTFileFlush(pLogger->File);
     2561        if (pLogger->pFile->File != NIL_RTFILE)
     2562        {
     2563            RTFileWrite(pLogger->pFile->File, pLogger->achScratch, pLogger->offScratch, NULL);
     2564            if (pLogger->fFlags & RTLOGFLAGS_FLUSH)
     2565                RTFileFlush(pLogger->pFile->File);
     2566        }
     2567        if (pLogger->pFile->cHistory)
     2568            pLogger->pFile->cbHistoryFileWritten += pLogger->offScratch;
    22442569    }
    22452570# endif
     
    22622587    /* empty the buffer. */
    22632588    pLogger->offScratch = 0;
     2589
     2590# ifdef IN_RING3
     2591    /* Rotate the log file if configured. Must be done after everything is
     2592     * flushed, since this will also use logging/flushing to write the header
     2593     * and footer messages. */
     2594    if ((pLogger->fDestFlags & RTLOGDEST_FILE) && pLogger->pFile->cHistory)
     2595    {
     2596        rtlogRotate(pLogger,
     2597                    RTTimeProgramSecTS() / pLogger->pFile->uHistoryTimeSlotLength,
     2598                    false /* fFirst */);
     2599    }
     2600#endif
    22642601}
    22652602
     
    27293066}
    27303067
     3068/**
     3069 * Write to a logger instance (worker function).
     3070 *
     3071 * This function will check whether the instance, group and flags makes up a
     3072 * logging kind which is currently enabled before writing anything to the log.
     3073 *
     3074 * @param   pLogger     Pointer to logger instance. Must be non-NULL.
     3075 * @param   fFlags      The logging flags.
     3076 * @param   iGroup      The group.
     3077 *                      The value ~0U is reserved for compatibility with RTLogLogger[V] and is
     3078 *                      only for internal usage!
     3079 * @param   pszFormat   Format string.
     3080 * @param   args        Format arguments.
     3081 */
     3082static void rtlogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
     3083{
     3084    /*
     3085     * Format the message and perhaps flush it.
     3086     */
     3087    if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF))
     3088    {
     3089        RTLOGOUTPUTPREFIXEDARGS OutputArgs;
     3090        OutputArgs.pLogger = pLogger;
     3091        OutputArgs.iGroup = iGroup;
     3092        OutputArgs.fFlags = fFlags;
     3093        RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);
     3094    }
     3095    else
     3096        RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);
     3097    if (    !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)
     3098        &&  pLogger->offScratch)
     3099        rtlogFlush(pLogger);
     3100}
     3101
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette