Index: trunk/src/VBox/Runtime/common/log/log.cpp
===================================================================
--- trunk/src/VBox/Runtime/common/log/log.cpp	(revision 36312)
+++ trunk/src/VBox/Runtime/common/log/log.cpp	(revision 36344)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2011 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -89,7 +89,12 @@
 static void rtR0LogLoggerExFallback(uint32_t fDestFlags, uint32_t fFlags, const char *pszFormat, va_list va);
 #endif
+#ifdef IN_RING3
+static int rtlogFileOpen(PRTLOGGER pLogger, char *pszErrorMsg, size_t cchErrorMsg);
+static void rtlogRotate(PRTLOGGER pLogger, uint32_t uTimeSlot, bool fFirst);
+#endif
 static void rtlogFlush(PRTLOGGER pLogger);
 static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars);
 static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars);
+static void rtlogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args);
 
 
@@ -190,4 +195,7 @@
     { "file",     sizeof("file"    ) - 1,  RTLOGDEST_FILE }, /* Must be 1st! */
     { "dir",      sizeof("dir"     ) - 1,  RTLOGDEST_FILE }, /* Must be 2nd! */
+    { "history",  sizeof("history" ) - 1,  0 },              /* Must be 3rd! */
+    { "histsize", sizeof("histsize") - 1,  0 },              /* Must be 4th! */
+    { "histtime", sizeof("histtime") - 1,  0 },              /* Must be 5th! */
     { "stdout",   sizeof("stdout"  ) - 1,  RTLOGDEST_STDOUT },
     { "stderr",   sizeof("stderr"  ) - 1,  RTLOGDEST_STDERR },
