public void QueueLogWriterCanBeDisposedEarly()
        {
            var innerLogWriter = new TestLogWriter <MessageEntry>(SetupLog, false);
            BackgroundMultiLogWriter         backgroundMultiLogWriter;
            IQueueEntryWriter <MessageEntry> queueEntryWriter;

            SetupBackgroundLogWriter(innerLogWriter, out backgroundMultiLogWriter, out queueEntryWriter);

            using (backgroundMultiLogWriter)
            {
                using (queueEntryWriter as IDisposable)
                {
                    ExampleHelper.LogTestMessagesInParallel(queueEntryWriter, 8, 8, _testOutputHelper);
                }
                Assert.False(queueEntryWriter.IsEnabled);
                Assert.False(queueEntryWriter.IsStarted);
                Assert.True(backgroundMultiLogWriter.IsStarted);

                // Can't restart after Dispose()
                Assert.Throws <ObjectDisposedException>(() => queueEntryWriter.Start());

                // Logging still doesn't throw after Dispose
                var msg = new MessageEntry("Logged while Dispose()ed - never logged.");
                queueEntryWriter.Write(ref msg);
            }
            Assert.False(backgroundMultiLogWriter.IsStarted);

            Assert.False(backgroundMultiLogWriter.IsBackgroundThreadRunning);
            Assert.Equal(8 * 8, innerLogWriter.Count);
        }
        public void StoppingQueueLogWriterHaltsWriting()
        {
            var innerLogWriter = new TestLogWriter <MessageEntry>(SetupLog, false);
            BackgroundMultiLogWriter         backgroundMultiLogWriter;
            IQueueEntryWriter <MessageEntry> queueEntryWriter;

            SetupBackgroundLogWriter(innerLogWriter, out backgroundMultiLogWriter, out queueEntryWriter);

            using (backgroundMultiLogWriter)
            {
                ExampleHelper.LogTestMessagesInParallel(queueEntryWriter, 8, 8, _testOutputHelper);
                queueEntryWriter.Stop();
                queueEntryWriter.Stop(); // Stopping twice shouldn't change things

                // These messages aren't logged
                ExampleHelper.LogTestMessagesInParallel(queueEntryWriter, 8, 8, _testOutputHelper);

                queueEntryWriter.Start();
                queueEntryWriter.Start(); // Starting twice shouldn't change things
                ExampleHelper.LogTestMessagesInParallel(queueEntryWriter, 8, 8, _testOutputHelper);
            }

            Assert.False(backgroundMultiLogWriter.IsBackgroundThreadRunning);
            Assert.Equal(2 * 8 * 8, innerLogWriter.Count);
        }
        public void CanRestartBackgroundMultiLogWriter()
        {
            var innerLogWriter = new TestLogWriter <MessageEntry>(SetupLog, false);
            BackgroundMultiLogWriter         backgroundMultiLogWriter;
            IQueueEntryWriter <MessageEntry> queueEntryWriter;

            SetupBackgroundLogWriter(innerLogWriter, out backgroundMultiLogWriter, out queueEntryWriter);

            // Re-starting shouldn't hurt anything
            Assert.True(backgroundMultiLogWriter.IsStarted);
            backgroundMultiLogWriter.Start();
            queueEntryWriter.Start();

            // Log some, then Stop
            ExampleHelper.LogTestMessagesInParallel(queueEntryWriter, 8, 8, _testOutputHelper);
            backgroundMultiLogWriter.Stop(); // Blocks until the background thread exits

            Assert.False(innerLogWriter.IsStarted);
            Assert.False(backgroundMultiLogWriter.IsStarted);
            Assert.Equal(8 * 8, innerLogWriter.Count);

            // After a Stop(), logging does nothing
            var msg = new MessageEntry("Logged while stopped - never logged.");

            queueEntryWriter.Write(ref msg);

            // After a Stop(), BackgroundMultiLogWriter and it's contained logwriters can be restarted
            backgroundMultiLogWriter.Start();

            // Log some, then Dispose
            ExampleHelper.LogTestMessagesInParallel(queueEntryWriter, 8, 8, _testOutputHelper);
            backgroundMultiLogWriter.Dispose(); // Blocks until the background thread exits

            Assert.False(innerLogWriter.IsStarted);
            Assert.False(backgroundMultiLogWriter.IsStarted);
            Assert.Equal(2 * 8 * 8, innerLogWriter.Count);

            // After a Dispose(), BackgroundMultiLogWriter can't be re-used
            Assert.Throws <ObjectDisposedException>(() => backgroundMultiLogWriter.Start());
            // Entries can still be written, though
            queueEntryWriter.Write(ref msg);
        }
        public void BackgroundLoggingMakesSlowLoggerActFast()
        {
            // Hi-res timer
            var stopwatch = new Stopwatch();

            // Slow log writer - starting, stopping, disposing, writing an entry, all take at least 10ms each.
            const int operationDelayMs  = 30;
            const int parallelThreads   = 8;
            const int messagesPerThread = 6;
            var       slowLogWriter     = new SlowTestLogWriter <MessageEntry>(SetupLog, operationDelayMs, false);

            BackgroundMultiLogWriter         backgroundMultiLogWriter;
            IQueueEntryWriter <MessageEntry> queueEntryWriter;

            stopwatch.Start();
            SetupBackgroundLogWriter(slowLogWriter, out backgroundMultiLogWriter, out queueEntryWriter);
            stopwatch.Stop();
            Assert.True((stopwatch.ElapsedMilliseconds <= operationDelayMs) || _inDebugger,
                        "Starting should be fast, slowLogWriter start delay should occur on background thread.  Elapsed: " + stopwatch.ElapsedMilliseconds);
            _testOutputHelper.WriteLine("Created + started BackgroundMultiLogWriter in {0}", stopwatch.Elapsed);

            using (backgroundMultiLogWriter)
            {
                stopwatch.Restart();
                ExampleHelper.LogTestMessagesInParallel(queueEntryWriter, messagesPerThread, parallelThreads, _testOutputHelper);
                stopwatch.Stop();
                Assert.True((stopwatch.ElapsedMilliseconds < operationDelayMs) || _inDebugger, "Log writing should be fast, until the queue is filled.");

                stopwatch.Restart();
            }
            stopwatch.Stop();
            _testOutputHelper.WriteLine("Stop + dispose took {0}", stopwatch.Elapsed);

            // At this point, everything should have been logged
            Assert.Equal(parallelThreads * messagesPerThread, slowLogWriter.Count);
            Assert.True(stopwatch.ElapsedMilliseconds > messagesPerThread * parallelThreads * operationDelayMs,
                        "Dispose is slow, b/c we wait for all writes to complete on the background thread");
        }
