private FileLogSink(LogGroupMutex logGroupMutex,
                            string logFileDir,
                            string logFileNameBase,
                            int rotateLogFileWhenLargerBytes,
                            FileStream logStream,
                            int initialRotationIndex,
                            DefaultFormat.Options formatOptions)
        {
            _logSessionId    = Guid.NewGuid();
            _logGroupMutex   = logGroupMutex;
            _logFileDir      = logFileDir;
            _logFileNameBase = logFileNameBase;
            _rotateLogFileWhenLargerBytes = (rotateLogFileWhenLargerBytes <= 0) ? -1 : rotateLogFileWhenLargerBytes;

            _logStream = logStream;
            _logWriter = new StreamWriter(logStream, LogTextEncoding);

            _rotationIndex = initialRotationIndex;

            _formatOptions = formatOptions ?? DefaultFormatOptions;
        }
        /// <summary>
        /// Attention: All loggers from all processes that write to the same <c>logFileNameBase</c> MUST use the same value for <c>rotateLogFileWhenLargerBytes</c>!
        /// </summary>
        public static bool TryCreateNew(
            string logFileDir,
            string logFileNameBase,
            Guid logGroupId,
            int rotateLogFileWhenLargerBytes,
            DefaultFormat.Options formatOptions,
            out FileLogSink newSink)
        {
            // Bad usage - throw.

            if (logFileNameBase == null)
            {
                throw new ArgumentNullException(nameof(logFileNameBase));
            }

            if (String.IsNullOrWhiteSpace(logFileNameBase))
            {
                throw new ArgumentException($"{nameof(logFileNameBase)} may not be white-space only.", nameof(logFileNameBase));
            }

            // Ok usage, but bad state - do not throw and return false.

            newSink = null;

            if (String.IsNullOrWhiteSpace(logFileDir))
            {
                return(false);
            }

            // Normalize in respect to final dir separator:
            logFileDir = Path.GetDirectoryName(Path.Combine(logFileDir, "."));

            // Ensure the directory exists:
            if (!EnsureDirectoryExists(logFileDir, out DirectoryInfo logFileDirInfo))
            {
                return(false);
            }

            LogGroupMutex logGroupMutex = null;

            try
            {
                logGroupMutex = new LogGroupMutex(logGroupId);
                if (!logGroupMutex.TryAcquire(out LogGroupMutex.Handle logGroupMutexHandle))
                {
                    logGroupMutex.Dispose();
                    return(false);
                }

                using (logGroupMutexHandle)
                {
                    DateTimeOffset now           = DateTimeOffset.Now;
                    int            rotationIndex = FindLatestRotationIndex(logFileDirInfo, logFileNameBase, now);

                    string     logFileName = ConstructFilename(logFileNameBase, now, rotationIndex);
                    string     logFilePath = Path.Combine(logFileDir, logFileName);
                    FileStream logStream   = new FileStream(logFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);

                    newSink = new FileLogSink(logGroupMutex, logFileDir, logFileNameBase, rotateLogFileWhenLargerBytes, logStream, rotationIndex, formatOptions);
                }

                if (newSink.TryLogInfo(
                        SelfLogSourceInfo.WithCallInfo().WithAssemblyName(),
                        "Logging session started",
                        new object[]
                {
                    "LogGroupId",
                    newSink.LogGroupId,
                    "LogSessionId",
                    newSink.LogSessionId,
                    "RotateLogFileWhenLargerBytes",
                    newSink.RotateLogFileWhenLargerBytes
                }))
                {
                    return(true);
                }
            }
            catch
            { }

            // If we did not succeed, the sink may be still constructed (e.g. TryLogInfo(..) returned false).
            // We need to dispose the sink before giving up, but be brepaed for it to sbe null.
            try
            {
                if (newSink != null)
                {
                    newSink.Dispose();  // This will also dispose the logGroupMutex owned by the newSink.
                }
                else
                {
                    logGroupMutex.Dispose();
                }
            }
            catch
            { }

            newSink = null;
            return(false);
        }