@@ -225,5 +233,5 @@
 {
 #ifndef IN_RC
-    if (pLogger->hSpinMtx != NIL_RTSEMFASTMUTEX)
+    if (pLogger->hSpinMtx != NIL_RTSEMSPINMUTEX)
         RTSemSpinMutexRelease(pLogger->hSpinMtx);
 #endif
@@ -233,4 +241,85 @@
 
 #ifndef IN_RC
+# ifdef IN_RING3
+/**
+ * Logging to file, output callback.
+ *
+ * @param  pvArg        User argument.
+ * @param  pachChars    Pointer to an array of utf-8 characters.
+ * @param  cbChars      Number of bytes in the character array pointed to by pachChars.
+ */
+static DECLCALLBACK(size_t) rtlogPhaseWrite(void *pvArg, const char *pachChars, size_t cbChars)
+{
+    PRTLOGGER pLogger = (PRTLOGGER)pvArg;
+    RTFileWrite(pLogger->pFile->File, pachChars, cbChars, NULL);
+    return cbChars;
+}
+
+/**
+ * Callback to format VBox formatting extentions.
+ * See @ref pg_rt_str_format for a reference on the format types.
+ *
+ * @returns The number of bytes formatted.
+ * @param   pvArg           Formatter argument.
+ * @param   pfnOutput       Pointer to output function.
+ * @param   pvArgOutput     Argument for the output function.
+ * @param   ppszFormat      Pointer to the format string pointer. Advance this till the char
+ *                          after the format specifier.
+ * @param   pArgs           Pointer to the argument list. Use this to fetch the arguments.
+ * @param   cchWidth        Format Width. -1 if not specified.
+ * @param   cchPrecision    Format Precision. -1 if not specified.
+ * @param   fFlags          Flags (RTSTR_NTFS_*).
+ * @param   chArgSize       The argument size specifier, 'l' or 'L'.
+ */
+static DECLCALLBACK(size_t) rtlogPhaseFormatStr(void *pvArg, PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+                                                const char **ppszFormat, va_list *pArgs, int cchWidth,
+                                                int cchPrecision, unsigned fFlags, char chArgSize)
+{
+    char ch = *(*ppszFormat)++;
+
+    AssertMsgFailed(("Invalid logger phase format type '%%%c%.10s'!\n", ch, *ppszFormat)); NOREF(ch);
+
+    return 0;
+}
+
+/**
+ * Log phase callback function, assumes the lock is already held
+ *
+ * @param   pLogger     The logger instance.
+ * @param   pszFormat   Format string.
+ * @param   ...         Optional arguments as specified in the format string.
+ */
+static DECLCALLBACK(void) rtlogPhaseMsgLocked(PRTLOGGER pLogger, const char *pszFormat, ...)
+{
+    va_list args;
+    AssertPtrReturnVoid(pLogger);
+    AssertPtrReturnVoid(pLogger->pFile);
+    Assert(pLogger->hSpinMtx != NIL_RTSEMSPINMUTEX);
+
+    va_start(args, pszFormat);
+    rtlogLoggerExV(pLogger, 0, ~0, pszFormat, args);
+    va_end(args);
+}
+
+/**
+ * Log phase callback function, assumes the lock is not held
+ *
+ * @param   pLogger     The logger instance.
+ * @param   pszFormat   Format string.
+ * @param   ...         Optional arguments as specified in the format string.
+ */
+static DECLCALLBACK(void) rtlogPhaseMsgNormal(PRTLOGGER pLogger, const char *pszFormat, ...)
+{
+    va_list args;
+    AssertPtrReturnVoid(pLogger);
+    AssertPtrReturnVoid(pLogger->pFile);
+    Assert(pLogger->hSpinMtx != NIL_RTSEMSPINMUTEX);
+
+    va_start(args, pszFormat);
+    RTLogLoggerExV(pLogger, 0, ~0, pszFormat, args);
+    va_end(args);
+}
+# endif /* IN_RING3 */
+
 /**
  * Create a logger instance, comprehensive version.
@@ -246,4 +335,8 @@
  *                              logger instance.
  * @param   fDestFlags          The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
+ * @param   pfnPhase            Callback function for starting logging and for ending or starting a new file for log history rotation.
+ * @param   cHistory            Number of old log files to keep when performing log history rotation.
+ * @param   cbHistoryFileMax    Maximum size of log file when performing history rotation. 0=no size limit.
+ * @param   uHistoryTimeSlotLength     Maximum time interval per log file when performing history rotation, in seconds. 0=no time limit.
  * @param   pszErrorMsg         A buffer which is filled with an error message if something fails. May be NULL.
  * @param   cchErrorMsg         The size of the error message buffer.
@@ -253,5 +346,7 @@
 RTDECL(int) RTLogCreateExV(PRTLOGGER *ppLogger, uint32_t fFlags, const char *pszGroupSettings,
                            const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
-                           uint32_t fDestFlags, char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, va_list args)
+                           uint32_t fDestFlags, PFNRTLOGPHASE pfnPhase, uint32_t cHistory,
+                           uint64_t cbHistoryFileMax, uint32_t uHistoryTimeSlotLength,
+                           char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, va_list args)
 {
     int        rc;
@@ -278,4 +373,7 @@
      */
     cb = RT_OFFSETOF(RTLOGGER, afGroups[cGroups + 1]) + RTPATH_MAX;
+#ifdef IN_RING3
+    cb += sizeof(RTLOGGERFILE);
+#endif
     pLogger = (PRTLOGGER)RTMemAllocZVar(cb);
     if (pLogger)
@@ -289,6 +387,21 @@
         pLogger->cMaxGroups  = cGroups;
         pLogger->cGroups     = cGroups;
-        pLogger->pszFilename = (char *)&pLogger->afGroups[cGroups + 1];
-        pLogger->File        = NIL_RTFILE;
+#ifdef IN_RING3
+        pLogger->pFile       = (PRTLOGGERFILE)((char *)&pLogger->afGroups[cGroups + 1] + RTPATH_MAX);
+        pLogger->pFile->File        = NIL_RTFILE;
+        pLogger->pFile->pszFilename = (char *)&pLogger->afGroups[cGroups + 1];
+        pLogger->pFile->pfnPhase    = pfnPhase;
+        pLogger->pFile->cHistory    = cHistory;
+        if (cbHistoryFileMax == 0)
+            pLogger->pFile->cbHistoryFileMax = UINT64_MAX;
+        else
+            pLogger->pFile->cbHistoryFileMax = cbHistoryFileMax;
+        if (uHistoryTimeSlotLength == 0)
+            pLogger->pFile->uHistoryTimeSlotLength = UINT32_MAX;
+        else
+            pLogger->pFile->uHistoryTimeSlotLength = uHistoryTimeSlotLength;
+#else /* !IN_RING3 */
+        pLogger->pFile       = NULL;
+#endif /* !IN_RING3 */
         pLogger->fFlags      = fFlags;
         pLogger->fDestFlags  = fDestFlags;
@@ -337,5 +450,5 @@
             if (pszFilenameFmt)
             {
-                RTStrPrintfV(pLogger->pszFilename, RTPATH_MAX, pszFilenameFmt, args);
+                RTStrPrintfV(pLogger->pFile->pszFilename, RTPATH_MAX, pszFilenameFmt, args);
                 pLogger->fDestFlags |= RTLOGDEST_FILE;
             }
@@ -384,15 +497,29 @@
             if (pLogger->fDestFlags & RTLOGDEST_FILE)
             {
-                uint32_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
                 if (pLogger->fFlags & RTLOGFLAGS_APPEND)
-                    fOpen |= RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND;
+                {
+                    rc = rtlogFileOpen(pLogger, pszErrorMsg, cchErrorMsg);
+                    /* Rotate in case of appending to a too big log file,
+                     * otherwise this simply doesn't do anything. */
+                    rtlogRotate(pLogger, 0, true /* fFirst */);
+                }
                 else
-                    fOpen |= RTFILE_O_CREATE_REPLACE;
-                if (pLogger->fFlags & RTLOGFLAGS_WRITE_THROUGH)
-                    fOpen |= RTFILE_O_WRITE_THROUGH;
-                rc = RTFileOpen(&pLogger->File, pLogger->pszFilename, fOpen);
-                if (RT_FAILURE(rc) && pszErrorMsg)
-                    RTStrPrintf(pszErrorMsg, cchErrorMsg, N_("could not open file '%s' (fOpen=%#x)"), pLogger->pszFilename, fOpen);
+                {
+                    /* Force rotation if it is configured. */
+                    pLogger->pFile->cbHistoryFileWritten = UINT64_MAX;
+                    rtlogRotate(pLogger, 0, true /* fFirst */);
+                    /* If the file is not open then rotation is not set up. */
+                    if (pLogger->pFile->File == NIL_RTFILE)
+                    {
+                        pLogger->pFile->cbHistoryFileWritten = 0;
+                        rc = rtlogFileOpen(pLogger, pszErrorMsg, cchErrorMsg);
+                    }
+                }
             }
+
+            /* Use the callback to generate some initial log contents. */
+            Assert(VALID_PTR(pLogger->pFile->pfnPhase) || pLogger->pFile->pfnPhase == NULL);
+            if (pLogger->pFile->pfnPhase)
+                pfnPhase(pLogger, RTLOGPHASE_BEGIN, rtlogPhaseMsgNormal);
 #endif  /* IN_RING3 */
 
@@ -425,5 +552,5 @@
             }
 #ifdef IN_RING3
