Course contentsShow
← C#
Lesson 1693 of 1,90815. Software Quality and TestingFree lesson

Log Aggregation in Test Runs

Consolidate and search logs from multiple test threads for easier debugging.

In this lesson, you will learn how to unify fragmented logs from concurrent test threads into a single, searchable stream to accelerate debugging.

The Problem of Concurrent Logging

When running tests in parallel, traditional console logging creates a chaotic "interleaved" output. If Thread A and Thread B are both writing to the standard output buffer, the resulting log file becomes an unreadable jumble of lines. Trying to reconstruct the sequence of events for a single failing test becomes a nightmare, especially when tests share dependencies or touch the same state.

Consolidating with a Thread-Safe Sink

To solve this, we must decouple the test execution thread from the log output. Instead of writing directly to Console.Out, we use a thread-safe "Collector" that captures logs in memory, tagged with the TestID or ThreadID.

Consider this simplified approach using a ConcurrentDictionary:

public class TestLogAggregator
{
    private static readonly ConcurrentDictionary<string, List<string>> _logs = new();

    public static void Log(string testId, string message)
    {
        _logs.AddOrUpdate(testId, 
            new List<string> { $"[{DateTime.Now:HH:mm:ss}] {message}" },
            (key, list) => { lock(list) { list.Add(message); return list; } });
    }

    public static IEnumerable<string> GetLogsForTest(string testId) 
        => _logs.GetValueOrDefault(testId) ?? Enumerable.Empty<string>();
}

By injecting this aggregator into your test classes, every thread writes its diagnostic data into a designated "bucket" keyed by the test context. Once the test completes—or fails—you can dump the entire contents of that specific bucket to the CI output gate.

Why This Matters

By segregating logs by test identity rather than execution time, you transform a disordered stream into a structured report. When a test fails in a multi-threaded environment, you no longer have to hunt through unrelated noise; you simply pull the isolated log history for that specific TestID. This approach turns the "needle in a haystack" problem into a clean, searchable diagnostic record that aligns perfectly with your CI/CD reporting requirements.

Key Takeaway: Never log directly to standard output during parallel testing; instead, use a thread-aware aggregator to maintain a clean, isolated log history for every individual test case.