How We Solved a Tough Logging Bug in Site Informant – Serilog, SQLite, and EF Core Concurrency

Published December 2025 by Site Informant Team

Every once in a while, a deceptively small issue turns into a deep technical adventure. This week, our team ran into a thorny logging failure that involved Serilog’s Durable HTTP Sink, SQLite WAL mode, JSON payload formatting, and a surprisingly tricky EF Core DbContext concurrency problem triggered by background processing.

The short version? Our logs stopped flowing — silently — and the root cause was a perfect storm of JSON formatting, SQLite locking behavior, and concurrent background tasks.

What We Saw First

The Site Informant logging dashboard showed only two rows: a manual test message and an early boot log. Everything else was missing.

But the Serilog durable buffer was filling up — and the serilog-selflog.txt file revealed a repeating error:

BadRequest: The logs field is required.
Invalid JSON payload.

The durable sink was sending batches, but our API rejected them. At first glance, the JSON looked correct… but the issue ran deeper.

Issue #1 – JSON Not Matching the Expected Model

Serilog's durable sink sends logs as newline-delimited JSON, not a JSON array. Our API expected:

[
  { "Timestamp": "...", "Level": "...", ... }
]

But the buffer contained:


{"Timestamp":"..."}
{"Timestamp":"..."}
{"Timestamp":"..."}

Once we added a custom ArrayBatchFormatter to wrap each batch in [ ... ], the API was finally willing to accept the payload.

Issue #2 – SQLite WAL Mode + Heavy Write Workloads

Our logger uses SQLite with WAL mode for concurrency. But WAL mode must be explicitly enabled on the deployed file — not just in development.

Once the production VM was updated with:

PRAGMA journal_mode = WAL;

The logger API was finally able to handle high write volume without locking the database.

Issue #3 – EF Core Concurrency in Background Services

Even after fixing logging, some messages in the buffer pointed to another problem:

A second operation was started on this context instance before a previous operation completed.

The culprit? Our SiteStatusProcessorService was performing up to 10 parallel checks, but each instance of SiteStatusDomainService shared a single injected DbContext.

This caused overlapping writes on the same context instance — something EF Core does not allow.

The fix was simple and elegant:

With that, all concurrency-related failures disappeared.

Lessons Learned

In the end, we not only fixed the logging system — we strengthened the entire monitoring pipeline. The result is faster, more resilient, and far easier to debug going forward.

What’s Next

Now that durable logging is fully operational, we’re rolling out:

Monitor your sites for free: Try Site Informant