-            RTFileClose(pLogger->File);
+            RTFileClose(pLogger->pFile->File);
 #endif
 #if defined(LOG_USE_C99) && defined(RT_WITHOUT_EXEC_ALLOC)
@@ -456,4 +583,8 @@
  *                              logger instance.
  * @param   fDestFlags          The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
+ * @param   pfnPhase            Callback function for starting logging and for ending or starting a new file for log history rotation.
+ * @param   cHistory            Number of old log files to keep when performing log history rotation.
+ * @param   cbHistoryFileMax    Maximum size of log file when performing history rotation. 0=no size limit.
+ * @param   uHistoryTimeSlotLength     Maximum time interval per log file when performing history rotation, in seconds. 0=no time limit.
  * @param   pszFilenameFmt      Log filename format string. Standard RTStrFormat().
  * @param   ...                 Format arguments.
@@ -461,5 +592,7 @@
 RTDECL(int) RTLogCreate(PRTLOGGER *ppLogger, uint32_t fFlags, const char *pszGroupSettings,
                         const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
-                        uint32_t fDestFlags, const char *pszFilenameFmt, ...)
+                        uint32_t fDestFlags, PFNRTLOGPHASE pfnPhase, uint32_t cHistory,
+                        uint64_t cbHistoryFileMax, uint32_t uHistoryTimeSlotLength,
+                        const char *pszFilenameFmt, ...)
 {
     va_list args;
@@ -467,5 +600,5 @@
 
     va_start(args, pszFilenameFmt);
-    rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, NULL, 0, pszFilenameFmt, args);
+    rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, pfnPhase, cHistory, cbHistoryFileMax, uHistoryTimeSlotLength, NULL, 0, pszFilenameFmt, args);
     va_end(args);
     return rc;