Example #5
0
        public void FinalizerCausesQueuedLogsToFlush()
        {
            var setupLog = new SetupLog();

            // Slow log writer - starting, stopping, disposing, writing an entry, all take at least 10ms each.
            const int opDelayMs              = 5;
            var       slowLogWriter          = new SlowTestLogWriter <MessageEntry>(setupLog, opDelayMs, false);
            const int countLoggingThreads    = 5;
            const int countMessagesPerThread = 5;
            const int expectedEntryCount     = countLoggingThreads * countMessagesPerThread;

            // Run the test code in a delegate so that local variable references can be GCed
            Action logTestMessages = () =>
            {
                var logManager = new LogManager(new LogManagerConfig(), setupLog);
                logManager.Config.UseLogWriter(slowLogWriter).BackgroundLogging = true;
                var entryWriter = logManager.GetEntryWriter <MessageEntry>();

                ExampleHelper.LogTestMessagesInParallel(entryWriter, countMessagesPerThread, countLoggingThreads, _testOutputHelper);

                // Key point: The LogManager is never disposed, and it has a number of queued
                // entries that haven't been written at this point.
            };

            logTestMessages();

            Assert.True(slowLogWriter.Count < expectedEntryCount);

            // Force a GC cyle, and wait for finalizers to complete.
            GC.Collect();
            GC.WaitForPendingFinalizers();

            Assert.Equal(expectedEntryCount, slowLogWriter.Count);

            // When the finalizer is called, an error is logged to the SetupLog.
            Assert.True(setupLog.Any(traceEntry => (traceEntry.TraceLevel == TraceLevel.Error) && (traceEntry.Message.StartsWith("In finalizer "))));
        }
        public void DisposeBlocksUntilBackgroundLoggingCompletes()
        {
            // Slow log writer - starting, stopping, disposing, writing an entry, all take at least 20ms each.
            const int operationDelayMs     = 5;
            const int parallelThreads      = 8;
            const int messagesPerThread    = 6;
            const int expectedMessageCount = parallelThreads * messagesPerThread;
            var       slowLogWriter        = new SlowTestLogWriter <MessageEntry>(SetupLog, operationDelayMs, false);

            BackgroundMultiLogWriter         backgroundMultiLogWriter;
            IQueueEntryWriter <MessageEntry> queueEntryWriter;

            SetupBackgroundLogWriter(slowLogWriter, out backgroundMultiLogWriter, out queueEntryWriter);

            using (backgroundMultiLogWriter)
            {
                ExampleHelper.LogTestMessagesInParallel(queueEntryWriter, messagesPerThread, parallelThreads, _testOutputHelper);
                Assert.True(backgroundMultiLogWriter.IsBackgroundThreadRunning);
                Assert.True(slowLogWriter.Count < expectedMessageCount);
                // Dispose waits for all queued logs to complete, and for the background thread to exit
            }
            Assert.Equal(expectedMessageCount, slowLogWriter.Count);
            Assert.False(backgroundMultiLogWriter.IsBackgroundThreadRunning);
        }