Skip to main content
Version: v1.2.7 (latest)

NDJSON Backend

The NDJSON backend writes delivery records as newline-delimited JSON to rolling files.

How It Works

Each delivery record is serialized as a single JSON line and appended to the current file:

{"id":"abc123","eventType":"order.placed","source":"https://orders.example.com","timestamp":"2026-06-14T10:30:12.451Z","publisherName":"default","channelType":"RabbitMqPublishChannel","attempt":1,"outcome":"Succeeded","elapsedTime":"00:00:00.1234567"}
{"id":"def456","eventType":"order.placed","source":"https://orders.example.com","timestamp":"2026-06-14T10:30:13.789Z","publisherName":"default","channelType":"RabbitMqPublishChannel","attempt":1,"outcome":"Failed","errorCode":"RabbitMQ.Client.Exceptions.BrokerUnreachableException","errorMessage":"None of the specified endpoints were reachable","elapsedTime":"00:00:05.0001234"}

File Management

File Naming

Files are named with a sortable, timestamp-based pattern:

delivery-log-20260614-103012.ndjson
delivery-log-20260614-103045.ndjson
delivery-log-20260614-104500.ndjson

Format: delivery-log-{yyyyMMdd-HHmmss}.ndjson

Auto-Roll

The backend rolls to a new file when either condition is met:

  1. Size-based roll — Current file exceeds MaxFileSizeBytes (default 10 MB)
  2. Time-based rollRollInterval has elapsed

Cleanup

After each write, the backend:

  1. Counts files in the directory
  2. Deletes the oldest files beyond MaxFileCount (default 30)
  3. Set MaxFileCount <= 0 to disable cleanup

Configuration

Basic Setup

builder.Services
.AddEventPublisher()
.AddDeliveryLog(log => log.UseNDJson(opts =>
{
opts.DirectoryPath = "/var/logs/delivery-logs";
opts.MaxFileSizeBytes = 10 * 1024 * 1024; // 10 MB
opts.RollInterval = TimeSpan.FromHours(6);
opts.MaxFileCount = 30;
}));

Options Reference

OptionTypeDefaultDescription
DirectoryPathstring%TEMP%/delivery-logsDirectory for NDJSON files
MaxFileSizeByteslong10 MBSize threshold for rolling
RollIntervalTimeSpan?nullTime threshold for rolling (null = disabled)
MaxFileCountint30Max files retained (≤0 = no cleanup)
opts.DirectoryPath = "/var/logs/hermodr/delivery-logs";
opts.MaxFileSizeBytes = 50 * 1024 * 1024; // 50 MB
opts.RollInterval = TimeSpan.FromHours(12);
opts.MaxFileCount = 60; // Keep ~30 days at 2 rolls/day

Performance Characteristics

Writes

  • Serialized through semaphores to prevent file corruption
  • Async I/O — non-blocking writes
  • Buffered — efficient sequential writes

Reads

  • Stream-based — files opened with FileShare.ReadWrite
  • Line-by-lineStreamReader.ReadLineAsync, never loads full file into memory
  • Concurrent — external readers can tail files while writer is active

Serialization

Records are serialized as camelCase JSON using a CloudEventJsonConverter that encodes the CloudEvent in structured mode:

{
"id": "abc123",
"eventType": "order.placed",
"source": "https://orders.example.com",
"timestamp": "2026-06-14T10:30:12.451Z",
"publisherName": "default",
"channelType": "RabbitMqPublishChannel",
"attempt": 1,
"outcome": "Succeeded",
"elapsedTime": "00:00:00.1234567",
"event": {
"specversion": "1.0",
"type": "order.placed",
"source": "https://orders.example.com",
"id": "evt-123",
"time": "2026-06-14T10:30:12.451Z",
"data": { ... }
}
}

Querying NDJSON Files

Use the NdJsonDeliveryLogReader for stream-based queries:

var reader = provider.GetRequiredService<NdJsonDeliveryLogReader>();

var query = new DeliveryLogStreamQuery
{
From = DateTimeOffset.UtcNow.AddHours(-1),
To = DateTimeOffset.UtcNow,
Outcome = EventDeliveryOutcome.Failed
};

await foreach (var record in reader.ReadAsync(query))
{
Console.WriteLine($"Failed: {record.Event.Type} - {record.ErrorMessage}");
}

Stream Query Options

OptionTypeDescription
FromDateTimeOffset?Filter records after this time
ToDateTimeOffset?Filter records before this time
EventTypestring?Filter by event type
OutcomeEventDeliveryOutcome?Filter by outcome
PublisherNamestring?Filter by publisher name
Limitint?Maximum records to return

Monitoring NDJSON Files

Tail Current File (Linux/macOS)

tail -f /var/logs/delivery-logs/delivery-log-*.ndjson

Count Records in File

wc -l /var/logs/delivery-logs/delivery-log-20260614-*.ndjson

Find Failures

grep '"outcome":"Failed"' /var/logs/delivery-logs/*.ndjson | tail -100

Parse with jq

cat /var/logs/delivery-logs/*.ndjson | \
jq 'select(.outcome == "Failed") | {eventType, errorCode, errorMessage}'