Skip to content

MessageHistory

Brian Lehnen edited this page Apr 9, 2026 · 4 revisions

Message History

Message history tracks the lifecycle of every message after it leaves the queue: when it was enqueued, when processing started, whether it succeeded or failed, the exception if any, and how long it took.

Once a message is committed or deleted, it's gone from the queue tables. History gives you an audit trail so you can answer "what happened to message X last Tuesday?"

Enabling history

History has two parts: the history table (created once) and runtime tracking (enabled per consumer).

Step 1: Create the queue with history enabled

Set EnableHistory = true on the transport options when creating the queue:

using (var creator = new QueueCreationContainer<SqLiteMessageQueueInit>())
{
    using (var creation = creator.GetQueueCreation<SqLiteMessageQueueCreation>(queueConnection))
    {
        creation.Options.EnableStatus = true;
        creation.Options.EnableStatusTable = true;
        creation.Options.EnableHistory = true;  // creates the history table
        var result = creation.CreateQueue();
    }
}

If the queue already exists without a history table, you'll need to recreate it or manually create the history table.

Step 2: Enable history on the consumer

Set Enabled = true on the consumer configuration before starting:

using (var consumer = queueContainer.CreateConsumer(queueConnection))
{
    consumer.Configuration.History.Enabled = true;
    consumer.Configuration.History.RetentionDays = 30;
    consumer.Configuration.History.StoreBody = false;
    consumer.Configuration.History.MaxExceptionLength = 4000;

    consumer.Start<MyMessage>((message, notification) =>
    {
        // process message
    }, notifications);
}

The consumer tracks: processing start, complete, error, rollback, delete, and expire events.

Note: The producer-side enqueue tracking also requires IHistoryConfiguration.Enabled = true in the producer's container. If you only enable it on the consumer, enqueue events won't be recorded (but processing/complete/error events will be).

What gets tracked

Each message gets a single history record that's updated as it moves through lifecycle states:

Status When recorded
Enqueued Message sent by producer
Processing Consumer dequeues the message
Complete Handler finishes and message is committed
Error Handler throws and message moves to error status
Deleted Message is deleted
Expired Message expires before being processed

Rollbacks increment a retry counter and reset the status back to Enqueued.

History record fields
Field Description
QueueId The message's queue identifier
CorrelationId Cross-system correlation ID from headers
Status Current lifecycle status
EnqueuedUtc When the message was added to the queue
StartedUtc When processing began
CompletedUtc When processing finished (success, error, or delete)
DurationMs Processing time in milliseconds
ExceptionText Truncated exception for error status
RetryCount Number of retries before final status
Route Message route, if routing is enabled
MessageType Body type name from header
Body Serialized body bytes (only when StoreBody = true)
Headers Serialized header bytes (only when StoreBody = true)
Body storage

By default, history does not store the message body. It's an audit trail, not a backup. Set StoreBody = true to store the serialized body and headers with each history record. This lets the dashboard display the body for completed messages, but increases storage on high-throughput queues.

Error messages already persist bodies in the error table, so the most common debugging scenario is covered without body storage.

Retention and purge

A ClearHistoryMonitor runs periodically (default: once per day) and deletes records older than RetentionDays (default: 30). It works the same way as the error message cleanup monitor, running as part of the maintenance monitors with both MaintenanceMode.Consumer and MaintenanceMode.External.

Configuration reference
Setting Default Description
Enabled false Enable history tracking
RetentionDays 30 Days to keep history before purging
MaxExceptionLength 4000 Truncate exception text to this length
StoreBody false Store serialized message body and headers
MonitorTime 1 day How often the purge monitor runs
TrackEnqueue true Record when messages are enqueued
TrackProcessing true Record when processing starts
TrackComplete true Record when processing succeeds
TrackError true Record when processing fails
TrackDelete true Record when messages are deleted
TrackExpire true Record when messages expire
Dashboard

The Dashboard API exposes history data via these endpoints:

  • GET /api/v1/dashboard/queues/{queueId}/history — paged list with optional status filter
  • GET /api/v1/dashboard/queues/{queueId}/history/{messageId} — single message history
  • GET /api/v1/dashboard/queues/{queueId}/history/count — count with optional filter
  • DELETE /api/v1/dashboard/queues/{queueId}/history — purge by age

The Dashboard UI has a History tab on the queue detail page with status filtering, color-coded status chips, duration formatting, expandable exception text, and a purge button.

Transport support

History is implemented for all 6 transports: SQL Server, PostgreSQL, SQLite, LiteDB, Redis, and Memory.

Clone this wiki locally