@@ -487,4 +620,8 @@
  *                              logger instance.
  * @param   fDestFlags          The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
+ * @param   pfnPhase            Callback function for starting logging and for ending or starting a new file for log history rotation.
+ * @param   cHistory            Number of old log files to keep when performing log history rotation.
+ * @param   cbHistoryFileMax    Maximum size of log file when performing history rotation. 0=no size limit.
+ * @param   uHistoryTimeSlotLength     Maximum time interval per log file when performing history rotation, in seconds. 0=no time limit.
  * @param   pszErrorMsg         A buffer which is filled with an error message if something fails. May be NULL.
  * @param   cchErrorMsg         The size of the error message buffer.
@@ -494,5 +631,7 @@
 RTDECL(int) RTLogCreateEx(PRTLOGGER *ppLogger, uint32_t fFlags, const char *pszGroupSettings,
                           const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
-                          uint32_t fDestFlags,  char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, ...)
+                          uint32_t fDestFlags, PFNRTLOGPHASE pfnPhase, uint32_t cHistory,
+                          uint64_t cbHistoryFileMax, uint32_t uHistoryTimeSlotLength,
+                          char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, ...)
 {
     va_list args;
@@ -500,5 +639,5 @@
 
     va_start(args, pszFilenameFmt);
-    rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, pszErrorMsg, cchErrorMsg, pszFilenameFmt, args);
+    rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, pfnPhase, cHistory, cbHistoryFileMax, uHistoryTimeSlotLength, pszErrorMsg, cchErrorMsg, pszFilenameFmt, args);
     va_end(args);
     return rc;
@@ -545,15 +684,21 @@
     rtlogFlush(pLogger);
 
+#ifdef IN_RING3
+    /*
+     * Add end of logging message.
+     */
+    if (pLogger->fDestFlags & RTLOGDEST_FILE && pLogger->pFile->File != NIL_RTFILE)
+        pLogger->pFile->pfnPhase(pLogger, RTLOGPHASE_END, rtlogPhaseMsgLocked);
+
     /*
      * Close output stuffs.
      */
-#ifdef IN_RING3
-    if (pLogger->File != NIL_RTFILE)
-    {
-        int rc2 = RTFileClose(pLogger->File);
+    if (pLogger->pFile->File != NIL_RTFILE)
+    {
+        int rc2 = RTFileClose(pLogger->pFile->File);
         AssertRC(rc2);
         if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
             rc = rc2;
-        pLogger->File = NIL_RTFILE;
+        pLogger->pFile->File = NIL_RTFILE;
     }
 #endif
@@ -766,6 +911,5 @@
     pLogger->fFlags       = fFlags;
     pLogger->fDestFlags   = fDestFlags & ~RTLOGDEST_FILE;
-    pLogger->File         = NIL_RTFILE;
-    pLogger->pszFilename  = NULL;
+    pLogger->pFile        = NULL;
     pLogger->papszGroups  = NULL;
     pLogger->cMaxGroups   = (uint32_t)((cbLogger - RT_OFFSETOF(RTLOGGER, afGroups[0])) / sizeof(pLogger->afGroups[0]));
@@ -1604,6 +1748,6 @@
                     {
                         AssertReturn(cch < RTPATH_MAX, VERR_OUT_OF_RANGE);
-                        memcpy(pLogger->pszFilename, pszVar, cch);
-                        pLogger->pszFilename[cch] = '\0';
+                        memcpy(pLogger->pFile->pszFilename, pszVar, cch);
+                        pLogger->pFile->pszFilename[cch] = '\0';
                     }
                     /* log directory */
@@ -1611,17 +1755,66 @@
                     {
                         char        szTmp[RTPATH_MAX];
-                        const char *pszFile = RTPathFilename(pLogger->pszFilename);
+                        const char *pszFile = RTPathFilename(pLogger->pFile->pszFilename);
                         size_t      cchFile = pszFile ? strlen(pszFile) : 0;
                         AssertReturn(cchFile + cch + 1 < RTPATH_MAX, VERR_OUT_OF_RANGE);
                         memcpy(szTmp, cchFile ? pszFile : "", cchFile + 1);
 
-                        memcpy(pLogger->pszFilename, pszVar, cch);
-                        pLogger->pszFilename[cch] = '\0';
-                        RTPathStripTrailingSlash(pLogger->pszFilename);
-
-                        cch = strlen(pLogger->pszFilename);
-                        pLogger->pszFilename[cch++] = '/';
-                        memcpy(&pLogger->pszFilename[cch], szTmp, cchFile);
-                        pLogger->pszFilename[cch+cchFile] = '\0';
+                        memcpy(pLogger->pFile->pszFilename, pszVar, cch);
+                        pLogger->pFile->pszFilename[cch] = '\0';
+                        RTPathStripTrailingSlash(pLogger->pFile->pszFilename);
+
+                        cch = strlen(pLogger->pFile->pszFilename);
+                        pLogger->pFile->pszFilename[cch++] = '/';
+                        memcpy(&pLogger->pFile->pszFilename[cch], szTmp, cchFile);
+                        pLogger->pFile->pszFilename[cch+cchFile] = '\0';
+                    }
+                    else if (i == 2 /* history */)
+                    {
+                        if (!fNo)
+                        {
+                            char szTmp[32];
+                            int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszVar, cch);
+                            if (RT_SUCCESS(rc))
+                                rc = RTStrToUInt32Full(szTmp, 0, &pLogger->pFile->cHistory);
+                            if (RT_FAILURE(rc))
+                                AssertMsgFailedReturn(("Invalid history value %s (%Rrc)!\n",
+                                                       szTmp, rc), rc);
+                        }
+                        else
+                            pLogger->pFile->cHistory = 0;
+                    }
+                    else if (i == 3 /* histsize */)
+                    {
+                        if (!fNo)
+                        {
+                            char szTmp[32];
+                            int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszVar, cch);
+                            if (RT_SUCCESS(rc))
+                                rc = RTStrToUInt64Full(szTmp, 0, &pLogger->pFile->cbHistoryFileMax);
+                            if (RT_FAILURE(rc))
+                                AssertMsgFailedReturn(("Invalid history file size value %s (%Rrc)!\n",
+                                                       szTmp, rc), rc);
+                            if (pLogger->pFile->cbHistoryFileMax == 0)
+                                pLogger->pFile->cbHistoryFileMax = UINT64_MAX;
+                        }
+                        else
+                            pLogger->pFile->cbHistoryFileMax = UINT64_MAX;
+                    }
+                    else if (i == 4 /* histtime */)
+                    {
+                        if (!fNo)
+                        {
+                            char szTmp[32];
+                            int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszVar, cch);
+                            if (RT_SUCCESS(rc))
+                                rc = RTStrToUInt32Full(szTmp, 0, &pLogger->pFile->uHistoryTimeSlotLength);
+                            if (RT_FAILURE(rc))
+                                AssertMsgFailedReturn(("Invalid history timespan value %s (%Rrc)!\n",
+                                                       szTmp, rc), rc);
+                            if (pLogger->pFile->uHistoryTimeSlotLength == 0)
+                                pLogger->pFile->uHistoryTimeSlotLength = UINT32_MAX;
+                        }
+                        else
+                            pLogger->pFile->uHistoryTimeSlotLength = UINT32_MAX;
                     }
                     else
@@ -1704,12 +1897,13 @@
         }
 
+#ifdef IN_RING3
     /*
      * Add the filename.
      */
     if (    (fDestFlags & RTLOGDEST_FILE)
-        &&  VALID_PTR(pLogger->pszFilename)
+        &&  VALID_PTR(pLogger->pFile->pszFilename)
         &&  RT_SUCCESS(rc))
     {
-        size_t cchFilename = strlen(pLogger->pszFilename);
+        size_t cchFilename = strlen(pLogger->pFile->pszFilename);
         if (cchFilename + sizeof("file=") - 1 + fNotFirst + 1 <= cchBuf)
         {
@@ -1718,9 +1912,10 @@
             else
                 APPEND_SZ("file=");
-            APPEND_PSZ(pLogger->pszFilename, cchFilename);
+            APPEND_PSZ(pLogger->pFile->pszFilename, cchFilename);
         }
         else
             rc = VERR_BUFFER_OVERFLOW;
     }
+#endif
 
 #undef APPEND_PSZ
@@ -2032,19 +2227,7 @@
 
     /*
-     * Format the message and perhaps flush it.
-     */
-    if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF))
-    {
-        RTLOGOUTPUTPREFIXEDARGS OutputArgs;
-        OutputArgs.pLogger = pLogger;
-        OutputArgs.iGroup = iGroup;
-        OutputArgs.fFlags = fFlags;
-        RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);
-    }
-    else
-        RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);
-    if (    !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)
-        &&  pLogger->offScratch)
-        rtlogFlush(pLogger);
+     * Call worker.
+     */
+    rtlogLoggerExV(pLogger, fFlags, iGroup, pszFormat, args);
 
     /*
@@ -2217,4 +2400,141 @@
 
 
+#ifdef IN_RING3
+/**
+ * Opens/creates the log file.
+ *
+ * @param   pLogger     The logger instance to update. NULL is not allowed!
+ * @param   pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.
+ * @param   cchErrorMsg The size of the error message buffer.
+ */
+static int rtlogFileOpen(PRTLOGGER pLogger, char *pszErrorMsg, size_t cchErrorMsg)
+{
+    uint32_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
+    if (pLogger->fFlags & RTLOGFLAGS_APPEND)
+        fOpen |= RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND;
+    else
+        fOpen |= RTFILE_O_CREATE_REPLACE;
+    if (pLogger->fFlags & RTLOGFLAGS_WRITE_THROUGH)
+        fOpen |= RTFILE_O_WRITE_THROUGH;
+    int rc = RTFileOpen(&pLogger->pFile->File, pLogger->pFile->pszFilename, fOpen);
+    if (RT_FAILURE(rc))
+    {
+        pLogger->pFile->File = NIL_RTFILE;
+        if (pszErrorMsg)
+            RTStrPrintf(pszErrorMsg, cchErrorMsg, N_("could not open file '%s' (fOpen=%#x)"), pLogger->pFile->pszFilename, fOpen);
+    }
+    else
+    {
+        rc = RTFileGetSize(pLogger->pFile->File, &pLogger->pFile->cbHistoryFileWritten);
+        if (RT_FAILURE(rc))
+        {
+            /* Don't complain if this fails, assume the file is empty. */
+            pLogger->pFile->cbHistoryFileWritten = 0;
+            rc = VINF_SUCCESS;
+        }
+    }
+    return rc;
+}
+
+
+/**
+ * Closes, rotates and opens the log files if necessary.
+ * Used by the rtlogFlush() function.
+ *
+ * @param   pLogger     The logger instance to update. NULL is not allowed!
+ * @param   uTimeSlit   Current time slot (for tikme based rotation).
+ * @param   fFirst      Flag whether this is the beginning of logging.
+ */
+static void rtlogRotate(PRTLOGGER pLogger, uint32_t uTimeSlot, bool fFirst)
+{
+    /* Suppress rotating empty log files simply because the time elapsed. */
+    if (RT_UNLIKELY(!pLogger->pFile->cbHistoryFileWritten))
+        pLogger->pFile->uHistoryTimeSlotStart = uTimeSlot;
+
+    /* Check rotation condition: file still small enough and not too old? */
+    if (RT_LIKELY(   pLogger->pFile->cbHistoryFileWritten < pLogger->pFile->cbHistoryFileMax
+                  && uTimeSlot == pLogger->pFile->uHistoryTimeSlotStart))
+        return;
+
+    /* Save "disabled" log flag and make sure logging is disabled.
+     * The logging in the functions called during log file history
+     * rotation would cause severe trouble otherwise. */
+    uint32_t fOFlags = pLogger->fFlags;
+    pLogger->fFlags |= RTLOGFLAGS_DISABLED;
+
+    /* Disable log rotation temporarily, otherwise with extreme settings and
+     * chatty phase logging we could run into endless rotation. */
+    uint32_t cOHistory = pLogger->pFile->cHistory;
+    pLogger->pFile->cHistory = 0;
+
+    /* Close the old log file. */
+    if (pLogger->pFile->File != NIL_RTFILE)
+    {
+        /* Use the callback to generate some final log contents, but only if
+         * this is a rotation with a fully set up logger. Leave the other case
+         * to the RTLogCreateExV function. */
+        if (pLogger->pFile->pfnPhase && !fFirst)
+        {
+            uint32_t fODestFlags = pLogger->fDestFlags;
+            pLogger->fDestFlags &= RTLOGDEST_FILE;
+            pLogger->pFile->pfnPhase(pLogger, RTLOGPHASE_PREROTATE, rtlogPhaseMsgLocked);
+            pLogger->fDestFlags = fODestFlags;
+        }
+        RTFileClose(pLogger->pFile->File);
+        pLogger->pFile->File = NIL_RTFILE;
+    }
+
+    /* Rotate the log files. */
+    if (cOHistory)
+    {
+        for (uint32_t i = cOHistory - 1; i + 1 > 0; i--)
+        {
+            char szOldName[RTPATH_MAX];
+            if (i > 0)
+                RTStrPrintf(szOldName, sizeof(szOldName), "%s.%u", pLogger->pFile->pszFilename, i);
+            else
+                RTStrCopy(szOldName, sizeof(szOldName), pLogger->pFile->pszFilename);
+            char szNewName[RTPATH_MAX];
+            RTStrPrintf(szNewName, sizeof(szNewName), "%s.%u", pLogger->pFile->pszFilename, i + 1);
+            if (RTFileRename(szOldName, szNewName,
+                             RTFILEMOVE_FLAGS_REPLACE) == VERR_FILE_NOT_FOUND)
+                RTFileDelete(szNewName);
+        }
+    }
+    /* Delete excess log files. */
+    for (uint32_t i = cOHistory + 1; i++; )
+    {
+        char szExcessName[RTPATH_MAX];
+        RTStrPrintf(szExcessName, sizeof(szExcessName), "%s.%u", pLogger->pFile->pszFilename, i);
+        int rc = RTFileDelete(szExcessName);
+        if (RT_FAILURE(rc))
+            break;
+    }
+
+    /* Update logger state and create new log file. */
+    pLogger->pFile->cbHistoryFileWritten = 0;
+    pLogger->pFile->uHistoryTimeSlotStart = uTimeSlot;
+    rtlogFileOpen(pLogger, NULL, 0);
+
+    /* Use the callback to generate some initial log contents, but only if this
+     * is a rotation with a fully set up logger. Leave the other case to the
+     * RTLogCreateExV function. */
+    if (pLogger->pFile->pfnPhase && !fFirst)
+    {
+        uint32_t fODestFlags = pLogger->fDestFlags;
+        pLogger->fDestFlags &= RTLOGDEST_FILE;
+        pLogger->pFile->pfnPhase(pLogger, RTLOGPHASE_POSTROTATE, rtlogPhaseMsgLocked);
+        pLogger->fDestFlags = fODestFlags;
+    }
+
+    /* Restore log rotation to the previous value. */
+    pLogger->pFile->cHistory = cOHistory;
+
+    /* Restore the log flags to the previous value. */
+    pLogger->fFlags = fOFlags;
+}
+#endif /* IN_RING3 */
+
+
 /**
  * Writes the buffer to the given log device without checking for buffered
@@ -2239,7 +2559,12 @@
     if (pLogger->fDestFlags & RTLOGDEST_FILE)
     {
-        RTFileWrite(pLogger->File, pLogger->achScratch, pLogger->offScratch, NULL);
-        if (pLogger->fFlags & RTLOGFLAGS_FLUSH)
-            RTFileFlush(pLogger->File);
+        if (pLogger->pFile->File != NIL_RTFILE)
+        {
+            RTFileWrite(pLogger->pFile->File, pLogger->achScratch, pLogger->offScratch, NULL);
+            if (pLogger->fFlags & RTLOGFLAGS_FLUSH)
+                RTFileFlush(pLogger->pFile->File);
+        }
+        if (pLogger->pFile->cHistory)
+            pLogger->pFile->cbHistoryFileWritten += pLogger->offScratch;
     }
 # endif
@@ -2262,4 +2587,16 @@
     /* empty the buffer. */
     pLogger->offScratch = 0;
+
+# ifdef IN_RING3
+    /* Rotate the log file if configured. Must be done after everything is
+     * flushed, since this will also use logging/flushing to write the header
+     * and footer messages. */
+    if ((pLogger->fDestFlags & RTLOGDEST_FILE) && pLogger->pFile->cHistory)
+    {
+        rtlogRotate(pLogger,
+                    RTTimeProgramSecTS() / pLogger->pFile->uHistoryTimeSlotLength,
+                    false /* fFirst */);
+    }
+#endif
 }
 
@@ -2729,2 +3066,36 @@
 }
 
+/**
+ * Write to a logger instance (worker function).
+ *
+ * This function will check whether the instance, group and flags makes up a
+ * logging kind which is currently enabled before writing anything to the log.
+ *
+ * @param   pLogger     Pointer to logger instance. Must be non-NULL.
+ * @param   fFlags      The logging flags.
+ * @param   iGroup      The group.
+ *                      The value ~0U is reserved for compatibility with RTLogLogger[V] and is
+ *                      only for internal usage!
+ * @param   pszFormat   Format string.
+ * @param   args        Format arguments.
+ */
+static void rtlogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
+{
+    /*
+     * Format the message and perhaps flush it.
+     */
+    if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF))
+    {
+        RTLOGOUTPUTPREFIXEDARGS OutputArgs;
+        OutputArgs.pLogger = pLogger;
+        OutputArgs.iGroup = iGroup;
+        OutputArgs.fFlags = fFlags;
+        RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);
+    }
+    else
+        RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);
+    if (    !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)
+        &&  pLogger->offScratch)
+        rtlogFlush(pLogger);
+}
+
